/* texmap.c - texture mapping on the GXT120P (Matrox MGA-1164SG) from user space. * Claude Opus 5.5, 2026-10-14. Builds on mga.c (RCM access) from ../mystique and on * Opus 4.5's mgatex.c. Register semantics: MGA-G100 specification (1998), whose * TEXCTL/TEXWIDTH/TMR pages fill the gap left in the public MGA-1064SG one. * usage: texmap MODE out.raw (out.raw = 640x480 16-bit little-endian pixels) */ #include #include #include #include #include "mga.h" /* second drawing register block (write-only) */ #define TMR0 0x2c00 #define TMR1 0x2c04 #define TMR2 0x2c08 #define TMR3 0x2c0c #define TMR4 0x2c10 #define TMR5 0x2c14 #define TMR6 0x2c18 #define TMR7 0x2c1c #define TMR8 0x2c20 #define TEXORG 0x2c24 #define TEXWIDTH 0x2c28 #define TEXHEIGHT 0x2c2c #define TEXCTL 0x2c30 #define TEXTRANS 0x2c34 #define DR2_ 0x1cc8 #define OP_TEXTRAP 0x00000006 #define NODITHER 0x40000000 /* MACCESS */ #define TW16 3 /* TEXCTL texformat */ #define TC_NPCEN (1 << 21) #define TC_TAKEY (1 << 25) #define TC_TAMASK (1 << 26) #define TC_CLAMPV (1 << 27) #define TC_CLAMPU (1 << 28) #define TC_TMOD (1 << 29) #define W 640 #define H 480 #define ZORG_OFF (W * 2 * H) /* Z right after the 640x480x16 frame */ #define TEX_OFF (2 * W * 2 * H) /* textures after Z: 0x12c000 .. 0x200000 */ typedef struct { double x, y, z, r, g, b, s, t, q; } vtx; /* s,t,q already divided by w */ static unsigned int maccess = PW16; static unsigned int texctl; /* format, pitch, flags of the bound texture */ static int nodraw = 0, persp = 0; static int tlog2 = 6; /* log2 of the bound texture's size */ static double qscale = 0; /* >0: normalise q per triangle (perspective) */ static unsigned short rgb(int r, int g, int b) { return ((r >> 3) << 11) | ((g >> 2) << 5) | (b >> 3); } static int fx15(double v) { return (int) floor(v * 32768.0 + 0.5); } /* 9.15 / 16.15 */ static int fx20(double v) { return (int) floor(v * 1048576.0 + 0.5); } /* 12.20 */ static int fx16(double v) { return (int) floor(v * 65536.0 + 0.5); } /* 16.16 */ static void setup(void) { mga_fifo(9); mga_out(MACCESS, maccess); mga_out(PITCH, W); mga_out(YDSTORG, 0); mga_out(ZORG, ZORG_OFF); mga_out(PLNWT, 0xffffffff); mga_out(CXLEFT, 0); mga_out(CXRIGHT, W - 1); mga_out(YTOP, 0); mga_out(YBOT, (VRAM_SIZE / (W * 2) - 1) * W); } static void ybot(int lines) /* clip: 3D must not draw below row 479, or its */ { /* Z writes land in the textures */ mga_fifo(1); mga_out(YBOT, (lines - 1) * W); } static void fill(int x, int y, int w, int h, unsigned int col) { mga_fifo(5); mga_out(DWGCTL, OP_TRAP | AT_RPL | SOLID | ARZERO | SGNZERO | SHFTZERO | BOP_COPY); mga_out(FCOL, col); mga_out(FXBNDRY, ((x + w) << 16) | x); mga_out(YDSTLEN + GO, (y << 16) | h); } /* size x size TW16 texture at byte offset off; le = store little-endian like the * framebuffer (the card does not swap the framebuffer aperture) */ static void upload(const unsigned short *tex, int size, int off, int le) { int i, n = size * size; volatile unsigned short *d = (volatile unsigned short *)(mga_fb + off); for (i = 0; i < n; i++) d[i] = le ? (unsigned short)((tex[i] >> 8) | (tex[i] << 8)) : tex[i]; } static void bind(int off, int size, unsigned int flags) { int l = 0, rf; while ((1 << l) < size) l++; tlog2 = l; rf = (getenv("RFW") ? atoi(getenv("RFW")) : 8 - l) & 63; texctl = TW16 | ((l - 3) << 16) | flags; /* tpitch: pitch = 8 << tpitch */ mga_fifo(5); mga_out(TEXORG, off); mga_out(TEXWIDTH, l | (rf << 9) | ((size - 1) << 18)); mga_out(TEXHEIGHT, l | (rf << 9) | ((size - 1) << 18)); mga_out(TEXCTL, texctl); mga_out(TEXTRANS, 0x0000ffff); /* tkmask 0, tckey FFFF: no colour key */ } /* XAA-style edge: x and Bresenham error at line y (as in mgatest.c) */ static void edge_at(int x0, int y0, int x1, int y1, int y, int *x, int *e) { int dx = x1 - x0, dy = y1 - y0, adx, k, step, sign, ex, xx; if (dx >= 0) { sign = 1; adx = dx; ex = 0; } else { sign = -1; adx = -dx; ex = 1 - dy; } step = (adx / dy) * sign; adx %= dy; xx = x0; for (k = y0; k < y; k++) { xx += step; if ((ex += adx) > 0) { xx += sign; ex -= dy; } } *x = xx; *e = ex; } /* parameters: 0 z, 1 r, 2 g, 3 b, 4 s, 5 t, 6 q */ #define NP 7 static FILE *plog; /* optional: every trapezoid's register values */ static void trap(unsigned int cmd, int ya, int yb, int lx0, int ly0, int lx1, int ly1, int rx0, int ry0, int rx1, int ry1, const double *p0, const double *dx, const double *dy, int px, int py) { int xl, el, xr, er, dxl = lx1 - lx0, dxr = rx1 - rx0, sdxl, sdxr, ar2, ar5, i; int tm[9]; double v[NP]; if (yb <= ya) return; edge_at(lx0, ly0, lx1, ly1, ya, &xl, &el); edge_at(rx0, ry0, rx1, ry1, ya, &xr, &er); if (nodraw) return; sdxl = dxl < 0; ar2 = sdxl ? dxl : -dxl; sdxr = dxr < 0; ar5 = sdxr ? dxr : -dxr; for (i = 0; i < NP; i++) v[i] = p0[i] + (xl - px) * dx[i] + (ya - py) * dy[i]; tm[0] = fx20(dx[4]); tm[1] = fx20(dy[4]); tm[2] = fx20(dx[5]); tm[3] = fx20(dy[5]); tm[4] = fx16(dx[6]); tm[5] = fx16(dy[6]); tm[6] = fx20(v[4]); tm[7] = fx20(v[5]); tm[8] = fx16(v[6]); if (plog) fprintf(plog, "trap %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d %d\n", ya, yb, xl, xr, tm[0], tm[1], tm[2], tm[3], tm[4], tm[5], tm[6], tm[7], tm[8], lx1 - lx0, ly1 - ly0, el); mga_fifo(32); mga_out(DWGCTL, cmd); mga_out(DR0, fx15(v[0])); mga_out(DR2, fx15(dx[0])); mga_out(DR3, fx15(dy[0])); mga_out(DR4, fx15(v[1])); mga_out(DR6, fx15(dx[1])); mga_out(DR7, fx15(dy[1])); mga_out(DR8, fx15(v[2])); mga_out(DR10, fx15(dx[2])); mga_out(DR11, fx15(dy[2])); mga_out(DR12, fx15(v[3])); mga_out(DR14, fx15(dx[3])); mga_out(DR15, fx15(dy[3])); for (i = 0; i < 9; i++) mga_out(TMR0 + 4 * i, tm[i]); mga_out(AR0, ly1 - ly0); mga_out(AR1, ar2 - el); mga_out(AR2, ar2); mga_out(AR4, ar5 - er); mga_out(AR5, ar5); mga_out(AR6, ry1 - ry0); mga_out(SGN, (sdxl << 1) | (sdxr << 5)); mga_out(FXBNDRY, ((xr & 0xffff) << 16) | (xl & 0xffff)); mga_out(YDSTLEN + GO, (ya << 16) | (yb - ya)); } /* vertices must have integer x, y; spans are [xl, xr) */ static void tri(vtx a, vtx b, vtx c, unsigned int cmd) { vtx t; double pa[NP], pb[NP], pc[NP], dx[NP], dy[NP], det; int i, longleft, ax, ay, bx, by, cx, cy; if (a.y > b.y) { t = a; a = b; b = t; } if (b.y > c.y) { t = b; b = c; c = t; } if (a.y > b.y) { t = a; a = b; b = t; } ax = (int) a.x; ay = (int) a.y; bx = (int) b.x; by = (int) b.y; cx = (int) c.x; cy = (int) c.y; if (ay == cy) return; det = (double)(bx - ax) * (cy - ay) - (double)(cx - ax) * (by - ay); if (det == 0) return; #define P(v, p) p[0] = v.z; p[1] = v.r; p[2] = v.g; p[3] = v.b; p[4] = v.s; p[5] = v.t; p[6] = v.q P(a, pa); P(b, pb); P(c, pc); if (qscale > 0) { /* projective: scale s, t, q so the largest q is qscale */ double m = pa[6] > pb[6] ? pa[6] : pb[6], k; if (pc[6] > m) m = pc[6]; k = qscale / m; for (i = 4; i < 7; i++) { pa[i] *= k; pb[i] *= k; pc[i] *= k; } } for (i = 0; i < NP; i++) { double e1 = pb[i] - pa[i], e2 = pc[i] - pa[i]; dx[i] = (e1 * (cy - ay) - e2 * (by - ay)) / det; dy[i] = (e2 * (bx - ax) - e1 * (cx - ax)) / det; } longleft = det > 0; if (longleft) { trap(cmd, ay, by, ax, ay, cx, cy, ax, ay, bx, by, pa, dx, dy, ax, ay); trap(cmd, by, cy, ax, ay, cx, cy, bx, by, cx, cy, pa, dx, dy, ax, ay); } else { trap(cmd, ay, by, ax, ay, bx, by, ax, ay, cx, cy, pa, dx, dy, ax, ay); trap(cmd, by, cy, bx, by, cx, cy, ax, ay, cx, cy, pa, dx, dy, ax, ay); } } static vtx V(double x, double y, double s, double t) { vtx v; v.x = x; v.y = y; v.z = 0; v.r = 200; v.g = 100; v.b = 50; v.s = s; v.t = t; v.q = 1; return v; } /* projected vertex: camera-space (X, Y, Z), texture (s, t) in 0..1 */ static vtx PV(double X, double Y, double Z, double s, double t, double shade) { vtx v; double w = 1 / Z; v.x = floor(320 + 400 * X * w + 0.5); v.y = floor(240 - 400 * Y * w + 0.5); v.z = 65000 * (1 - w); /* linear in screen space, like s/w */ v.r = v.g = v.b = shade; if (persp) { v.s = s * w; v.t = t * w; v.q = w; } else { v.s = s; v.t = t; v.q = 1; } return v; } static void quad4(vtx a, vtx b, vtx c, vtx d, unsigned int cmd) { tri(a, b, c, cmd); tri(a, c, d, cmd); } /* textures */ static unsigned short idtex[64 * 64], chk[64 * 64], big[256 * 256]; static void make_textures(void) { int x, y; for (y = 0; y < 64; y++) for (x = 0; x < 64; x++) { idtex[y * 64 + x] = (unsigned short)(0x8000 | (y << 6) | x); /* texel names itself */ chk[y * 64 + x] = ((x >> 3) + (y >> 3)) & 1 ? rgb(230, 40, 30) : rgb(250, 220, 60); } for (y = 0; y < 256; y++) for (x = 0; x < 256; x++) { /* a brick wall: mortar lines, offset rows, a little per-brick tint */ int row = y >> 5, bx = (x + (row & 1) * 32) & 255, by = y & 31, n = ((bx >> 6) * 7 + row * 13) % 5; int mortar = by < 3 || (bx & 63) < 3; int noise = ((x * 37 + y * 91 + x * y) >> 2) & 15; big[y * 256 + x] = mortar ? rgb(170 + noise, 165 + noise, 150 + noise) : rgb(150 + n * 15 + noise, 50 + n * 6 + noise / 2, 35 + noise / 2); } } static void save(const char *fn) { unsigned char *buf = malloc(W * H * 2); FILE *f; int i; mga_idle(); for (i = 0; i < W * H * 2; i += 4) *(unsigned int *)(buf + i) = *(volatile unsigned int *)(mga_fb + i); f = fopen(fn, "wb"); fwrite(buf, 1, W * H * 2, f); fclose(f); free(buf); } static double now(void) { return mga_now(); } static int cmpd(const void *a, const void *b) { double x = *(const double *) a, y = *(const double *) b; return x < y ? -1 : x > y; } /* ---------------------------------------------------------------- demo: a textured cube */ static void cube(double ang, unsigned int cmd) { static int f[6][4] = { {0,1,3,2}, {5,4,6,7}, {4,0,2,6}, {1,5,7,3}, {4,5,1,0}, {2,3,7,6} }; double P[8][3], ca = cos(ang), sa = sin(ang), cb = cos(ang * 0.7 + 0.5), sb = sin(ang * 0.7 + 0.5); double L[3] = { -0.45, 0.55, -0.70 }, ll = sqrt(0.45 * 0.45 + 0.55 * 0.55 + 0.70 * 0.70); int i, j, k; for (i = 0; i < 8; i++) { double x = i & 1 ? 1 : -1, y = i & 2 ? 1 : -1, z = i & 4 ? 1 : -1, t; t = x * ca + z * sa; z = -x * sa + z * ca; x = t; /* about y */ t = y * cb - z * sb; z = y * sb + z * cb; y = t; /* about x */ P[i][0] = x; P[i][1] = y + 0.2; P[i][2] = z + 4.6; } for (k = 0; k < 6; k++) { double c[3] = { 0, 0, 0 }, n[3], dif, sh; for (j = 0; j < 4; j++) for (i = 0; i < 3; i++) c[i] += P[f[k][j]][i] / 4; n[0] = c[0]; n[1] = c[1] - 0.2; n[2] = c[2] - 4.6; /* face centre - cube centre */ if (n[0] * c[0] + n[1] * c[1] + n[2] * c[2] >= 0) continue; /* faces away */ dif = (n[0] * L[0] + n[1] * L[1] + n[2] * L[2]) / ll; sh = 255 * (0.30 + 0.70 * (dif < 0 ? 0 : dif)); quad4(PV(P[f[k][0]][0], P[f[k][0]][1], P[f[k][0]][2], 0, 0, sh), PV(P[f[k][1]][0], P[f[k][1]][1], P[f[k][1]][2], 1, 0, sh), PV(P[f[k][2]][0], P[f[k][2]][1], P[f[k][2]][2], 1, 1, sh), PV(P[f[k][3]][0], P[f[k][3]][1], P[f[k][3]][2], 0, 1, sh), cmd); } } /* the torus from mgatest.c (32 x 16 quads), brick-textured and lit; returns triangles */ #define TU 32 #define TV 16 static int torus(int f, int nfr, unsigned int cmd) { static vtx sv[TU + 1][TV + 1]; static double nz[TU + 1][TV + 1]; double PI = 3.14159265358979; double ay = 2 * PI * f / nfr, ax = 1.05 + 0.35 * sin(2 * PI * 2 * f / nfr); double cy = cos(ay), sy = sin(ay), cx = cos(ax), sx = sin(ax); double L[3], len; int u, v, n = 0; L[0] = -0.5; L[1] = 0.6; L[2] = -0.65; len = sqrt(L[0]*L[0] + L[1]*L[1] + L[2]*L[2]); L[0] /= len; L[1] /= len; L[2] /= len; for (u = 0; u <= TU; u++) for (v = 0; v <= TV; v++) { /* seam vertices repeated for s,t */ double a = 2 * PI * u / TU, b = 2 * PI * v / TV; double p[3], m[3], t, dif, z, w; vtx *q = &sv[u][v]; p[0] = (1 + 0.42 * cos(b)) * cos(a); p[1] = 0.42 * sin(b); p[2] = (1 + 0.42 * cos(b)) * sin(a); m[0] = cos(b) * cos(a); m[1] = sin(b); m[2] = cos(b) * sin(a); t = p[1] * cx - p[2] * sx; p[2] = p[1] * sx + p[2] * cx; p[1] = t; t = m[1] * cx - m[2] * sx; m[2] = m[1] * sx + m[2] * cx; m[1] = t; t = p[0] * cy + p[2] * sy; p[2] = -p[0] * sy + p[2] * cy; p[0] = t; t = m[0] * cy + m[2] * sy; m[2] = -m[0] * sy + m[2] * cy; m[0] = t; z = p[2] + 3.6; w = 1 / z; dif = m[0]*L[0] + m[1]*L[1] + m[2]*L[2]; if (dif < 0) dif = 0; q->x = floor(320 + p[0] * 520 * w + 0.5); q->y = floor(240 - p[1] * 520 * w + 0.5); q->z = 65000 * (1 - w); q->r = q->g = q->b = 255 * (0.22 + 0.78 * dif); q->s = 6.0 * u / TU * w; q->t = 2.0 * v / TV * w; q->q = w; nz[u][v] = m[2]; } for (u = 0; u < TU; u++) for (v = 0; v < TV; v++) { if (nz[u][v] > 0.2 && nz[u+1][v] > 0.2 && nz[u+1][v+1] > 0.2 && nz[u][v+1] > 0.2) continue; tri(sv[u][v], sv[u+1][v], sv[u+1][v+1], cmd); tri(sv[u][v], sv[u+1][v+1], sv[u][v+1], cmd); n += 2; } return n; } static void floor_plane(unsigned int cmd) { /* a receding floor, 8x8 repeats of the texture, split into 8 strips so no one * triangle spans a huge range of w */ int i; double z0, z1; for (i = 0; i < 8; i++) { z0 = 1.2 + i * 1.6; z1 = z0 + 1.6; quad4(PV(-6, -1.4, z1, 0, (i + 1) * 1.0, 255), PV(6, -1.4, z1, 8, (i + 1) * 1.0, 255), PV(6, -1.4, z0, 8, i * 1.0, 255), PV(-6, -1.4, z0, 0, i * 1.0, 255), cmd); } } int main(int argc, char **argv) { char *mode = argc > 1 ? argv[1] : ""; char *out = argc > 2 ? argv[2] : NULL; unsigned int base = OP_TEXTRAP | SHFTZERO | BOP_COPY; int texchk = TEX_OFF, texid = TEX_OFF + 8192, texbig = TEX_OFF + 16384; if (argc < 2) { fprintf(stderr, "usage: texmap key|affine|native|dither|persp|modulate|rate|demo [out]\n"); return 2; } make_textures(); qscale = getenv("QSCALE") ? atof(getenv("QSCALE")) : 1.0; if (mga_open()) return 1; if (!strcmp(mode, "dither")) maccess = PW16; /* default: dithering on */ else maccess = PW16 | NODITHER; setup(); fill(0, 0, W, H, 0x20e420e4); /* dark blue background */ mga_idle(); upload(chk, 64, texchk, 1); upload(idtex, 64, texid, !!strcmp(mode, "native")); upload(big, 256, texbig, 1); if (!strcmp(mode, "key")) { /* Opus 4.5's TEXCTL flags (takey = tamask = 0) with flat I = (200,100,50) */ bind(texchk, 64, TC_NPCEN | TC_CLAMPU | TC_CLAMPV); mga_fifo(1); mga_out(TEXCTL, texctl & ~(TC_TAKEY | TC_TAMASK)); quad4(V(40, 40, 0, 0), V(296, 40, 1, 0), V(296, 296, 1, 1), V(40, 296, 0, 1), base | AT_I); /* the same with takey = 1 */ bind(texchk, 64, TC_NPCEN | TC_TAKEY | TC_CLAMPU | TC_CLAMPV); quad4(V(340, 40, 0, 0), V(596, 40, 1, 0), V(596, 296, 1, 1), V(340, 296, 0, 1), base | AT_I); } if (!strcmp(mode, "affine") || !strcmp(mode, "native") || !strcmp(mode, "dither")) { plog = fopen("affine.log", "w"); bind(texid, 64, TC_NPCEN | TC_TAKEY); /* axis-aligned: 4 pixels per texel */ quad4(V(16, 16, 0, 0), V(272, 16, 1, 0), V(272, 272, 1, 1), V(16, 272, 0, 1), base | AT_I); /* rotated, scaled and repeated twice */ quad4(V(450, 20, 0, 0), V(620, 200, 2, 0), V(440, 400, 2, 2), V(290, 210, 0, 2), base | AT_I); fclose(plog); plog = NULL; } ybot(H); if (!strcmp(mode, "persp")) { /* same receding floor drawn perspective-correct (top) and affine (bottom) */ plog = fopen("persp.log", "w"); bind(texid, 64, TC_TAKEY); persp = 1; quad4(PV(-2, -1, 6, 0, 1, 255), PV(2, -1, 6, 1, 1, 255), PV(2, -1, 1.5, 1, 0, 255), PV(-2, -1, 1.5, 0, 0, 255), base | AT_I); fclose(plog); plog = NULL; bind(texchk, 64, TC_TAKEY); quad4(PV(-3.5, 1, 6, 0, 1, 255), PV(-0.5, 1, 6, 1, 1, 255), PV(-0.5, 1, 1.5, 1, 0, 255), PV(-3.5, 1, 1.5, 0, 0, 255), base | AT_I); persp = 0; bind(texchk, 64, TC_NPCEN | TC_TAKEY); quad4(PV(0.5, 1, 6, 0, 1, 255), PV(3.5, 1, 6, 1, 1, 255), PV(3.5, 1, 1.5, 1, 0, 255), PV(0.5, 1, 1.5, 0, 0, 255), base | AT_I); } if (!strcmp(mode, "modulate")) { vtx a, b, c, d; bind(texid, 64, TC_NPCEN | TC_TAKEY | TC_TMOD); a = V(16, 16, 0, 0); b = V(272, 16, 1, 0); c = V(272, 272, 1, 1); d = V(16, 272, 0, 1); a.r = b.r = c.r = d.r = a.g = b.g = c.g = d.g = a.b = b.b = c.b = d.b = 255; quad4(a, b, c, d, base | AT_I); bind(texbig, 256, TC_NPCEN | TC_TAKEY | TC_TMOD); a = V(320, 40, 0, 0); b = V(620, 40, 1, 0); c = V(620, 440, 1, 1); d = V(320, 440, 0, 1); a.r = 255; a.g = 255; a.b = 255; b.r = 255; b.g = 160; b.b = 60; c.r = 30; c.g = 30; c.b = 60; d.r = 90; d.g = 120; d.b = 255; quad4(a, b, c, d, base | AT_I); } if (!strcmp(mode, "rate")) { /* full-screen 640x480 quads: flat fill, affine, perspective; 64 and 256 textures */ static char *name[] = { "flat 16bpp TRAP", "Gouraud TRAP (I)", "texture 64 affine", "texture 64 persp", "texture 256 affine", "texture 256 persp", "texture 256 persp+modulate+Z" }; static int pm[7] = { 0, 0, 0, 1, 0, 1, 1 }; int m, i, n = 20; double t, tt[20]; for (m = 0; m < 7; m++) { for (i = 0; i < n; i++) { vtx a = V(0, 0, 0, 0), b = V(640, 0, 1, 0), c = V(640, 480, 1, 1), d = V(0, 480, 0, 1); unsigned int cmd = base | AT_I; if (m >= 2) bind(m < 4 ? texchk : texbig, m < 4 ? 64 : 256, TC_TAKEY | (pm[m] ? 0 : TC_NPCEN) | (m == 6 ? TC_TMOD : 0)); if (pm[m]) { a.q = b.q = 1; c.q = d.q = 0.5; c.s *= 0.5; c.t *= 0.5; d.t *= 0.5; } if (m == 6) { cmd = base | AT_ZI | Z_ZLTE; ybot(2 * H); fill(0, 480, 640, 480, 0xffffffff); ybot(H); } mga_idle(); t = now(); if (m == 0) fill(0, 0, 640, 480, 0x18c318c3); else if (m == 1) quad4(a, b, c, d, OP_TRAP | SHFTZERO | BOP_COPY | AT_I); else quad4(a, b, c, d, cmd); mga_idle(); tt[i] = now() - t; } qsort(tt, n, sizeof(double), cmpd); printf("%-30s min %6.2f ms median %6.2f ms -> %6.1f Mpixel/s\n", name[m], tt[0] * 1e3, tt[n / 2] * 1e3, 640 * 480 / tt[n / 2] / 1e6); } } if (!strcmp(mode, "torus")) { /* the Opus 5.5 torus, now textured: same timing method as mgatest's demo */ int nfr = argc > 3 ? atoi(argv[3]) : 72, fr, i, ntri = 0; double *tf = malloc(nfr * sizeof(double)), *tc = malloc(nfr * sizeof(double)), t; unsigned char *buf = malloc(W * H * 2); FILE *o = out ? fopen(out, "wb") : NULL; unsigned int cmd = base | AT_ZI | Z_ZLT; for (fr = 0; fr < nfr; fr++) { t = now(); ybot(2 * H); fill(0, 0, W, H, 0x20e420e4); fill(0, 480, W, H, 0xffffffff); ybot(H); bind(texbig, 256, TC_TAKEY | TC_TMOD); ntri += torus(fr, nfr, cmd); mga_idle(); tf[fr] = now() - t; if (o) { for (i = 0; i < W * H * 2; i += 4) *(unsigned int *)(buf + i) = *(volatile unsigned int *)(mga_fb + i); fwrite(buf, 1, W * H * 2, o); } } nodraw = 1; for (fr = 0; fr < nfr; fr++) { t = now(); torus(fr, nfr, cmd); tc[fr] = now() - t; } nodraw = 0; if (o) fclose(o); qsort(tf, nfr, sizeof(double), cmpd); qsort(tc, nfr, sizeof(double), cmpd); printf("%d frames, %d triangles/frame: draw+wait min %.1f ms median %.1f ms (%.1f fps); CPU only median %.2f ms\n", nfr, ntri / nfr, tf[0] * 1e3, tf[nfr / 2] * 1e3, 1 / tf[nfr / 2], tc[nfr / 2] * 1e3); out = NULL; } if (!strcmp(mode, "demo")) { /* NFR frames of a lit, Z-buffered, perspective-textured cube over a textured floor */ int nfr = argc > 3 ? atoi(argv[3]) : 60, fr, i; double *tf = malloc(nfr * sizeof(double)), *tc = malloc(nfr * sizeof(double)), t; unsigned char *buf = malloc(W * H * 2); FILE *o = out ? fopen(out, "wb") : NULL; unsigned int cmd = base | AT_ZI | Z_ZLT; persp = 1; for (fr = 0; fr < nfr; fr++) { double ang = 2 * 3.14159265358979 * fr / nfr; t = now(); ybot(2 * H); fill(0, 0, W, H, 0x20e420e4); fill(0, 480, W, H, 0xffffffff); /* Z = far */ ybot(H); bind(texchk, 64, TC_TAKEY | TC_TMOD); floor_plane(cmd); bind(texbig, 256, TC_TAKEY | TC_TMOD); cube(ang, cmd); mga_idle(); tf[fr] = now() - t; if (o) { for (i = 0; i < W * H * 2; i += 4) *(unsigned int *)(buf + i) = *(volatile unsigned int *)(mga_fb + i); fwrite(buf, 1, W * H * 2, o); } } nodraw = 1; for (fr = 0; fr < nfr; fr++) { t = now(); floor_plane(cmd); cube(2 * 3.14159265358979 * fr / nfr, cmd); tc[fr] = now() - t; } nodraw = 0; if (o) fclose(o); qsort(tf, nfr, sizeof(double), cmpd); qsort(tc, nfr, sizeof(double), cmpd); printf("%d frames: draw+wait min %.1f ms median %.1f ms (%.1f fps); CPU only median %.2f ms\n", nfr, tf[0] * 1e3, tf[nfr / 2] * 1e3, 1 / tf[nfr / 2], tc[nfr / 2] * 1e3); out = NULL; } if (mga_idle()) { fprintf(stderr, "engine hung; STATUS 0x%08x\n", mga_in(STATUS)); } if (out) save(out); mga_close(); return 0; }