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 | |
43 | static 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 | */ |
56 | typedef 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 | |
72 | typedef 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 | |
82 | typedef struct |
83 | { |
84 | unsigned int color; |
85 | } PSP_DrawStateCache; |
86 | |
87 | typedef 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 | |
105 | typedef struct |
106 | { |
107 | float x, y, z; |
108 | } VertV; |
109 | |
110 | typedef struct |
111 | { |
112 | float u, v; |
113 | float x, y, z; |
114 | } VertTV; |
115 | |
116 | typedef struct |
117 | { |
118 | SDL_Color col; |
119 | float x, y, z; |
120 | } VertCV; |
121 | |
122 | typedef 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 | |
132 | static 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 | |
146 | static 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 | |
159 | static void Swap(float *a, float *b) |
160 | { |
161 | float n = *a; |
162 | *a = *b; |
163 | *b = n; |
164 | } |
165 | |
166 | static inline int InVram(void *data) |
167 | { |
168 | return data < (void *)0x04200000; |
169 | } |
170 | |
171 | // Return next power of 2 |
172 | static 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 | |
186 | static void psp_on_vblank(u32 sub, PSP_RenderData *data) |
187 | { |
188 | if (data) { |
189 | data->vblank_not_reached = false; |
190 | } |
191 | } |
192 | |
193 | static 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 |
210 | static 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 | |
220 | static 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 | |
232 | static 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 | |
245 | static 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 | |
254 | static void TextureStorageFree(void *storage) |
255 | { |
256 | if (InVram(storage)) { |
257 | vfree(storage); |
258 | } else { |
259 | SDL_free(storage); |
260 | } |
261 | } |
262 | |
263 | static 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 | |
319 | static 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 | |
389 | static 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 | |
408 | static 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 | |
422 | static 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 | |
437 | static 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 | |
447 | static 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 | |
478 | static void PSP_WindowEvent(SDL_Renderer *renderer, const SDL_WindowEvent *event) |
479 | { |
480 | } |
481 | |
482 | static 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 | |
538 | static 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 | |
543 | static 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 | |
557 | static 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 | |
572 | static 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 | |
585 | static bool PSP_LockTexture(SDL_Renderer *renderer, SDL_Texture *texture, |
586 | const SDL_Rect *rect, void **pixels, int *pitch); |
587 | |
588 | static 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 | |
613 | static 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 | |
625 | static 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 | |
638 | static bool PSP_SetRenderTarget(SDL_Renderer *renderer, SDL_Texture *texture) |
639 | { |
640 | return true; |
641 | } |
642 | |
643 | static bool PSP_QueueNoOp(SDL_Renderer *renderer, SDL_RenderCommand *cmd) |
644 | { |
645 | return true; // nothing to do in this backend. |
646 | } |
647 | |
648 | static 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 | |
668 | static 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 | |
762 | static 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 | |
787 | static 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 | |
871 | static 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 | |
959 | static 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 | |
970 | static 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 | |
996 | static 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 | |
1067 | static 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 | |
1072 | static 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 | |
1259 | static 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 | |
1281 | static 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 | |
1300 | static 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 | |
1322 | static 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 | |
1329 | static 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 | |
1433 | SDL_RenderDriver PSP_RenderDriver = { |
1434 | PSP_CreateRenderer, "PSP" |
1435 | }; |
1436 | |
1437 | #endif // SDL_VIDEO_RENDER_PSP |
1438 | |