2 RetroRay - integrated standalone vintage modeller/renderer
3 Copyright (C) 2023 John Tsiombikas <nuclear@mutantstargoat.com>
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program. If not, see <https://www.gnu.org/licenses/>.
24 #define MAX_MIPMAP_LEVELS 9
26 #define PACK_RGB565(r, g, b) \
27 (((uint16_t)((r) & 0xf8) << 8) | \
28 ((uint16_t)((g) & 0xfc) << 3) | \
29 ((uint16_t)((b) & 0xf8) >> 3))
31 #define PACK_RGBA4444(r, g, b, a) \
32 (((uint16_t)((a) & 0xf0) << 8) | \
33 ((uint16_t)((r) & 0xf0) << 4) | \
34 ((uint16_t)(g) & 0xf0) | \
35 ((uint16_t)((b) & 0xf0) >> 4))
47 long addr; /* -1 if not resident */
49 struct mipmap mip[MAX_MIPMAP_LEVELS];
55 static void print_board_info(GrHwConfiguration *hw, int idx);
57 static int grlod(int x, int y);
58 static int graspect(int x, int y);
59 static int grfmt(int ifmt);
60 static int texdim(int x, int lvl);
61 static int texlevels(int x, int y);
62 static int texsize(int x, int y, int pixsz);
63 static void halve_image_lum(uint8_t *dest, uint8_t *src, int xsz, int ysz);
64 static void halve_image_rgb(uint8_t *dest, uint8_t *src, int xsz, int ysz);
65 static void halve_image_rgba(uint32_t *dest, uint32_t *src, int xsz, int ysz);
67 static void init_texman(void);
69 static GrHwConfiguration hwcfg;
72 static struct teximg textures[MAX_TEXTURES];
75 void gaw_glide_reset(void)
80 static const char *sstnames[] = {"Voodoo", "SST96", "ATB", "Voodoo2"};
82 void gaw_glide_init(int xsz, int ysz)
85 static const struct { int xsz, ysz, res; } glideres[] = {
86 {640, 480, GR_RESOLUTION_640x480},
87 {800, 600, GR_RESOLUTION_800x600},
88 {1024, 768, GR_RESOLUTION_1024x768}
91 for(i=0; i<sizeof glideres/sizeof *glideres; i++) {
92 if(xsz == glideres[i].xsz && ysz == glideres[i].ysz) {
93 res = glideres[i].res;
98 fprintf(stderr, "Unsupported resolution: %dx%d\n", xsz, ysz);
104 if(!grSstQueryBoards(&hwcfg)) {
105 fprintf(stderr, "No 3dfx graphics board detected!\n");
110 if(!grSstQueryHardware(&hwcfg)) {
111 fprintf(stderr, "No 3dfx graphics board detected!\n");
115 printf("Found %d 3dfx graphics board(s)\n", hwcfg.num_sst);
116 for(i=0; i<hwcfg.num_sst; i++) {
117 print_board_info(&hwcfg, i);
122 switch(hwcfg.SSTs[0].type) {
123 case GR_SSTTYPE_VOODOO:
124 case GR_SSTTYPE_Voodoo2:
125 num_tmu = hwcfg.SSTs[0].sstBoard.VoodooConfig.nTexelfx;
127 case GR_SSTTYPE_SST96:
128 num_tmu = hwcfg.SSTs[0].sstBoard.SST96Config.nTexelfx;
134 if(!grSstWinOpen(0, res, GR_REFRESH_60Hz, GR_COLORFORMAT_ARGB,
135 GR_ORIGIN_UPPER_LEFT, 2, 1)) {
136 fprintf(stderr, "Failed to initialize 3dfx device\n");
146 void gaw_glide_destroy(void)
152 void gaw_enable(int what)
156 grDepthBufferMode(GR_DEPTHBUFFER_WBUFFER);
163 gaw_swtnl_enable(what);
166 void gaw_disable(int what)
170 grDepthBufferMode(GR_DEPTHBUFFER_DISABLE);
177 gaw_swtnl_disable(what);
180 void gaw_clear(unsigned int flags)
182 if(!(flags & GAW_COLORBUF)) {
183 grColorMask(FXFALSE, FXFALSE);
185 if(flags & GAW_DEPTHBUF) {
186 grDepthBufferMode(GR_DEPTHBUFFER_WBUFFER);
190 grBufferClear(ST->clear_color, 0xff, GR_WDEPTHVALUE_FARTHEST);
192 if(flags & GAW_DEPTHBUF) {
193 grDepthBufferMode(!(ST->opt & GAW_DEPTH_TEST) ? GR_DEPTHBUFFER_DISABLE : GR_DEPTHBUFFER_WBUFFER);
194 /* TODO also revert depth mask */
196 if(!(flags & GAW_COLORBUF)) {
197 grColorMask(FXTRUE, FXFALSE);
201 void gaw_color_mask(int rmask, int gmask, int bmask, int amask)
203 int rgbmask = rmask | gmask | bmask;
204 grColorMask(rgbmask ? FXTRUE : FXFALSE, FXFALSE);
207 void gaw_depth_mask(int mask)
209 grDepthMask(mask ? FXTRUE : FXFALSE);
212 static int alloc_tex(void)
215 for(i=0; i<MAX_TEXTURES; i++) {
216 if(ST->textypes[i] == 0) {
223 unsigned int gaw_create_tex1d(int texfilter)
226 if((idx = alloc_tex()) == -1) {
229 ST->textypes[idx] = 1;
231 memset(textures + idx, 0, sizeof *textures);
236 unsigned int gaw_create_tex2d(int texfilter)
239 if((idx = alloc_tex()) == -1) {
242 ST->textypes[idx] = 2;
244 memset(textures + idx, 0, sizeof *textures);
249 void gaw_destroy_tex(unsigned int texid)
253 if(!ST->textypes[idx]) return;
255 free(textures[idx].mip[0].pixels);
256 memset(textures + idx, 0, sizeof *textures);
257 ST->textypes[idx] = 0;
260 void gaw_texfilter1d(int texfilter)
264 void gaw_texfilter2d(int texfilter)
268 void gaw_texwrap1d(int wrap)
272 void gaw_texwrap2d(int uwrap, int vwrap)
276 void gaw_tex1d(int ifmt, int xsz, int fmt, void *pix)
278 gaw_tex2d(ifmt, xsz, 1, fmt, pix);
281 void gaw_tex2d(int ifmt, int xsz, int ysz, int fmt, void *pix)
288 if(ST->cur_tex < 0) return;
289 if(xsz > 256 || ysz > 256) {
290 fprintf(stderr, "unsupported texture size: %dx%d\n", xsz, ysz);
293 if(xsz / ysz > 8 || ysz / xsz > 8) {
294 fprintf(stderr, "unsupported texture aspect: %dx%d\n", xsz, ysz);
298 pixsz = ifmt == GAW_LUMINANCE ? 1 : 2;
300 tex = textures + ST->cur_tex;
301 memset(tex, 0, sizeof *tex);
303 tinf.smallLod = GR_LOD_1;
304 tinf.largeLod = grlod(xsz, ysz);
305 tinf.aspectRatio = graspect(xsz, ysz);
306 tinf.format = grfmt(ifmt);
307 tex->res_size = grTexTextureMemRequired(GR_MIPMAPLEVELMASK_BOTH, &tinf);
309 tex->levels = texlevels(xsz, ysz);
310 tex->size = texsize(xsz, ysz, pixsz);
312 free(tex->mip[0].pixels);
313 tex->mip[0].pixels = malloc_nf(tex->size);
315 tmpbuf = malloc_nf(xsz * ysz * 2);
318 for(i=0; i<tex->levels; i++) {
319 tex->mip[i].width = xsz;
320 tex->mip[i].height = ysz;
321 tex->mip[i].size = xsz * ysz * pixsz;
323 tex->mip[i].pixels = (uint16_t*)((char*)tex->mip[i - 1].pixels + tex->mip[i - 1].size);
332 src = (char*)tmpbuf + xsz * ysz;
334 dest = (char*)tmpbuf + xsz * ysz;
340 halve_image_lum(dest, i > 1 ? src : pix, xsz, ysz);
344 halve_image_rgb(dest, i > 1 ? src : pix, xsz, ysz);
348 halve_image_rgba(dest, i > 1 ? src : pix, xsz, ysz);
351 gaw_subtex2d(i, 0, 0, xsz, ysz, fmt, tmpbuf);
353 gaw_subtex2d(0, 0, 0, xsz, ysz, fmt, pix);
357 if(xsz > 1) xsz >>= 1;
358 if(ysz > 1) ysz >>= 1;
364 void gaw_subtex2d(int lvl, int x, int y, int xsz, int ysz, int fmt, void *pix)
366 int i, j, r, g, b, a, val, lvlwidth;
371 if(ST->cur_tex < 0) return;
372 tex = textures + ST->cur_tex;
373 if(!tex->mip[lvl].pixels) {
374 fprintf(stderr, "subtex2d error: texture data not allocated\n");
377 if(x + xsz > tex->mip[lvl].width) {
378 xsz = tex->mip[lvl].width - x;
380 if(y + ysz > tex->mip[lvl].height) {
381 ysz = tex->mip[lvl].height - y;
384 lvlwidth = tex->mip[lvl].width;
389 dest = (uint16_t*)((char*)tex->mip[lvl].pixels + y * lvlwidth + x);
390 for(i=0; i<ysz; i++) {
391 memcpy(dest, src, xsz);
393 dest += lvlwidth >> 1;
398 dest = (uint16_t*)tex->mip[lvl].pixels + y * lvlwidth + x;
399 for(i=0; i<ysz; i++) {
400 for(j=0; j<xsz; j++) {
405 dest[j] = PACK_RGB565(r, g, b);
412 dest = (uint16_t*)tex->mip[lvl].pixels + y * lvlwidth + x;
413 for(i=0; i<ysz; i++) {
414 for(j=0; j<xsz; j++) {
420 dest[j] = PACK_RGBA4444(r, g, b, a);
431 void gaw_bind_tex1d(int tex)
433 ST->cur_tex = (int)tex - 1;
436 void gaw_bind_tex2d(int tex)
438 ST->cur_tex = (int)tex - 1;
442 void gaw_sw_dump_textures(void)
444 /* TODO dump whole pyramid for each tex */
448 void gaw_swtnl_drawprim(int prim, struct vertex *v, int vnum)
453 for(i=0; i<vnum; i++) {
454 /* viewport transformation */
455 vert[i].x = (v[i].x * 0.5f + 0.5f) * (float)ST->vport[2] + ST->vport[0];
456 vert[i].y = (v[i].y * 0.5f + 0.5f) * (float)ST->vport[3] + ST->vport[1];
457 vert[i].y = ST->height - vert[i].y - 1;
459 vert[i].oow = 1.0f / v[i].w;
466 vert[i].tmuvtx[0].sow = v[i].u * 256.0f / v[i].w;
467 vert[i].tmuvtx[0].tow = v[i].v * 256.0f / v[i].w;
468 vert[i].tmuvtx[0].oow = (float)0xfff / v[i].w;
471 grDrawTriangle(vert, vert + (i - 1), vert + i);
476 static void print_board_info(GrHwConfiguration *hw, int idx)
478 int i, texmem, totalmem;
479 int type = hw->SSTs[idx].type;
480 const char *name = type < sizeof sstnames / sizeof *sstnames ? sstnames[type] : "unknown";
481 GrVoodooConfig_t *voodoo;
483 printf(" - 3dfx board %d: %s ", idx, name);
485 case GR_SSTTYPE_VOODOO:
486 case GR_SSTTYPE_Voodoo2:
487 voodoo = &hw->SSTs[idx].sstBoard.VoodooConfig;
488 if(voodoo->sliDetect) {
492 for(i=0; i<voodoo->nTexelfx; i++) {
493 texmem += voodoo->tmuConfig[i].tmuRam;
495 totalmem = voodoo->fbRam + texmem;
496 printf("%dmb (%dmb texture RAM, %d tex units)\n", totalmem, texmem,
501 putchar('\n'); /* TODO */
505 /* texture helper functions */
506 static int grlod(int x, int y)
511 case 256: return GR_LOD_256;
512 case 128: return GR_LOD_128;
513 case 64: return GR_LOD_64;
514 case 32: return GR_LOD_32;
515 case 16: return GR_LOD_16;
516 case 8: return GR_LOD_8;
517 case 4: return GR_LOD_4;
518 case 2: return GR_LOD_2;
519 case 1: return GR_LOD_1;
526 static int graspect(int x, int y)
530 if(x == y) return GR_ASPECT_1x1;
533 steps = GR_ASPECT_1x1;
534 while(y < x && steps > GR_ASPECT_8x1) {
540 while(x < y && steps < GR_ASPECT_1x8) {
548 static int grfmt(int ifmt)
552 return GR_TEXFMT_INTENSITY_8;
554 return GR_TEXFMT_RGB_565;
556 return GR_TEXFMT_ARGB_4444;
560 return GR_TEXFMT_RGB_565;
563 static int texdim(int x, int lvl)
565 while(x > 1 && lvl-- > 0) {
571 static int texlevels(int x, int y)
574 while(x > 1 || y > 1) {
582 static int texsize(int x, int y, int pixsz)
585 while(x > 1 || y > 1) {
593 static void halve_image_lum(uint8_t *dest, uint8_t *src, int xsz, int ysz)
596 int dest_w = xsz >> 1;
597 int dest_h = ysz >> 1;
599 for(i=0; i<dest_h; i++) {
600 for(j=0; j<dest_w; j++) {
601 dest[j] = (*src + src[1] + src[xsz] + src[xsz + 1]) >> 2;
609 static void halve_image_rgb(uint8_t *dest, uint8_t *src, int xsz, int ysz)
612 int dest_w = xsz >> 1;
613 int dest_h = ysz >> 1;
614 int sscanlen = xsz * 3;
615 uint8_t *srcodd = src + sscanlen;
617 for(i=0; i<dest_h; i++) {
618 for(j=0; j<dest_w; j++) {
619 dest[0] = (src[0] + src[3] + srcodd[0] + srcodd[3]) >> 2;
620 dest[1] = (src[1] + src[4] + srcodd[1] + srcodd[4]) >> 2;
621 dest[2] = (src[2] + src[5] + srcodd[2] + srcodd[5]) >> 2;
632 #define PACK_RGBA(r, g, b, a) \
633 (((a) << 24) | ((r) << 16) | ((g) << 8) | (b))
634 #define UNPACK_R(pix) ((pix) & 0xff)
635 #define UNPACK_G(pix) (((pix) >> 8) & 0xff)
636 #define UNPACK_B(pix) (((pix) >> 16) & 0xff)
637 #define UNPACK_A(pix) ((pix) >> 24)
639 static void halve_image_rgba(uint32_t *dest, uint32_t *src, int xsz, int ysz)
642 int dest_w = xsz >> 1;
643 int dest_h = ysz >> 1;
645 for(i=0; i<dest_h; i++) {
646 for(j=0; j<dest_w; j++) {
647 r = (UNPACK_R(*src) + UNPACK_R(src[1]) + UNPACK_R(src[xsz]) +
648 UNPACK_R(src[xsz + 1])) >> 2;
649 g = (UNPACK_G(*src) + UNPACK_G(src[1]) + UNPACK_G(src[xsz]) +
650 UNPACK_G(src[xsz + 1])) >> 2;
651 b = (UNPACK_B(*src) + UNPACK_B(src[1]) + UNPACK_B(src[xsz]) +
652 UNPACK_B(src[xsz + 1])) >> 2;
653 a = (UNPACK_A(*src) + UNPACK_A(src[1]) + UNPACK_A(src[xsz]) +
654 UNPACK_A(src[xsz + 1])) >> 2;
655 dest[j] = PACK_RGBA(r, g, b, a);
663 /* texture memory manager */
665 uint32_t start, size;
666 } tmumem[GLIDE_NUM_TMU];
668 void init_texman(void)
673 for(i=0; i<num_tmu; i++) {
674 tmumem[i].start = grTexMinAddress(i);
675 end = grTexMaxAddress(i) + 8;
676 tmumem[i].size = end - tmumem[i].start;
678 printf("TMU%d TRAM range %06x - %06x (%d bytes)\n", i, tmumem[i].start, end,