/* xtorus.c - the textured torus, drawn by the GXT120P and shown in an X window (Xvnc). * The card renders off screen, IDUMP copies the frame out, the CPU converts it to the * X server's 32-bit pixels and XShmPutImage (or XPutImage) puts it in the window. * Claude Opus 5.5, 2026-10-14. * usage: xtorus [-s W H] [-n frames] [-noshm] [-o capture.raw] (DISPLAY must be set) */ #include #include #include #include #include #include #include #include #include #include #include "tex3d.h" /* MIT-SHM (no XShm.h on this system; prototypes from the X11R6 header) */ typedef struct { unsigned long shmseg; int shmid; char *shmaddr; Bool readOnly; } XShmSegmentInfo; extern Bool XShmQueryExtension(Display *); extern XImage *XShmCreateImage(Display *, Visual *, unsigned int, int, char *, XShmSegmentInfo *, unsigned int, unsigned int); extern Bool XShmAttach(Display *, XShmSegmentInfo *); extern Bool XShmDetach(Display *, XShmSegmentInfo *); extern Bool XShmPutImage(Display *, Drawable, GC, XImage *, int, int, int, int, unsigned int, unsigned int, Bool); static double now(void) { struct timeval tv; gettimeofday(&tv, 0); return tv.tv_sec + tv.tv_usec / 1e6; } /* stop between frames, never in the middle of a readback: a process killed while the * engine is streaming an IDUMP leaves the card waiting for reads that never come */ static volatile int quit = 0; static void onsig(int s) { quit = 1; } static int cmpd(const void *a, const void *b) { double x = *(const double *) a, y = *(const double *) b; return x < y ? -1 : x > y; } static unsigned short rgb(int r, int g, int b) { return ((r >> 3) << 11) | ((g >> 2) << 5) | (b >> 3); } static unsigned short brick[256 * 256]; static void make_brick(void) { int x, y; for (y = 0; y < 256; y++) for (x = 0; x < 256; x++) { 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; brick[y * 256 + x] = mortar ? rgb(170 + noise, 165 + noise, 150 + noise) : rgb(150 + n * 15 + noise, 50 + n * 6 + noise / 2, 35 + noise / 2); } } /* the torus of mystique.html / texmap.html, scaled to a W x H screen; returns triangles */ #define TU 32 #define TV 16 static int W = 320, H = 240; static int torus(int f, int nfr) { static t3_vtx sv[TU + 1][TV + 1]; static double nz[TU + 1][TV + 1]; double PI = 3.14159265358979, sc = W / 640.0; 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), 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++) { double a = 2 * PI * u / TU, b = 2 * PI * v / TV, p[3], m[3], t, dif, z, w; t3_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(W / 2 + p[0] * 520 * sc * w + 0.5); q->y = floor(H / 2 - p[1] * 520 * sc * 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; t3_tri(sv[u][v], sv[u+1][v], sv[u+1][v+1]); t3_tri(sv[u][v], sv[u+1][v+1], sv[u][v+1]); n += 2; } return n; } /* where the X visual wants red, green and blue (shift of each 8-bit field) */ static int rsh = 16, gsh = 8, bsh = 0; static int shift_of(unsigned long mask) { int s = 0; while (mask && !(mask & 1)) { mask >>= 1; s++; } return s; } /* 565 pairs (first pixel in the high half) -> X's 32-bit pixels */ static void convert(const unsigned int *src, unsigned int *dst, int n) { static unsigned int lo[256], hi[256]; static int init = 0; int i; if (!init) { /* split tables: high byte RRRRRGGG, low byte GGGBBBBB; 8-bit values replicate their top bits, as raw2png.py does */ for (i = 0; i < 256; i++) { int r = i >> 3, g = (i & 7) << 3, gl = i >> 5, b = i & 31; hi[i] = ((unsigned) (r << 3 | r >> 2) << rsh) | ((unsigned) (g << 2 | (i & 7) >> 1) << gsh); lo[i] = ((unsigned) (gl << 2) << gsh) | ((unsigned) (b << 3 | b >> 2) << bsh); } init = 1; } for (i = 0; i < n / 2; i++) { unsigned int p = src[i]; dst[2 * i] = hi[p >> 24] | lo[(p >> 16) & 255]; dst[2 * i + 1] = hi[(p >> 8) & 255] | lo[p & 255]; } } int main(int argc, char **argv) { int nfr = 72, useshm = 1, i, fr, ntri = 0, tex; char *capfile = NULL; Display *dpy; Window win; GC gc; XImage *img; XShmSegmentInfo shm; unsigned int *pix; double *tr, *td, *tc, *tp, *tt, t0, t1, t2, t3, t4; FILE *cap = NULL; char msg[200]; for (i = 1; i < argc; i++) { if (!strcmp(argv[i], "-s") && i + 2 < argc) { W = atoi(argv[i + 1]); H = atoi(argv[i + 2]); i += 2; } else if (!strcmp(argv[i], "-n") && i + 1 < argc) nfr = atoi(argv[++i]); else if (!strcmp(argv[i], "-noshm")) useshm = 0; else if (!strcmp(argv[i], "-o") && i + 1 < argc) capfile = argv[++i]; } if (!(dpy = XOpenDisplay(NULL))) { fprintf(stderr, "can't open display\n"); return 1; } if (DefaultDepth(dpy, DefaultScreen(dpy)) != 24) { fprintf(stderr, "needs a 24-bit display\n"); return 1; } if (useshm && !XShmQueryExtension(dpy)) { printf("no MIT-SHM, using XPutImage\n"); useshm = 0; } win = XCreateSimpleWindow(dpy, DefaultRootWindow(dpy), 20, 20, W, H + 24, 0, 0, 0x202020); XStoreName(dpy, win, "xtorus: GXT120P 3D"); XSelectInput(dpy, win, ExposureMask); XMapWindow(dpy, win); gc = XCreateGC(dpy, win, 0, NULL); XSetForeground(dpy, gc, 0xe0e0e0); if (useshm) { img = XShmCreateImage(dpy, DefaultVisual(dpy, 0), 24, ZPixmap, NULL, &shm, W, H); shm.shmid = shmget(IPC_PRIVATE, img->bytes_per_line * H, IPC_CREAT | 0600); shm.shmaddr = img->data = shmat(shm.shmid, 0, 0); shm.readOnly = False; XShmAttach(dpy, &shm); XSync(dpy, False); shmctl(shm.shmid, IPC_RMID, 0); } else { img = XCreateImage(dpy, DefaultVisual(dpy, 0), 24, ZPixmap, 0, malloc(W * H * 4), W, H, 32, 0); } printf("image: %d bits/pixel, byte order %s, server %s; MIT-SHM %s\n", img->bits_per_pixel, img->byte_order == MSBFirst ? "MSB" : "LSB", ImageByteOrder(dpy) == MSBFirst ? "MSB" : "LSB", useshm ? "yes" : "no"); if (img->bits_per_pixel != 32) { fprintf(stderr, "expected 32-bit pixels\n"); return 1; } rsh = shift_of(img->red_mask); gsh = shift_of(img->green_mask); bsh = shift_of(img->blue_mask); printf("visual masks: red %06lx green %06lx blue %06lx\n", img->red_mask, img->green_mask, img->blue_mask); img->byte_order = MSBFirst; /* our words are native (big-endian) */ make_brick(); if (t3_open(W, H)) { fprintf(stderr, "can't get the card (is X on the console running?)\n"); return 1; } tex = t3_texture(brick, 256); pix = (unsigned int *) malloc(W * H * 2 + 8); tr = malloc(5 * nfr * sizeof(double)); td = tr + nfr; tc = td + nfr; tp = tc + nfr; tt = tp + nfr; if (capfile) cap = fopen(capfile, "wb"); signal(SIGINT, onsig); signal(SIGTERM, onsig); signal(SIGHUP, onsig); for (fr = 0; fr < nfr && !quit; fr++) { t0 = now(); t3_clear(0x18c3); t3_bind(tex, T3_PERSP | T3_MODUL); ntri += torus(fr, nfr); t3_finish(); t1 = now(); t3_read(pix); t2 = now(); convert(pix, (unsigned int *) img->data, W * H); t3 = now(); if (useshm) XShmPutImage(dpy, win, gc, img, 0, 0, 0, 0, W, H, False); else XPutImage(dpy, win, gc, img, 0, 0, 0, 0, W, H); if (fr > 0) { sprintf(msg, "card %4.1f read %4.1f convert %4.1f X %4.1f ms %4.1f fps", tr[fr - 1] * 1e3, td[fr - 1] * 1e3, tc[fr - 1] * 1e3, tp[fr - 1] * 1e3, 1 / tt[fr - 1]); XClearArea(dpy, win, 0, H, W, 24, False); XDrawString(dpy, win, gc, 4, H + 16, msg, strlen(msg)); } XSync(dpy, False); t4 = now(); tr[fr] = t1 - t0; td[fr] = t2 - t1; tc[fr] = t3 - t2; tp[fr] = t4 - t3; tt[fr] = t4 - t0; if (cap) fwrite(pix, 1, W * H * 2, cap); } if (cap) fclose(cap); t3_close(); if (fr < 2) return 0; for (i = 0; i < 5; i++) qsort(tr + i * nfr, fr, sizeof(double), cmpd); printf("%dx%d, %d frames, %d triangles/frame; medians: card %.1f ms, read %.1f ms, convert %.1f ms, " "X %.1f ms; frame %.1f ms = %.1f fps\n", W, H, fr, ntri / fr, tr[fr / 2] * 1e3, td[fr / 2] * 1e3, tc[fr / 2] * 1e3, tp[fr / 2] * 1e3, tt[fr / 2] * 1e3, 1 / tt[fr / 2]); if (getenv("XTORUS_HOLD")) sleep(atoi(getenv("XTORUS_HOLD"))); /* leave the window up */ XCloseDisplay(dpy); return 0; }