1/*
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2025 Sam Lantinga <slouken@libsdl.org>
4
5 This software is provided 'as-is', without any express or implied
6 warranty. In no event will the authors be held liable for any damages
7 arising from the use of this software.
8
9 Permission is granted to anyone to use this software for any purpose,
10 including commercial applications, and to alter it and redistribute it
11 freely, subject to the following restrictions:
12
13 1. The origin of this software must not be misrepresented; you must not
14 claim that you wrote the original software. If you use this software
15 in a product, an acknowledgment in the product documentation would be
16 appreciated but is not required.
17 2. Altered source versions must be plainly marked as such, and must not be
18 misrepresented as being the original software.
19 3. This notice may not be removed or altered from any source distribution.
20*/
21#include "SDL_internal.h"
22
23#ifdef SDL_VIDEO_RENDER_PS2
24
25#include "../SDL_sysrender.h"
26
27#include <kernel.h>
28#include <malloc.h>
29#include <gsKit.h>
30#include <dmaKit.h>
31#include <gsToolkit.h>
32
33#ifdef HAVE_GCC_DIAGNOSTIC_PRAGMA
34#pragma GCC diagnostic push
35#pragma GCC diagnostic ignored "-Wdeclaration-after-statement"
36#endif
37
38#include <gsInline.h>
39
40#ifdef HAVE_GCC_DIAGNOSTIC_PRAGMA
41#pragma GCC diagnostic pop
42#endif
43
44// turn black GS Screen
45#define GS_BLACK GS_SETREG_RGBA(0x00, 0x00, 0x00, 0x80)
46// Size of Persistent drawbuffer (Single Buffered)
47#define RENDER_QUEUE_PER_POOLSIZE 1024 * 256 // 256K of persistent renderqueue
48/* Size of Oneshot drawbuffer (Double Buffered, so it uses this size * 2) */
49#define RENDER_QUEUE_OS_POOLSIZE 1024 * 1024 * 2 // 2048K of oneshot renderqueue
50
51typedef struct
52{
53 GSGLOBAL *gsGlobal;
54 uint64_t drawColor;
55 SDL_Rect *viewport;
56 int32_t vsync_callback_id;
57 int vsync; // 0 (Disabled), 1 (Enabled), -1 (Dynamic)
58} PS2_RenderData;
59
60static int vsync_sema_id = 0;
61
62// PRIVATE METHODS
63static int vsync_handler(void)
64{
65 iSignalSema(vsync_sema_id);
66
67 ExitHandler();
68 return 0;
69}
70
71// Copy of gsKit_sync_flip, but without the 'flip'
72static void gsKit_sync(GSGLOBAL *gsGlobal)
73{
74 if (!gsGlobal->FirstFrame) {
75 WaitSema(vsync_sema_id);
76 }
77 while (PollSema(vsync_sema_id) >= 0)
78 ;
79}
80
81// Copy of gsKit_sync_flip, but without the 'sync'
82static void gsKit_flip(GSGLOBAL *gsGlobal)
83{
84 if (!gsGlobal->FirstFrame) {
85 if (gsGlobal->DoubleBuffering == GS_SETTING_ON) {
86 GS_SET_DISPFB2(gsGlobal->ScreenBuffer[gsGlobal->ActiveBuffer & 1] / 8192,
87 gsGlobal->Width / 64, gsGlobal->PSM, 0, 0);
88
89 gsGlobal->ActiveBuffer ^= 1;
90 }
91 }
92
93 gsKit_setactive(gsGlobal);
94}
95
96static int PixelFormatToPS2PSM(Uint32 format)
97{
98 switch (format) {
99 case SDL_PIXELFORMAT_ABGR1555:
100 return GS_PSM_CT16;
101 default:
102 return GS_PSM_CT32;
103 }
104}
105
106static gs_rgbaq float_color_to_RGBAQ(const SDL_FColor *color, float color_scale)
107{
108 uint8_t colorR = (uint8_t)SDL_roundf(SDL_clamp(color->r * color_scale, 0.0f, 1.0f) * 255.0f);
109 uint8_t colorG = (uint8_t)SDL_roundf(SDL_clamp(color->g * color_scale, 0.0f, 1.0f) * 255.0f);
110 uint8_t colorB = (uint8_t)SDL_roundf(SDL_clamp(color->b * color_scale, 0.0f, 1.0f) * 255.0f);
111 uint8_t colorA = (uint8_t)SDL_roundf(SDL_clamp(color->a, 0.0f, 1.0f) * 255.0f);
112
113 return color_to_RGBAQ(colorR, colorG, colorB, colorA, 0x00);
114}
115
116static uint64_t float_GS_SETREG_RGBAQ(const SDL_FColor *color, float color_scale)
117{
118 uint8_t colorR = (uint8_t)SDL_roundf(SDL_clamp(color->r * color_scale, 0.0f, 1.0f) * 255.0f);
119 uint8_t colorG = (uint8_t)SDL_roundf(SDL_clamp(color->g * color_scale, 0.0f, 1.0f) * 255.0f);
120 uint8_t colorB = (uint8_t)SDL_roundf(SDL_clamp(color->b * color_scale, 0.0f, 1.0f) * 255.0f);
121 uint8_t colorA = (uint8_t)SDL_roundf(SDL_clamp(color->a, 0.0f, 1.0f) * 255.0f);
122
123 return GS_SETREG_RGBAQ(colorR, colorG, colorB, colorA, 0x00);
124}
125
126static void PS2_WindowEvent(SDL_Renderer *renderer, const SDL_WindowEvent *event)
127{
128}
129
130static bool PS2_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SDL_PropertiesID create_props)
131{
132 GSTEXTURE *ps2_tex = (GSTEXTURE *)SDL_calloc(1, sizeof(GSTEXTURE));
133
134 if (!ps2_tex) {
135 return false;
136 }
137
138 ps2_tex->Width = texture->w;
139 ps2_tex->Height = texture->h;
140 ps2_tex->PSM = PixelFormatToPS2PSM(texture->format);
141 ps2_tex->Mem = SDL_aligned_alloc(128, gsKit_texture_size_ee(ps2_tex->Width, ps2_tex->Height, ps2_tex->PSM));
142
143 if (!ps2_tex->Mem) {
144 SDL_free(ps2_tex);
145 return false;
146 }
147
148 texture->internal = ps2_tex;
149
150 return true;
151}
152
153static bool PS2_LockTexture(SDL_Renderer *renderer, SDL_Texture *texture,
154 const SDL_Rect *rect, void **pixels, int *pitch)
155{
156 GSTEXTURE *ps2_texture = (GSTEXTURE *)texture->internal;
157
158 *pixels =
159 (void *)((Uint8 *)ps2_texture->Mem + rect->y * ps2_texture->Width * SDL_BYTESPERPIXEL(texture->format) +
160 rect->x * SDL_BYTESPERPIXEL(texture->format));
161 *pitch = ps2_texture->Width * SDL_BYTESPERPIXEL(texture->format);
162 return true;
163}
164
165static void PS2_UnlockTexture(SDL_Renderer *renderer, SDL_Texture *texture)
166{
167 GSTEXTURE *ps2_texture = (GSTEXTURE *)texture->internal;
168 PS2_RenderData *data = (PS2_RenderData *)renderer->internal;
169
170 gsKit_TexManager_invalidate(data->gsGlobal, ps2_texture);
171}
172
173static bool PS2_UpdateTexture(SDL_Renderer *renderer, SDL_Texture *texture,
174 const SDL_Rect *rect, const void *pixels, int pitch)
175{
176 const Uint8 *src;
177 Uint8 *dst;
178 int row, length, dpitch;
179 src = pixels;
180
181 PS2_LockTexture(renderer, texture, rect, (void **)&dst, &dpitch);
182 length = rect->w * SDL_BYTESPERPIXEL(texture->format);
183 if (length == pitch && length == dpitch) {
184 SDL_memcpy(dst, src, length * rect->h);
185 } else {
186 for (row = 0; row < rect->h; ++row) {
187 SDL_memcpy(dst, src, length);
188 src += pitch;
189 dst += dpitch;
190 }
191 }
192
193 PS2_UnlockTexture(renderer, texture);
194
195 return true;
196}
197
198static bool PS2_SetRenderTarget(SDL_Renderer *renderer, SDL_Texture *texture)
199{
200 return true;
201}
202
203static bool PS2_QueueSetViewport(SDL_Renderer *renderer, SDL_RenderCommand *cmd)
204{
205 PS2_RenderData *data = (PS2_RenderData *)renderer->internal;
206 const SDL_Rect *viewport = &cmd->data.viewport.rect;
207 data->viewport = (SDL_Rect *)viewport;
208
209 data->gsGlobal->OffsetX = (int)((2048.0f + (float)viewport->x) * 16.0f);
210 data->gsGlobal->OffsetY = (int)((2048.0f + (float)viewport->y) * 16.0f);
211 gsKit_set_scissor(data->gsGlobal, GS_SETREG_SCISSOR(viewport->x, viewport->x + viewport->w, viewport->y, viewport->y + viewport->h));
212
213 return true;
214}
215
216static bool PS2_QueueNoOp(SDL_Renderer *renderer, SDL_RenderCommand *cmd)
217{
218 return true; // nothing to do in this backend.
219}
220
221static bool PS2_QueueDrawPoints(SDL_Renderer *renderer, SDL_RenderCommand *cmd, const SDL_FPoint *points, int count)
222{
223 PS2_RenderData *data = (PS2_RenderData *)renderer->internal;
224 GSPRIMPOINT *vertices = (GSPRIMPOINT *)SDL_AllocateRenderVertices(renderer, count * sizeof(GSPRIMPOINT), 4, &cmd->data.draw.first);
225 gs_rgbaq rgbaq;
226 int i;
227
228 if (!vertices) {
229 return false;
230 }
231
232 cmd->data.draw.count = count;
233
234 rgbaq = float_color_to_RGBAQ(&cmd->data.draw.color, cmd->data.draw.color_scale);
235
236 for (i = 0; i < count; i++, vertices++, points++) {
237 vertices->xyz2 = vertex_to_XYZ2(data->gsGlobal, points->x, points->y, 0);
238 vertices->rgbaq = rgbaq;
239 }
240 return true;
241}
242
243static bool PS2_QueueGeometry(SDL_Renderer *renderer, SDL_RenderCommand *cmd, SDL_Texture *texture,
244 const float *xy, int xy_stride, const SDL_FColor *color, int color_stride, const float *uv, int uv_stride,
245 int num_vertices, const void *indices, int num_indices, int size_indices,
246 float scale_x, float scale_y)
247{
248 int i;
249 int count = indices ? num_indices : num_vertices;
250 PS2_RenderData *data = (PS2_RenderData *)renderer->internal;
251 const float color_scale = cmd->data.draw.color_scale;
252
253 cmd->data.draw.count = count;
254 size_indices = indices ? size_indices : 0;
255
256 if (texture) {
257 GSPRIMUVPOINT *vertices = (GSPRIMUVPOINT *) SDL_AllocateRenderVertices(renderer, count * sizeof(GSPRIMUVPOINT), 4, &cmd->data.draw.first);
258 GSTEXTURE *ps2_tex = (GSTEXTURE *) texture->internal;
259
260 if (!vertices) {
261 return false;
262 }
263
264 for (i = 0; i < count; i++) {
265 int j;
266 float *xy_;
267 float *uv_;
268 SDL_FColor *col_;
269 if (size_indices == 4) {
270 j = ((const Uint32 *)indices)[i];
271 } else if (size_indices == 2) {
272 j = ((const Uint16 *)indices)[i];
273 } else if (size_indices == 1) {
274 j = ((const Uint8 *)indices)[i];
275 } else {
276 j = i;
277 }
278
279 xy_ = (float *)((char *)xy + j * xy_stride);
280 col_ = (SDL_FColor *)((char *)color + j * color_stride);
281 uv_ = (float *)((char *)uv + j * uv_stride);
282
283 vertices->xyz2 = vertex_to_XYZ2(data->gsGlobal, xy_[0] * scale_x, xy_[1] * scale_y, 0);
284 vertices->rgbaq = float_color_to_RGBAQ(col_, color_scale);
285 vertices->uv = vertex_to_UV(ps2_tex, uv_[0] * ps2_tex->Width, uv_[1] * ps2_tex->Height);
286
287 vertices++;
288 }
289
290 } else {
291 GSPRIMPOINT *vertices = (GSPRIMPOINT *)SDL_AllocateRenderVertices(renderer, count * sizeof(GSPRIMPOINT), 4, &cmd->data.draw.first);
292
293 if (!vertices) {
294 return false;
295 }
296
297 for (i = 0; i < count; i++) {
298 int j;
299 float *xy_;
300 SDL_FColor *col_;
301 if (size_indices == 4) {
302 j = ((const Uint32 *)indices)[i];
303 } else if (size_indices == 2) {
304 j = ((const Uint16 *)indices)[i];
305 } else if (size_indices == 1) {
306 j = ((const Uint8 *)indices)[i];
307 } else {
308 j = i;
309 }
310
311 xy_ = (float *)((char *)xy + j * xy_stride);
312 col_ = (SDL_FColor *)((char *)color + j * color_stride);
313
314 vertices->xyz2 = vertex_to_XYZ2(data->gsGlobal, xy_[0] * scale_x, xy_[1] * scale_y, 0);
315 vertices->rgbaq = float_color_to_RGBAQ(col_, color_scale);
316
317 vertices++;
318 }
319 }
320
321 return true;
322}
323
324static bool PS2_RenderSetViewPort(SDL_Renderer *renderer, SDL_RenderCommand *cmd)
325{
326 return true; // nothing to do in this backend.
327}
328
329static bool PS2_RenderSetClipRect(SDL_Renderer *renderer, SDL_RenderCommand *cmd)
330{
331 PS2_RenderData *data = (PS2_RenderData *)renderer->internal;
332 SDL_Rect *viewport = data->viewport;
333
334 const SDL_Rect *rect = &cmd->data.cliprect.rect;
335
336 if (cmd->data.cliprect.enabled) {
337 // We need to do it relative to saved viewport
338 viewport->x += rect->x;
339 viewport->y += rect->y;
340 viewport->w = SDL_min(viewport->w, rect->w);
341 viewport->h = SDL_min(viewport->h, rect->h);
342 }
343 gsKit_set_scissor(data->gsGlobal, GS_SETREG_SCISSOR(viewport->x, viewport->x + viewport->w, viewport->y, viewport->y + viewport->h));
344
345 return true;
346}
347
348static bool PS2_RenderSetDrawColor(SDL_Renderer *renderer, SDL_RenderCommand *cmd)
349{
350 PS2_RenderData *data = (PS2_RenderData *)renderer->internal;
351
352 data->drawColor = float_GS_SETREG_RGBAQ(&cmd->data.color.color, cmd->data.color.color_scale);
353 return true;
354}
355
356static bool PS2_RenderClear(SDL_Renderer *renderer, SDL_RenderCommand *cmd)
357{
358 int offsetX, offsetY;
359 SDL_Rect *viewport;
360
361 PS2_RenderData *data = (PS2_RenderData *)renderer->internal;
362
363 // Clear the screen, so let's put default viewport
364 gsKit_set_scissor(data->gsGlobal, GS_SCISSOR_RESET);
365 // Put back original offset
366 offsetX = data->gsGlobal->OffsetX;
367 offsetY = data->gsGlobal->OffsetY;
368 data->gsGlobal->OffsetX = (int)(2048.0f * 16.0f);
369 data->gsGlobal->OffsetY = (int)(2048.0f * 16.0f);
370 gsKit_clear(data->gsGlobal, float_GS_SETREG_RGBAQ(&cmd->data.color.color, cmd->data.color.color_scale));
371
372 // Put back original offset
373 data->gsGlobal->OffsetX = offsetX;
374 data->gsGlobal->OffsetY = offsetY;
375
376 // // Put back view port
377 viewport = data->viewport;
378 gsKit_set_scissor(data->gsGlobal, GS_SETREG_SCISSOR(viewport->x, viewport->x + viewport->w, viewport->y, viewport->y + viewport->h));
379
380 return true;
381}
382
383static void PS2_SetBlendMode(PS2_RenderData *data, int blendMode)
384{
385#define A_COLOR_SOURCE 0
386#define A_COLOR_DEST 1
387#define A_COLOR_NULL 2
388#define A_ALPHA_SOURCE 0
389#define A_ALPHA_DEST 1
390#define A_ALPHA_FIX 2
391
392 switch (blendMode) {
393 case SDL_BLENDMODE_NONE:
394 {
395 data->gsGlobal->PrimAlphaEnable = GS_SETTING_OFF;
396 break;
397 }
398 case SDL_BLENDMODE_BLEND:
399 {
400 gsKit_set_primalpha(data->gsGlobal, GS_SETREG_ALPHA(A_COLOR_SOURCE, A_COLOR_DEST, A_ALPHA_SOURCE, A_COLOR_DEST, 0), 0);
401 data->gsGlobal->PrimAlphaEnable = GS_SETTING_ON;
402 break;
403 }
404 case SDL_BLENDMODE_BLEND_PREMULTIPLIED:
405 {
406 // FIXME: What are the settings for this?
407 gsKit_set_primalpha(data->gsGlobal, GS_SETREG_ALPHA(A_COLOR_SOURCE, A_COLOR_DEST, A_ALPHA_SOURCE, A_COLOR_DEST, 0), 0);
408 data->gsGlobal->PrimAlphaEnable = GS_SETTING_ON;
409 break;
410 }
411 case SDL_BLENDMODE_ADD:
412 {
413 gsKit_set_primalpha(data->gsGlobal, GS_SETREG_ALPHA(A_COLOR_SOURCE, A_COLOR_NULL, A_ALPHA_FIX, A_COLOR_DEST, 0x80), 0);
414 data->gsGlobal->PrimAlphaEnable = GS_SETTING_ON;
415 break;
416 }
417 case SDL_BLENDMODE_ADD_PREMULTIPLIED:
418 {
419 // FIXME: What are the settings for this?
420 gsKit_set_primalpha(data->gsGlobal, GS_SETREG_ALPHA(A_COLOR_SOURCE, A_COLOR_NULL, A_ALPHA_FIX, A_COLOR_DEST, 0x80), 0);
421 data->gsGlobal->PrimAlphaEnable = GS_SETTING_ON;
422 break;
423 }
424 case SDL_BLENDMODE_MUL:
425 case SDL_BLENDMODE_MOD:
426 {
427 // We don't fully support MOD and MUL, however this is the best we can do
428 gsKit_set_primalpha(data->gsGlobal, GS_SETREG_ALPHA(A_COLOR_DEST, A_COLOR_NULL, A_ALPHA_SOURCE, A_COLOR_SOURCE, 0x80), 0);
429 data->gsGlobal->PrimAlphaEnable = GS_SETTING_ON;
430 break;
431 }
432 }
433}
434
435static bool PS2_RenderGeometry(SDL_Renderer *renderer, void *vertices, SDL_RenderCommand *cmd)
436{
437 PS2_RenderData *data = (PS2_RenderData *)renderer->internal;
438 const size_t count = cmd->data.draw.count;
439
440 PS2_SetBlendMode(data, cmd->data.draw.blend);
441
442 if (cmd->data.draw.texture) {
443 const GSPRIMUVPOINT *verts = (GSPRIMUVPOINT *) (vertices + cmd->data.draw.first);
444 GSTEXTURE *ps2_tex = (GSTEXTURE *)cmd->data.draw.texture->internal;
445
446 switch (cmd->data.draw.texture_scale_mode) {
447 case SDL_SCALEMODE_PIXELART:
448 case SDL_SCALEMODE_NEAREST:
449 ps2_tex->Filter = GS_FILTER_NEAREST;
450 break;
451 case SDL_SCALEMODE_LINEAR:
452 ps2_tex->Filter = GS_FILTER_LINEAR;
453 break;
454 default:
455 break;
456 }
457 gsKit_TexManager_bind(data->gsGlobal, ps2_tex);
458 gsKit_prim_list_triangle_goraud_texture_uv_3d(data->gsGlobal, ps2_tex, count, verts);
459 } else {
460 const GSPRIMPOINT *verts = (GSPRIMPOINT *)(vertices + cmd->data.draw.first);
461 gsKit_prim_list_triangle_gouraud_3d(data->gsGlobal, count, verts);
462 }
463
464 return true;
465}
466
467static bool PS2_RenderLines(SDL_Renderer *renderer, void *vertices, SDL_RenderCommand *cmd)
468{
469 PS2_RenderData *data = (PS2_RenderData *)renderer->internal;
470 const size_t count = cmd->data.draw.count;
471 const GSPRIMPOINT *verts = (GSPRIMPOINT *)(vertices + cmd->data.draw.first);
472
473 PS2_SetBlendMode(data, cmd->data.draw.blend);
474 gsKit_prim_list_line_goraud_3d(data->gsGlobal, count, verts);
475
476 // We're done!
477 return true;
478}
479
480static bool PS2_RenderPoints(SDL_Renderer *renderer, void *vertices, SDL_RenderCommand *cmd)
481{
482 PS2_RenderData *data = (PS2_RenderData *)renderer->internal;
483 const size_t count = cmd->data.draw.count;
484 const GSPRIMPOINT *verts = (GSPRIMPOINT *)(vertices + cmd->data.draw.first);
485
486 PS2_SetBlendMode(data, cmd->data.draw.blend);
487 gsKit_prim_list_points(data->gsGlobal, count, verts);
488
489 // We're done!
490 return true;
491}
492
493static void PS2_InvalidateCachedState(SDL_Renderer *renderer)
494{
495 // currently this doesn't do anything. If this needs to do something (and someone is mixing their own rendering calls in!), update this.
496}
497
498static bool PS2_RunCommandQueue(SDL_Renderer *renderer, SDL_RenderCommand *cmd, void *vertices, size_t vertsize)
499{
500 while (cmd) {
501 switch (cmd->command) {
502 case SDL_RENDERCMD_SETVIEWPORT:
503 {
504 PS2_RenderSetViewPort(renderer, cmd);
505 // FIXME: We need to update the clip rect too, see https://github.com/libsdl-org/SDL/issues/9094
506 break;
507 }
508 case SDL_RENDERCMD_SETCLIPRECT:
509 {
510 PS2_RenderSetClipRect(renderer, cmd);
511 break;
512 }
513 case SDL_RENDERCMD_SETDRAWCOLOR:
514 {
515 PS2_RenderSetDrawColor(renderer, cmd);
516 break;
517 }
518 case SDL_RENDERCMD_CLEAR:
519 {
520 PS2_RenderClear(renderer, cmd);
521 break;
522 }
523 case SDL_RENDERCMD_DRAW_POINTS:
524 {
525 PS2_RenderPoints(renderer, vertices, cmd);
526 break;
527 }
528 case SDL_RENDERCMD_DRAW_LINES:
529 {
530 PS2_RenderLines(renderer, vertices, cmd);
531 break;
532 }
533 case SDL_RENDERCMD_FILL_RECTS: // unused
534 break;
535 case SDL_RENDERCMD_COPY: // unused
536 break;
537 case SDL_RENDERCMD_COPY_EX: // unused
538 break;
539 case SDL_RENDERCMD_GEOMETRY:
540 {
541 PS2_RenderGeometry(renderer, vertices, cmd);
542 break;
543 }
544 case SDL_RENDERCMD_NO_OP:
545 break;
546 }
547 cmd = cmd->next;
548 }
549 return true;
550}
551
552static bool PS2_RenderPresent(SDL_Renderer *renderer)
553{
554 PS2_RenderData *data = (PS2_RenderData *)renderer->internal;
555
556 if (data->gsGlobal->DoubleBuffering == GS_SETTING_OFF) {
557 if (data->vsync == -1) { // Dynamic
558 gsKit_sync(data->gsGlobal);
559 } else if (data->vsync == 1) {
560 gsKit_vsync_wait();
561 }
562 gsKit_queue_exec(data->gsGlobal);
563 } else {
564 gsKit_queue_exec(data->gsGlobal);
565 gsKit_finish();
566 if (data->vsync == -1) { // Dynamic
567 gsKit_sync(data->gsGlobal);
568 } else if (data->vsync == 1) {
569 gsKit_vsync_wait();
570 }
571 gsKit_flip(data->gsGlobal);
572 }
573 gsKit_TexManager_nextFrame(data->gsGlobal);
574 gsKit_clear(data->gsGlobal, GS_BLACK);
575 return true;
576}
577
578static void PS2_DestroyTexture(SDL_Renderer *renderer, SDL_Texture *texture)
579{
580 GSTEXTURE *ps2_texture = (GSTEXTURE *)texture->internal;
581 PS2_RenderData *data = (PS2_RenderData *)renderer->internal;
582
583 if (!data) {
584 return;
585 }
586
587 if (!ps2_texture) {
588 return;
589 }
590
591 // Free from vram
592 gsKit_TexManager_free(data->gsGlobal, ps2_texture);
593
594 SDL_aligned_free(ps2_texture->Mem);
595 SDL_free(ps2_texture);
596 texture->internal = NULL;
597}
598
599static void PS2_DestroyRenderer(SDL_Renderer *renderer)
600{
601 PS2_RenderData *data = (PS2_RenderData *)renderer->internal;
602
603 if (data) {
604 gsKit_clear(data->gsGlobal, GS_BLACK);
605 gsKit_vram_clear(data->gsGlobal);
606 gsKit_deinit_global(data->gsGlobal);
607 gsKit_remove_vsync_handler(data->vsync_callback_id);
608
609 SDL_free(data);
610 }
611
612 if (vsync_sema_id >= 0) {
613 DeleteSema(vsync_sema_id);
614 }
615}
616
617static bool PS2_SetVSync(SDL_Renderer *renderer, const int vsync)
618{
619 PS2_RenderData *data = (PS2_RenderData *)renderer->internal;
620 switch (vsync) {
621 case -1:
622 case 0:
623 case 1:
624 // Supported
625 break;
626 default:
627 return SDL_Unsupported();
628 }
629 data->vsync = vsync;
630 return true;
631}
632
633static bool PS2_CreateRenderer(SDL_Renderer *renderer, SDL_Window *window, SDL_PropertiesID create_props)
634{
635 PS2_RenderData *data;
636 GSGLOBAL *gsGlobal;
637 ee_sema_t sema;
638
639 SDL_SetupRendererColorspace(renderer, create_props);
640
641 if (renderer->output_colorspace != SDL_COLORSPACE_SRGB) {
642 return SDL_SetError("Unsupported output colorspace");
643 }
644
645 data = (PS2_RenderData *)SDL_calloc(1, sizeof(*data));
646 if (!data) {
647 return false;
648 }
649
650 // Specific gsKit init
651 sema.init_count = 0;
652 sema.max_count = 1;
653 sema.option = 0;
654 vsync_sema_id = CreateSema(&sema);
655
656 gsGlobal = gsKit_init_global_custom(RENDER_QUEUE_OS_POOLSIZE, RENDER_QUEUE_PER_POOLSIZE);
657
658 gsGlobal->Mode = GS_MODE_NTSC;
659 gsGlobal->Height = 448;
660
661 gsGlobal->PSM = GS_PSM_CT24;
662 gsGlobal->PSMZ = GS_PSMZ_16S;
663 gsGlobal->ZBuffering = GS_SETTING_OFF;
664 gsGlobal->DoubleBuffering = GS_SETTING_ON;
665 gsGlobal->PrimAlphaEnable = GS_SETTING_ON;
666 gsGlobal->Dithering = GS_SETTING_OFF;
667
668 gsKit_set_primalpha(gsGlobal, GS_SETREG_ALPHA(0, 1, 0, 1, 0), 0);
669
670 dmaKit_init(D_CTRL_RELE_OFF, D_CTRL_MFD_OFF, D_CTRL_STS_UNSPEC, D_CTRL_STD_OFF, D_CTRL_RCYC_8, 1 << DMA_CHANNEL_GIF);
671 dmaKit_chan_init(DMA_CHANNEL_GIF);
672
673 gsKit_set_clamp(gsGlobal, GS_CMODE_REPEAT);
674
675 gsKit_vram_clear(gsGlobal);
676
677 gsKit_init_screen(gsGlobal);
678
679 gsKit_TexManager_init(gsGlobal);
680
681 data->vsync_callback_id = gsKit_add_vsync_handler(vsync_handler);
682
683 gsKit_mode_switch(gsGlobal, GS_ONESHOT);
684
685 gsKit_clear(gsGlobal, GS_BLACK);
686
687 data->gsGlobal = gsGlobal;
688
689 renderer->WindowEvent = PS2_WindowEvent;
690 renderer->CreateTexture = PS2_CreateTexture;
691 renderer->UpdateTexture = PS2_UpdateTexture;
692 renderer->LockTexture = PS2_LockTexture;
693 renderer->UnlockTexture = PS2_UnlockTexture;
694 renderer->SetRenderTarget = PS2_SetRenderTarget;
695 renderer->QueueSetViewport = PS2_QueueSetViewport;
696 renderer->QueueSetDrawColor = PS2_QueueNoOp;
697 renderer->QueueDrawPoints = PS2_QueueDrawPoints;
698 renderer->QueueDrawLines = PS2_QueueDrawPoints;
699 renderer->QueueGeometry = PS2_QueueGeometry;
700 renderer->InvalidateCachedState = PS2_InvalidateCachedState;
701 renderer->RunCommandQueue = PS2_RunCommandQueue;
702 renderer->RenderPresent = PS2_RenderPresent;
703 renderer->DestroyTexture = PS2_DestroyTexture;
704 renderer->DestroyRenderer = PS2_DestroyRenderer;
705 renderer->SetVSync = PS2_SetVSync;
706 renderer->internal = data;
707 PS2_InvalidateCachedState(renderer);
708 renderer->window = window;
709
710 renderer->name = PS2_RenderDriver.name;
711 SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_ABGR1555);
712 SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_ABGR8888);
713 SDL_SetNumberProperty(SDL_GetRendererProperties(renderer), SDL_PROP_RENDERER_MAX_TEXTURE_SIZE_NUMBER, 1024);
714
715 return true;
716}
717
718SDL_RenderDriver PS2_RenderDriver = {
719 PS2_CreateRenderer, "PS2 gsKit"
720};
721
722#endif // SDL_VIDEO_RENDER_PS2
723