fixed simple draw_cursor
[retroray] / src / gaw / gaw_glide.c
1 /*
2 RetroRay - integrated standalone vintage modeller/renderer
3 Copyright (C) 2023  John Tsiombikas <nuclear@mutantstargoat.com>
4
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.
9
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.
14
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/>.
17 */
18 #include <stdio.h>
19 #include <stdlib.h>
20 #include "glide.h"
21 #include "gaw.h"
22 #include "gawswtnl.h"
23
24 #define MAX_MIPMAP_LEVELS       9
25
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))
30
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))
36
37 struct mipmap {
38         int width, height;
39         int size;
40         void *pixels;
41 };
42
43 struct teximg {
44         int idx;
45         int fmt;
46         long size, res_size;
47         long addr;      /* -1 if not resident */
48
49         struct mipmap mip[MAX_MIPMAP_LEVELS];
50         int levels;
51
52         long last_bind;
53 };
54
55 static void print_board_info(GrHwConfiguration *hw, int idx);
56
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);
66
67 static void init_texman(void);
68
69 static GrHwConfiguration hwcfg;
70 static int num_tmu;
71
72 static struct teximg textures[MAX_TEXTURES];
73
74
75 void gaw_glide_reset(void)
76 {
77         gaw_swtnl_reset();
78 }
79
80 static const char *sstnames[] = {"Voodoo", "SST96", "ATB", "Voodoo2"};
81
82 void gaw_glide_init(int xsz, int ysz)
83 {
84         int i, res = -1;
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}
89         };
90
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;
94                         break;
95                 }
96         }
97         if(res == -1) {
98                 fprintf(stderr, "Unsupported resolution: %dx%d\n", xsz, ysz);
99                 abort();
100         }
101
102         gaw_swtnl_init();
103
104         if(!grSstQueryBoards(&hwcfg)) {
105                 fprintf(stderr, "No 3dfx graphics board detected!\n");
106                 abort();
107         }
108
109         grGlideInit();
110         if(!grSstQueryHardware(&hwcfg)) {
111                 fprintf(stderr, "No 3dfx graphics board detected!\n");
112                 abort();
113         }
114
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);
118         }
119
120         grSstSelect(0);
121
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;
126                 break;
127         case GR_SSTTYPE_SST96:
128                 num_tmu = hwcfg.SSTs[0].sstBoard.SST96Config.nTexelfx;
129                 break;
130         default:
131                 num_tmu = 1;
132         }
133
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");
137                 abort();
138         }
139
140         ST->width = xsz;
141         ST->height = ysz;
142
143         init_texman();
144 }
145
146 void gaw_glide_destroy(void)
147 {
148         gaw_swtnl_destroy();
149         grSstWinClose();
150 }
151
152 void gaw_enable(int what)
153 {
154         switch(what) {
155         case GAW_DEPTH_TEST:
156                 grDepthBufferMode(GR_DEPTHBUFFER_WBUFFER);
157                 break;
158
159         default:
160                 break;
161         }
162
163         gaw_swtnl_enable(what);
164 }
165
166 void gaw_disable(int what)
167 {
168         switch(what) {
169         case GAW_DEPTH_TEST:
170                 grDepthBufferMode(GR_DEPTHBUFFER_DISABLE);
171                 break;
172
173         default:
174                 break;
175         }
176
177         gaw_swtnl_disable(what);
178 }
179
180 void gaw_clear(unsigned int flags)
181 {
182         if(!(flags & GAW_COLORBUF)) {
183                 grColorMask(FXFALSE, FXFALSE);
184         }
185         if(flags & GAW_DEPTHBUF) {
186                 grDepthBufferMode(GR_DEPTHBUFFER_WBUFFER);
187                 grDepthMask(FXTRUE);
188         }
189
190         grBufferClear(ST->clear_color, 0xff, GR_WDEPTHVALUE_FARTHEST);
191
192         if(flags & GAW_DEPTHBUF) {
193                 grDepthBufferMode(!(ST->opt & GAW_DEPTH_TEST) ? GR_DEPTHBUFFER_DISABLE : GR_DEPTHBUFFER_WBUFFER);
194                 /* TODO also revert depth mask */
195         }
196         if(!(flags & GAW_COLORBUF)) {
197                 grColorMask(FXTRUE, FXFALSE);
198         }
199 }
200
201 void gaw_color_mask(int rmask, int gmask, int bmask, int amask)
202 {
203         int rgbmask = rmask | gmask | bmask;
204         grColorMask(rgbmask ? FXTRUE : FXFALSE, FXFALSE);
205 }
206
207 void gaw_depth_mask(int mask)
208 {
209         grDepthMask(mask ? FXTRUE : FXFALSE);
210 }
211
212 static int alloc_tex(void)
213 {
214         int i;
215         for(i=0; i<MAX_TEXTURES; i++) {
216                 if(ST->textypes[i] == 0) {
217                         return i;
218                 }
219         }
220         return -1;
221 }
222
223 unsigned int gaw_create_tex1d(int texfilter)
224 {
225         int idx;
226         if((idx = alloc_tex()) == -1) {
227                 return 0;
228         }
229         ST->textypes[idx] = 1;
230
231         memset(textures + idx, 0, sizeof *textures);
232         ST->cur_tex = idx;
233         return idx + 1;
234 }
235
236 unsigned int gaw_create_tex2d(int texfilter)
237 {
238         int idx;
239         if((idx = alloc_tex()) == -1) {
240                 return 0;
241         }
242         ST->textypes[idx] = 2;
243
244         memset(textures + idx, 0, sizeof *textures);
245         ST->cur_tex = idx;
246         return idx + 1;
247 }
248
249 void gaw_destroy_tex(unsigned int texid)
250 {
251         int idx = texid - 1;
252
253         if(!ST->textypes[idx]) return;
254
255         free(textures[idx].mip[0].pixels);
256         memset(textures + idx, 0, sizeof *textures);
257         ST->textypes[idx] = 0;
258 }
259
260 void gaw_texfilter1d(int texfilter)
261 {
262 }
263
264 void gaw_texfilter2d(int texfilter)
265 {
266 }
267
268 void gaw_texwrap1d(int wrap)
269 {
270 }
271
272 void gaw_texwrap2d(int uwrap, int vwrap)
273 {
274 }
275
276 void gaw_tex1d(int ifmt, int xsz, int fmt, void *pix)
277 {
278         gaw_tex2d(ifmt, xsz, 1, fmt, pix);
279 }
280
281 void gaw_tex2d(int ifmt, int xsz, int ysz, int fmt, void *pix)
282 {
283         int i, sz, pixsz;
284         struct teximg *tex;
285         GrTexInfo tinf;
286         void *tmpbuf = 0;
287
288         if(ST->cur_tex < 0) return;
289         if(xsz > 256 || ysz > 256) {
290                 fprintf(stderr, "unsupported texture size: %dx%d\n", xsz, ysz);
291                 return;
292         }
293         if(xsz / ysz > 8 || ysz / xsz > 8) {
294                 fprintf(stderr, "unsupported texture aspect: %dx%d\n", xsz, ysz);
295                 return;
296         }
297
298         pixsz = ifmt == GAW_LUMINANCE ? 1 : 2;
299
300         tex = textures + ST->cur_tex;
301         memset(tex, 0, sizeof *tex);
302
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);
308
309         tex->levels = texlevels(xsz, ysz);
310         tex->size = texsize(xsz, ysz, pixsz);
311
312         free(tex->mip[0].pixels);
313         tex->mip[0].pixels = malloc_nf(tex->size);
314         if(pix) {
315                 tmpbuf = malloc_nf(xsz * ysz * 2);
316         }
317
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;
322                 if(i > 0) {
323                         tex->mip[i].pixels = (uint16_t*)((char*)tex->mip[i - 1].pixels + tex->mip[i - 1].size);
324                 }
325
326                 if(pix) {
327                         if(i > 0) {
328                                 void *dest, *src;
329
330                                 if(i & 1) {
331                                         dest = tmpbuf;
332                                         src = (char*)tmpbuf + xsz * ysz;
333                                 } else {
334                                         dest = (char*)tmpbuf + xsz * ysz;
335                                         src = tmpbuf;
336                                 }
337
338                                 switch(fmt) {
339                                 case GAW_LUMINANCE:
340                                         halve_image_lum(dest, i > 1 ? src : pix, xsz, ysz);
341                                         break;
342
343                                 case GAW_RGB:
344                                         halve_image_rgb(dest, i > 1 ? src : pix, xsz, ysz);
345                                         break;
346
347                                 case GAW_RGBA:
348                                         halve_image_rgba(dest, i > 1 ? src : pix, xsz, ysz);
349                                         break;
350                                 }
351                                 gaw_subtex2d(i, 0, 0, xsz, ysz, fmt, tmpbuf);
352                         } else {
353                                 gaw_subtex2d(0, 0, 0, xsz, ysz, fmt, pix);
354                         }
355                 }
356
357                 if(xsz > 1) xsz >>= 1;
358                 if(ysz > 1) ysz >>= 1;
359         }
360
361         free(tmpbuf);
362 }
363
364 void gaw_subtex2d(int lvl, int x, int y, int xsz, int ysz, int fmt, void *pix)
365 {
366         int i, j, r, g, b, a, val, lvlwidth;
367         uint16_t *dest;
368         unsigned char *src;
369         struct teximg *tex;
370
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");
375                 return;
376         }
377         if(x + xsz > tex->mip[lvl].width) {
378                 xsz = tex->mip[lvl].width - x;
379         }
380         if(y + ysz > tex->mip[lvl].height) {
381                 ysz = tex->mip[lvl].height - y;
382         }
383
384         lvlwidth = tex->mip[lvl].width;
385         src = pix;
386
387         switch(fmt) {
388         case GAW_LUMINANCE:
389                 dest = (uint16_t*)((char*)tex->mip[lvl].pixels + y * lvlwidth + x);
390                 for(i=0; i<ysz; i++) {
391                         memcpy(dest, src, xsz);
392                         src += xsz;
393                         dest += lvlwidth >> 1;
394                 }
395                 break;
396
397         case GAW_RGB:
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++) {
401                                 b = src[0];
402                                 g = src[1];
403                                 r = src[2];
404                                 src += 3;
405                                 dest[j] = PACK_RGB565(r, g, b);
406                         }
407                         dest += lvlwidth;
408                 }
409                 break;
410
411         case GAW_RGBA:
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++) {
415                                 b = src[0];
416                                 g = src[1];
417                                 r = src[2];
418                                 a = src[3];
419                                 src += 4;
420                                 dest[j] = PACK_RGBA4444(r, g, b, a);
421                         }
422                         dest += lvlwidth;
423                 }
424                 break;
425
426         default:
427                 break;
428         }
429 }
430
431 void gaw_bind_tex1d(int tex)
432 {
433         ST->cur_tex = (int)tex - 1;
434 }
435
436 void gaw_bind_tex2d(int tex)
437 {
438         ST->cur_tex = (int)tex - 1;
439 }
440
441
442 void gaw_sw_dump_textures(void)
443 {
444         /* TODO dump whole pyramid for each tex */
445 }
446
447
448 void gaw_swtnl_drawprim(int prim, struct vertex *v, int vnum)
449 {
450         int i;
451         GrVertex vert[16];
452
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;
458
459                 vert[i].oow = 1.0f / v[i].w;
460
461                 vert[i].r = v->r;
462                 vert[i].g = v->g;
463                 vert[i].b = v->b;
464                 vert[i].a = v->a;
465
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;
469
470                 if(i >= 2) {
471                         grDrawTriangle(vert, vert + (i - 1), vert + i);
472                 }
473         }
474 }
475
476 static void print_board_info(GrHwConfiguration *hw, int idx)
477 {
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;
482
483         printf(" - 3dfx board %d: %s ", idx, name);
484         switch(type) {
485         case GR_SSTTYPE_VOODOO:
486         case GR_SSTTYPE_Voodoo2:
487                 voodoo = &hw->SSTs[idx].sstBoard.VoodooConfig;
488                 if(voodoo->sliDetect) {
489                         printf("SLI ");
490                 }
491                 texmem = 0;
492                 for(i=0; i<voodoo->nTexelfx; i++) {
493                         texmem += voodoo->tmuConfig[i].tmuRam;
494                 }
495                 totalmem = voodoo->fbRam + texmem;
496                 printf("%dmb (%dmb texture RAM, %d tex units)\n", totalmem, texmem,
497                                 voodoo->nTexelfx);
498                 break;
499
500         default:
501                 putchar('\n');  /* TODO */
502         }
503 }
504
505 /* texture helper functions */
506 static int grlod(int x, int y)
507 {
508         if(y > x) x = y;
509
510         switch(x) {
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;
520         default:
521                 break;
522         }
523         return GR_LOD_1;
524 }
525
526 static int graspect(int x, int y)
527 {
528         int steps;
529
530         if(x == y) return GR_ASPECT_1x1;
531
532         if(x > y) {
533                 steps = GR_ASPECT_1x1;
534                 while(y < x && steps > GR_ASPECT_8x1) {
535                         y <<= 1;
536                         steps--;
537                 }
538         } else {
539                 steps = 0;
540                 while(x < y && steps < GR_ASPECT_1x8) {
541                         x <<= 1;
542                         steps++;
543                 }
544         }
545         return steps;
546 }
547
548 static int grfmt(int ifmt)
549 {
550         switch(ifmt) {
551         case GAW_LUMINANCE:
552                 return GR_TEXFMT_INTENSITY_8;
553         case GAW_RGB:
554                 return GR_TEXFMT_RGB_565;
555         case GAW_RGBA:
556                 return GR_TEXFMT_ARGB_4444;
557         default:
558                 break;
559         }
560         return GR_TEXFMT_RGB_565;
561 }
562
563 static int texdim(int x, int lvl)
564 {
565         while(x > 1 && lvl-- > 0) {
566                 x >>= 1;
567         }
568         return x;
569 }
570
571 static int texlevels(int x, int y)
572 {
573         int lvl = 0;
574         while(x > 1 || y > 1) {
575                 lvl++;
576                 x >>= 1;
577                 y >>= 1;
578         }
579         return lvl;
580 }
581
582 static int texsize(int x, int y, int pixsz)
583 {
584         long sz = 0;
585         while(x > 1 || y > 1) {
586                 sz += x * y * pixsz;
587                 x >>= 1;
588                 y >>= 1;
589         }
590         return sz;
591 }
592
593 static void halve_image_lum(uint8_t *dest, uint8_t *src, int xsz, int ysz)
594 {
595         int i, j;
596         int dest_w = xsz >> 1;
597         int dest_h = ysz >> 1;
598
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;
602                         src += 2;
603                 }
604                 dest += dest_w;
605                 src += xsz;
606         }
607 }
608
609 static void halve_image_rgb(uint8_t *dest, uint8_t *src, int xsz, int ysz)
610 {
611         int i, j;
612         int dest_w = xsz >> 1;
613         int dest_h = ysz >> 1;
614         int sscanlen = xsz * 3;
615         uint8_t *srcodd = src + sscanlen;
616
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;
622                         dest += 3;
623                         src += 6;
624                         srcodd += 6;
625                 }
626                 src += sscanlen;
627                 srcodd += sscanlen;
628         }
629 }
630
631
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)
638
639 static void halve_image_rgba(uint32_t *dest, uint32_t *src, int xsz, int ysz)
640 {
641         int i, j, r, g, b;
642         int dest_w = xsz >> 1;
643         int dest_h = ysz >> 1;
644
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);
656                         src += 2;
657                 }
658                 src += xsz;
659                 dest += dest_w;
660         }
661 }
662
663 /* texture memory manager */
664 struct tmumem {
665         uint32_t start, size;
666 } tmumem[GLIDE_NUM_TMU];
667
668 void init_texman(void)
669 {
670         int i;
671         uint32_t end;
672
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;
677
678                 printf("TMU%d TRAM range %06x - %06x (%d bytes)\n", i, tmumem[i].start, end,
679                         tmumem[i].size);
680         }
681 }