1 | /* |
2 | Simple DirectMedia Layer |
3 | Copyright (C) 1997-2021 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 | #if SDL_VIDEO_RENDER_PSP |
24 | |
25 | #include "SDL_hints.h" |
26 | #include "../SDL_sysrender.h" |
27 | |
28 | #include <pspkernel.h> |
29 | #include <pspdisplay.h> |
30 | #include <pspgu.h> |
31 | #include <pspgum.h> |
32 | #include <stdio.h> |
33 | #include <string.h> |
34 | #include <math.h> |
35 | #include <pspge.h> |
36 | #include <stdarg.h> |
37 | #include <stdlib.h> |
38 | #include <vram.h> |
39 | |
40 | |
41 | |
42 | |
43 | /* PSP renderer implementation, based on the PGE */ |
44 | |
45 | #define PSP_SCREEN_WIDTH 480 |
46 | #define PSP_SCREEN_HEIGHT 272 |
47 | |
48 | #define PSP_FRAME_BUFFER_WIDTH 512 |
49 | #define PSP_FRAME_BUFFER_SIZE (PSP_FRAME_BUFFER_WIDTH*PSP_SCREEN_HEIGHT) |
50 | |
51 | static unsigned int __attribute__((aligned(16))) DisplayList[262144]; |
52 | |
53 | |
54 | #define COL5650(r,g,b,a) ((r>>3) | ((g>>2)<<5) | ((b>>3)<<11)) |
55 | #define COL5551(r,g,b,a) ((r>>3) | ((g>>3)<<5) | ((b>>3)<<10) | (a>0?0x7000:0)) |
56 | #define COL4444(r,g,b,a) ((r>>4) | ((g>>4)<<4) | ((b>>4)<<8) | ((a>>4)<<12)) |
57 | #define COL8888(r,g,b,a) ((r) | ((g)<<8) | ((b)<<16) | ((a)<<24)) |
58 | |
59 | |
60 | typedef struct |
61 | { |
62 | void* frontbuffer ; |
63 | void* backbuffer ; |
64 | SDL_bool initialized ; |
65 | SDL_bool displayListAvail ; |
66 | unsigned int psm ; |
67 | unsigned int bpp ; |
68 | |
69 | SDL_bool vsync; |
70 | unsigned int currentColor; |
71 | int currentBlendMode; |
72 | |
73 | } PSP_RenderData; |
74 | |
75 | |
76 | typedef struct |
77 | { |
78 | void *data; /**< Image data. */ |
79 | unsigned int size; /**< Size of data in bytes. */ |
80 | unsigned int width; /**< Image width. */ |
81 | unsigned int height; /**< Image height. */ |
82 | unsigned int textureWidth; /**< Texture width (power of two). */ |
83 | unsigned int textureHeight; /**< Texture height (power of two). */ |
84 | unsigned int bits; /**< Image bits per pixel. */ |
85 | unsigned int format; /**< Image format - one of ::pgePixelFormat. */ |
86 | unsigned int pitch; |
87 | SDL_bool swizzled; /**< Is image swizzled. */ |
88 | |
89 | } PSP_TextureData; |
90 | |
91 | typedef struct |
92 | { |
93 | float x, y, z; |
94 | } VertV; |
95 | |
96 | |
97 | typedef struct |
98 | { |
99 | float u, v; |
100 | float x, y, z; |
101 | |
102 | } VertTV; |
103 | |
104 | #define PI 3.14159265358979f |
105 | |
106 | #define radToDeg(x) ((x)*180.f/PI) |
107 | #define degToRad(x) ((x)*PI/180.f) |
108 | |
109 | float MathAbs(float x) |
110 | { |
111 | float result; |
112 | |
113 | __asm__ volatile ( |
114 | "mtv %1, S000\n" |
115 | "vabs.s S000, S000\n" |
116 | "mfv %0, S000\n" |
117 | : "=r" (result) : "r" (x)); |
118 | |
119 | return result; |
120 | } |
121 | |
122 | void MathSincos(float r, float *s, float *c) |
123 | { |
124 | __asm__ volatile ( |
125 | "mtv %2, S002\n" |
126 | "vcst.s S003, VFPU_2_PI\n" |
127 | "vmul.s S002, S002, S003\n" |
128 | "vrot.p C000, S002, [s, c]\n" |
129 | "mfv %0, S000\n" |
130 | "mfv %1, S001\n" |
131 | : "=r" (*s), "=r" (*c): "r" (r)); |
132 | } |
133 | |
134 | void Swap(float *a, float *b) |
135 | { |
136 | float n=*a; |
137 | *a = *b; |
138 | *b = n; |
139 | } |
140 | |
141 | /* Return next power of 2 */ |
142 | static int |
143 | TextureNextPow2(unsigned int w) |
144 | { |
145 | if(w == 0) |
146 | return 0; |
147 | |
148 | unsigned int n = 2; |
149 | |
150 | while(w > n) |
151 | n <<= 1; |
152 | |
153 | return n; |
154 | } |
155 | |
156 | |
157 | static int |
158 | PixelFormatToPSPFMT(Uint32 format) |
159 | { |
160 | switch (format) { |
161 | case SDL_PIXELFORMAT_BGR565: |
162 | return GU_PSM_5650; |
163 | case SDL_PIXELFORMAT_ABGR1555: |
164 | return GU_PSM_5551; |
165 | case SDL_PIXELFORMAT_ABGR4444: |
166 | return GU_PSM_4444; |
167 | case SDL_PIXELFORMAT_ABGR8888: |
168 | return GU_PSM_8888; |
169 | default: |
170 | return GU_PSM_8888; |
171 | } |
172 | } |
173 | |
174 | void |
175 | StartDrawing(SDL_Renderer * renderer) |
176 | { |
177 | PSP_RenderData *data = (PSP_RenderData *) renderer->driverdata; |
178 | if(data->displayListAvail) |
179 | return; |
180 | |
181 | sceGuStart(GU_DIRECT, DisplayList); |
182 | data->displayListAvail = SDL_TRUE; |
183 | } |
184 | |
185 | |
186 | int |
187 | TextureSwizzle(PSP_TextureData *psp_texture) |
188 | { |
189 | if(psp_texture->swizzled) |
190 | return 1; |
191 | |
192 | int bytewidth = psp_texture->textureWidth*(psp_texture->bits>>3); |
193 | int height = psp_texture->size / bytewidth; |
194 | |
195 | int rowblocks = (bytewidth>>4); |
196 | int rowblocksadd = (rowblocks-1)<<7; |
197 | unsigned int blockaddress = 0; |
198 | unsigned int *src = (unsigned int*) psp_texture->data; |
199 | |
200 | unsigned char *data = NULL; |
201 | data = malloc(psp_texture->size); |
202 | |
203 | int j; |
204 | |
205 | for(j = 0; j < height; j++, blockaddress += 16) |
206 | { |
207 | unsigned int *block; |
208 | |
209 | block = (unsigned int*)&data[blockaddress]; |
210 | |
211 | int i; |
212 | |
213 | for(i = 0; i < rowblocks; i++) |
214 | { |
215 | *block++ = *src++; |
216 | *block++ = *src++; |
217 | *block++ = *src++; |
218 | *block++ = *src++; |
219 | block += 28; |
220 | } |
221 | |
222 | if((j & 0x7) == 0x7) |
223 | blockaddress += rowblocksadd; |
224 | } |
225 | |
226 | free(psp_texture->data); |
227 | psp_texture->data = data; |
228 | psp_texture->swizzled = SDL_TRUE; |
229 | |
230 | return 1; |
231 | } |
232 | int TextureUnswizzle(PSP_TextureData *psp_texture) |
233 | { |
234 | if(!psp_texture->swizzled) |
235 | return 1; |
236 | |
237 | int blockx, blocky; |
238 | |
239 | int bytewidth = psp_texture->textureWidth*(psp_texture->bits>>3); |
240 | int height = psp_texture->size / bytewidth; |
241 | |
242 | int widthblocks = bytewidth/16; |
243 | int heightblocks = height/8; |
244 | |
245 | int dstpitch = (bytewidth - 16)/4; |
246 | int dstrow = bytewidth * 8; |
247 | |
248 | unsigned int *src = (unsigned int*) psp_texture->data; |
249 | |
250 | unsigned char *data = NULL; |
251 | |
252 | data = malloc(psp_texture->size); |
253 | |
254 | if(!data) |
255 | return 0; |
256 | |
257 | sceKernelDcacheWritebackAll(); |
258 | |
259 | int j; |
260 | |
261 | unsigned char *ydst = (unsigned char *)data; |
262 | |
263 | for(blocky = 0; blocky < heightblocks; ++blocky) |
264 | { |
265 | unsigned char *xdst = ydst; |
266 | |
267 | for(blockx = 0; blockx < widthblocks; ++blockx) |
268 | { |
269 | unsigned int *block; |
270 | |
271 | block = (unsigned int*)xdst; |
272 | |
273 | for(j = 0; j < 8; ++j) |
274 | { |
275 | *(block++) = *(src++); |
276 | *(block++) = *(src++); |
277 | *(block++) = *(src++); |
278 | *(block++) = *(src++); |
279 | block += dstpitch; |
280 | } |
281 | |
282 | xdst += 16; |
283 | } |
284 | |
285 | ydst += dstrow; |
286 | } |
287 | |
288 | free(psp_texture->data); |
289 | |
290 | psp_texture->data = data; |
291 | |
292 | psp_texture->swizzled = SDL_FALSE; |
293 | |
294 | return 1; |
295 | } |
296 | |
297 | static void |
298 | PSP_WindowEvent(SDL_Renderer * renderer, const SDL_WindowEvent *event) |
299 | { |
300 | } |
301 | |
302 | |
303 | static int |
304 | PSP_CreateTexture(SDL_Renderer * renderer, SDL_Texture * texture) |
305 | { |
306 | /* PSP_RenderData *renderdata = (PSP_RenderData *) renderer->driverdata; */ |
307 | PSP_TextureData* psp_texture = (PSP_TextureData*) SDL_calloc(1, sizeof(*psp_texture)); |
308 | |
309 | if(!psp_texture) |
310 | return -1; |
311 | |
312 | psp_texture->swizzled = SDL_FALSE; |
313 | psp_texture->width = texture->w; |
314 | psp_texture->height = texture->h; |
315 | psp_texture->textureHeight = TextureNextPow2(texture->h); |
316 | psp_texture->textureWidth = TextureNextPow2(texture->w); |
317 | psp_texture->format = PixelFormatToPSPFMT(texture->format); |
318 | |
319 | switch(psp_texture->format) |
320 | { |
321 | case GU_PSM_5650: |
322 | case GU_PSM_5551: |
323 | case GU_PSM_4444: |
324 | psp_texture->bits = 16; |
325 | break; |
326 | |
327 | case GU_PSM_8888: |
328 | psp_texture->bits = 32; |
329 | break; |
330 | |
331 | default: |
332 | return -1; |
333 | } |
334 | |
335 | psp_texture->pitch = psp_texture->textureWidth * SDL_BYTESPERPIXEL(texture->format); |
336 | psp_texture->size = psp_texture->textureHeight*psp_texture->pitch; |
337 | psp_texture->data = SDL_calloc(1, psp_texture->size); |
338 | |
339 | if(!psp_texture->data) |
340 | { |
341 | SDL_free(psp_texture); |
342 | return SDL_OutOfMemory(); |
343 | } |
344 | texture->driverdata = psp_texture; |
345 | |
346 | return 0; |
347 | } |
348 | |
349 | static int |
350 | PSP_SetTextureColorMod(SDL_Renderer * renderer, SDL_Texture * texture) |
351 | { |
352 | return SDL_Unsupported(); |
353 | } |
354 | |
355 | void |
356 | TextureActivate(SDL_Texture * texture) |
357 | { |
358 | PSP_TextureData *psp_texture = (PSP_TextureData *) texture->driverdata; |
359 | int scaleMode = (texture->scaleMode == SDL_ScaleModeNearest) ? GU_NEAREST : GU_LINEAR; |
360 | |
361 | /* Swizzling is useless with small textures. */ |
362 | if (texture->w >= 16 || texture->h >= 16) |
363 | { |
364 | TextureSwizzle(psp_texture); |
365 | } |
366 | |
367 | sceGuEnable(GU_TEXTURE_2D); |
368 | sceGuTexWrap(GU_REPEAT, GU_REPEAT); |
369 | sceGuTexMode(psp_texture->format, 0, 0, psp_texture->swizzled); |
370 | sceGuTexFilter(scaleMode, scaleMode); /* GU_NEAREST good for tile-map */ |
371 | /* GU_LINEAR good for scaling */ |
372 | sceGuTexImage(0, psp_texture->textureWidth, psp_texture->textureHeight, psp_texture->textureWidth, psp_texture->data); |
373 | sceGuTexFunc(GU_TFX_REPLACE, GU_TCC_RGBA); |
374 | } |
375 | |
376 | |
377 | static int |
378 | PSP_UpdateTexture(SDL_Renderer * renderer, SDL_Texture * texture, |
379 | const SDL_Rect * rect, const void *pixels, int pitch) |
380 | { |
381 | /* PSP_TextureData *psp_texture = (PSP_TextureData *) texture->driverdata; */ |
382 | const Uint8 *src; |
383 | Uint8 *dst; |
384 | int row, length,dpitch; |
385 | src = pixels; |
386 | |
387 | PSP_LockTexture(renderer, texture,rect,(void **)&dst, &dpitch); |
388 | length = rect->w * SDL_BYTESPERPIXEL(texture->format); |
389 | if (length == pitch && length == dpitch) { |
390 | SDL_memcpy(dst, src, length*rect->h); |
391 | } else { |
392 | for (row = 0; row < rect->h; ++row) { |
393 | SDL_memcpy(dst, src, length); |
394 | src += pitch; |
395 | dst += dpitch; |
396 | } |
397 | } |
398 | |
399 | sceKernelDcacheWritebackAll(); |
400 | return 0; |
401 | } |
402 | |
403 | static int |
404 | PSP_LockTexture(SDL_Renderer * renderer, SDL_Texture * texture, |
405 | const SDL_Rect * rect, void **pixels, int *pitch) |
406 | { |
407 | PSP_TextureData *psp_texture = (PSP_TextureData *) texture->driverdata; |
408 | |
409 | *pixels = |
410 | (void *) ((Uint8 *) psp_texture->data + rect->y * psp_texture->pitch + |
411 | rect->x * SDL_BYTESPERPIXEL(texture->format)); |
412 | *pitch = psp_texture->pitch; |
413 | return 0; |
414 | } |
415 | |
416 | static void |
417 | PSP_UnlockTexture(SDL_Renderer * renderer, SDL_Texture * texture) |
418 | { |
419 | PSP_TextureData *psp_texture = (PSP_TextureData *) texture->driverdata; |
420 | SDL_Rect rect; |
421 | |
422 | /* We do whole texture updates, at least for now */ |
423 | rect.x = 0; |
424 | rect.y = 0; |
425 | rect.w = texture->w; |
426 | rect.h = texture->h; |
427 | PSP_UpdateTexture(renderer, texture, &rect, psp_texture->data, psp_texture->pitch); |
428 | } |
429 | |
430 | static void |
431 | PSP_SetTextureScaleMode(SDL_Renderer * renderer, SDL_Texture * texture, SDL_ScaleMode scaleMode) |
432 | { |
433 | /* Nothing to do because TextureActivate takes care of it */ |
434 | } |
435 | |
436 | static int |
437 | PSP_SetRenderTarget(SDL_Renderer * renderer, SDL_Texture * texture) |
438 | { |
439 | return 0; |
440 | } |
441 | |
442 | static int |
443 | PSP_QueueSetViewport(SDL_Renderer * renderer, SDL_RenderCommand *cmd) |
444 | { |
445 | return 0; /* nothing to do in this backend. */ |
446 | } |
447 | |
448 | static int |
449 | PSP_QueueDrawPoints(SDL_Renderer * renderer, SDL_RenderCommand *cmd, const SDL_FPoint * points, int count) |
450 | { |
451 | VertV *verts = (VertV *) SDL_AllocateRenderVertices(renderer, count * sizeof (VertV), 4, &cmd->data.draw.first); |
452 | int i; |
453 | |
454 | if (!verts) { |
455 | return -1; |
456 | } |
457 | |
458 | cmd->data.draw.count = count; |
459 | |
460 | for (i = 0; i < count; i++, verts++, points++) { |
461 | verts->x = points->x; |
462 | verts->y = points->y; |
463 | verts->z = 0.0f; |
464 | } |
465 | |
466 | return 0; |
467 | } |
468 | |
469 | static int |
470 | PSP_QueueFillRects(SDL_Renderer * renderer, SDL_RenderCommand *cmd, const SDL_FRect * rects, int count) |
471 | { |
472 | VertV *verts = (GLfloat *) SDL_AllocateRenderVertices(renderer, count * 2 * sizeof (VertV), 4, &cmd->data.draw.first); |
473 | int i; |
474 | |
475 | if (!verts) { |
476 | return -1; |
477 | } |
478 | |
479 | cmd->data.draw.count = count; |
480 | for (i = 0; i < count; i++, rects++) { |
481 | const SDL_FRect *rect = &rects[i]; |
482 | verts->x = rect->x; |
483 | verts->y = rect->y; |
484 | verts->z = 0.0f; |
485 | verts++; |
486 | |
487 | verts->x = rect->x + rect->w; |
488 | verts->y = rect->y + rect->h; |
489 | verts->z = 0.0f; |
490 | verts++; |
491 | } |
492 | |
493 | return 0; |
494 | } |
495 | |
496 | static int |
497 | PSP_QueueCopy(SDL_Renderer * renderer, SDL_RenderCommand *cmd, SDL_Texture * texture, |
498 | const SDL_Rect * srcrect, const SDL_FRect * dstrect) |
499 | { |
500 | VertTV *verts; |
501 | const float x = dstrect->x; |
502 | const float y = dstrect->y; |
503 | const float width = dstrect->w; |
504 | const float height = dstrect->h; |
505 | |
506 | const float u0 = srcrect->x; |
507 | const float v0 = srcrect->y; |
508 | const float u1 = srcrect->x + srcrect->w; |
509 | const float v1 = srcrect->y + srcrect->h; |
510 | |
511 | if((MathAbs(u1) - MathAbs(u0)) < 64.0f) |
512 | { |
513 | verts = (VertTV *) SDL_AllocateRenderVertices(renderer, 2 * sizeof (VertTV), 4, &cmd->data.draw.first); |
514 | if (!verts) { |
515 | return -1; |
516 | } |
517 | |
518 | cmd->data.draw.count = 1; |
519 | |
520 | verts->u = u0; |
521 | verts->v = v0; |
522 | verts->x = x; |
523 | verts->y = y; |
524 | verts->z = 0; |
525 | verts++; |
526 | |
527 | verts->u = u1; |
528 | verts->v = v1; |
529 | verts->x = x + width; |
530 | verts->y = y + height; |
531 | verts->z = 0; |
532 | verts++; |
533 | } |
534 | else |
535 | { |
536 | float start, end; |
537 | float curU = u0; |
538 | float curX = x; |
539 | const float endX = x + width; |
540 | const float slice = 64.0f; |
541 | const size_t count = SDL_ceilf(width / slice); |
542 | size_t i; |
543 | float ustep = (u1 - u0)/width * slice; |
544 | |
545 | if(ustep < 0.0f) |
546 | ustep = -ustep; |
547 | |
548 | cmd->data.draw.count = count; |
549 | |
550 | verts = (VertTV *) SDL_AllocateRenderVertices(renderer, count * sizeof (VertTV), 4, &cmd->data.draw.first); |
551 | if (!verts) { |
552 | return -1; |
553 | } |
554 | |
555 | |
556 | for(i = 0, start = 0, end = width; i < count; i++, start += slice) |
557 | { |
558 | const float polyWidth = ((curX + slice) > endX) ? (endX - curX) : slice; |
559 | const float sourceWidth = ((curU + ustep) > u1) ? (u1 - curU) : ustep; |
560 | |
561 | SDL_assert(start < end); |
562 | |
563 | verts->u = curU; |
564 | verts->v = v0; |
565 | verts->x = curX; |
566 | verts->y = y; |
567 | verts->z = 0; |
568 | |
569 | curU += sourceWidth; |
570 | curX += polyWidth; |
571 | |
572 | verts->u = curU; |
573 | verts->v = v1; |
574 | verts->x = curX; |
575 | verts->y = (y + height); |
576 | verts->z = 0; |
577 | } |
578 | } |
579 | |
580 | return 0; |
581 | } |
582 | |
583 | static int |
584 | PSP_QueueCopyEx(SDL_Renderer * renderer, SDL_RenderCommand *cmd, SDL_Texture * texture, |
585 | const SDL_Rect * srcrect, const SDL_FRect * dstrect, |
586 | const double angle, const SDL_FPoint *center, const SDL_RendererFlip flip) |
587 | { |
588 | VertTV *verts = (VertTV *) SDL_AllocateRenderVertices(renderer, 4 * sizeof (VertTV), 4, &cmd->data.draw.first); |
589 | const float centerx = center->x; |
590 | const float centery = center->y; |
591 | const float x = dstrect->x + centerx; |
592 | const float y = dstrect->y + centery; |
593 | const float width = dstrect->w - centerx; |
594 | const float height = dstrect->h - centery; |
595 | float s, c; |
596 | |
597 | float u0 = srcrect->x; |
598 | float v0 = srcrect->y; |
599 | float u1 = srcrect->x + srcrect->w; |
600 | float v1 = srcrect->y + srcrect->h; |
601 | |
602 | |
603 | if (!verts) { |
604 | return -1; |
605 | } |
606 | |
607 | cmd->data.draw.count = 1; |
608 | |
609 | MathSincos(degToRad(angle), &s, &c); |
610 | |
611 | const float cw = c * width; |
612 | const float sw = s * width; |
613 | const float ch = c * height; |
614 | const float sh = s * height; |
615 | |
616 | if (flip & SDL_FLIP_VERTICAL) { |
617 | Swap(&v0, &v1); |
618 | } |
619 | |
620 | if (flip & SDL_FLIP_HORIZONTAL) { |
621 | Swap(&u0, &u1); |
622 | } |
623 | |
624 | verts->u = u0; |
625 | verts->v = v0; |
626 | verts->x = x - cw + sh; |
627 | verts->y = y - sw - ch; |
628 | verts->z = 0; |
629 | verts++; |
630 | |
631 | verts->u = u0; |
632 | verts->v = v1; |
633 | verts->x = x - cw - sh; |
634 | verts->y = y - sw + ch; |
635 | verts->z = 0; |
636 | verts++; |
637 | |
638 | verts->u = u1; |
639 | verts->v = v1; |
640 | verts->x = x + cw - sh; |
641 | verts->y = y + sw + ch; |
642 | verts->z = 0; |
643 | verts++; |
644 | |
645 | verts->u = u1; |
646 | verts->v = v0; |
647 | verts->x = x + cw + sh; |
648 | verts->y = y + sw - ch; |
649 | verts->z = 0; |
650 | verts++; |
651 | |
652 | return 0; |
653 | } |
654 | |
655 | static void |
656 | PSP_SetBlendMode(SDL_Renderer * renderer, int blendMode) |
657 | { |
658 | PSP_RenderData *data = (PSP_RenderData *) renderer->driverdata; |
659 | if (blendMode != data-> currentBlendMode) { |
660 | switch (blendMode) { |
661 | case SDL_BLENDMODE_NONE: |
662 | sceGuTexFunc(GU_TFX_REPLACE, GU_TCC_RGBA); |
663 | sceGuDisable(GU_BLEND); |
664 | break; |
665 | case SDL_BLENDMODE_BLEND: |
666 | sceGuTexFunc(GU_TFX_MODULATE , GU_TCC_RGBA); |
667 | sceGuEnable(GU_BLEND); |
668 | sceGuBlendFunc(GU_ADD, GU_SRC_ALPHA, GU_ONE_MINUS_SRC_ALPHA, 0, 0 ); |
669 | break; |
670 | case SDL_BLENDMODE_ADD: |
671 | sceGuTexFunc(GU_TFX_MODULATE , GU_TCC_RGBA); |
672 | sceGuEnable(GU_BLEND); |
673 | sceGuBlendFunc(GU_ADD, GU_SRC_ALPHA, GU_FIX, 0, 0x00FFFFFF ); |
674 | break; |
675 | case SDL_BLENDMODE_MOD: |
676 | sceGuTexFunc(GU_TFX_MODULATE , GU_TCC_RGBA); |
677 | sceGuEnable(GU_BLEND); |
678 | sceGuBlendFunc(GU_ADD, GU_FIX, GU_SRC_COLOR, 0, 0); |
679 | break; |
680 | case SDL_BLENDMODE_MUL: |
681 | sceGuTexFunc(GU_TFX_MODULATE , GU_TCC_RGBA); |
682 | sceGuEnable(GU_BLEND); |
683 | sceGuBlendFunc(GU_ADD, GU_DST_COLOR, GU_ONE_MINUS_SRC_ALPHA, 0, 0); |
684 | break; |
685 | } |
686 | data->currentBlendMode = blendMode; |
687 | } |
688 | } |
689 | |
690 | static int |
691 | PSP_RunCommandQueue(SDL_Renderer * renderer, SDL_RenderCommand *cmd, void *vertices, size_t vertsize) |
692 | { |
693 | PSP_RenderData *data = (PSP_RenderData *) renderer->driverdata; |
694 | size_t i; |
695 | |
696 | StartDrawing(renderer); |
697 | |
698 | /* note that before the renderer interface change, this would do extrememly small |
699 | batches with sceGuGetMemory()--a few vertices at a time--and it's not clear that |
700 | this won't fail if you try to push 100,000 draw calls in a single batch. |
701 | I don't know what the limits on PSP hardware are. It might be useful to have |
702 | rendering backends report a reasonable maximum, so the higher level can flush |
703 | if we appear to be exceeding that. */ |
704 | Uint8 *gpumem = (Uint8 *) sceGuGetMemory(vertsize); |
705 | if (!gpumem) { |
706 | return SDL_SetError("Couldn't obtain a %d-byte vertex buffer!" , (int) vertsize); |
707 | } |
708 | SDL_memcpy(gpumem, vertices, vertsize); |
709 | |
710 | while (cmd) { |
711 | switch (cmd->command) { |
712 | case SDL_RENDERCMD_SETDRAWCOLOR: { |
713 | break; /* !!! FIXME: we could cache drawstate like color */ |
714 | } |
715 | |
716 | case SDL_RENDERCMD_SETVIEWPORT: { |
717 | SDL_Rect *viewport = &data->drawstate.viewport; |
718 | if (SDL_memcmp(viewport, &cmd->data.viewport.rect, sizeof (SDL_Rect)) != 0) { |
719 | SDL_memcpy(viewport, &cmd->data.viewport.rect, sizeof (SDL_Rect)); |
720 | data->drawstate.viewport_dirty = SDL_TRUE; |
721 | } |
722 | break; |
723 | } |
724 | |
725 | case SDL_RENDERCMD_SETCLIPRECT: { |
726 | const SDL_Rect *rect = &cmd->data.cliprect.rect; |
727 | if (data->drawstate.cliprect_enabled != cmd->data.cliprect.enabled) { |
728 | data->drawstate.cliprect_enabled = cmd->data.cliprect.enabled; |
729 | data->drawstate.cliprect_enabled_dirty = SDL_TRUE; |
730 | } |
731 | if (SDL_memcmp(&data->drawstate.cliprect, rect, sizeof (SDL_Rect)) != 0) { |
732 | SDL_memcpy(&data->drawstate.cliprect, rect, sizeof (SDL_Rect)); |
733 | data->drawstate.cliprect_dirty = SDL_TRUE; |
734 | } |
735 | break; |
736 | } |
737 | |
738 | case SDL_RENDERCMD_CLEAR: { |
739 | const Uint8 r = cmd->data.color.r; |
740 | const Uint8 g = cmd->data.color.g; |
741 | const Uint8 b = cmd->data.color.b; |
742 | const Uint8 a = cmd->data.color.a; |
743 | const Uint32 color = ((a << 24) | (b << 16) | (g << 8) | r); |
744 | /* !!! FIXME: we could cache drawstate like clear color */ |
745 | sceGuClearColor(color); |
746 | sceGuClearDepth(0); |
747 | sceGuClear(GU_COLOR_BUFFER_BIT|GU_DEPTH_BUFFER_BIT|GU_FAST_CLEAR_BIT); |
748 | break; |
749 | } |
750 | |
751 | case SDL_RENDERCMD_DRAW_POINTS: { |
752 | const size_t count = cmd->data.draw.count; |
753 | const VertV *verts = (VertV *) (gpumem + cmd->data.draw.first); |
754 | const Uint8 r = cmd->data.draw.r; |
755 | const Uint8 g = cmd->data.draw.g; |
756 | const Uint8 b = cmd->data.draw.b; |
757 | const Uint8 a = cmd->data.draw.a; |
758 | const Uint32 color = ((a << 24) | (b << 16) | (g << 8) | r); |
759 | /* !!! FIXME: we could cache draw state like color, texturing, etc */ |
760 | sceGuColor(color); |
761 | sceGuDisable(GU_TEXTURE_2D); |
762 | sceGuShadeModel(GU_FLAT); |
763 | sceGuDrawArray(GU_POINTS, GU_VERTEX_32BITF|GU_TRANSFORM_2D, count, 0, verts); |
764 | sceGuShadeModel(GU_SMOOTH); |
765 | sceGuEnable(GU_TEXTURE_2D); |
766 | break; |
767 | } |
768 | |
769 | case SDL_RENDERCMD_DRAW_LINES: { |
770 | const size_t count = cmd->data.draw.count; |
771 | const VertV *verts = (VertV *) (gpumem + cmd->data.draw.first); |
772 | const Uint8 r = cmd->data.draw.r; |
773 | const Uint8 g = cmd->data.draw.g; |
774 | const Uint8 b = cmd->data.draw.b; |
775 | const Uint8 a = cmd->data.draw.a; |
776 | const Uint32 color = ((a << 24) | (b << 16) | (g << 8) | r); |
777 | /* !!! FIXME: we could cache draw state like color, texturing, etc */ |
778 | sceGuColor(color); |
779 | sceGuDisable(GU_TEXTURE_2D); |
780 | sceGuShadeModel(GU_FLAT); |
781 | sceGuDrawArray(GU_LINE_STRIP, GU_VERTEX_32BITF|GU_TRANSFORM_2D, count, 0, verts); |
782 | sceGuShadeModel(GU_SMOOTH); |
783 | sceGuEnable(GU_TEXTURE_2D); |
784 | break; |
785 | } |
786 | |
787 | case SDL_RENDERCMD_FILL_RECTS: { |
788 | const size_t count = cmd->data.draw.count; |
789 | const VertV *verts = (VertV *) (gpumem + cmd->data.draw.first); |
790 | const Uint8 r = cmd->data.draw.r; |
791 | const Uint8 g = cmd->data.draw.g; |
792 | const Uint8 b = cmd->data.draw.b; |
793 | const Uint8 a = cmd->data.draw.a; |
794 | const Uint32 color = ((a << 24) | (b << 16) | (g << 8) | r); |
795 | /* !!! FIXME: we could cache draw state like color, texturing, etc */ |
796 | sceGuColor(color); |
797 | sceGuDisable(GU_TEXTURE_2D); |
798 | sceGuShadeModel(GU_FLAT); |
799 | sceGuDrawArray(GU_SPRITES, GU_VERTEX_32BITF|GU_TRANSFORM_2D, 2 * count, 0, verts); |
800 | sceGuShadeModel(GU_SMOOTH); |
801 | sceGuEnable(GU_TEXTURE_2D); |
802 | break; |
803 | } |
804 | |
805 | case SDL_RENDERCMD_COPY: { |
806 | const size_t count = cmd->data.draw.count; |
807 | const VertTV *verts = (VertTV *) (gpumem + cmd->data.draw.first); |
808 | const Uint8 alpha = cmd->data.draw.a; |
809 | TextureActivate(cmd->data.draw.texture); |
810 | PSP_SetBlendMode(renderer, cmd->data.draw.blend); |
811 | |
812 | if(alpha != 255) { /* !!! FIXME: is this right? */ |
813 | sceGuTexFunc(GU_TFX_MODULATE, GU_TCC_RGBA); |
814 | sceGuColor(GU_RGBA(255, 255, 255, alpha)); |
815 | } else { |
816 | sceGuTexFunc(GU_TFX_REPLACE, GU_TCC_RGBA); |
817 | sceGuColor(0xFFFFFFFF); |
818 | } |
819 | |
820 | sceGuDrawArray(GU_SPRITES, GU_TEXTURE_32BITF|GU_VERTEX_32BITF|GU_TRANSFORM_2D, 2 * count, 0, verts); |
821 | |
822 | if(alpha != 255) { |
823 | sceGuTexFunc(GU_TFX_REPLACE, GU_TCC_RGBA); |
824 | } |
825 | break; |
826 | } |
827 | |
828 | case SDL_RENDERCMD_COPY_EX: { |
829 | const VertTV *verts = (VertTV *) (gpumem + cmd->data.draw.first); |
830 | const Uint8 alpha = cmd->data.draw.a; |
831 | TextureActivate(cmd->data.draw.texture); |
832 | PSP_SetBlendMode(renderer, cmd->data.draw.blend); |
833 | |
834 | if(alpha != 255) { /* !!! FIXME: is this right? */ |
835 | sceGuTexFunc(GU_TFX_MODULATE, GU_TCC_RGBA); |
836 | sceGuColor(GU_RGBA(255, 255, 255, alpha)); |
837 | } else { |
838 | sceGuTexFunc(GU_TFX_REPLACE, GU_TCC_RGBA); |
839 | sceGuColor(0xFFFFFFFF); |
840 | } |
841 | |
842 | sceGuDrawArray(GU_TRIANGLE_FAN, GU_TEXTURE_32BITF|GU_VERTEX_32BITF|GU_TRANSFORM_2D, 4, 0, verts); |
843 | |
844 | if(alpha != 255) { |
845 | sceGuTexFunc(GU_TFX_REPLACE, GU_TCC_RGBA); |
846 | } |
847 | break; |
848 | } |
849 | |
850 | case SDL_RENDERCMD_NO_OP: |
851 | break; |
852 | } |
853 | |
854 | cmd = cmd->next; |
855 | } |
856 | |
857 | return 0; |
858 | } |
859 | |
860 | static int |
861 | PSP_RenderReadPixels(SDL_Renderer * renderer, const SDL_Rect * rect, |
862 | Uint32 pixel_format, void * pixels, int pitch) |
863 | { |
864 | return SDL_Unsupported(); |
865 | } |
866 | |
867 | static void |
868 | PSP_RenderPresent(SDL_Renderer * renderer) |
869 | { |
870 | PSP_RenderData *data = (PSP_RenderData *) renderer->driverdata; |
871 | if(!data->displayListAvail) |
872 | return; |
873 | |
874 | data->displayListAvail = SDL_FALSE; |
875 | sceGuFinish(); |
876 | sceGuSync(0,0); |
877 | |
878 | /* if(data->vsync) */ |
879 | sceDisplayWaitVblankStart(); |
880 | |
881 | data->backbuffer = data->frontbuffer; |
882 | data->frontbuffer = vabsptr(sceGuSwapBuffers()); |
883 | |
884 | } |
885 | |
886 | static void |
887 | PSP_DestroyTexture(SDL_Renderer * renderer, SDL_Texture * texture) |
888 | { |
889 | PSP_RenderData *renderdata = (PSP_RenderData *) renderer->driverdata; |
890 | PSP_TextureData *psp_texture = (PSP_TextureData *) texture->driverdata; |
891 | |
892 | if (renderdata == 0) |
893 | return; |
894 | |
895 | if(psp_texture == 0) |
896 | return; |
897 | |
898 | SDL_free(psp_texture->data); |
899 | SDL_free(psp_texture); |
900 | texture->driverdata = NULL; |
901 | } |
902 | |
903 | static void |
904 | PSP_DestroyRenderer(SDL_Renderer * renderer) |
905 | { |
906 | PSP_RenderData *data = (PSP_RenderData *) renderer->driverdata; |
907 | if (data) { |
908 | if (!data->initialized) |
909 | return; |
910 | |
911 | StartDrawing(renderer); |
912 | |
913 | sceGuTerm(); |
914 | /* vfree(data->backbuffer); */ |
915 | /* vfree(data->frontbuffer); */ |
916 | |
917 | data->initialized = SDL_FALSE; |
918 | data->displayListAvail = SDL_FALSE; |
919 | SDL_free(data); |
920 | } |
921 | SDL_free(renderer); |
922 | } |
923 | |
924 | SDL_Renderer * |
925 | PSP_CreateRenderer(SDL_Window * window, Uint32 flags) |
926 | { |
927 | |
928 | SDL_Renderer *renderer; |
929 | PSP_RenderData *data; |
930 | int pixelformat; |
931 | renderer = (SDL_Renderer *) SDL_calloc(1, sizeof(*renderer)); |
932 | if (!renderer) { |
933 | SDL_OutOfMemory(); |
934 | return NULL; |
935 | } |
936 | |
937 | data = (PSP_RenderData *) SDL_calloc(1, sizeof(*data)); |
938 | if (!data) { |
939 | PSP_DestroyRenderer(renderer); |
940 | SDL_OutOfMemory(); |
941 | return NULL; |
942 | } |
943 | |
944 | |
945 | renderer->WindowEvent = PSP_WindowEvent; |
946 | renderer->CreateTexture = PSP_CreateTexture; |
947 | renderer->SetTextureColorMod = PSP_SetTextureColorMod; |
948 | renderer->UpdateTexture = PSP_UpdateTexture; |
949 | renderer->LockTexture = PSP_LockTexture; |
950 | renderer->UnlockTexture = PSP_UnlockTexture; |
951 | renderer->SetTextureScaleMode = PSP_SetTextureScaleMode; |
952 | renderer->SetRenderTarget = PSP_SetRenderTarget; |
953 | renderer->QueueSetViewport = PSP_QueueSetViewport; |
954 | renderer->QueueSetDrawColor = PSP_QueueSetViewport; /* SetViewport and SetDrawColor are (currently) no-ops. */ |
955 | renderer->QueueDrawPoints = PSP_QueueDrawPoints; |
956 | renderer->QueueDrawLines = PSP_QueueDrawPoints; /* lines and points queue vertices the same way. */ |
957 | renderer->QueueFillRects = PSP_QueueFillRects; |
958 | renderer->QueueCopy = PSP_QueueCopy; |
959 | renderer->QueueCopyEx = PSP_QueueCopyEx; |
960 | renderer->RunCommandQueue = PSP_RunCommandQueue; |
961 | renderer->RenderReadPixels = PSP_RenderReadPixels; |
962 | renderer->RenderPresent = PSP_RenderPresent; |
963 | renderer->DestroyTexture = PSP_DestroyTexture; |
964 | renderer->DestroyRenderer = PSP_DestroyRenderer; |
965 | renderer->info = PSP_RenderDriver.info; |
966 | renderer->info.flags = (SDL_RENDERER_ACCELERATED | SDL_RENDERER_TARGETTEXTURE); |
967 | renderer->driverdata = data; |
968 | renderer->window = window; |
969 | |
970 | if (data->initialized != SDL_FALSE) |
971 | return 0; |
972 | data->initialized = SDL_TRUE; |
973 | |
974 | if (flags & SDL_RENDERER_PRESENTVSYNC) { |
975 | data->vsync = SDL_TRUE; |
976 | } else { |
977 | data->vsync = SDL_FALSE; |
978 | } |
979 | |
980 | pixelformat=PixelFormatToPSPFMT(SDL_GetWindowPixelFormat(window)); |
981 | switch(pixelformat) |
982 | { |
983 | case GU_PSM_4444: |
984 | case GU_PSM_5650: |
985 | case GU_PSM_5551: |
986 | data->frontbuffer = (unsigned int *)(PSP_FRAME_BUFFER_SIZE<<1); |
987 | data->backbuffer = (unsigned int *)(0); |
988 | data->bpp = 2; |
989 | data->psm = pixelformat; |
990 | break; |
991 | default: |
992 | data->frontbuffer = (unsigned int *)(PSP_FRAME_BUFFER_SIZE<<2); |
993 | data->backbuffer = (unsigned int *)(0); |
994 | data->bpp = 4; |
995 | data->psm = GU_PSM_8888; |
996 | break; |
997 | } |
998 | |
999 | sceGuInit(); |
1000 | /* setup GU */ |
1001 | sceGuStart(GU_DIRECT, DisplayList); |
1002 | sceGuDrawBuffer(data->psm, data->frontbuffer, PSP_FRAME_BUFFER_WIDTH); |
1003 | sceGuDispBuffer(PSP_SCREEN_WIDTH, PSP_SCREEN_HEIGHT, data->backbuffer, PSP_FRAME_BUFFER_WIDTH); |
1004 | |
1005 | |
1006 | sceGuOffset(2048 - (PSP_SCREEN_WIDTH>>1), 2048 - (PSP_SCREEN_HEIGHT>>1)); |
1007 | sceGuViewport(2048, 2048, PSP_SCREEN_WIDTH, PSP_SCREEN_HEIGHT); |
1008 | |
1009 | data->frontbuffer = vabsptr(data->frontbuffer); |
1010 | data->backbuffer = vabsptr(data->backbuffer); |
1011 | |
1012 | /* Scissoring */ |
1013 | sceGuScissor(0, 0, PSP_SCREEN_WIDTH, PSP_SCREEN_HEIGHT); |
1014 | sceGuEnable(GU_SCISSOR_TEST); |
1015 | |
1016 | /* Backface culling */ |
1017 | sceGuFrontFace(GU_CCW); |
1018 | sceGuEnable(GU_CULL_FACE); |
1019 | |
1020 | /* Texturing */ |
1021 | sceGuEnable(GU_TEXTURE_2D); |
1022 | sceGuShadeModel(GU_SMOOTH); |
1023 | sceGuTexWrap(GU_REPEAT, GU_REPEAT); |
1024 | |
1025 | /* Blending */ |
1026 | sceGuEnable(GU_BLEND); |
1027 | sceGuBlendFunc(GU_ADD, GU_SRC_ALPHA, GU_ONE_MINUS_SRC_ALPHA, 0, 0); |
1028 | |
1029 | sceGuTexFilter(GU_LINEAR,GU_LINEAR); |
1030 | |
1031 | sceGuFinish(); |
1032 | sceGuSync(0,0); |
1033 | sceDisplayWaitVblankStartCB(); |
1034 | sceGuDisplay(GU_TRUE); |
1035 | |
1036 | return renderer; |
1037 | } |
1038 | |
1039 | SDL_RenderDriver PSP_RenderDriver = { |
1040 | .CreateRenderer = PSP_CreateRenderer, |
1041 | .info = { |
1042 | .name = "PSP" , |
1043 | .flags = SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC | SDL_RENDERER_TARGETTEXTURE, |
1044 | .num_texture_formats = 4, |
1045 | .texture_formats = { [0] = SDL_PIXELFORMAT_BGR565, |
1046 | [1] = SDL_PIXELFORMAT_ABGR1555, |
1047 | [2] = SDL_PIXELFORMAT_ABGR4444, |
1048 | [3] = SDL_PIXELFORMAT_ABGR8888, |
1049 | }, |
1050 | .max_texture_width = 512, |
1051 | .max_texture_height = 512, |
1052 | } |
1053 | }; |
1054 | |
1055 | #endif /* SDL_VIDEO_RENDER_PSP */ |
1056 | |
1057 | /* vi: set ts=4 sw=4 expandtab: */ |
1058 | |
1059 | |