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_PSP
24
25#include "../SDL_sysrender.h"
26
27#include "SDL_render_psp_c.h"
28
29#include <pspkernel.h>
30#include <pspdisplay.h>
31#include <pspgu.h>
32#include <pspgum.h>
33#include <stdio.h>
34#include <string.h>
35#include <math.h>
36#include <pspge.h>
37#include <stdarg.h>
38#include <stdlib.h>
39#include <vram.h>
40
41// PSP renderer implementation, based on the PGE
42
43static unsigned int __attribute__((aligned(16))) DisplayList[262144];
44
45#define COL5650(r, g, b, a) ((r >> 3) | ((g >> 2) << 5) | ((b >> 3) << 11))
46#define COL5551(r, g, b, a) ((r >> 3) | ((g >> 3) << 5) | ((b >> 3) << 10) | (a > 0 ? 0x7000 : 0))
47#define COL4444(r, g, b, a) ((r >> 4) | ((g >> 4) << 4) | ((b >> 4) << 8) | ((a >> 4) << 12))
48#define COL8888(r, g, b, a) ((r) | ((g) << 8) | ((b) << 16) | ((a) << 24))
49
50/**
51 * Holds psp specific texture data
52 *
53 * Part of a hot-list of textures that are used as render targets
54 * When short of vram we spill Least-Recently-Used render targets to system memory
55 */
56typedef struct PSP_TextureData
57{
58 void *data; /**< Image data. */
59 unsigned int size; /**< Size of data in bytes. */
60 unsigned int width; /**< Image width. */
61 unsigned int height; /**< Image height. */
62 unsigned int textureWidth; /**< Texture width (power of two). */
63 unsigned int textureHeight; /**< Texture height (power of two). */
64 unsigned int bits; /**< Image bits per pixel. */
65 unsigned int format; /**< Image format - one of ::pgePixelFormat. */
66 unsigned int pitch;
67 bool swizzled; /**< Is image swizzled. */
68 struct PSP_TextureData *prevhotw; /**< More recently used render target */
69 struct PSP_TextureData *nexthotw; /**< Less recently used render target */
70} PSP_TextureData;
71
72typedef struct
73{
74 SDL_BlendMode mode;
75 unsigned int color;
76 int shadeModel;
77 SDL_Texture *texture;
78 SDL_ScaleMode texture_scale_mode;
79 SDL_TextureAddressMode texture_address_mode;
80} PSP_BlendState;
81
82typedef struct
83{
84 unsigned int color;
85} PSP_DrawStateCache;
86
87typedef struct
88{
89 void *frontbuffer; /**< main screen buffer */
90 void *backbuffer; /**< buffer presented to display */
91 SDL_Texture *boundTarget; /**< currently bound rendertarget */
92 bool initialized; /**< is driver initialized */
93 bool displayListAvail; /**< is the display list already initialized for this frame */
94 unsigned int psm; /**< format of the display buffers */
95 unsigned int bpp; /**< bits per pixel of the main display */
96
97 bool vsync; /**< whether we do vsync */
98 PSP_BlendState blendState; /**< current blend mode */
99 PSP_TextureData *most_recent_target; /**< start of render target LRU double linked list */
100 PSP_TextureData *least_recent_target; /**< end of the LRU list */
101
102 bool vblank_not_reached; /**< whether vblank wasn't reached */
103} PSP_RenderData;
104
105typedef struct
106{
107 float x, y, z;
108} VertV;
109
110typedef struct
111{
112 float u, v;
113 float x, y, z;
114} VertTV;
115
116typedef struct
117{
118 SDL_Color col;
119 float x, y, z;
120} VertCV;
121
122typedef struct
123{
124 float u, v;
125 SDL_Color col;
126 float x, y, z;
127} VertTCV;
128
129#define radToDeg(x) ((x)*180.f / SDL_PI_F)
130#define degToRad(x) ((x)*SDL_PI_F / 180.f)
131
132static float MathAbs(float x)
133{
134 float result;
135
136 __asm__ volatile(
137 "mtv %1, S000\n"
138 "vabs.s S000, S000\n"
139 "mfv %0, S000\n"
140 : "=r"(result)
141 : "r"(x));
142
143 return result;
144}
145
146static void MathSincos(float r, float *s, float *c)
147{
148 __asm__ volatile(
149 "mtv %2, S002\n"
150 "vcst.s S003, VFPU_2_PI\n"
151 "vmul.s S002, S002, S003\n"
152 "vrot.p C000, S002, [s, c]\n"
153 "mfv %0, S000\n"
154 "mfv %1, S001\n"
155 : "=r"(*s), "=r"(*c)
156 : "r"(r));
157}
158
159static void Swap(float *a, float *b)
160{
161 float n = *a;
162 *a = *b;
163 *b = n;
164}
165
166static inline int InVram(void *data)
167{
168 return data < (void *)0x04200000;
169}
170
171// Return next power of 2
172static int TextureNextPow2(unsigned int w)
173{
174 unsigned int n = 2;
175 if (w == 0) {
176 return 0;
177 }
178
179 while (w > n) {
180 n <<= 1;
181 }
182
183 return n;
184}
185
186static void psp_on_vblank(u32 sub, PSP_RenderData *data)
187{
188 if (data) {
189 data->vblank_not_reached = false;
190 }
191}
192
193static int PixelFormatToPSPFMT(SDL_PixelFormat format)
194{
195 switch (format) {
196 case SDL_PIXELFORMAT_BGR565:
197 return GU_PSM_5650;
198 case SDL_PIXELFORMAT_ABGR1555:
199 return GU_PSM_5551;
200 case SDL_PIXELFORMAT_ABGR4444:
201 return GU_PSM_4444;
202 case SDL_PIXELFORMAT_ABGR8888:
203 return GU_PSM_8888;
204 default:
205 return GU_PSM_8888;
206 }
207}
208
209/// SECTION render target LRU management
210static void LRUTargetRelink(PSP_TextureData *psp_texture)
211{
212 if (psp_texture->prevhotw) {
213 psp_texture->prevhotw->nexthotw = psp_texture->nexthotw;
214 }
215 if (psp_texture->nexthotw) {
216 psp_texture->nexthotw->prevhotw = psp_texture->prevhotw;
217 }
218}
219
220static void LRUTargetPushFront(PSP_RenderData *data, PSP_TextureData *psp_texture)
221{
222 psp_texture->nexthotw = data->most_recent_target;
223 if (data->most_recent_target) {
224 data->most_recent_target->prevhotw = psp_texture;
225 }
226 data->most_recent_target = psp_texture;
227 if (!data->least_recent_target) {
228 data->least_recent_target = psp_texture;
229 }
230}
231
232static void LRUTargetRemove(PSP_RenderData *data, PSP_TextureData *psp_texture)
233{
234 LRUTargetRelink(psp_texture);
235 if (data->most_recent_target == psp_texture) {
236 data->most_recent_target = psp_texture->nexthotw;
237 }
238 if (data->least_recent_target == psp_texture) {
239 data->least_recent_target = psp_texture->prevhotw;
240 }
241 psp_texture->prevhotw = NULL;
242 psp_texture->nexthotw = NULL;
243}
244
245static void LRUTargetBringFront(PSP_RenderData *data, PSP_TextureData *psp_texture)
246{
247 if (data->most_recent_target == psp_texture) {
248 return; // nothing to do
249 }
250 LRUTargetRemove(data, psp_texture);
251 LRUTargetPushFront(data, psp_texture);
252}
253
254static void TextureStorageFree(void *storage)
255{
256 if (InVram(storage)) {
257 vfree(storage);
258 } else {
259 SDL_free(storage);
260 }
261}
262
263static bool TextureSwizzle(PSP_TextureData *psp_texture, void *dst)
264{
265 int bytewidth, height;
266 int rowblocks, rowblocksadd;
267 int i, j;
268 unsigned int blockaddress = 0;
269 unsigned int *src = NULL;
270 unsigned char *data = NULL;
271
272 if (psp_texture->swizzled) {
273 return true;
274 }
275
276 bytewidth = psp_texture->textureWidth * (psp_texture->bits >> 3);
277 height = psp_texture->size / bytewidth;
278
279 rowblocks = (bytewidth >> 4);
280 rowblocksadd = (rowblocks - 1) << 7;
281
282 src = (unsigned int *)psp_texture->data;
283
284 data = dst;
285 if (!data) {
286 data = SDL_malloc(psp_texture->size);
287 }
288
289 if (!data) {
290 return false;
291 }
292
293 for (j = 0; j < height; j++, blockaddress += 16) {
294 unsigned int *block;
295
296 block = (unsigned int *)&data[blockaddress];
297
298 for (i = 0; i < rowblocks; i++) {
299 *block++ = *src++;
300 *block++ = *src++;
301 *block++ = *src++;
302 *block++ = *src++;
303 block += 28;
304 }
305
306 if ((j & 0x7) == 0x7) {
307 blockaddress += rowblocksadd;
308 }
309 }
310
311 TextureStorageFree(psp_texture->data);
312 psp_texture->data = data;
313 psp_texture->swizzled = true;
314
315 sceKernelDcacheWritebackRange(psp_texture->data, psp_texture->size);
316 return true;
317}
318
319static bool TextureUnswizzle(PSP_TextureData *psp_texture, void *dst)
320{
321 int bytewidth, height;
322 int widthblocks, heightblocks;
323 int dstpitch, dstrow;
324 int blockx, blocky;
325 int j;
326 unsigned int *src = NULL;
327 unsigned char *data = NULL;
328 unsigned char *ydst = NULL;
329
330 if (!psp_texture->swizzled) {
331 return true;
332 }
333
334 bytewidth = psp_texture->textureWidth * (psp_texture->bits >> 3);
335 height = psp_texture->size / bytewidth;
336
337 widthblocks = bytewidth / 16;
338 heightblocks = height / 8;
339
340 dstpitch = (bytewidth - 16) / 4;
341 dstrow = bytewidth * 8;
342
343 src = (unsigned int *)psp_texture->data;
344
345 data = dst;
346
347 if (!data) {
348 data = SDL_malloc(psp_texture->size);
349 }
350
351 if (!data) {
352 return false;
353 }
354
355 ydst = (unsigned char *)data;
356
357 for (blocky = 0; blocky < heightblocks; ++blocky) {
358 unsigned char *xdst = ydst;
359
360 for (blockx = 0; blockx < widthblocks; ++blockx) {
361 unsigned int *block;
362
363 block = (unsigned int *)xdst;
364
365 for (j = 0; j < 8; ++j) {
366 *(block++) = *(src++);
367 *(block++) = *(src++);
368 *(block++) = *(src++);
369 *(block++) = *(src++);
370 block += dstpitch;
371 }
372
373 xdst += 16;
374 }
375
376 ydst += dstrow;
377 }
378
379 TextureStorageFree(psp_texture->data);
380
381 psp_texture->data = data;
382
383 psp_texture->swizzled = false;
384
385 sceKernelDcacheWritebackRange(psp_texture->data, psp_texture->size);
386 return true;
387}
388
389static bool TextureSpillToSram(PSP_RenderData *data, PSP_TextureData *psp_texture)
390{
391 // Assumes the texture is in VRAM
392 if (psp_texture->swizzled) {
393 // Texture was swizzled in vram, just copy to system memory
394 void *sdata = SDL_malloc(psp_texture->size);
395 if (!sdata) {
396 return false;
397 }
398
399 SDL_memcpy(sdata, psp_texture->data, psp_texture->size);
400 vfree(psp_texture->data);
401 psp_texture->data = sdata;
402 return true;
403 } else {
404 return TextureSwizzle(psp_texture, NULL); // Will realloc in sysram
405 }
406}
407
408static bool TexturePromoteToVram(PSP_RenderData *data, PSP_TextureData *psp_texture, bool target)
409{
410 // Assumes texture in sram and a large enough continuous block in vram
411 void *tdata = vramalloc(psp_texture->size);
412 if (psp_texture->swizzled && target) {
413 return TextureUnswizzle(psp_texture, tdata);
414 } else {
415 SDL_memcpy(tdata, psp_texture->data, psp_texture->size);
416 SDL_free(psp_texture->data);
417 psp_texture->data = tdata;
418 return true;
419 }
420}
421
422static bool TextureSpillLRU(PSP_RenderData *data, size_t wanted)
423{
424 PSP_TextureData *lru = data->least_recent_target;
425 if (lru) {
426 if (!TextureSpillToSram(data, lru)) {
427 return false;
428 }
429 LRUTargetRemove(data, lru);
430 } else {
431 // Asked to spill but there nothing to spill
432 return SDL_SetError("Could not spill more VRAM to system memory. VRAM : %dKB,(%dKB), wanted %dKB", vmemavail() / 1024, vlargestblock() / 1024, wanted / 1024);
433 }
434 return true;
435}
436
437static bool TextureSpillTargetsForSpace(PSP_RenderData *data, size_t size)
438{
439 while (vlargestblock() < size) {
440 if (!TextureSpillLRU(data, size)) {
441 return false;
442 }
443 }
444 return true;
445}
446
447static bool TextureBindAsTarget(PSP_RenderData *data, PSP_TextureData *psp_texture)
448{
449 unsigned int dstFormat;
450
451 if (!InVram(psp_texture->data)) {
452 // Bring back the texture in vram
453 if (!TextureSpillTargetsForSpace(data, psp_texture->size)) {
454 return false;
455 }
456 if (!TexturePromoteToVram(data, psp_texture, true)) {
457 return false;
458 }
459 }
460 LRUTargetBringFront(data, psp_texture);
461 sceGuDrawBufferList(psp_texture->format, vrelptr(psp_texture->data), psp_texture->textureWidth);
462
463 // Stencil alpha dst hack
464 dstFormat = psp_texture->format;
465 if (dstFormat == GU_PSM_5551) {
466 sceGuEnable(GU_STENCIL_TEST);
467 sceGuStencilOp(GU_REPLACE, GU_REPLACE, GU_REPLACE);
468 sceGuStencilFunc(GU_GEQUAL, 0xff, 0xff);
469 sceGuEnable(GU_ALPHA_TEST);
470 sceGuAlphaFunc(GU_GREATER, 0x00, 0xff);
471 } else {
472 sceGuDisable(GU_STENCIL_TEST);
473 sceGuDisable(GU_ALPHA_TEST);
474 }
475 return true;
476}
477
478static void PSP_WindowEvent(SDL_Renderer *renderer, const SDL_WindowEvent *event)
479{
480}
481
482static bool PSP_CreateTexture(SDL_Renderer *renderer, SDL_Texture *texture, SDL_PropertiesID create_props)
483{
484 PSP_RenderData *data = renderer->internal;
485 PSP_TextureData *psp_texture = (PSP_TextureData *)SDL_calloc(1, sizeof(*psp_texture));
486
487 if (!psp_texture) {
488 return false;
489 }
490
491 psp_texture->swizzled = false;
492 psp_texture->width = texture->w;
493 psp_texture->height = texture->h;
494 psp_texture->textureHeight = TextureNextPow2(texture->h);
495 psp_texture->textureWidth = TextureNextPow2(texture->w);
496 psp_texture->format = PixelFormatToPSPFMT(texture->format);
497
498 switch (psp_texture->format) {
499 case GU_PSM_5650:
500 case GU_PSM_5551:
501 case GU_PSM_4444:
502 psp_texture->bits = 16;
503 break;
504
505 case GU_PSM_8888:
506 psp_texture->bits = 32;
507 break;
508
509 default:
510 SDL_free(psp_texture);
511 return false;
512 }
513
514 psp_texture->pitch = psp_texture->textureWidth * SDL_BYTESPERPIXEL(texture->format);
515 psp_texture->size = psp_texture->textureHeight * psp_texture->pitch;
516 if (texture->access == SDL_TEXTUREACCESS_TARGET) {
517 if (!TextureSpillTargetsForSpace(renderer->internal, psp_texture->size)) {
518 SDL_free(psp_texture);
519 return false;
520 }
521 psp_texture->data = vramalloc(psp_texture->size);
522 if (psp_texture->data) {
523 LRUTargetPushFront(data, psp_texture);
524 }
525 } else {
526 psp_texture->data = SDL_calloc(1, psp_texture->size);
527 }
528
529 if (!psp_texture->data) {
530 SDL_free(psp_texture);
531 return false;
532 }
533 texture->internal = psp_texture;
534
535 return true;
536}
537
538static bool TextureShouldSwizzle(PSP_TextureData *psp_texture, SDL_Texture *texture)
539{
540 return !((texture->access == SDL_TEXTUREACCESS_TARGET) && InVram(psp_texture->data)) && texture->access != SDL_TEXTUREACCESS_STREAMING && (texture->w >= 16 || texture->h >= 16);
541}
542
543static void SetTextureAddressMode(SDL_TextureAddressMode addressMode)
544{
545 switch (addressMode) {
546 case SDL_TEXTURE_ADDRESS_CLAMP:
547 sceGuTexWrap(GU_CLAMP, GU_CLAMP);
548 break;
549 case SDL_TEXTURE_ADDRESS_WRAP:
550 sceGuTexWrap(GU_REPEAT, GU_REPEAT);
551 break;
552 default:
553 break;
554 }
555}
556
557static void SetTextureScaleMode(SDL_ScaleMode scaleMode)
558{
559 switch (scaleMode) {
560 case SDL_SCALEMODE_PIXELART:
561 case SDL_SCALEMODE_NEAREST:
562 sceGuTexFilter(GU_NEAREST, GU_NEAREST);
563 break;
564 case SDL_SCALEMODE_LINEAR:
565 sceGuTexFilter(GU_LINEAR, GU_LINEAR);
566 break;
567 default:
568 break;
569 }
570}
571
572static void TextureActivate(SDL_Texture *texture)
573{
574 PSP_TextureData *psp_texture = (PSP_TextureData *)texture->internal;
575
576 // Swizzling is useless with small textures.
577 if (TextureShouldSwizzle(psp_texture, texture)) {
578 TextureSwizzle(psp_texture, NULL);
579 }
580
581 sceGuTexMode(psp_texture->format, 0, 0, psp_texture->swizzled);
582 sceGuTexImage(0, psp_texture->textureWidth, psp_texture->textureHeight, psp_texture->textureWidth, psp_texture->data);
583}
584
585static bool PSP_LockTexture(SDL_Renderer *renderer, SDL_Texture *texture,
586 const SDL_Rect *rect, void **pixels, int *pitch);
587
588static bool PSP_UpdateTexture(SDL_Renderer *renderer, SDL_Texture *texture,
589 const SDL_Rect *rect, const void *pixels, int pitch)
590{
591 /* PSP_TextureData *psp_texture = (PSP_TextureData *) texture->internal; */
592 const Uint8 *src;
593 Uint8 *dst;
594 int row, length, dpitch;
595 src = pixels;
596
597 PSP_LockTexture(renderer, texture, rect, (void **)&dst, &dpitch);
598 length = rect->w * SDL_BYTESPERPIXEL(texture->format);
599 if (length == pitch && length == dpitch) {
600 SDL_memcpy(dst, src, length * rect->h);
601 } else {
602 for (row = 0; row < rect->h; ++row) {
603 SDL_memcpy(dst, src, length);
604 src += pitch;
605 dst += dpitch;
606 }
607 }
608
609 sceKernelDcacheWritebackAll();
610 return true;
611}
612
613static bool PSP_LockTexture(SDL_Renderer *renderer, SDL_Texture *texture,
614 const SDL_Rect *rect, void **pixels, int *pitch)
615{
616 PSP_TextureData *psp_texture = (PSP_TextureData *)texture->internal;
617
618 *pixels =
619 (void *)((Uint8 *)psp_texture->data + rect->y * psp_texture->pitch +
620 rect->x * SDL_BYTESPERPIXEL(texture->format));
621 *pitch = psp_texture->pitch;
622 return true;
623}
624
625static void PSP_UnlockTexture(SDL_Renderer *renderer, SDL_Texture *texture)
626{
627 PSP_TextureData *psp_texture = (PSP_TextureData *)texture->internal;
628 SDL_Rect rect;
629
630 // We do whole texture updates, at least for now
631 rect.x = 0;
632 rect.y = 0;
633 rect.w = texture->w;
634 rect.h = texture->h;
635 PSP_UpdateTexture(renderer, texture, &rect, psp_texture->data, psp_texture->pitch);
636}
637
638static bool PSP_SetRenderTarget(SDL_Renderer *renderer, SDL_Texture *texture)
639{
640 return true;
641}
642
643static bool PSP_QueueNoOp(SDL_Renderer *renderer, SDL_RenderCommand *cmd)
644{
645 return true; // nothing to do in this backend.
646}
647
648static bool PSP_QueueDrawPoints(SDL_Renderer *renderer, SDL_RenderCommand *cmd, const SDL_FPoint *points, int count)
649{
650 VertV *verts = (VertV *)SDL_AllocateRenderVertices(renderer, count * sizeof(VertV), 4, &cmd->data.draw.first);
651 int i;
652
653 if (!verts) {
654 return false;
655 }
656
657 cmd->data.draw.count = count;
658
659 for (i = 0; i < count; i++, verts++, points++) {
660 verts->x = points->x;
661 verts->y = points->y;
662 verts->z = 0.0f;
663 }
664
665 return true;
666}
667
668static bool PSP_QueueGeometry(SDL_Renderer *renderer, SDL_RenderCommand *cmd, SDL_Texture *texture,
669 const float *xy, int xy_stride, const SDL_FColor *color, int color_stride, const float *uv, int uv_stride,
670 int num_vertices, const void *indices, int num_indices, int size_indices,
671 float scale_x, float scale_y)
672{
673 int i;
674 int count = indices ? num_indices : num_vertices;
675 const float color_scale = cmd->data.draw.color_scale;
676
677 cmd->data.draw.count = count;
678 size_indices = indices ? size_indices : 0;
679
680 if (!texture) {
681 VertCV *verts;
682 verts = (VertCV *)SDL_AllocateRenderVertices(renderer, count * sizeof(VertCV), 4, &cmd->data.draw.first);
683 if (!verts) {
684 return false;
685 }
686
687 for (i = 0; i < count; i++) {
688 int j;
689 float *xy_;
690 SDL_FColor *col_;
691 if (size_indices == 4) {
692 j = ((const Uint32 *)indices)[i];
693 } else if (size_indices == 2) {
694 j = ((const Uint16 *)indices)[i];
695 } else if (size_indices == 1) {
696 j = ((const Uint8 *)indices)[i];
697 } else {
698 j = i;
699 }
700
701 xy_ = (float *)((char *)xy + j * xy_stride);
702 col_ = (SDL_FColor *)((char *)color + j * color_stride);
703
704 verts->x = xy_[0] * scale_x;
705 verts->y = xy_[1] * scale_y;
706 verts->z = 0;
707
708 verts->col.r = (Uint8)SDL_roundf(SDL_clamp(col_->r * color_scale, 0.0f, 1.0f) * 255.0f);
709 verts->col.g = (Uint8)SDL_roundf(SDL_clamp(col_->g * color_scale, 0.0f, 1.0f) * 255.0f);
710 verts->col.b = (Uint8)SDL_roundf(SDL_clamp(col_->b * color_scale, 0.0f, 1.0f) * 255.0f);
711 verts->col.a = (Uint8)SDL_roundf(SDL_clamp(col_->a, 0.0f, 1.0f) * 255.0f);
712
713 verts++;
714 }
715 } else {
716 PSP_TextureData *psp_texture = (PSP_TextureData *)texture->internal;
717 VertTCV *verts;
718 verts = (VertTCV *)SDL_AllocateRenderVertices(renderer, count * sizeof(VertTCV), 4, &cmd->data.draw.first);
719 if (!verts) {
720 return false;
721 }
722
723 for (i = 0; i < count; i++) {
724 int j;
725 float *xy_;
726 SDL_FColor *col_;
727 float *uv_;
728
729 if (size_indices == 4) {
730 j = ((const Uint32 *)indices)[i];
731 } else if (size_indices == 2) {
732 j = ((const Uint16 *)indices)[i];
733 } else if (size_indices == 1) {
734 j = ((const Uint8 *)indices)[i];
735 } else {
736 j = i;
737 }
738
739 xy_ = (float *)((char *)xy + j * xy_stride);
740 col_ = (SDL_FColor *)((char *)color + j * color_stride);
741 uv_ = (float *)((char *)uv + j * uv_stride);
742
743 verts->x = xy_[0] * scale_x;
744 verts->y = xy_[1] * scale_y;
745 verts->z = 0;
746
747 verts->col.r = (Uint8)SDL_roundf(SDL_clamp(col_->r * color_scale, 0.0f, 1.0f) * 255.0f);
748 verts->col.g = (Uint8)SDL_roundf(SDL_clamp(col_->g * color_scale, 0.0f, 1.0f) * 255.0f);
749 verts->col.b = (Uint8)SDL_roundf(SDL_clamp(col_->b * color_scale, 0.0f, 1.0f) * 255.0f);
750 verts->col.a = (Uint8)SDL_roundf(SDL_clamp(col_->a, 0.0f, 1.0f) * 255.0f);
751
752 verts->u = uv_[0] * psp_texture->textureWidth;
753 verts->v = uv_[1] * psp_texture->textureHeight;
754
755 verts++;
756 }
757 }
758
759 return true;
760}
761
762static bool PSP_QueueFillRects(SDL_Renderer *renderer, SDL_RenderCommand *cmd, const SDL_FRect *rects, int count)
763{
764 VertV *verts = (VertV *)SDL_AllocateRenderVertices(renderer, count * 2 * sizeof(VertV), 4, &cmd->data.draw.first);
765 int i;
766
767 if (!verts) {
768 return false;
769 }
770
771 cmd->data.draw.count = count;
772 for (i = 0; i < count; i++, rects++) {
773 verts->x = rects->x;
774 verts->y = rects->y;
775 verts->z = 0.0f;
776 verts++;
777
778 verts->x = rects->x + rects->w + 0.5f;
779 verts->y = rects->y + rects->h + 0.5f;
780 verts->z = 0.0f;
781 verts++;
782 }
783
784 return true;
785}
786
787static bool PSP_QueueCopy(SDL_Renderer *renderer, SDL_RenderCommand *cmd, SDL_Texture *texture,
788 const SDL_FRect *srcrect, const SDL_FRect *dstrect)
789{
790 VertTV *verts;
791 const float x = dstrect->x;
792 const float y = dstrect->y;
793 const float width = dstrect->w;
794 const float height = dstrect->h;
795
796 const float u0 = srcrect->x;
797 const float v0 = srcrect->y;
798 const float u1 = srcrect->x + srcrect->w;
799 const float v1 = srcrect->y + srcrect->h;
800
801 if ((MathAbs(u1) - MathAbs(u0)) < 64.0f) {
802 verts = (VertTV *)SDL_AllocateRenderVertices(renderer, 2 * sizeof(VertTV), 4, &cmd->data.draw.first);
803 if (!verts) {
804 return false;
805 }
806
807 cmd->data.draw.count = 1;
808
809 verts->u = u0;
810 verts->v = v0;
811 verts->x = x;
812 verts->y = y;
813 verts->z = 0;
814 verts++;
815
816 verts->u = u1;
817 verts->v = v1;
818 verts->x = x + width;
819 verts->y = y + height;
820 verts->z = 0;
821 verts++;
822 } else {
823 float start, end;
824 float curU = u0;
825 float curX = x;
826 const float endX = x + width;
827 const float slice = 64.0f;
828 const size_t count = (size_t)SDL_ceilf(width / slice);
829 size_t i;
830 float ustep = (u1 - u0) / width * slice;
831
832 if (ustep < 0.0f) {
833 ustep = -ustep;
834 }
835
836 cmd->data.draw.count = count;
837
838 verts = (VertTV *)SDL_AllocateRenderVertices(renderer, count * 2 * sizeof(VertTV), 4, &cmd->data.draw.first);
839 if (!verts) {
840 return false;
841 }
842
843 for (i = 0, start = 0, end = width; i < count; i++, start += slice) {
844 const float polyWidth = ((curX + slice) > endX) ? (endX - curX) : slice;
845 const float sourceWidth = ((curU + ustep) > u1) ? (u1 - curU) : ustep;
846
847 SDL_assert(start < end);
848
849 verts->u = curU;
850 verts->v = v0;
851 verts->x = curX;
852 verts->y = y;
853 verts->z = 0;
854 verts++;
855
856 curU += sourceWidth;
857 curX += polyWidth;
858
859 verts->u = curU;
860 verts->v = v1;
861 verts->x = curX;
862 verts->y = (y + height);
863 verts->z = 0;
864 verts++;
865 }
866 }
867
868 return true;
869}
870
871static bool PSP_QueueCopyEx(SDL_Renderer *renderer, SDL_RenderCommand *cmd, SDL_Texture *texture,
872 const SDL_FRect *srcrect, const SDL_FRect *dstrect,
873 const double angle, const SDL_FPoint *center, const SDL_FlipMode flip, float scale_x, float scale_y)
874{
875 VertTV *verts = (VertTV *)SDL_AllocateRenderVertices(renderer, 4 * sizeof(VertTV), 4, &cmd->data.draw.first);
876 const float centerx = center->x;
877 const float centery = center->y;
878 const float x = dstrect->x + centerx;
879 const float y = dstrect->y + centery;
880 const float width = dstrect->w - centerx;
881 const float height = dstrect->h - centery;
882 float s, c;
883 float cw1, sw1, ch1, sh1, cw2, sw2, ch2, sh2;
884
885 float u0 = srcrect->x;
886 float v0 = srcrect->y;
887 float u1 = srcrect->x + srcrect->w;
888 float v1 = srcrect->y + srcrect->h;
889
890 if (!verts) {
891 return false;
892 }
893
894 cmd->data.draw.count = 1;
895
896 MathSincos(degToRad((float)(360 - angle)), &s, &c);
897
898 cw1 = c * -centerx;
899 sw1 = s * -centerx;
900 ch1 = c * -centery;
901 sh1 = s * -centery;
902 cw2 = c * width;
903 sw2 = s * width;
904 ch2 = c * height;
905 sh2 = s * height;
906
907 if (flip & SDL_FLIP_VERTICAL) {
908 Swap(&v0, &v1);
909 }
910
911 if (flip & SDL_FLIP_HORIZONTAL) {
912 Swap(&u0, &u1);
913 }
914
915 verts->u = u0;
916 verts->v = v0;
917 verts->x = x + cw1 + sh1;
918 verts->y = y - sw1 + ch1;
919 verts->z = 0;
920 verts++;
921
922 verts->u = u0;
923 verts->v = v1;
924 verts->x = x + cw1 + sh2;
925 verts->y = y - sw1 + ch2;
926 verts->z = 0;
927 verts++;
928
929 verts->u = u1;
930 verts->v = v1;
931 verts->x = x + cw2 + sh2;
932 verts->y = y - sw2 + ch2;
933 verts->z = 0;
934 verts++;
935
936 verts->u = u1;
937 verts->v = v0;
938 verts->x = x + cw2 + sh1;
939 verts->y = y - sw2 + ch1;
940 verts->z = 0;
941
942 if (scale_x != 1.0f || scale_y != 1.0f) {
943 verts->x *= scale_x;
944 verts->y *= scale_y;
945 verts--;
946 verts->x *= scale_x;
947 verts->y *= scale_y;
948 verts--;
949 verts->x *= scale_x;
950 verts->y *= scale_y;
951 verts--;
952 verts->x *= scale_x;
953 verts->y *= scale_y;
954 }
955
956 return true;
957}
958
959static void ResetBlendState(PSP_BlendState *state)
960{
961 sceGuColor(0xffffffff);
962 state->color = 0xffffffff;
963 state->mode = SDL_BLENDMODE_INVALID;
964 state->texture = NULL;
965 sceGuDisable(GU_TEXTURE_2D);
966 sceGuShadeModel(GU_SMOOTH);
967 state->shadeModel = GU_SMOOTH;
968}
969
970static void StartDrawing(SDL_Renderer *renderer)
971{
972 PSP_RenderData *data = (PSP_RenderData *)renderer->internal;
973
974 // Check if we need to start GU displaylist
975 if (!data->displayListAvail) {
976 sceGuStart(GU_DIRECT, DisplayList);
977 data->displayListAvail = true;
978 // ResetBlendState(&data->blendState);
979 }
980
981 // Check if we need a draw buffer change
982 if (renderer->target != data->boundTarget) {
983 SDL_Texture *texture = renderer->target;
984 if (texture) {
985 PSP_TextureData *psp_texture = (PSP_TextureData *)texture->internal;
986 // Set target, registering LRU
987 TextureBindAsTarget(data, psp_texture);
988 } else {
989 // Set target back to screen
990 sceGuDrawBufferList(data->psm, vrelptr(data->frontbuffer), PSP_FRAME_BUFFER_WIDTH);
991 }
992 data->boundTarget = texture;
993 }
994}
995
996static void PSP_SetBlendState(PSP_RenderData *data, PSP_BlendState *state)
997{
998 PSP_BlendState *current = &data->blendState;
999
1000 if (state->mode != current->mode) {
1001 switch (state->mode) {
1002 case SDL_BLENDMODE_NONE:
1003 sceGuTexFunc(GU_TFX_REPLACE, GU_TCC_RGBA);
1004 sceGuDisable(GU_BLEND);
1005 break;
1006 case SDL_BLENDMODE_BLEND:
1007 sceGuTexFunc(GU_TFX_MODULATE, GU_TCC_RGBA);
1008 sceGuBlendFunc(GU_ADD, GU_SRC_ALPHA, GU_ONE_MINUS_SRC_ALPHA, 0, 0);
1009 sceGuEnable(GU_BLEND);
1010 break;
1011 case SDL_BLENDMODE_BLEND_PREMULTIPLIED:
1012 sceGuTexFunc(GU_TFX_MODULATE , GU_TCC_RGBA);
1013 sceGuBlendFunc(GU_ADD, GU_FIX, GU_ONE_MINUS_SRC_ALPHA, 0x00FFFFFF, 0 );
1014 sceGuEnable(GU_BLEND);
1015 break;
1016 case SDL_BLENDMODE_ADD:
1017 sceGuTexFunc(GU_TFX_MODULATE, GU_TCC_RGBA);
1018 sceGuBlendFunc(GU_ADD, GU_SRC_ALPHA, GU_FIX, 0, 0x00FFFFFF);
1019 sceGuEnable(GU_BLEND);
1020 break;
1021 case SDL_BLENDMODE_ADD_PREMULTIPLIED:
1022 sceGuTexFunc(GU_TFX_MODULATE, GU_TCC_RGBA);
1023 sceGuBlendFunc(GU_ADD, GU_FIX, GU_FIX, 0, 0x00FFFFFF);
1024 sceGuEnable(GU_BLEND);
1025 break;
1026 case SDL_BLENDMODE_MOD:
1027 sceGuTexFunc(GU_TFX_MODULATE, GU_TCC_RGBA);
1028 sceGuBlendFunc(GU_ADD, GU_FIX, GU_SRC_COLOR, 0, 0);
1029 sceGuEnable(GU_BLEND);
1030 break;
1031 case SDL_BLENDMODE_MUL:
1032 sceGuTexFunc(GU_TFX_MODULATE, GU_TCC_RGBA);
1033 // FIXME SDL_BLENDMODE_MUL is simplified, and dstA is in fact un-changed.
1034 sceGuBlendFunc(GU_ADD, GU_DST_COLOR, GU_ONE_MINUS_SRC_ALPHA, 0, 0);
1035 sceGuEnable(GU_BLEND);
1036 break;
1037 case SDL_BLENDMODE_INVALID:
1038 break;
1039 }
1040 }
1041
1042 if (state->color != current->color) {
1043 sceGuColor(state->color);
1044 }
1045
1046 if (state->shadeModel != current->shadeModel) {
1047 sceGuShadeModel(state->shadeModel);
1048 }
1049
1050 if (state->texture != current->texture) {
1051 if (state->texture) {
1052 TextureActivate(state->texture);
1053 sceGuEnable(GU_TEXTURE_2D);
1054 } else {
1055 sceGuDisable(GU_TEXTURE_2D);
1056 }
1057 }
1058
1059 if (state->texture) {
1060 SetTextureScaleMode(state->texture_scale_mode);
1061 SetTextureAddressMode(state->texture_address_mode);
1062 }
1063
1064 *current = *state;
1065}
1066
1067static void PSP_InvalidateCachedState(SDL_Renderer *renderer)
1068{
1069 // currently this doesn't do anything. If this needs to do something (and someone is mixing their own rendering calls in!), update this.
1070}
1071
1072static bool PSP_RunCommandQueue(SDL_Renderer *renderer, SDL_RenderCommand *cmd, void *vertices, size_t vertsize)
1073{
1074 PSP_RenderData *data = (PSP_RenderData *)renderer->internal;
1075 Uint8 *gpumem = NULL;
1076 PSP_DrawStateCache drawstate;
1077
1078 drawstate.color = 0;
1079
1080 StartDrawing(renderer);
1081
1082 /* note that before the renderer interface change, this would do extremely small
1083 batches with sceGuGetMemory()--a few vertices at a time--and it's not clear that
1084 this won't fail if you try to push 100,000 draw calls in a single batch.
1085 I don't know what the limits on PSP hardware are. It might be useful to have
1086 rendering backends report a reasonable maximum, so the higher level can flush
1087 if we appear to be exceeding that. */
1088 gpumem = (Uint8 *)sceGuGetMemory(vertsize);
1089 if (!gpumem) {
1090 return SDL_SetError("Couldn't obtain a %d-byte vertex buffer!", (int)vertsize);
1091 }
1092 SDL_memcpy(gpumem, vertices, vertsize);
1093
1094 while (cmd) {
1095 switch (cmd->command) {
1096 case SDL_RENDERCMD_SETDRAWCOLOR:
1097 {
1098 const Uint8 r = (Uint8)SDL_roundf(SDL_clamp(cmd->data.color.color.r * cmd->data.color.color_scale, 0.0f, 1.0f) * 255.0f);
1099 const Uint8 g = (Uint8)SDL_roundf(SDL_clamp(cmd->data.color.color.g * cmd->data.color.color_scale, 0.0f, 1.0f) * 255.0f);
1100 const Uint8 b = (Uint8)SDL_roundf(SDL_clamp(cmd->data.color.color.b * cmd->data.color.color_scale, 0.0f, 1.0f) * 255.0f);
1101 const Uint8 a = (Uint8)SDL_roundf(SDL_clamp(cmd->data.color.color.a, 0.0f, 1.0f) * 255.0f);
1102 drawstate.color = GU_RGBA(r, g, b, a);
1103 break;
1104 }
1105
1106 case SDL_RENDERCMD_SETVIEWPORT:
1107 {
1108 SDL_Rect *viewport = &cmd->data.viewport.rect;
1109 sceGuOffset(2048 - (viewport->w >> 1), 2048 - (viewport->h >> 1));
1110 sceGuViewport(2048, 2048, viewport->w, viewport->h);
1111 sceGuScissor(viewport->x, viewport->y, viewport->w, viewport->h);
1112 // FIXME: We need to update the clip rect too, see https://github.com/libsdl-org/SDL/issues/9094
1113 break;
1114 }
1115
1116 case SDL_RENDERCMD_SETCLIPRECT:
1117 {
1118 const SDL_Rect *rect = &cmd->data.cliprect.rect;
1119 if (cmd->data.cliprect.enabled) {
1120 sceGuEnable(GU_SCISSOR_TEST);
1121 sceGuScissor(rect->x, rect->y, rect->w, rect->h);
1122 } else {
1123 sceGuDisable(GU_SCISSOR_TEST);
1124 }
1125 break;
1126 }
1127
1128 case SDL_RENDERCMD_CLEAR:
1129 {
1130 const Uint8 r = (Uint8)SDL_roundf(SDL_clamp(cmd->data.color.color.r * cmd->data.color.color_scale, 0.0f, 1.0f) * 255.0f);
1131 const Uint8 g = (Uint8)SDL_roundf(SDL_clamp(cmd->data.color.color.g * cmd->data.color.color_scale, 0.0f, 1.0f) * 255.0f);
1132 const Uint8 b = (Uint8)SDL_roundf(SDL_clamp(cmd->data.color.color.b * cmd->data.color.color_scale, 0.0f, 1.0f) * 255.0f);
1133 const Uint8 a = (Uint8)SDL_roundf(SDL_clamp(cmd->data.color.color.a, 0.0f, 1.0f) * 255.0f);
1134 sceGuClearColor(GU_RGBA(r, g, b, a));
1135 sceGuClearStencil(a);
1136 sceGuClear(GU_COLOR_BUFFER_BIT | GU_STENCIL_BUFFER_BIT);
1137 break;
1138 }
1139
1140 case SDL_RENDERCMD_DRAW_POINTS:
1141 {
1142 const size_t count = cmd->data.draw.count;
1143 const VertV *verts = (VertV *)(gpumem + cmd->data.draw.first);
1144 PSP_BlendState state = {
1145 .color = drawstate.color,
1146 .texture = NULL,
1147 .texture_scale_mode = SDL_SCALEMODE_INVALID,
1148 .texture_address_mode = SDL_TEXTURE_ADDRESS_INVALID,
1149 .mode = cmd->data.draw.blend,
1150 .shadeModel = GU_FLAT
1151 };
1152 PSP_SetBlendState(data, &state);
1153 sceGuDrawArray(GU_POINTS, GU_VERTEX_32BITF | GU_TRANSFORM_2D, count, 0, verts);
1154 break;
1155 }
1156
1157 case SDL_RENDERCMD_DRAW_LINES:
1158 {
1159 const size_t count = cmd->data.draw.count;
1160 const VertV *verts = (VertV *)(gpumem + cmd->data.draw.first);
1161 PSP_BlendState state = {
1162 .color = drawstate.color,
1163 .texture = NULL,
1164 .texture_scale_mode = SDL_SCALEMODE_INVALID,
1165 .texture_address_mode = SDL_TEXTURE_ADDRESS_INVALID,
1166 .mode = cmd->data.draw.blend,
1167 .shadeModel = GU_FLAT
1168 };
1169 PSP_SetBlendState(data, &state);
1170 sceGuDrawArray(GU_LINE_STRIP, GU_VERTEX_32BITF | GU_TRANSFORM_2D, count, 0, verts);
1171 break;
1172 }
1173
1174 case SDL_RENDERCMD_FILL_RECTS:
1175 {
1176 const size_t count = cmd->data.draw.count;
1177 const VertV *verts = (VertV *)(gpumem + cmd->data.draw.first);
1178 PSP_BlendState state = {
1179 .color = drawstate.color,
1180 .texture = NULL,
1181 .texture_scale_mode = SDL_SCALEMODE_INVALID,
1182 .texture_address_mode = SDL_TEXTURE_ADDRESS_INVALID,
1183 .mode = cmd->data.draw.blend,
1184 .shadeModel = GU_FLAT
1185 };
1186 PSP_SetBlendState(data, &state);
1187 sceGuDrawArray(GU_SPRITES, GU_VERTEX_32BITF | GU_TRANSFORM_2D, 2 * count, 0, verts);
1188 break;
1189 }
1190
1191 case SDL_RENDERCMD_COPY:
1192 {
1193 const size_t count = cmd->data.draw.count;
1194 const VertTV *verts = (VertTV *)(gpumem + cmd->data.draw.first);
1195 PSP_BlendState state = {
1196 .color = drawstate.color,
1197 .texture = cmd->data.draw.texture,
1198 .texture_scale_mode = cmd->data.draw.texture_scale_mode,
1199 .texture_address_mode = cmd->data.draw.texture_address_mode,
1200 .mode = cmd->data.draw.blend,
1201 .shadeModel = GU_SMOOTH
1202 };
1203 PSP_SetBlendState(data, &state);
1204 sceGuDrawArray(GU_SPRITES, GU_TEXTURE_32BITF | GU_VERTEX_32BITF | GU_TRANSFORM_2D, 2 * count, 0, verts);
1205 break;
1206 }
1207
1208 case SDL_RENDERCMD_COPY_EX:
1209 {
1210 const VertTV *verts = (VertTV *)(gpumem + cmd->data.draw.first);
1211 PSP_BlendState state = {
1212 .color = drawstate.color,
1213 .texture = cmd->data.draw.texture,
1214 .texture_scale_mode = cmd->data.draw.texture_scale_mode,
1215 .texture_address_mode = cmd->data.draw.texture_address_mode,
1216 .mode = cmd->data.draw.blend,
1217 .shadeModel = GU_SMOOTH
1218 };
1219 PSP_SetBlendState(data, &state);
1220 sceGuDrawArray(GU_TRIANGLE_FAN, GU_TEXTURE_32BITF | GU_VERTEX_32BITF | GU_TRANSFORM_2D, 4, 0, verts);
1221 break;
1222 }
1223
1224 case SDL_RENDERCMD_GEOMETRY:
1225 {
1226 const size_t count = cmd->data.draw.count;
1227 if (!cmd->data.draw.texture) {
1228 const VertCV *verts = (VertCV *)(gpumem + cmd->data.draw.first);
1229 sceGuDisable(GU_TEXTURE_2D);
1230 // In GU_SMOOTH mode
1231 sceGuDrawArray(GU_TRIANGLES, GU_COLOR_8888 | GU_VERTEX_32BITF | GU_TRANSFORM_2D, count, 0, verts);
1232 sceGuEnable(GU_TEXTURE_2D);
1233 } else {
1234 const VertTCV *verts = (VertTCV *)(gpumem + cmd->data.draw.first);
1235 PSP_BlendState state = {
1236 .color = drawstate.color,
1237 .texture = cmd->data.draw.texture,
1238 .texture_scale_mode = cmd->data.draw.texture_scale_mode,
1239 .texture_address_mode = cmd->data.draw.texture_address_mode,
1240 .mode = cmd->data.draw.blend,
1241 .shadeModel = GU_SMOOTH
1242 };
1243 PSP_SetBlendState(data, &state);
1244 sceGuDrawArray(GU_TRIANGLES, GU_TEXTURE_32BITF | GU_COLOR_8888 | GU_VERTEX_32BITF | GU_TRANSFORM_2D, count, 0, verts);
1245 }
1246 break;
1247 }
1248
1249 case SDL_RENDERCMD_NO_OP:
1250 break;
1251 }
1252
1253 cmd = cmd->next;
1254 }
1255
1256 return true;
1257}
1258
1259static bool PSP_RenderPresent(SDL_Renderer *renderer)
1260{
1261 PSP_RenderData *data = (PSP_RenderData *)renderer->internal;
1262 if (!data->displayListAvail) {
1263 return false;
1264 }
1265
1266 data->displayListAvail = false;
1267 sceGuFinish();
1268 sceGuSync(0, 0);
1269
1270 if ((data->vsync) && (data->vblank_not_reached)) {
1271 sceDisplayWaitVblankStart();
1272 }
1273 data->vblank_not_reached = true;
1274
1275 data->backbuffer = data->frontbuffer;
1276 data->frontbuffer = vabsptr(sceGuSwapBuffers());
1277
1278 return true;
1279}
1280
1281static void PSP_DestroyTexture(SDL_Renderer *renderer, SDL_Texture *texture)
1282{
1283 PSP_RenderData *renderdata = (PSP_RenderData *)renderer->internal;
1284 PSP_TextureData *psp_texture = (PSP_TextureData *)texture->internal;
1285
1286 if (!renderdata) {
1287 return;
1288 }
1289
1290 if (!psp_texture) {
1291 return;
1292 }
1293
1294 LRUTargetRemove(renderdata, psp_texture);
1295 TextureStorageFree(psp_texture->data);
1296 SDL_free(psp_texture);
1297 texture->internal = NULL;
1298}
1299
1300static void PSP_DestroyRenderer(SDL_Renderer *renderer)
1301{
1302 PSP_RenderData *data = (PSP_RenderData *)renderer->internal;
1303 if (data) {
1304 if (!data->initialized) {
1305 return;
1306 }
1307
1308 sceKernelDisableSubIntr(PSP_VBLANK_INT, 0);
1309 sceKernelReleaseSubIntrHandler(PSP_VBLANK_INT, 0);
1310 sceDisplayWaitVblankStart();
1311 sceGuDisplay(GU_FALSE);
1312 sceGuTerm();
1313 vfree(data->backbuffer);
1314 vfree(data->frontbuffer);
1315
1316 data->initialized = false;
1317 data->displayListAvail = false;
1318 SDL_free(data);
1319 }
1320}
1321
1322static bool PSP_SetVSync(SDL_Renderer *renderer, const int vsync)
1323{
1324 PSP_RenderData *data = renderer->internal;
1325 data->vsync = vsync;
1326 return true;
1327}
1328
1329static bool PSP_CreateRenderer(SDL_Renderer *renderer, SDL_Window *window, SDL_PropertiesID create_props)
1330{
1331 PSP_RenderData *data;
1332 int pixelformat;
1333 void *doublebuffer = NULL;
1334
1335 SDL_SetupRendererColorspace(renderer, create_props);
1336
1337 if (renderer->output_colorspace != SDL_COLORSPACE_SRGB) {
1338 return SDL_SetError("Unsupported output colorspace");
1339 }
1340
1341 data = (PSP_RenderData *)SDL_calloc(1, sizeof(*data));
1342 if (!data) {
1343 return false;
1344 }
1345
1346 renderer->WindowEvent = PSP_WindowEvent;
1347 renderer->CreateTexture = PSP_CreateTexture;
1348 renderer->UpdateTexture = PSP_UpdateTexture;
1349 renderer->LockTexture = PSP_LockTexture;
1350 renderer->UnlockTexture = PSP_UnlockTexture;
1351 renderer->SetRenderTarget = PSP_SetRenderTarget;
1352 renderer->QueueSetViewport = PSP_QueueNoOp;
1353 renderer->QueueSetDrawColor = PSP_QueueNoOp;
1354 renderer->QueueDrawPoints = PSP_QueueDrawPoints;
1355 renderer->QueueDrawLines = PSP_QueueDrawPoints; // lines and points queue vertices the same way.
1356 renderer->QueueGeometry = PSP_QueueGeometry;
1357 renderer->QueueFillRects = PSP_QueueFillRects;
1358 renderer->QueueCopy = PSP_QueueCopy;
1359 renderer->QueueCopyEx = PSP_QueueCopyEx;
1360 renderer->InvalidateCachedState = PSP_InvalidateCachedState;
1361 renderer->RunCommandQueue = PSP_RunCommandQueue;
1362 renderer->RenderPresent = PSP_RenderPresent;
1363 renderer->DestroyTexture = PSP_DestroyTexture;
1364 renderer->DestroyRenderer = PSP_DestroyRenderer;
1365 renderer->SetVSync = PSP_SetVSync;
1366 renderer->internal = data;
1367 PSP_InvalidateCachedState(renderer);
1368 renderer->window = window;
1369
1370 renderer->name = PSP_RenderDriver.name;
1371 SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_BGR565);
1372 SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_ABGR1555);
1373 SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_ABGR4444);
1374 SDL_AddSupportedTextureFormat(renderer, SDL_PIXELFORMAT_ABGR8888);
1375 SDL_SetNumberProperty(SDL_GetRendererProperties(renderer), SDL_PROP_RENDERER_MAX_TEXTURE_SIZE_NUMBER, 512);
1376
1377 data->initialized = true;
1378 data->most_recent_target = NULL;
1379 data->least_recent_target = NULL;
1380
1381 pixelformat = PixelFormatToPSPFMT(SDL_GetWindowPixelFormat(window));
1382 switch (pixelformat) {
1383 case GU_PSM_4444:
1384 case GU_PSM_5650:
1385 case GU_PSM_5551:
1386 data->bpp = 2;
1387 data->psm = pixelformat;
1388 break;
1389 default:
1390 data->bpp = 4;
1391 data->psm = GU_PSM_8888;
1392 break;
1393 }
1394
1395 doublebuffer = vramalloc(PSP_FRAME_BUFFER_SIZE * data->bpp * 2);
1396 data->backbuffer = doublebuffer;
1397 data->frontbuffer = ((uint8_t *)doublebuffer) + PSP_FRAME_BUFFER_SIZE * data->bpp;
1398
1399 sceGuInit();
1400 // setup GU
1401 sceGuStart(GU_DIRECT, DisplayList);
1402 sceGuDrawBuffer(data->psm, vrelptr(data->frontbuffer), PSP_FRAME_BUFFER_WIDTH);
1403 sceGuDispBuffer(PSP_SCREEN_WIDTH, PSP_SCREEN_HEIGHT, vrelptr(data->backbuffer), PSP_FRAME_BUFFER_WIDTH);
1404
1405 sceGuOffset(2048 - (PSP_SCREEN_WIDTH >> 1), 2048 - (PSP_SCREEN_HEIGHT >> 1));
1406 sceGuViewport(2048, 2048, PSP_SCREEN_WIDTH, PSP_SCREEN_HEIGHT);
1407
1408 sceGuDisable(GU_DEPTH_TEST);
1409
1410 // Scissoring
1411 sceGuScissor(0, 0, PSP_SCREEN_WIDTH, PSP_SCREEN_HEIGHT);
1412 sceGuEnable(GU_SCISSOR_TEST);
1413
1414 // Backface culling
1415 sceGuDisable(GU_CULL_FACE);
1416
1417 // Setup initial blend state
1418 ResetBlendState(&data->blendState);
1419
1420 sceGuFinish();
1421 sceGuSync(0, 0);
1422 sceDisplayWaitVblankStartCB();
1423 sceGuDisplay(GU_TRUE);
1424
1425 // Improve performance when VSYC is enabled and it is not reaching the 60 FPS
1426 data->vblank_not_reached = true;
1427 sceKernelRegisterSubIntrHandler(PSP_VBLANK_INT, 0, psp_on_vblank, data);
1428 sceKernelEnableSubIntr(PSP_VBLANK_INT, 0);
1429
1430 return true;
1431}
1432
1433SDL_RenderDriver PSP_RenderDriver = {
1434 PSP_CreateRenderer, "PSP"
1435};
1436
1437#endif // SDL_VIDEO_RENDER_PSP
1438