4d09634463d41ed501ca45ed2d77e90578aec6ab
[pcsx_rearmed.git] / plugins / gpu_unai / gpulib_if.cpp
1 /***************************************************************************
2 *   Copyright (C) 2010 PCSX4ALL Team                                      *
3 *   Copyright (C) 2010 Unai                                               *
4 *   Copyright (C) 2011 notaz                                              *
5 *   Copyright (C) 2016 Senquack (dansilsby <AT> gmail <DOT> com)          *
6 *                                                                         *
7 *   This program is free software; you can redistribute it and/or modify  *
8 *   it under the terms of the GNU General Public License as published by  *
9 *   the Free Software Foundation; either version 2 of the License, or     *
10 *   (at your option) any later version.                                   *
11 *                                                                         *
12 *   This program is distributed in the hope that it will be useful,       *
13 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
14 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
15 *   GNU General Public License for more details.                          *
16 *                                                                         *
17 *   You should have received a copy of the GNU General Public License     *
18 *   along with this program; if not, write to the                         *
19 *   Free Software Foundation, Inc.,                                       *
20 *   51 Franklin Street, Fifth Floor, Boston, MA 02111-1307 USA.           *
21 ***************************************************************************/
22
23 #include <stddef.h>
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include "../gpulib/gpu.h"
28 #include "old/if.h"
29
30 #ifdef THREAD_RENDERING
31 #include "../gpulib/gpulib_thread_if.h"
32 #define do_cmd_list real_do_cmd_list
33 #define renderer_init real_renderer_init
34 #define renderer_finish real_renderer_finish
35 #define renderer_sync_ecmds real_renderer_sync_ecmds
36 #define renderer_update_caches real_renderer_update_caches
37 #define renderer_flush_queues real_renderer_flush_queues
38 #define renderer_set_interlace real_renderer_set_interlace
39 #define renderer_set_config real_renderer_set_config
40 #define renderer_notify_res_change real_renderer_notify_res_change
41 #define renderer_notify_update_lace real_renderer_notify_update_lace
42 #define renderer_sync real_renderer_sync
43 #define ex_regs scratch_ex_regs
44 #endif
45
46 //#include "port.h"
47 #include "gpu_unai.h"
48
49 // GPU fixed point math
50 #include "gpu_fixedpoint.h"
51
52 // Inner loop driver instantiation file
53 #include "gpu_inner.h"
54
55 // GPU internal image drawing functions
56 #include "gpu_raster_image.h"
57
58 // GPU internal line drawing functions
59 #include "gpu_raster_line.h"
60
61 // GPU internal polygon drawing functions
62 #include "gpu_raster_polygon.h"
63
64 // GPU internal sprite drawing functions
65 #include "gpu_raster_sprite.h"
66
67 // GPU command buffer execution/store
68 #include "gpu_command.h"
69
70 /////////////////////////////////////////////////////////////////////////////
71
72 #ifndef GPU_UNAI_NO_OLD
73 #define IS_OLD_RENDERER() gpu_unai.config.old_renderer
74 #else
75 #define IS_OLD_RENDERER() false
76 #endif
77
78 #define DOWNSCALE_VRAM_SIZE (1024 * 512 * 2 * 2 + 4096)
79
80 INLINE void scale_640_to_320(le16_t *dest, const le16_t *src, bool isRGB24) {
81   size_t uCount = 320;
82
83   if(isRGB24) {
84     const uint8_t* src8 = (const uint8_t *)src;
85     uint8_t* dst8 = (uint8_t *)dest;
86
87     do {
88       *dst8++ = *src8++;
89       *dst8++ = *src8++;
90       *dst8++ = *src8;
91       src8 += 4;
92     } while(--uCount);
93   } else {
94     const le16_t* src16 = src;
95     le16_t* dst16 = dest;
96
97     do {
98       *dst16++ = *src16;
99       src16 += 2;
100     } while(--uCount);
101   }
102 }
103
104 INLINE void scale_512_to_320(le16_t *dest, const le16_t *src, bool isRGB24) {
105   size_t uCount = 64;
106
107   if(isRGB24) {
108     const uint8_t* src8 = (const uint8_t *)src;
109     uint8_t* dst8 = (uint8_t *)dest;
110
111     do {
112       *dst8++ = *src8++;
113       *dst8++ = *src8++;
114       *dst8++ = *src8++;
115       *dst8++ = *src8++;
116       *dst8++ = *src8++;
117       *dst8++ = *src8;
118       src8 += 4;
119       *dst8++ = *src8++;
120       *dst8++ = *src8++;
121       *dst8++ = *src8++;
122       *dst8++ = *src8++;
123       *dst8++ = *src8++;
124       *dst8++ = *src8;
125       src8 += 4;
126       *dst8++ = *src8++;
127       *dst8++ = *src8++;
128       *dst8++ = *src8;
129       src8 += 4;
130     } while(--uCount);
131   } else {
132     const le16_t* src16 = src;
133     le16_t* dst16 = dest;
134
135     do {
136       *dst16++ = *src16++;
137       *dst16++ = *src16;
138       src16 += 2;
139       *dst16++ = *src16++;
140       *dst16++ = *src16;
141       src16 += 2;
142       *dst16++ = *src16;
143       src16 += 2;
144     } while(--uCount);
145   }
146 }
147
148 static uint16_t *get_downscale_buffer(int *x, int *y, int *w, int *h, int *vram_h)
149 {
150   le16_t *dest = gpu_unai.downscale_vram;
151   const le16_t *src = gpu_unai.vram;
152   bool isRGB24 = (gpu_unai.GPU_GP1 & 0x00200000 ? true : false);
153   int stride = 1024, dstride = 1024, lines = *h, orig_w = *w;
154
155   // PS1 fb read wraps around (fixes black screen in 'Tobal no. 1')
156   unsigned int fb_mask = 1024 * 512 - 1;
157
158   if (*h > 240) {
159     *h /= 2;
160     stride *= 2;
161     lines = *h;
162
163     // Ensure start at a non-skipped line
164     while (*y & gpu_unai.inn.ilace_mask) ++*y;
165   }
166
167   unsigned int fb_offset_src = (*y * dstride + *x) & fb_mask;
168   unsigned int fb_offset_dest = fb_offset_src;
169
170   if (*w == 512 || *w == 640) {
171     *w = 320;
172   }
173
174   switch(orig_w) {
175   case 640:
176     do {
177       scale_640_to_320(dest + fb_offset_dest, src + fb_offset_src, isRGB24);
178       fb_offset_src = (fb_offset_src + stride) & fb_mask;
179       fb_offset_dest = (fb_offset_dest + dstride) & fb_mask;
180     } while(--lines);
181
182     break;
183   case 512:
184     do {
185       scale_512_to_320(dest + fb_offset_dest, src + fb_offset_src, isRGB24);
186       fb_offset_src = (fb_offset_src + stride) & fb_mask;
187       fb_offset_dest = (fb_offset_dest + dstride) & fb_mask;
188     } while(--lines);
189     break;
190   default:
191     size_t size = isRGB24 ? *w * 3 : *w * 2;
192
193     do {
194       memcpy(dest + fb_offset_dest, src + fb_offset_src, size);
195       fb_offset_src = (fb_offset_src + stride) & fb_mask;
196       fb_offset_dest = (fb_offset_dest + dstride) & fb_mask;
197     } while(--lines);
198     break;
199   }
200
201   return (uint16_t *)gpu_unai.downscale_vram;
202 }
203
204 static void map_downscale_buffer(void)
205 {
206   if (gpu_unai.downscale_vram)
207     return;
208
209   gpu_unai.downscale_vram = (le16_t*)gpu.mmap(DOWNSCALE_VRAM_SIZE);
210
211   if (gpu_unai.downscale_vram == NULL || gpu_unai.downscale_vram == (le16_t *)(intptr_t)-1) {
212     fprintf(stderr, "failed to map downscale buffer\n");
213     gpu_unai.downscale_vram = NULL;
214     gpu.get_downscale_buffer = NULL;
215   }
216   else {
217     gpu.get_downscale_buffer = get_downscale_buffer;
218   }
219 }
220
221 static void unmap_downscale_buffer(void)
222 {
223   if (gpu_unai.downscale_vram == NULL)
224     return;
225
226   gpu.munmap(gpu_unai.downscale_vram, DOWNSCALE_VRAM_SIZE);
227   gpu_unai.downscale_vram = NULL;
228   gpu.get_downscale_buffer = NULL;
229 }
230
231 int renderer_init(void)
232 {
233   memset((void*)&gpu_unai, 0, sizeof(gpu_unai));
234   gpu_unai.vram = (le16_t *)gpu.vram;
235
236   // Original standalone gpu_unai initialized TextureWindow[]. I added the
237   //  same behavior here, since it seems unsafe to leave [2],[3] unset when
238   //  using HLE and Rearmed gpu_neon sets this similarly on init. -senquack
239   gpu_unai.TextureWindow[0] = 0;
240   gpu_unai.TextureWindow[1] = 0;
241   gpu_unai.TextureWindow[2] = 255;
242   gpu_unai.TextureWindow[3] = 255;
243   //senquack - new vars must be updated whenever texture window is changed:
244   //           (used for polygon-drawing in gpu_inner.h, gpu_raster_polygon.h)
245   const u32 fb = FIXED_BITS;  // # of fractional fixed-pt bits of u4/v4
246   gpu_unai.inn.u_msk = (((u32)gpu_unai.TextureWindow[2]) << fb) | ((1 << fb) - 1);
247   gpu_unai.inn.v_msk = (((u32)gpu_unai.TextureWindow[3]) << fb) | ((1 << fb) - 1);
248
249   // Configuration options
250   gpu_unai.config = gpu_unai_config_ext;
251   //senquack - disabled, not sure this is needed and would require modifying
252   // sprite-span functions, perhaps unnecessarily. No Abe Oddysey hack was
253   // present in latest PCSX4ALL sources we were using.
254   //gpu_unai.config.enableAbbeyHack = gpu_unai_config_ext.abe_hack;
255   gpu_unai.inn.ilace_mask = gpu_unai.config.ilace_force;
256
257 #if defined(GPU_UNAI_USE_INT_DIV_MULTINV) || (!defined(GPU_UNAI_NO_OLD) && !defined(GPU_UNAI_USE_FLOATMATH))
258   // s_invTable
259   for(int i=1;i<=(1<<TABLE_BITS);++i)
260   {
261     double v = 1.0 / double(i);
262 #ifdef GPU_TABLE_10_BITS
263     v *= double(0xffffffff>>1);
264 #else
265     v *= double(0x80000000);
266 #endif
267     s_invTable[i-1]=s32(v);
268   }
269 #endif
270
271   SetupLightLUT();
272   SetupDitheringConstants();
273
274   if (gpu_unai.config.scale_hires) {
275     map_downscale_buffer();
276   }
277
278   return 0;
279 }
280
281 void renderer_finish(void)
282 {
283   unmap_downscale_buffer();
284 }
285
286 void renderer_notify_res_change(void)
287 {
288   gpu_unai.inn.ilace_mask = gpu_unai.config.ilace_force;
289
290 #ifndef HAVE_PRE_ARMV7 /* XXX */
291   if (gpu_unai.config.scale_hires)
292 #endif
293   {
294     gpu_unai.inn.ilace_mask |= !!(gpu.status & PSX_GPU_STATUS_INTERLACE);
295   }
296
297   /*
298   printf("res change hres: %d   vres: %d   depth: %d   ilace_mask: %d\n",
299       gpu.screen.hres, gpu.screen.vres, (gpu.status & PSX_GPU_STATUS_RGB24) ? 24 : 15,
300       gpu_unai.ilace_mask);
301   */
302 }
303
304 void renderer_notify_scanout_change(int x, int y)
305 {
306 }
307
308 #ifdef USE_GPULIB
309 // Handles GP0 draw settings commands 0xE1...0xE6
310 static void gpuGP0Cmd_0xEx(gpu_unai_t &gpu_unai, u32 cmd_word)
311 {
312   // Assume incoming GP0 command is 0xE1..0xE6, convert to 1..6
313   u8 num = (cmd_word >> 24) & 7;
314   gpu.ex_regs[num] = cmd_word; // Update gpulib register
315   switch (num) {
316     case 1: {
317       // GP0(E1h) - Draw Mode setting (aka "Texpage")
318       u32 cur_texpage = gpu_unai.GPU_GP1 & 0x7FF;
319       u32 new_texpage = cmd_word & 0x7FF;
320       if (cur_texpage != new_texpage) {
321         gpu_unai.GPU_GP1 = (gpu_unai.GPU_GP1 & ~0x7FF) | new_texpage;
322         gpuSetTexture(gpu_unai.GPU_GP1);
323       }
324     } break;
325
326     case 2: {
327       // GP0(E2h) - Texture Window setting
328       if (cmd_word != gpu_unai.TextureWindowCur) {
329         static const u8 TextureMask[32] = {
330           255, 7, 15, 7, 31, 7, 15, 7, 63, 7, 15, 7, 31, 7, 15, 7,
331           127, 7, 15, 7, 31, 7, 15, 7, 63, 7, 15, 7, 31, 7, 15, 7
332         };
333         gpu_unai.TextureWindowCur = cmd_word;
334         gpu_unai.TextureWindow[0] = ((cmd_word >> 10) & 0x1F) << 3;
335         gpu_unai.TextureWindow[1] = ((cmd_word >> 15) & 0x1F) << 3;
336         gpu_unai.TextureWindow[2] = TextureMask[(cmd_word >> 0) & 0x1F];
337         gpu_unai.TextureWindow[3] = TextureMask[(cmd_word >> 5) & 0x1F];
338         gpu_unai.TextureWindow[0] &= ~gpu_unai.TextureWindow[2];
339         gpu_unai.TextureWindow[1] &= ~gpu_unai.TextureWindow[3];
340
341         // Inner loop vars must be updated whenever texture window is changed:
342         const u32 fb = FIXED_BITS;  // # of fractional fixed-pt bits of u4/v4
343         gpu_unai.inn.u_msk = (((u32)gpu_unai.TextureWindow[2]) << fb) | ((1 << fb) - 1);
344         gpu_unai.inn.v_msk = (((u32)gpu_unai.TextureWindow[3]) << fb) | ((1 << fb) - 1);
345
346         gpuSetTexture(gpu_unai.GPU_GP1);
347       }
348     } break;
349
350     case 3: {
351       // GP0(E3h) - Set Drawing Area top left (X1,Y1)
352       gpu_unai.DrawingArea[0] = cmd_word         & 0x3FF;
353       gpu_unai.DrawingArea[1] = (cmd_word >> 10) & 0x3FF;
354     } break;
355
356     case 4: {
357       // GP0(E4h) - Set Drawing Area bottom right (X2,Y2)
358       gpu_unai.DrawingArea[2] = (cmd_word         & 0x3FF) + 1;
359       gpu_unai.DrawingArea[3] = ((cmd_word >> 10) & 0x3FF) + 1;
360     } break;
361
362     case 5: {
363       // GP0(E5h) - Set Drawing Offset (X,Y)
364       gpu_unai.DrawingOffset[0] = GPU_EXPANDSIGN(cmd_word);
365       gpu_unai.DrawingOffset[1] = GPU_EXPANDSIGN(cmd_word >> 11);
366     } break;
367
368     case 6: {
369       // GP0(E6h) - Mask Bit Setting
370       gpu_unai.Masking  = (cmd_word & 0x2) <<  1;
371       gpu_unai.PixelMSB = (cmd_word & 0x1) <<  8;
372     } break;
373   }
374 }
375 #endif
376
377 #include "../gpulib/gpu_timing.h"
378
379 // Strip lower 3 bits of each color and determine if lighting should be used:
380 static inline bool need_lighting(u32 rgb_raw)
381 {
382   return (rgb_raw & HTOLE32(0xF8F8F8)) != HTOLE32(0x808080);
383 }
384
385 static inline void textured_sprite(int &cpu_cycles_sum, int &cpu_cycles)
386 {
387   u32 PRIM = le32_to_u32(gpu_unai.PacketBuffer.U4[0]) >> 24;
388   gpuSetCLUT(le32_to_u32(gpu_unai.PacketBuffer.U4[2]) >> 16);
389   u32 driver_idx = Blending_Mode | gpu_unai.TEXT_MODE | gpu_unai.Masking | Blending | (gpu_unai.PixelMSB>>1);
390   s32 w = 0, h = 0;
391
392   //senquack - Only color 808080h-878787h allows skipping lighting calculation:
393   // This fixes Silent Hill running animation on loading screens:
394   // (On PSX, color values 0x00-0x7F darken the source texture's color,
395   //  0x81-FF lighten textures (ultimately clamped to 0x1F),
396   //  0x80 leaves source texture color unchanged, HOWEVER,
397   //   gpu_unai uses a simple lighting LUT whereby only the upper
398   //   5 bits of an 8-bit color are used, so 0x80-0x87 all behave as
399   //   0x80.
400   //
401   // NOTE: I've changed all textured sprite draw commands here and
402   //  elsewhere to use proper behavior, but left poly commands
403   //  alone, I don't want to slow rendering down too much. (TODO)
404   if (need_lighting(le32_raw(gpu_unai.PacketBuffer.U4[0])))
405     driver_idx |= Lighting;
406   PS driver = gpuSpriteDrivers[driver_idx];
407   PtrUnion packet = { .ptr = (void*)&gpu_unai.PacketBuffer };
408   gpuDrawS(packet, driver, &w, &h);
409   gput_sum(cpu_cycles_sum, cpu_cycles, gput_sprite(w, h));
410 }
411
412 extern const unsigned char cmd_lengths[256];
413
414 int do_cmd_list(u32 *list_, int list_len,
415  int *cycles_sum_out, int *cycles_last, int *last_cmd)
416 {
417   int cpu_cycles_sum = 0, cpu_cycles = *cycles_last;
418   u32 cmd = 0, len, i;
419   le32_t *list = (le32_t *)list_;
420   le32_t *list_start = list;
421   le32_t *list_end = list + list_len;
422
423   if (IS_OLD_RENDERER())
424     return oldunai_do_cmd_list(list_, list_len, cycles_sum_out, cycles_last, last_cmd);
425
426   for (; list < list_end; list += 1 + len)
427   {
428     cmd = le32_to_u32(*list) >> 24;
429     len = cmd_lengths[cmd];
430     if (list + 1 + len > list_end) {
431       cmd = -1;
432       break;
433     }
434
435     #define PRIM cmd
436     gpu_unai.PacketBuffer.U4[0] = list[0];
437     for (i = 1; i <= len; i++)
438       gpu_unai.PacketBuffer.U4[i] = list[i];
439
440     PtrUnion packet = { .ptr = (void*)&gpu_unai.PacketBuffer };
441
442     switch (cmd)
443     {
444       case 0x02:
445         gpuClearImage(packet);
446         gput_sum(cpu_cycles_sum, cpu_cycles,
447            gput_fill(le16_to_s16(packet.U2[4]) & 0x3ff, le16_to_s16(packet.U2[5]) & 0x1ff));
448         break;
449
450       case 0x20:
451       case 0x21:
452       case 0x22:
453       case 0x23: {          // Monochrome 3-pt poly
454         PP driver = gpuPolySpanDrivers[
455           //(gpu_unai.blit_mask?1024:0) |
456           Blending_Mode |
457           gpu_unai.Masking | Blending | gpu_unai.PixelMSB
458         ];
459         gpuDrawPolyF(packet, driver, false);
460         gput_sum(cpu_cycles_sum, cpu_cycles, gput_poly_base());
461       } break;
462
463       case 0x24:
464       case 0x25:
465       case 0x26:
466       case 0x27: {          // Textured 3-pt poly
467         gpuSetCLUT   (le32_to_u32(gpu_unai.PacketBuffer.U4[2]) >> 16);
468         gpuSetTexture(le32_to_u32(gpu_unai.PacketBuffer.U4[4]) >> 16);
469
470         u32 driver_idx =
471           //(gpu_unai.blit_mask?1024:0) |
472           Dithering |
473           Blending_Mode | gpu_unai.TEXT_MODE |
474           gpu_unai.Masking | Blending | gpu_unai.PixelMSB;
475
476         if (!FastLightingEnabled()) {
477           driver_idx |= Lighting;
478         } else {
479           if (!((gpu_unai.PacketBuffer.U1[0]>0x5F) && (gpu_unai.PacketBuffer.U1[1]>0x5F) && (gpu_unai.PacketBuffer.U1[2]>0x5F)))
480             driver_idx |= Lighting;
481         }
482
483         PP driver = gpuPolySpanDrivers[driver_idx];
484         gpuDrawPolyFT(packet, driver, false);
485         gput_sum(cpu_cycles_sum, cpu_cycles, gput_poly_base_t());
486       } break;
487
488       case 0x28:
489       case 0x29:
490       case 0x2A:
491       case 0x2B: {          // Monochrome 4-pt poly
492         PP driver = gpuPolySpanDrivers[
493           //(gpu_unai.blit_mask?1024:0) |
494           Blending_Mode |
495           gpu_unai.Masking | Blending | gpu_unai.PixelMSB
496         ];
497         gpuDrawPolyF(packet, driver, true); // is_quad = true
498         gput_sum(cpu_cycles_sum, cpu_cycles, gput_quad_base());
499       } break;
500
501       case 0x2C:
502       case 0x2D:
503       case 0x2E:
504       case 0x2F: {          // Textured 4-pt poly
505         u32 simplified_count;
506         gpuSetTexture(le32_to_u32(gpu_unai.PacketBuffer.U4[4]) >> 16);
507         if ((simplified_count = prim_try_simplify_quad_t(gpu_unai.PacketBuffer.U4,
508               gpu_unai.PacketBuffer.U4)))
509         {
510           for (i = 0;; ) {
511             textured_sprite(cpu_cycles_sum, cpu_cycles);
512             if (++i >= simplified_count)
513               break;
514             memcpy(&gpu_unai.PacketBuffer.U4[0], &gpu_unai.PacketBuffer.U4[i * 4], 16);
515           }
516           break;
517         }
518         gpuSetCLUT(le32_to_u32(gpu_unai.PacketBuffer.U4[2]) >> 16);
519
520         u32 driver_idx =
521           //(gpu_unai.blit_mask?1024:0) |
522           Dithering |
523           Blending_Mode | gpu_unai.TEXT_MODE |
524           gpu_unai.Masking | Blending | gpu_unai.PixelMSB;
525
526         if (!FastLightingEnabled()) {
527           driver_idx |= Lighting;
528         } else {
529           if (!((gpu_unai.PacketBuffer.U1[0]>0x5F) && (gpu_unai.PacketBuffer.U1[1]>0x5F) && (gpu_unai.PacketBuffer.U1[2]>0x5F)))
530             driver_idx |= Lighting;
531         }
532
533         PP driver = gpuPolySpanDrivers[driver_idx];
534         gpuDrawPolyFT(packet, driver, true); // is_quad = true
535         gput_sum(cpu_cycles_sum, cpu_cycles, gput_quad_base_t());
536       } break;
537
538       case 0x30:
539       case 0x31:
540       case 0x32:
541       case 0x33: {          // Gouraud-shaded 3-pt poly
542         //NOTE: The '129' here is CF_GOURAUD | CF_LIGHT, however
543         // this is an untextured poly, so CF_LIGHT (texture blend)
544         // shouldn't apply. Until the original array of template
545         // instantiation ptrs is fixed, we're stuck with this. (TODO)
546         u8 gouraud = 129;
547         u32 xor_ = 0, rgb0 = le32_raw(gpu_unai.PacketBuffer.U4[0]);
548         for (i = 1; i < 3; i++)
549           xor_ |= rgb0 ^ le32_raw(gpu_unai.PacketBuffer.U4[i * 2]);
550         if ((xor_ & HTOLE32(0xf8f8f8)) == 0)
551           gouraud = 0;
552         PP driver = gpuPolySpanDrivers[
553           //(gpu_unai.blit_mask?1024:0) |
554           Dithering |
555           Blending_Mode |
556           gpu_unai.Masking | Blending | gouraud | gpu_unai.PixelMSB
557         ];
558         if (gouraud)
559           gpuDrawPolyG(packet, driver, false);
560         else
561           gpuDrawPolyF(packet, driver, false, POLYTYPE_G);
562         gput_sum(cpu_cycles_sum, cpu_cycles, gput_poly_base_g());
563       } break;
564
565       case 0x34:
566       case 0x35:
567       case 0x36:
568       case 0x37: {          // Gouraud-shaded, textured 3-pt poly
569         gpuSetCLUT    (le32_to_u32(gpu_unai.PacketBuffer.U4[2]) >> 16);
570         gpuSetTexture (le32_to_u32(gpu_unai.PacketBuffer.U4[5]) >> 16);
571         u8 lighting = Lighting;
572         u8 gouraud = lighting ? (1<<7) : 0;
573         if (lighting) {
574           u32 xor_ = 0, rgb0 = le32_raw(gpu_unai.PacketBuffer.U4[0]);
575           for (i = 1; i < 3; i++)
576             xor_ |= rgb0 ^ le32_raw(gpu_unai.PacketBuffer.U4[i * 3]);
577           if ((xor_ & HTOLE32(0xf8f8f8)) == 0) {
578             gouraud = 0;
579             if (!need_lighting(rgb0))
580               lighting = 0;
581           }
582         }
583         PP driver = gpuPolySpanDrivers[
584           //(gpu_unai.blit_mask?1024:0) |
585           Dithering |
586           Blending_Mode | gpu_unai.TEXT_MODE |
587           gpu_unai.Masking | Blending | gouraud | lighting | gpu_unai.PixelMSB
588         ];
589         if (gouraud)
590           gpuDrawPolyGT(packet, driver, false); // is_quad = true
591         else
592           gpuDrawPolyFT(packet, driver, false, POLYTYPE_GT);
593         gput_sum(cpu_cycles_sum, cpu_cycles, gput_poly_base_gt());
594       } break;
595
596       case 0x38:
597       case 0x39:
598       case 0x3A:
599       case 0x3B: {          // Gouraud-shaded 4-pt poly
600         // See notes regarding '129' for 0x30..0x33 further above -senquack
601         u8 gouraud = 129;
602         u32 xor_ = 0, rgb0 = le32_raw(gpu_unai.PacketBuffer.U4[0]);
603         for (i = 1; i < 4; i++)
604           xor_ |= rgb0 ^ le32_raw(gpu_unai.PacketBuffer.U4[i * 2]);
605         if ((xor_ & HTOLE32(0xf8f8f8)) == 0)
606           gouraud = 0;
607         PP driver = gpuPolySpanDrivers[
608           //(gpu_unai.blit_mask?1024:0) |
609           Dithering |
610           Blending_Mode |
611           gpu_unai.Masking | Blending | gouraud | gpu_unai.PixelMSB
612         ];
613         if (gouraud)
614           gpuDrawPolyG(packet, driver, true); // is_quad = true
615         else
616           gpuDrawPolyF(packet, driver, true, POLYTYPE_G);
617         gput_sum(cpu_cycles_sum, cpu_cycles, gput_quad_base_g());
618       } break;
619
620       case 0x3C:
621       case 0x3D:
622       case 0x3E:
623       case 0x3F: {          // Gouraud-shaded, textured 4-pt poly
624         u32 simplified_count;
625         gpuSetTexture(le32_to_u32(gpu_unai.PacketBuffer.U4[5]) >> 16);
626         if ((simplified_count = prim_try_simplify_quad_gt(gpu_unai.PacketBuffer.U4,
627               gpu_unai.PacketBuffer.U4)))
628         {
629           for (i = 0;; ) {
630             textured_sprite(cpu_cycles_sum, cpu_cycles);
631             if (++i >= simplified_count)
632               break;
633             memcpy(&gpu_unai.PacketBuffer.U4[0], &gpu_unai.PacketBuffer.U4[i * 4], 16);
634           }
635           break;
636         }
637         gpuSetCLUT(le32_to_u32(gpu_unai.PacketBuffer.U4[2]) >> 16);
638         u8 lighting = Lighting;
639         u8 gouraud = lighting ? (1<<7) : 0;
640         if (lighting) {
641           u32 xor_ = 0, rgb0 = le32_raw(gpu_unai.PacketBuffer.U4[0]);
642           for (i = 1; i < 4; i++)
643             xor_ |= rgb0 ^ le32_raw(gpu_unai.PacketBuffer.U4[i * 3]);
644           if ((xor_ & HTOLE32(0xf8f8f8)) == 0) {
645             gouraud = 0;
646             if (!need_lighting(rgb0))
647               lighting = 0;
648           }
649         }
650         PP driver = gpuPolySpanDrivers[
651           //(gpu_unai.blit_mask?1024:0) |
652           Dithering |
653           Blending_Mode | gpu_unai.TEXT_MODE |
654           gpu_unai.Masking | Blending | gouraud | lighting | gpu_unai.PixelMSB
655         ];
656         if (gouraud)
657           gpuDrawPolyGT(packet, driver, true); // is_quad = true
658         else
659           gpuDrawPolyFT(packet, driver, true, POLYTYPE_GT);
660         gput_sum(cpu_cycles_sum, cpu_cycles, gput_quad_base_gt());
661       } break;
662
663       case 0x40:
664       case 0x41:
665       case 0x42:
666       case 0x43: {          // Monochrome line
667         // Shift index right by one, as untextured prims don't use lighting
668         u32 driver_idx = (Blending_Mode | gpu_unai.Masking | Blending | (gpu_unai.PixelMSB>>3)) >> 1;
669         PSD driver = gpuPixelSpanDrivers[driver_idx];
670         gpuDrawLineF(packet, driver);
671         gput_sum(cpu_cycles_sum, cpu_cycles, gput_line(0));
672       } break;
673
674       case 0x48 ... 0x4F: { // Monochrome line strip
675         u32 num_vertexes = 1;
676         le32_t *list_position = &list[2];
677
678         // Shift index right by one, as untextured prims don't use lighting
679         u32 driver_idx = (Blending_Mode | gpu_unai.Masking | Blending | (gpu_unai.PixelMSB>>3)) >> 1;
680         PSD driver = gpuPixelSpanDrivers[driver_idx];
681         gpuDrawLineF(packet, driver);
682
683         while(1)
684         {
685           gpu_unai.PacketBuffer.U4[1] = gpu_unai.PacketBuffer.U4[2];
686           gpu_unai.PacketBuffer.U4[2] = *list_position++;
687           gpuDrawLineF(packet, driver);
688           gput_sum(cpu_cycles_sum, cpu_cycles, gput_line(0));
689
690           num_vertexes++;
691           if(list_position >= list_end) {
692             cmd = -1;
693             goto breakloop;
694           }
695           if((le32_raw(*list_position) & HTOLE32(0xf000f000)) == HTOLE32(0x50005000))
696             break;
697         }
698
699         len += (num_vertexes - 2);
700       } break;
701
702       case 0x50:
703       case 0x51:
704       case 0x52:
705       case 0x53: {          // Gouraud-shaded line
706         // Shift index right by one, as untextured prims don't use lighting
707         u32 driver_idx = (Blending_Mode | gpu_unai.Masking | Blending | (gpu_unai.PixelMSB>>3)) >> 1;
708         // Index MSB selects Gouraud-shaded PixelSpanDriver:
709         driver_idx |= (1 << 5);
710         PSD driver = gpuPixelSpanDrivers[driver_idx];
711         gpuDrawLineG(packet, driver);
712         gput_sum(cpu_cycles_sum, cpu_cycles, gput_line(0));
713       } break;
714
715       case 0x58 ... 0x5F: { // Gouraud-shaded line strip
716         u32 num_vertexes = 1;
717         le32_t *list_position = &list[2];
718
719         // Shift index right by one, as untextured prims don't use lighting
720         u32 driver_idx = (Blending_Mode | gpu_unai.Masking | Blending | (gpu_unai.PixelMSB>>3)) >> 1;
721         // Index MSB selects Gouraud-shaded PixelSpanDriver:
722         driver_idx |= (1 << 5);
723         PSD driver = gpuPixelSpanDrivers[driver_idx];
724         gpuDrawLineG(packet, driver);
725
726         while(1)
727         {
728           gpu_unai.PacketBuffer.U4[0] = gpu_unai.PacketBuffer.U4[2];
729           gpu_unai.PacketBuffer.U4[1] = gpu_unai.PacketBuffer.U4[3];
730           gpu_unai.PacketBuffer.U4[2] = *list_position++;
731           gpu_unai.PacketBuffer.U4[3] = *list_position++;
732           gpuDrawLineG(packet, driver);
733           gput_sum(cpu_cycles_sum, cpu_cycles, gput_line(0));
734
735           num_vertexes++;
736           if(list_position >= list_end) {
737             cmd = -1;
738             goto breakloop;
739           }
740           if((le32_raw(*list_position) & HTOLE32(0xf000f000)) == HTOLE32(0x50005000))
741             break;
742         }
743
744         len += (num_vertexes - 2) * 2;
745       } break;
746
747       case 0x60:
748       case 0x61:
749       case 0x62:
750       case 0x63: {          // Monochrome rectangle (variable size)
751         PT driver = gpuTileDrivers[(Blending_Mode | gpu_unai.Masking | Blending | (gpu_unai.PixelMSB>>3)) >> 1];
752         s32 w = 0, h = 0;
753         gpuDrawT(packet, driver, &w, &h);
754         gput_sum(cpu_cycles_sum, cpu_cycles, gput_sprite(w, h));
755       } break;
756
757       case 0x64:
758       case 0x65:
759       case 0x66:
760       case 0x67:            // Textured rectangle (variable size)
761         textured_sprite(cpu_cycles_sum, cpu_cycles);
762         break;
763
764       case 0x68:
765       case 0x69:
766       case 0x6A:
767       case 0x6B: {          // Monochrome rectangle (1x1 dot)
768         gpu_unai.PacketBuffer.U4[2] = u32_to_le32(0x00010001);
769         PT driver = gpuTileDrivers[(Blending_Mode | gpu_unai.Masking | Blending | (gpu_unai.PixelMSB>>3)) >> 1];
770         s32 w = 0, h = 0;
771         gpuDrawT(packet, driver, &w, &h);
772         gput_sum(cpu_cycles_sum, cpu_cycles, gput_sprite(1, 1));
773       } break;
774
775       case 0x70:
776       case 0x71:
777       case 0x72:
778       case 0x73: {          // Monochrome rectangle (8x8)
779         gpu_unai.PacketBuffer.U4[2] = u32_to_le32(0x00080008);
780         PT driver = gpuTileDrivers[(Blending_Mode | gpu_unai.Masking | Blending | (gpu_unai.PixelMSB>>3)) >> 1];
781         s32 w = 0, h = 0;
782         gpuDrawT(packet, driver, &w, &h);
783         gput_sum(cpu_cycles_sum, cpu_cycles, gput_sprite(w, h));
784       } break;
785
786       case 0x74:
787       case 0x75:
788       case 0x76:
789       case 0x77: {          // Textured rectangle (8x8)
790         gpu_unai.PacketBuffer.U4[3] = u32_to_le32(0x00080008);
791         textured_sprite(cpu_cycles_sum, cpu_cycles);
792       } break;
793
794       case 0x78:
795       case 0x79:
796       case 0x7A:
797       case 0x7B: {          // Monochrome rectangle (16x16)
798         gpu_unai.PacketBuffer.U4[2] = u32_to_le32(0x00100010);
799         PT driver = gpuTileDrivers[(Blending_Mode | gpu_unai.Masking | Blending | (gpu_unai.PixelMSB>>3)) >> 1];
800         s32 w = 0, h = 0;
801         gpuDrawT(packet, driver, &w, &h);
802         gput_sum(cpu_cycles_sum, cpu_cycles, gput_sprite(w, h));
803       } break;
804
805       case 0x7C:
806       case 0x7D:
807       case 0x7E:
808       case 0x7F: {          // Textured rectangle (16x16)
809         gpu_unai.PacketBuffer.U4[3] = u32_to_le32(0x00100010);
810         textured_sprite(cpu_cycles_sum, cpu_cycles);
811       } break;
812
813 #ifdef TEST
814       case 0x80:          //  vid -> vid
815         gpuMoveImage(packet);
816         break;
817
818       case 0xA0:          //  sys -> vid
819       {
820         u32 load_width = list[2] & 0xffff;
821         u32 load_height = list[2] >> 16;
822         u32 load_size = load_width * load_height;
823
824         len += load_size / 2;
825       } break;
826
827       case 0xC0:
828         break;
829 #else
830       case 0x1F:                   //  irq?
831       case 0x80 ... 0x9F:          //  vid -> vid
832       case 0xA0 ... 0xBF:          //  sys -> vid
833       case 0xC0 ... 0xDF:          //  vid -> sys
834         // Handled by gpulib
835         goto breakloop;
836 #endif
837       case 0xE1 ... 0xE6: { // Draw settings
838         gpuGP0Cmd_0xEx(gpu_unai, le32_to_u32(gpu_unai.PacketBuffer.U4[0]));
839       } break;
840     }
841   }
842
843 breakloop:
844   gpu.ex_regs[1] &= ~0x1ff;
845   gpu.ex_regs[1] |= gpu_unai.GPU_GP1 & 0x1ff;
846
847   *cycles_sum_out += cpu_cycles_sum;
848   *cycles_last = cpu_cycles;
849   *last_cmd = cmd;
850   return list - list_start;
851 }
852
853 void renderer_sync_ecmds(u32 *ecmds)
854 {
855   if (!IS_OLD_RENDERER()) {
856     int dummy;
857     do_cmd_list(&ecmds[1], 6, &dummy, &dummy, &dummy);
858   }
859   else
860     oldunai_renderer_sync_ecmds(ecmds);
861 }
862
863 void renderer_update_caches(int x, int y, int w, int h, int state_changed)
864 {
865 }
866
867 void renderer_flush_queues(void)
868 {
869 }
870
871 void renderer_set_interlace(int enable, int is_odd)
872 {
873   renderer_notify_res_change();
874 }
875
876 #include "../../frontend/plugin_lib.h"
877 // Handle any gpulib settings applicable to gpu_unai:
878 void renderer_set_config(const struct rearmed_cbs *cbs)
879 {
880   gpu_unai.vram = (le16_t *)gpu.vram;
881   gpu_unai.config.old_renderer  = cbs->gpu_unai.old_renderer;
882   gpu_unai.config.ilace_force   = cbs->gpu_unai.ilace_force;
883   gpu_unai.config.lighting      = cbs->gpu_unai.lighting;
884   gpu_unai.config.fast_lighting = cbs->gpu_unai.fast_lighting;
885   gpu_unai.config.blending      = cbs->gpu_unai.blending;
886   gpu_unai.config.scale_hires   = cbs->gpu_unai.scale_hires;
887   gpu_unai.config.dithering     = cbs->dithering != 0;
888   gpu_unai.config.force_dithering = cbs->dithering >> 1;
889
890   gpu.state.downscale_enable    = gpu_unai.config.scale_hires;
891   if (gpu_unai.config.scale_hires) {
892     map_downscale_buffer();
893   } else {
894     unmap_downscale_buffer();
895   }
896   oldunai_renderer_set_config(cbs);
897 }
898
899 void renderer_sync(void)
900 {
901 }
902
903 void renderer_notify_update_lace(int updated)
904 {
905 }
906
907 // vim:shiftwidth=2:expandtab