1 /*  Copyright 2012 Guillaume Duhamel
2 
3     This file is part of Yabause.
4 
5     Yabause 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 2 of the License, or
8     (at your option) any later version.
9 
10     Yabause 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 Yabause; if not, write to the Free Software
17     Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
18 */
19 
20 #include "titan.h"
21 #include "../vidshared.h"
22 #include "../vidsoft.h"
23 #include "../threads.h"
24 
25 #include <stdlib.h>
26 
27 /* private */
28 typedef u32 (*TitanBlendFunc)(u32 top, u32 bottom);
29 typedef int FASTCALL (*TitanTransFunc)(u32 pixel);
30 void TitanRenderLines(pixel_t * dispbuffer, int start_line, int end_line);
31 extern int vdp2_interlace;
32 
33 int vidsoft_num_priority_threads = 0;
34 
35 struct PixelData
36 {
37    u32 pixel;
38    u8 priority;
39    u8 linescreen;
40    u8 shadow_type;
41    u8 shadow_enabled;
42 };
43 
44 static struct TitanContext {
45    int inited;
46    struct PixelData * vdp2framebuffer[6];
47    u32 * linescreen[4];
48    int vdp2width;
49    int vdp2height;
50    TitanBlendFunc blend;
51    TitanTransFunc trans;
52    struct PixelData * backscreen;
53    int layer_priority[6];
54 } tt_context = {
55    0,
56    { NULL, NULL, NULL, NULL, NULL, NULL },
57    { NULL, NULL, NULL, NULL },
58    320,
59    224,
60    NULL,NULL,NULL
61 };
62 
63 struct
64 {
65    volatile int need_draw[5];
66    volatile int draw_finished[5];
67    struct
68    {
69       volatile int start;
70       volatile int end;
71    }lines[5];
72 
73    pixel_t * dispbuffer;
74    int use_simplified;
75 }priority_thread_context;
76 
77 #if defined WORDS_BIGENDIAN
78 #ifdef USE_RGB_555
TitanFixAlpha(u32 pixel)79 static INLINE u32 TitanFixAlpha(u32 pixel) { return (((pixel >> 27) & 0x1F) | ((pixel >> 14) & 0x7C0) | (pixel >> 1) & 0xF8); }
80 #elif USE_RGB_565
TitanFixAlpha(u32 pixel)81 static INLINE u32 TitanFixAlpha(u32 pixel) { return (((pixel >> 27) & 0x1F) | ((pixel >> 13) & 0x7E0) | (pixel & 0xF8)); }
82 #else
TitanFixAlpha(u32 pixel)83 static INLINE u32 TitanFixAlpha(u32 pixel) { return ((((pixel & 0x3F) << 2) + 0x03) | (pixel & 0xFFFFFF00)); }
84 #endif
85 
TitanGetAlpha(u32 pixel)86 static INLINE u8 TitanGetAlpha(u32 pixel) { return pixel & 0x3F; }
TitanGetRed(u32 pixel)87 static INLINE u8 TitanGetRed(u32 pixel) { return (pixel >> 8) & 0xFF; }
TitanGetGreen(u32 pixel)88 static INLINE u8 TitanGetGreen(u32 pixel) { return (pixel >> 16) & 0xFF; }
TitanGetBlue(u32 pixel)89 static INLINE u8 TitanGetBlue(u32 pixel) { return (pixel >> 24) & 0xFF; }
TitanCreatePixel(u8 alpha,u8 red,u8 green,u8 blue)90 static INLINE u32 TitanCreatePixel(u8 alpha, u8 red, u8 green, u8 blue) { return alpha | (red << 8) | (green << 16) | (blue << 24); }
91 #else
92 #ifdef USE_RGB_555
TitanFixAlpha(u32 pixel)93 static INLINE u32 TitanFixAlpha(u32 pixel) { return (((pixel << 7) & 0x7C00) | ((pixel >> 6) & 0x3C0) | ((pixel >> 19) & 0x1F)); }
94 #elif USE_RGB_565
TitanFixAlpha(u32 pixel)95 static INLINE u32 TitanFixAlpha(u32 pixel) { return (((pixel << 8) & 0xF800) | ((pixel >> 5) & 0x7C0) | ((pixel >> 19) & 0x1F)); }
96 #else
TitanFixAlpha(u32 pixel)97 static INLINE u32 TitanFixAlpha(u32 pixel) { return ((((pixel & 0x3F000000) << 2) + 0x03000000) | (pixel & 0x00FFFFFF)); }
98 #endif
99 
TitanGetAlpha(u32 pixel)100 static INLINE u8 TitanGetAlpha(u32 pixel) { return (pixel >> 24) & 0x3F; }
TitanGetRed(u32 pixel)101 static INLINE u8 TitanGetRed(u32 pixel) { return (pixel >> 16) & 0xFF; }
TitanGetGreen(u32 pixel)102 static INLINE u8 TitanGetGreen(u32 pixel) { return (pixel >> 8) & 0xFF; }
TitanGetBlue(u32 pixel)103 static INLINE u8 TitanGetBlue(u32 pixel) { return pixel & 0xFF; }
TitanCreatePixel(u8 alpha,u8 red,u8 green,u8 blue)104 static INLINE u32 TitanCreatePixel(u8 alpha, u8 red, u8 green, u8 blue) { return (alpha << 24) | (red << 16) | (green << 8) | blue; }
105 #endif
106 
107 
set_layer_y(const int start_line,int * layer_y)108 void set_layer_y(const int start_line, int * layer_y)
109 {
110    if (vdp2_interlace)
111       *layer_y = start_line / 2;
112    else
113       *layer_y = start_line;
114 }
115 
TitanRenderLinesSimplified(pixel_t * dispbuffer,int start_line,int end_line)116 void TitanRenderLinesSimplified(pixel_t * dispbuffer, int start_line, int end_line)
117 {
118    int x, y, i, layer, j, layer_y;
119    int line_increment, interlace_line;
120    int sorted_layers[8] = { 0 };
121    int num_layers = 0;
122 
123    if (!tt_context.inited || (!tt_context.trans))
124    {
125       return;
126    }
127 
128    Vdp2GetInterlaceInfo(&interlace_line, &line_increment);
129 
130    //pre-sort the layers so it doesn't have to be done per-pixel
131    for (i = 7; i >= 0; i--)
132    {
133       for (layer = TITAN_RBG0; layer >= 0; layer--)
134       {
135          if (tt_context.layer_priority[layer] > 0 && tt_context.layer_priority[layer] == i)
136             sorted_layers[num_layers++] = layer;
137       }
138    }
139 
140    //last layer is always the back screen
141    sorted_layers[num_layers++] = TITAN_BACK;
142 
143    set_layer_y(start_line, &layer_y);
144 
145    for (y = start_line + interlace_line; y < end_line; y += line_increment)
146    {
147       for (x = 0; x < tt_context.vdp2width; x++)
148       {
149          int layer_pos = (layer_y * tt_context.vdp2width) + x;
150          i = (y * tt_context.vdp2width) + x;
151 
152          dispbuffer[i] = 0;
153 
154          for (j = 0; j < num_layers; j++)
155          {
156             struct PixelData sprite = tt_context.vdp2framebuffer[TITAN_SPRITE][layer_pos];
157 
158             int bg_layer = sorted_layers[j];
159 
160             //if the top layer is the back screen
161             if (bg_layer == TITAN_BACK)
162             {
163                //use a sprite pixel if it is not transparent
164                if (sprite.pixel)
165                {
166                   dispbuffer[i] = TitanFixAlpha(sprite.pixel);
167                   break;
168                }
169                else
170                {
171                   //otherwise use the back screen pixel
172                   dispbuffer[i] = TitanFixAlpha(tt_context.backscreen[y].pixel);
173                   break;
174                }
175             }
176             //if the top layer is a sprite pixel
177             else if (sprite.priority >= tt_context.layer_priority[bg_layer])
178             {
179                //use the sprite pixel if it is not transparent
180                if (sprite.pixel)
181                {
182                   dispbuffer[i] = TitanFixAlpha(sprite.pixel);
183                   break;
184                }
185             }
186             else
187             {
188                //use the bg layer if it is not covered with a sprite pixel and not transparent
189                if (tt_context.vdp2framebuffer[bg_layer][layer_pos].pixel)
190                {
191                   dispbuffer[i] = TitanFixAlpha(tt_context.vdp2framebuffer[bg_layer][layer_pos].pixel);
192                   break;
193                }
194             }
195          }
196       }
197       layer_y++;
198    }
199 }
200 
TitanRenderSimplifiedCheck(pixel_t * buf,int start,int end,int can_use_simplified_rendering)201 void TitanRenderSimplifiedCheck(pixel_t * buf, int start, int end, int can_use_simplified_rendering)
202 {
203    if (can_use_simplified_rendering)
204       TitanRenderLinesSimplified(buf, start, end);
205    else
206       TitanRenderLines(buf, start, end);
207 }
208 
209 #define DECLARE_PRIORITY_THREAD(FUNC_NAME, THREAD_NUMBER) \
210 void FUNC_NAME(void* data) \
211 { \
212    for (;;) \
213    { \
214       if (priority_thread_context.need_draw[THREAD_NUMBER]) \
215       { \
216          priority_thread_context.need_draw[THREAD_NUMBER] = 0; \
217          TitanRenderSimplifiedCheck(priority_thread_context.dispbuffer, priority_thread_context.lines[THREAD_NUMBER].start, priority_thread_context.lines[THREAD_NUMBER].end, priority_thread_context.use_simplified); \
218          priority_thread_context.draw_finished[THREAD_NUMBER] = 1; \
219       } \
220       YabThreadSleep(); \
221    } \
222 }
223 
224 DECLARE_PRIORITY_THREAD(VidsoftPriorityThread0, 0);
225 DECLARE_PRIORITY_THREAD(VidsoftPriorityThread1, 1);
226 DECLARE_PRIORITY_THREAD(VidsoftPriorityThread2, 2);
227 DECLARE_PRIORITY_THREAD(VidsoftPriorityThread3, 3);
228 DECLARE_PRIORITY_THREAD(VidsoftPriorityThread4, 4);
229 
TitanBlendPixelsTop(u32 top,u32 bottom)230 static u32 TitanBlendPixelsTop(u32 top, u32 bottom)
231 {
232    u8 alpha, ralpha, tr, tg, tb, br, bg, bb;
233 
234    alpha = (TitanGetAlpha(top) << 2) + 3;
235    ralpha = 0xFF - alpha;
236 
237    tr = (TitanGetRed(top) * alpha) / 0xFF;
238    tg = (TitanGetGreen(top) * alpha) / 0xFF;
239    tb = (TitanGetBlue(top) * alpha) / 0xFF;
240 
241    br = (TitanGetRed(bottom) * ralpha) / 0xFF;
242    bg = (TitanGetGreen(bottom) * ralpha) / 0xFF;
243    bb = (TitanGetBlue(bottom) * ralpha) / 0xFF;
244 
245    return TitanCreatePixel(0x3F, tr + br, tg + bg, tb + bb);
246 }
247 
TitanBlendPixelsBottom(u32 top,u32 bottom)248 static u32 TitanBlendPixelsBottom(u32 top, u32 bottom)
249 {
250    u8 alpha, ralpha, tr, tg, tb, br, bg, bb;
251 
252    if ((top & 0x80000000) == 0) return top;
253 
254    alpha = (TitanGetAlpha(bottom) << 2) + 3;
255    ralpha = 0xFF - alpha;
256 
257    tr = (TitanGetRed(top) * alpha) / 0xFF;
258    tg = (TitanGetGreen(top) * alpha) / 0xFF;
259    tb = (TitanGetBlue(top) * alpha) / 0xFF;
260 
261    br = (TitanGetRed(bottom) * ralpha) / 0xFF;
262    bg = (TitanGetGreen(bottom) * ralpha) / 0xFF;
263    bb = (TitanGetBlue(bottom) * ralpha) / 0xFF;
264 
265    return TitanCreatePixel(TitanGetAlpha(top), tr + br, tg + bg, tb + bb);
266 }
267 
TitanBlendPixelsAdd(u32 top,u32 bottom)268 static u32 TitanBlendPixelsAdd(u32 top, u32 bottom)
269 {
270    u32 r, g, b;
271 
272    r = TitanGetRed(top) + TitanGetRed(bottom);
273    if (r > 0xFF) r = 0xFF;
274 
275    g = TitanGetGreen(top) + TitanGetGreen(bottom);
276    if (g > 0xFF) g = 0xFF;
277 
278    b = TitanGetBlue(top) + TitanGetBlue(bottom);
279    if (b > 0xFF) b = 0xFF;
280 
281    return TitanCreatePixel(0x3F, r, g, b);
282 }
283 
TitanTransAlpha(u32 pixel)284 static INLINE int FASTCALL TitanTransAlpha(u32 pixel)
285 {
286    return TitanGetAlpha(pixel) < 0x3F;
287 }
288 
TitanTransBit(u32 pixel)289 static INLINE int FASTCALL TitanTransBit(u32 pixel)
290 {
291    return pixel & 0x80000000;
292 }
293 
TitanDigPixel(int pos,int y)294 static u32 TitanDigPixel(int pos, int y)
295 {
296    struct PixelData pixel_stack[2] = { 0 };
297 
298    int pixel_stack_pos = 0;
299 
300    int priority;
301 
302    //sort the pixels from highest to lowest priority
303    for (priority = 7; priority > 0; priority--)
304    {
305       int which_layer;
306 
307       for (which_layer = TITAN_SPRITE; which_layer >= 0; which_layer--)
308       {
309          if (tt_context.vdp2framebuffer[which_layer][pos].priority == priority)
310          {
311             pixel_stack[pixel_stack_pos] = tt_context.vdp2framebuffer[which_layer][pos];
312             pixel_stack_pos++;
313 
314             if (pixel_stack_pos == 2)
315                goto finished;//backscreen is unnecessary in this case
316          }
317       }
318    }
319 
320    pixel_stack[pixel_stack_pos] = tt_context.backscreen[pos];
321 
322 finished:
323 
324    if (pixel_stack[0].linescreen)
325    {
326       pixel_stack[0].pixel = tt_context.blend(pixel_stack[0].pixel, tt_context.linescreen[pixel_stack[0].linescreen][y]);
327    }
328 
329    if ((pixel_stack[0].shadow_type == TITAN_MSB_SHADOW) && ((pixel_stack[0].pixel & 0xFFFFFF) == 0))
330    {
331       //transparent sprite shadow
332       if (pixel_stack[1].shadow_enabled)
333       {
334          pixel_stack[0].pixel = TitanBlendPixelsTop(0x20000000, pixel_stack[1].pixel);
335       }
336       else
337       {
338          pixel_stack[0].pixel = pixel_stack[1].pixel;
339       }
340    }
341    else if (pixel_stack[0].shadow_type == TITAN_MSB_SHADOW && ((pixel_stack[0].pixel & 0xFFFFFF) != 0))
342    {
343       if (tt_context.trans(pixel_stack[0].pixel))
344       {
345          u32 bottom = pixel_stack[1].pixel;
346          pixel_stack[0].pixel = tt_context.blend(pixel_stack[0].pixel, bottom);
347       }
348 
349       //sprite self-shadowing, only if sprite window is not enabled
350       if (!(Vdp2Regs->SPCTL & 0x10))
351          pixel_stack[0].pixel = TitanBlendPixelsTop(0x20000000, pixel_stack[0].pixel);
352    }
353    else if (pixel_stack[0].shadow_type == TITAN_NORMAL_SHADOW)
354    {
355       if (pixel_stack[1].shadow_enabled)
356       {
357          pixel_stack[0].pixel = TitanBlendPixelsTop(0x20000000, pixel_stack[1].pixel);
358       }
359       else
360       {
361          pixel_stack[0].pixel = pixel_stack[1].pixel;
362       }
363    }
364    else
365    {
366       if (tt_context.trans(pixel_stack[0].pixel))
367       {
368          u32 bottom = pixel_stack[1].pixel;
369          pixel_stack[0].pixel = tt_context.blend(pixel_stack[0].pixel, bottom);
370       }
371    }
372 
373    return pixel_stack[0].pixel;
374 }
375 
376 /* public */
TitanInit()377 int TitanInit()
378 {
379    int i;
380 
381    if (! tt_context.inited)
382    {
383       for(i = 0;i < 6;i++)
384       {
385          if ((tt_context.vdp2framebuffer[i] = (struct PixelData *)calloc(sizeof(struct PixelData), 704 * 256)) == NULL)
386             return -1;
387       }
388 
389       /* linescreen 0 is not initialized as it's not used... */
390       for(i = 1;i < 4;i++)
391       {
392          if ((tt_context.linescreen[i] = (u32 *)calloc(sizeof(u32), 512)) == NULL)
393             return -1;
394       }
395 
396       if ((tt_context.backscreen = (struct PixelData  *)calloc(sizeof(struct PixelData), 704 * 512)) == NULL)
397          return -1;
398 
399       for (i = 0; i < 5; i++)
400       {
401          priority_thread_context.draw_finished[i] = 1;
402          priority_thread_context.need_draw[i] = 0;
403       }
404 
405       YabThreadStart(YAB_THREAD_VIDSOFT_PRIORITY_0, VidsoftPriorityThread0, NULL);
406       YabThreadStart(YAB_THREAD_VIDSOFT_PRIORITY_1, VidsoftPriorityThread1, NULL);
407       YabThreadStart(YAB_THREAD_VIDSOFT_PRIORITY_2, VidsoftPriorityThread2, NULL);
408       YabThreadStart(YAB_THREAD_VIDSOFT_PRIORITY_3, VidsoftPriorityThread3, NULL);
409       YabThreadStart(YAB_THREAD_VIDSOFT_PRIORITY_4, VidsoftPriorityThread4, NULL);
410 
411       tt_context.inited = 1;
412    }
413 
414    for(i = 0;i < 6;i++)
415       memset(tt_context.vdp2framebuffer[i], 0, sizeof(u32) * 704 * 256);
416 
417    for(i = 1;i < 4;i++)
418       memset(tt_context.linescreen[i], 0, sizeof(u32) * 512);
419 
420    return 0;
421 }
422 
TitanErase()423 void TitanErase()
424 {
425    int i = 0;
426 
427    int height = tt_context.vdp2height;
428 
429    if (vdp2_interlace)
430       height /= 2;
431 
432    for (i = 0; i < 6; i++)
433       memset(tt_context.vdp2framebuffer[i], 0, sizeof(struct PixelData) * tt_context.vdp2width * height);
434 }
435 
TitanDeInit()436 int TitanDeInit()
437 {
438    int i;
439 
440    for(i = 0;i < 6;i++)
441       free(tt_context.vdp2framebuffer[i]);
442 
443    for(i = 1;i < 4;i++)
444       free(tt_context.linescreen[i]);
445 
446    return 0;
447 }
448 
TitanSetResolution(int width,int height)449 void TitanSetResolution(int width, int height)
450 {
451    tt_context.vdp2width = width;
452    tt_context.vdp2height = height;
453 }
454 
TitanGetResolution(int * width,int * height)455 void TitanGetResolution(int * width, int * height)
456 {
457    *width = tt_context.vdp2width;
458    *height = tt_context.vdp2height;
459 }
460 
TitanSetBlendingMode(int blend_mode)461 void TitanSetBlendingMode(int blend_mode)
462 {
463    if (blend_mode == TITAN_BLEND_BOTTOM)
464    {
465       tt_context.blend = TitanBlendPixelsBottom;
466       tt_context.trans = TitanTransBit;
467    }
468    else if (blend_mode == TITAN_BLEND_ADD)
469    {
470       tt_context.blend = TitanBlendPixelsAdd;
471       tt_context.trans = TitanTransBit;
472    }
473    else
474    {
475       tt_context.blend = TitanBlendPixelsTop;
476       tt_context.trans = TitanTransAlpha;
477    }
478 }
479 
TitanPutBackHLine(s32 y,u32 color)480 void TitanPutBackHLine(s32 y, u32 color)
481 {
482    struct PixelData* buffer = &tt_context.backscreen[(y * tt_context.vdp2width)];
483    int i;
484 
485    for (i = 0; i < tt_context.vdp2width; i++)
486       buffer[i].pixel = color;
487 }
488 
TitanPutLineHLine(int linescreen,s32 y,u32 color)489 void TitanPutLineHLine(int linescreen, s32 y, u32 color)
490 {
491    if (linescreen == 0) return;
492 
493    {
494       u32 * buffer = tt_context.linescreen[linescreen] + y;
495       *buffer = color;
496    }
497 }
498 
TitanPutPixel(int priority,s32 x,s32 y,u32 color,int linescreen,vdp2draw_struct * info)499 void TitanPutPixel(int priority, s32 x, s32 y, u32 color, int linescreen, vdp2draw_struct* info)
500 {
501    if (priority == 0) return;
502 
503    {
504       int pos = (y * tt_context.vdp2width) + x;
505       tt_context.vdp2framebuffer[info->titan_which_layer][pos].pixel = color;
506       tt_context.vdp2framebuffer[info->titan_which_layer][pos].priority = priority;
507       tt_context.vdp2framebuffer[info->titan_which_layer][pos].linescreen = linescreen;
508       tt_context.vdp2framebuffer[info->titan_which_layer][pos].shadow_enabled = info->titan_shadow_enabled;
509       tt_context.vdp2framebuffer[info->titan_which_layer][pos].shadow_type = info->titan_shadow_type;
510    }
511 }
512 
TitanPutHLine(int priority,s32 x,s32 y,s32 width,u32 color)513 void TitanPutHLine(int priority, s32 x, s32 y, s32 width, u32 color)
514 {
515    if (priority == 0) return;
516 
517    {
518       struct PixelData * buffer = &tt_context.vdp2framebuffer[priority][ (y * tt_context.vdp2width) + x];
519       int i;
520 
521       for (i = 0; i < width; i++)
522          buffer[i].pixel = color;
523    }
524 }
525 
TitanRenderLines(pixel_t * dispbuffer,int start_line,int end_line)526 void TitanRenderLines(pixel_t * dispbuffer, int start_line, int end_line)
527 {
528    int x, y, layer_y;
529    u32 dot;
530    int line_increment, interlace_line;
531 
532    if (!tt_context.inited || (!tt_context.trans))
533    {
534       return;
535    }
536 
537    Vdp2GetInterlaceInfo(&interlace_line, &line_increment);
538 
539    set_layer_y(start_line, &layer_y);
540 
541    for (y = start_line + interlace_line; y < end_line; y += line_increment)
542    {
543       for (x = 0; x < tt_context.vdp2width; x++)
544       {
545          int i = (y * tt_context.vdp2width) + x;
546          int layer_pos = (layer_y * tt_context.vdp2width) + x;
547 
548          dispbuffer[i] = 0;
549 
550          dot = TitanDigPixel(layer_pos, y);
551 
552          if (dot)
553          {
554             dispbuffer[i] = TitanFixAlpha(dot);
555          }
556       }
557 
558       layer_y++;
559    }
560 }
561 
562 //num + 1 needs to be an even number to avoid issues with interlace modes
VIDSoftSetNumPriorityThreads(int num)563 void VIDSoftSetNumPriorityThreads(int num)
564 {
565    vidsoft_num_priority_threads = num > 5 ? 5 : num;
566 
567    if (num == 2)
568       vidsoft_num_priority_threads = 1;
569 
570    if (num == 4)
571       vidsoft_num_priority_threads = 3;
572 }
573 
TitanStartPriorityThread(int which)574 void TitanStartPriorityThread(int which)
575 {
576    priority_thread_context.need_draw[which] = 1;
577    priority_thread_context.draw_finished[which] = 0;
578    YabThreadWake(YAB_THREAD_VIDSOFT_PRIORITY_0 + which);
579 }
580 
TitanWaitForPriorityThread(int which)581 void TitanWaitForPriorityThread(int which)
582 {
583    while (!priority_thread_context.draw_finished[which]){}
584 }
585 
TitanRenderThreads(pixel_t * dispbuffer,int can_use_simplified)586 void TitanRenderThreads(pixel_t * dispbuffer, int can_use_simplified)
587 {
588    int i;
589    int total_jobs = vidsoft_num_priority_threads + 1;//main thread runs a job
590    int num_lines_per_job = tt_context.vdp2height / total_jobs;
591    int remainder = tt_context.vdp2height % total_jobs;
592    int starts[6] = { 0 };
593    int ends[6] = { 0 };
594 
595    priority_thread_context.dispbuffer = dispbuffer;
596    priority_thread_context.use_simplified = can_use_simplified;
597 
598    for (i = 0; i < total_jobs; i++)
599    {
600       starts[i] = num_lines_per_job * i;
601       ends[i] = ((i + 1) * num_lines_per_job);
602    }
603 
604    for (i = 0; i < vidsoft_num_priority_threads; i++)
605    {
606       priority_thread_context.lines[i].start = starts[i+1];
607       priority_thread_context.lines[i].end = ends[i+1];
608    }
609 
610    //put any remaining lines on the last thread
611    priority_thread_context.lines[vidsoft_num_priority_threads - 1].end += remainder;
612 
613    for (i = 0; i < vidsoft_num_priority_threads; i++)
614    {
615       TitanStartPriorityThread(i);
616    }
617 
618    TitanRenderSimplifiedCheck(dispbuffer, starts[0], ends[0], can_use_simplified);
619 
620    for (i = 0; i < vidsoft_num_priority_threads; i++)
621    {
622       TitanWaitForPriorityThread(i);
623    }
624 }
625 
TitanRender(pixel_t * dispbuffer)626 void TitanRender(pixel_t * dispbuffer)
627 {
628    int can_use_simplified_rendering = 1;
629 
630    if (!tt_context.inited || (!tt_context.trans))
631    {
632       return;
633    }
634 
635    //using color calculation
636    if ((Vdp2Regs->CCCTL & 0x807f) != 0)
637       can_use_simplified_rendering = 0;
638 
639    //using special priority
640    if ((Vdp2Regs->SFPRMD & 0x3ff) != 0)
641       can_use_simplified_rendering = 0;
642 
643    //using line screen
644    if ((Vdp2Regs->LNCLEN & 0x1f) != 0)
645       can_use_simplified_rendering = 0;
646 
647    //using shadow
648    if ((Vdp2Regs->SDCTL & 0x13F) != 0)
649       can_use_simplified_rendering = 0;
650 
651    tt_context.layer_priority[TITAN_NBG0] = Vdp2Regs->PRINA & 0x7;
652    tt_context.layer_priority[TITAN_NBG1] = ((Vdp2Regs->PRINA >> 8) & 0x7);
653    tt_context.layer_priority[TITAN_NBG2] = (Vdp2Regs->PRINB & 0x7);
654    tt_context.layer_priority[TITAN_NBG3] = ((Vdp2Regs->PRINB >> 8) & 0x7);
655    tt_context.layer_priority[TITAN_RBG0] = (Vdp2Regs->PRIR & 0x7);
656 
657    if (vidsoft_num_priority_threads > 0)
658    {
659       TitanRenderThreads(dispbuffer, can_use_simplified_rendering);
660    }
661    else
662    {
663       TitanRenderSimplifiedCheck(dispbuffer, 0, tt_context.vdp2height, can_use_simplified_rendering);
664    }
665 }
666 
667 #ifdef WORDS_BIGENDIAN
TitanWriteColor(pixel_t * dispbuffer,s32 bufwidth,s32 x,s32 y,u32 color)668 void TitanWriteColor(pixel_t * dispbuffer, s32 bufwidth, s32 x, s32 y, u32 color)
669 {
670    int pos = (y * bufwidth) + x;
671    pixel_t * buffer = dispbuffer + pos;
672    *buffer = ((color >> 24) & 0xFF) | ((color >> 8) & 0xFF00) | ((color & 0xFF00) << 8) | ((color & 0xFF) << 24);
673 }
674 #else
TitanWriteColor(pixel_t * dispbuffer,s32 bufwidth,s32 x,s32 y,u32 color)675 void TitanWriteColor(pixel_t * dispbuffer, s32 bufwidth, s32 x, s32 y, u32 color)
676 {
677    int pos = (y * bufwidth) + x;
678    pixel_t * buffer = dispbuffer + pos;
679    *buffer = color;
680 }
681 #endif
682