|  | /* | 
|  | Simple DirectMedia Layer | 
|  | Copyright (C) 1997-2021 Sam Lantinga <slouken@libsdl.org> | 
|  |  | 
|  | This software is provided 'as-is', without any express or implied | 
|  | warranty.  In no event will the authors be held liable for any damages | 
|  | arising from the use of this software. | 
|  |  | 
|  | Permission is granted to anyone to use this software for any purpose, | 
|  | including commercial applications, and to alter it and redistribute it | 
|  | freely, subject to the following restrictions: | 
|  |  | 
|  | 1. The origin of this software must not be misrepresented; you must not | 
|  | claim that you wrote the original software. If you use this software | 
|  | in a product, an acknowledgment in the product documentation would be | 
|  | appreciated but is not required. | 
|  | 2. Altered source versions must be plainly marked as such, and must not be | 
|  | misrepresented as being the original software. | 
|  | 3. This notice may not be removed or altered from any source distribution. | 
|  | */ | 
|  | #include "../../SDL_internal.h" | 
|  |  | 
|  | #if SDL_VIDEO_RENDER_PSP | 
|  |  | 
|  | #include "SDL_hints.h" | 
|  | #include "../SDL_sysrender.h" | 
|  |  | 
|  | #include <pspkernel.h> | 
|  | #include <pspdisplay.h> | 
|  | #include <pspgu.h> | 
|  | #include <pspgum.h> | 
|  | #include <stdio.h> | 
|  | #include <string.h> | 
|  | #include <math.h> | 
|  | #include <pspge.h> | 
|  | #include <stdarg.h> | 
|  | #include <stdlib.h> | 
|  | #include <vram.h> | 
|  |  | 
|  |  | 
|  |  | 
|  |  | 
|  | /* PSP renderer implementation, based on the PGE  */ | 
|  |  | 
|  | #define PSP_SCREEN_WIDTH    480 | 
|  | #define PSP_SCREEN_HEIGHT   272 | 
|  |  | 
|  | #define PSP_FRAME_BUFFER_WIDTH  512 | 
|  | #define PSP_FRAME_BUFFER_SIZE   (PSP_FRAME_BUFFER_WIDTH*PSP_SCREEN_HEIGHT) | 
|  |  | 
|  | static unsigned int __attribute__((aligned(16))) DisplayList[262144]; | 
|  |  | 
|  |  | 
|  | #define COL5650(r,g,b,a)    ((r>>3) | ((g>>2)<<5) | ((b>>3)<<11)) | 
|  | #define COL5551(r,g,b,a)    ((r>>3) | ((g>>3)<<5) | ((b>>3)<<10) | (a>0?0x7000:0)) | 
|  | #define COL4444(r,g,b,a)    ((r>>4) | ((g>>4)<<4) | ((b>>4)<<8) | ((a>>4)<<12)) | 
|  | #define COL8888(r,g,b,a)    ((r) | ((g)<<8) | ((b)<<16) | ((a)<<24)) | 
|  |  | 
|  |  | 
|  | typedef struct | 
|  | { | 
|  | void*           frontbuffer ; | 
|  | void*           backbuffer ; | 
|  | SDL_bool        initialized ; | 
|  | SDL_bool        displayListAvail ; | 
|  | unsigned int    psm ; | 
|  | unsigned int    bpp ; | 
|  |  | 
|  | SDL_bool        vsync; | 
|  | unsigned int    currentColor; | 
|  | int             currentBlendMode; | 
|  |  | 
|  | } PSP_RenderData; | 
|  |  | 
|  |  | 
|  | typedef struct | 
|  | { | 
|  | void                *data;                              /**< Image data. */ | 
|  | unsigned int        size;                               /**< Size of data in bytes. */ | 
|  | unsigned int        width;                              /**< Image width. */ | 
|  | unsigned int        height;                             /**< Image height. */ | 
|  | unsigned int        textureWidth;                       /**< Texture width (power of two). */ | 
|  | unsigned int        textureHeight;                      /**< Texture height (power of two). */ | 
|  | unsigned int        bits;                               /**< Image bits per pixel. */ | 
|  | unsigned int        format;                             /**< Image format - one of ::pgePixelFormat. */ | 
|  | unsigned int        pitch; | 
|  | SDL_bool            swizzled;                           /**< Is image swizzled. */ | 
|  |  | 
|  | } PSP_TextureData; | 
|  |  | 
|  | typedef struct | 
|  | { | 
|  | float   x, y, z; | 
|  | } VertV; | 
|  |  | 
|  |  | 
|  | typedef struct | 
|  | { | 
|  | float   u, v; | 
|  | float   x, y, z; | 
|  |  | 
|  | } VertTV; | 
|  |  | 
|  | #define PI   3.14159265358979f | 
|  |  | 
|  | #define radToDeg(x) ((x)*180.f/PI) | 
|  | #define degToRad(x) ((x)*PI/180.f) | 
|  |  | 
|  | float MathAbs(float x) | 
|  | { | 
|  | float result; | 
|  |  | 
|  | __asm__ volatile ( | 
|  | "mtv      %1, S000\n" | 
|  | "vabs.s   S000, S000\n" | 
|  | "mfv      %0, S000\n" | 
|  | : "=r"(result) : "r"(x)); | 
|  |  | 
|  | return result; | 
|  | } | 
|  |  | 
|  | void MathSincos(float r, float *s, float *c) | 
|  | { | 
|  | __asm__ volatile ( | 
|  | "mtv      %2, S002\n" | 
|  | "vcst.s   S003, VFPU_2_PI\n" | 
|  | "vmul.s   S002, S002, S003\n" | 
|  | "vrot.p   C000, S002, [s, c]\n" | 
|  | "mfv      %0, S000\n" | 
|  | "mfv      %1, S001\n" | 
|  | : "=r"(*s), "=r"(*c): "r"(r)); | 
|  | } | 
|  |  | 
|  | void Swap(float *a, float *b) | 
|  | { | 
|  | float n=*a; | 
|  | *a = *b; | 
|  | *b = n; | 
|  | } | 
|  |  | 
|  | /* Return next power of 2 */ | 
|  | static int | 
|  | TextureNextPow2(unsigned int w) | 
|  | { | 
|  | if(w == 0) | 
|  | return 0; | 
|  |  | 
|  | unsigned int n = 2; | 
|  |  | 
|  | while(w > n) | 
|  | n <<= 1; | 
|  |  | 
|  | return n; | 
|  | } | 
|  |  | 
|  |  | 
|  | static int | 
|  | PixelFormatToPSPFMT(Uint32 format) | 
|  | { | 
|  | switch (format) { | 
|  | case SDL_PIXELFORMAT_BGR565: | 
|  | return GU_PSM_5650; | 
|  | case SDL_PIXELFORMAT_ABGR1555: | 
|  | return GU_PSM_5551; | 
|  | case SDL_PIXELFORMAT_ABGR4444: | 
|  | return GU_PSM_4444; | 
|  | case SDL_PIXELFORMAT_ABGR8888: | 
|  | return GU_PSM_8888; | 
|  | default: | 
|  | return GU_PSM_8888; | 
|  | } | 
|  | } | 
|  |  | 
|  | void | 
|  | StartDrawing(SDL_Renderer * renderer) | 
|  | { | 
|  | PSP_RenderData *data = (PSP_RenderData *) renderer->driverdata; | 
|  | if(data->displayListAvail) | 
|  | return; | 
|  |  | 
|  | sceGuStart(GU_DIRECT, DisplayList); | 
|  | data->displayListAvail = SDL_TRUE; | 
|  | } | 
|  |  | 
|  |  | 
|  | int | 
|  | TextureSwizzle(PSP_TextureData *psp_texture) | 
|  | { | 
|  | if(psp_texture->swizzled) | 
|  | return 1; | 
|  |  | 
|  | int bytewidth = psp_texture->textureWidth*(psp_texture->bits>>3); | 
|  | int height = psp_texture->size / bytewidth; | 
|  |  | 
|  | int rowblocks = (bytewidth>>4); | 
|  | int rowblocksadd = (rowblocks-1)<<7; | 
|  | unsigned int blockaddress = 0; | 
|  | unsigned int *src = (unsigned int*) psp_texture->data; | 
|  |  | 
|  | unsigned char *data = NULL; | 
|  | data = malloc(psp_texture->size); | 
|  |  | 
|  | int j; | 
|  |  | 
|  | for(j = 0; j < height; j++, blockaddress += 16) | 
|  | { | 
|  | unsigned int *block; | 
|  |  | 
|  | block = (unsigned int*)&data[blockaddress]; | 
|  |  | 
|  | int i; | 
|  |  | 
|  | for(i = 0; i < rowblocks; i++) | 
|  | { | 
|  | *block++ = *src++; | 
|  | *block++ = *src++; | 
|  | *block++ = *src++; | 
|  | *block++ = *src++; | 
|  | block += 28; | 
|  | } | 
|  |  | 
|  | if((j & 0x7) == 0x7) | 
|  | blockaddress += rowblocksadd; | 
|  | } | 
|  |  | 
|  | free(psp_texture->data); | 
|  | psp_texture->data = data; | 
|  | psp_texture->swizzled = SDL_TRUE; | 
|  |  | 
|  | return 1; | 
|  | } | 
|  | int TextureUnswizzle(PSP_TextureData *psp_texture) | 
|  | { | 
|  | if(!psp_texture->swizzled) | 
|  | return 1; | 
|  |  | 
|  | int blockx, blocky; | 
|  |  | 
|  | int bytewidth = psp_texture->textureWidth*(psp_texture->bits>>3); | 
|  | int height = psp_texture->size / bytewidth; | 
|  |  | 
|  | int widthblocks = bytewidth/16; | 
|  | int heightblocks = height/8; | 
|  |  | 
|  | int dstpitch = (bytewidth - 16)/4; | 
|  | int dstrow = bytewidth * 8; | 
|  |  | 
|  | unsigned int *src = (unsigned int*) psp_texture->data; | 
|  |  | 
|  | unsigned char *data = NULL; | 
|  |  | 
|  | data = malloc(psp_texture->size); | 
|  |  | 
|  | if(!data) | 
|  | return 0; | 
|  |  | 
|  | sceKernelDcacheWritebackAll(); | 
|  |  | 
|  | int j; | 
|  |  | 
|  | unsigned char *ydst = (unsigned char *)data; | 
|  |  | 
|  | for(blocky = 0; blocky < heightblocks; ++blocky) | 
|  | { | 
|  | unsigned char *xdst = ydst; | 
|  |  | 
|  | for(blockx = 0; blockx < widthblocks; ++blockx) | 
|  | { | 
|  | unsigned int *block; | 
|  |  | 
|  | block = (unsigned int*)xdst; | 
|  |  | 
|  | for(j = 0; j < 8; ++j) | 
|  | { | 
|  | *(block++) = *(src++); | 
|  | *(block++) = *(src++); | 
|  | *(block++) = *(src++); | 
|  | *(block++) = *(src++); | 
|  | block += dstpitch; | 
|  | } | 
|  |  | 
|  | xdst += 16; | 
|  | } | 
|  |  | 
|  | ydst += dstrow; | 
|  | } | 
|  |  | 
|  | free(psp_texture->data); | 
|  |  | 
|  | psp_texture->data = data; | 
|  |  | 
|  | psp_texture->swizzled = SDL_FALSE; | 
|  |  | 
|  | return 1; | 
|  | } | 
|  |  | 
|  | static void | 
|  | PSP_WindowEvent(SDL_Renderer * renderer, const SDL_WindowEvent *event) | 
|  | { | 
|  | } | 
|  |  | 
|  |  | 
|  | static int | 
|  | PSP_CreateTexture(SDL_Renderer * renderer, SDL_Texture * texture) | 
|  | { | 
|  | /*      PSP_RenderData *renderdata = (PSP_RenderData *) renderer->driverdata; */ | 
|  | PSP_TextureData* psp_texture = (PSP_TextureData*) SDL_calloc(1, sizeof(*psp_texture)); | 
|  |  | 
|  | if(!psp_texture) | 
|  | return -1; | 
|  |  | 
|  | psp_texture->swizzled = SDL_FALSE; | 
|  | psp_texture->width = texture->w; | 
|  | psp_texture->height = texture->h; | 
|  | psp_texture->textureHeight = TextureNextPow2(texture->h); | 
|  | psp_texture->textureWidth = TextureNextPow2(texture->w); | 
|  | psp_texture->format = PixelFormatToPSPFMT(texture->format); | 
|  |  | 
|  | switch(psp_texture->format) | 
|  | { | 
|  | case GU_PSM_5650: | 
|  | case GU_PSM_5551: | 
|  | case GU_PSM_4444: | 
|  | psp_texture->bits = 16; | 
|  | break; | 
|  |  | 
|  | case GU_PSM_8888: | 
|  | psp_texture->bits = 32; | 
|  | break; | 
|  |  | 
|  | default: | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | psp_texture->pitch = psp_texture->textureWidth * SDL_BYTESPERPIXEL(texture->format); | 
|  | psp_texture->size = psp_texture->textureHeight*psp_texture->pitch; | 
|  | psp_texture->data = SDL_calloc(1, psp_texture->size); | 
|  |  | 
|  | if(!psp_texture->data) | 
|  | { | 
|  | SDL_free(psp_texture); | 
|  | return SDL_OutOfMemory(); | 
|  | } | 
|  | texture->driverdata = psp_texture; | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int | 
|  | PSP_SetTextureColorMod(SDL_Renderer * renderer, SDL_Texture * texture) | 
|  | { | 
|  | return SDL_Unsupported(); | 
|  | } | 
|  |  | 
|  | void | 
|  | TextureActivate(SDL_Texture * texture) | 
|  | { | 
|  | PSP_TextureData *psp_texture = (PSP_TextureData *) texture->driverdata; | 
|  | int scaleMode = (texture->scaleMode == SDL_ScaleModeNearest) ? GU_NEAREST : GU_LINEAR; | 
|  |  | 
|  | /* Swizzling is useless with small textures. */ | 
|  | if (texture->w >= 16 || texture->h >= 16) | 
|  | { | 
|  | TextureSwizzle(psp_texture); | 
|  | } | 
|  |  | 
|  | sceGuEnable(GU_TEXTURE_2D); | 
|  | sceGuTexWrap(GU_REPEAT, GU_REPEAT); | 
|  | sceGuTexMode(psp_texture->format, 0, 0, psp_texture->swizzled); | 
|  | sceGuTexFilter(scaleMode, scaleMode); /* GU_NEAREST good for tile-map */ | 
|  | /* GU_LINEAR good for scaling */ | 
|  | sceGuTexImage(0, psp_texture->textureWidth, psp_texture->textureHeight, psp_texture->textureWidth, psp_texture->data); | 
|  | sceGuTexFunc(GU_TFX_REPLACE, GU_TCC_RGBA); | 
|  | } | 
|  |  | 
|  |  | 
|  | static int | 
|  | PSP_UpdateTexture(SDL_Renderer * renderer, SDL_Texture * texture, | 
|  | const SDL_Rect * rect, const void *pixels, int pitch) | 
|  | { | 
|  | /*  PSP_TextureData *psp_texture = (PSP_TextureData *) texture->driverdata; */ | 
|  | const Uint8 *src; | 
|  | Uint8 *dst; | 
|  | int row, length,dpitch; | 
|  | src = pixels; | 
|  |  | 
|  | PSP_LockTexture(renderer, texture,rect,(void **)&dst, &dpitch); | 
|  | length = rect->w * SDL_BYTESPERPIXEL(texture->format); | 
|  | if (length == pitch && length == dpitch) { | 
|  | SDL_memcpy(dst, src, length*rect->h); | 
|  | } else { | 
|  | for (row = 0; row < rect->h; ++row) { | 
|  | SDL_memcpy(dst, src, length); | 
|  | src += pitch; | 
|  | dst += dpitch; | 
|  | } | 
|  | } | 
|  |  | 
|  | sceKernelDcacheWritebackAll(); | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int | 
|  | PSP_LockTexture(SDL_Renderer * renderer, SDL_Texture * texture, | 
|  | const SDL_Rect * rect, void **pixels, int *pitch) | 
|  | { | 
|  | PSP_TextureData *psp_texture = (PSP_TextureData *) texture->driverdata; | 
|  |  | 
|  | *pixels = | 
|  | (void *) ((Uint8 *) psp_texture->data + rect->y * psp_texture->pitch + | 
|  | rect->x * SDL_BYTESPERPIXEL(texture->format)); | 
|  | *pitch = psp_texture->pitch; | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static void | 
|  | PSP_UnlockTexture(SDL_Renderer * renderer, SDL_Texture * texture) | 
|  | { | 
|  | PSP_TextureData *psp_texture = (PSP_TextureData *) texture->driverdata; | 
|  | SDL_Rect rect; | 
|  |  | 
|  | /* We do whole texture updates, at least for now */ | 
|  | rect.x = 0; | 
|  | rect.y = 0; | 
|  | rect.w = texture->w; | 
|  | rect.h = texture->h; | 
|  | PSP_UpdateTexture(renderer, texture, &rect, psp_texture->data, psp_texture->pitch); | 
|  | } | 
|  |  | 
|  | static void | 
|  | PSP_SetTextureScaleMode(SDL_Renderer * renderer, SDL_Texture * texture, SDL_ScaleMode scaleMode) | 
|  | { | 
|  | /* Nothing to do because TextureActivate takes care of it */ | 
|  | } | 
|  |  | 
|  | static int | 
|  | PSP_SetRenderTarget(SDL_Renderer * renderer, SDL_Texture * texture) | 
|  | { | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int | 
|  | PSP_QueueSetViewport(SDL_Renderer * renderer, SDL_RenderCommand *cmd) | 
|  | { | 
|  | return 0;  /* nothing to do in this backend. */ | 
|  | } | 
|  |  | 
|  | static int | 
|  | PSP_QueueDrawPoints(SDL_Renderer * renderer, SDL_RenderCommand *cmd, const SDL_FPoint * points, int count) | 
|  | { | 
|  | VertV *verts = (VertV *) SDL_AllocateRenderVertices(renderer, count * sizeof (VertV), 4, &cmd->data.draw.first); | 
|  | int i; | 
|  |  | 
|  | if (!verts) { | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | cmd->data.draw.count = count; | 
|  |  | 
|  | for (i = 0; i < count; i++, verts++, points++) { | 
|  | verts->x = points->x; | 
|  | verts->y = points->y; | 
|  | verts->z = 0.0f; | 
|  | } | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int | 
|  | PSP_QueueFillRects(SDL_Renderer * renderer, SDL_RenderCommand *cmd, const SDL_FRect * rects, int count) | 
|  | { | 
|  | VertV *verts = (GLfloat *) SDL_AllocateRenderVertices(renderer, count * 2 * sizeof (VertV), 4, &cmd->data.draw.first); | 
|  | int i; | 
|  |  | 
|  | if (!verts) { | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | cmd->data.draw.count = count; | 
|  | for (i = 0; i < count; i++, rects++) { | 
|  | const SDL_FRect *rect = &rects[i]; | 
|  | verts->x = rect->x; | 
|  | verts->y = rect->y; | 
|  | verts->z = 0.0f; | 
|  | verts++; | 
|  |  | 
|  | verts->x = rect->x + rect->w; | 
|  | verts->y = rect->y + rect->h; | 
|  | verts->z = 0.0f; | 
|  | verts++; | 
|  | } | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int | 
|  | PSP_QueueCopy(SDL_Renderer * renderer, SDL_RenderCommand *cmd, SDL_Texture * texture, | 
|  | const SDL_Rect * srcrect, const SDL_FRect * dstrect) | 
|  | { | 
|  | VertTV *verts; | 
|  | const float x = dstrect->x; | 
|  | const float y = dstrect->y; | 
|  | const float width = dstrect->w; | 
|  | const float height = dstrect->h; | 
|  |  | 
|  | const float u0 = srcrect->x; | 
|  | const float v0 = srcrect->y; | 
|  | const float u1 = srcrect->x + srcrect->w; | 
|  | const float v1 = srcrect->y + srcrect->h; | 
|  |  | 
|  | if((MathAbs(u1) - MathAbs(u0)) < 64.0f) | 
|  | { | 
|  | verts = (VertTV *) SDL_AllocateRenderVertices(renderer, 2 * sizeof (VertTV), 4, &cmd->data.draw.first); | 
|  | if (!verts) { | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | cmd->data.draw.count = 1; | 
|  |  | 
|  | verts->u = u0; | 
|  | verts->v = v0; | 
|  | verts->x = x; | 
|  | verts->y = y; | 
|  | verts->z = 0; | 
|  | verts++; | 
|  |  | 
|  | verts->u = u1; | 
|  | verts->v = v1; | 
|  | verts->x = x + width; | 
|  | verts->y = y + height; | 
|  | verts->z = 0; | 
|  | verts++; | 
|  | } | 
|  | else | 
|  | { | 
|  | float start, end; | 
|  | float curU = u0; | 
|  | float curX = x; | 
|  | const float endX = x + width; | 
|  | const float slice = 64.0f; | 
|  | const size_t count = SDL_ceilf(width / slice); | 
|  | size_t i; | 
|  | float ustep = (u1 - u0)/width * slice; | 
|  |  | 
|  | if(ustep < 0.0f) | 
|  | ustep = -ustep; | 
|  |  | 
|  | cmd->data.draw.count = count; | 
|  |  | 
|  | verts = (VertTV *) SDL_AllocateRenderVertices(renderer, count * sizeof (VertTV), 4, &cmd->data.draw.first); | 
|  | if (!verts) { | 
|  | return -1; | 
|  | } | 
|  |  | 
|  |  | 
|  | for(i = 0, start = 0, end = width; i < count; i++, start += slice) | 
|  | { | 
|  | const float polyWidth = ((curX + slice) > endX) ? (endX - curX) : slice; | 
|  | const float sourceWidth = ((curU + ustep) > u1) ? (u1 - curU) : ustep; | 
|  |  | 
|  | SDL_assert(start < end); | 
|  |  | 
|  | verts->u = curU; | 
|  | verts->v = v0; | 
|  | verts->x = curX; | 
|  | verts->y = y; | 
|  | verts->z = 0; | 
|  |  | 
|  | curU += sourceWidth; | 
|  | curX += polyWidth; | 
|  |  | 
|  | verts->u = curU; | 
|  | verts->v = v1; | 
|  | verts->x = curX; | 
|  | verts->y = (y + height); | 
|  | verts->z = 0; | 
|  | } | 
|  | } | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int | 
|  | PSP_QueueCopyEx(SDL_Renderer * renderer, SDL_RenderCommand *cmd, SDL_Texture * texture, | 
|  | const SDL_Rect * srcrect, const SDL_FRect * dstrect, | 
|  | const double angle, const SDL_FPoint *center, const SDL_RendererFlip flip) | 
|  | { | 
|  | VertTV *verts = (VertTV *) SDL_AllocateRenderVertices(renderer, 4 * sizeof (VertTV), 4, &cmd->data.draw.first); | 
|  | const float centerx = center->x; | 
|  | const float centery = center->y; | 
|  | const float x = dstrect->x + centerx; | 
|  | const float y = dstrect->y + centery; | 
|  | const float width = dstrect->w - centerx; | 
|  | const float height = dstrect->h - centery; | 
|  | float s, c; | 
|  |  | 
|  | float u0 = srcrect->x; | 
|  | float v0 = srcrect->y; | 
|  | float u1 = srcrect->x + srcrect->w; | 
|  | float v1 = srcrect->y + srcrect->h; | 
|  |  | 
|  |  | 
|  | if (!verts) { | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | cmd->data.draw.count = 1; | 
|  |  | 
|  | MathSincos(degToRad(angle), &s, &c); | 
|  |  | 
|  | const float cw = c * width; | 
|  | const float sw = s * width; | 
|  | const float ch = c * height; | 
|  | const float sh = s * height; | 
|  |  | 
|  | if (flip & SDL_FLIP_VERTICAL) { | 
|  | Swap(&v0, &v1); | 
|  | } | 
|  |  | 
|  | if (flip & SDL_FLIP_HORIZONTAL) { | 
|  | Swap(&u0, &u1); | 
|  | } | 
|  |  | 
|  | verts->u = u0; | 
|  | verts->v = v0; | 
|  | verts->x = x - cw + sh; | 
|  | verts->y = y - sw - ch; | 
|  | verts->z = 0; | 
|  | verts++; | 
|  |  | 
|  | verts->u = u0; | 
|  | verts->v = v1; | 
|  | verts->x = x - cw - sh; | 
|  | verts->y = y - sw + ch; | 
|  | verts->z = 0; | 
|  | verts++; | 
|  |  | 
|  | verts->u = u1; | 
|  | verts->v = v1; | 
|  | verts->x = x + cw - sh; | 
|  | verts->y = y + sw + ch; | 
|  | verts->z = 0; | 
|  | verts++; | 
|  |  | 
|  | verts->u = u1; | 
|  | verts->v = v0; | 
|  | verts->x = x + cw + sh; | 
|  | verts->y = y + sw - ch; | 
|  | verts->z = 0; | 
|  | verts++; | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static void | 
|  | PSP_SetBlendMode(SDL_Renderer * renderer, int blendMode) | 
|  | { | 
|  | PSP_RenderData *data = (PSP_RenderData *) renderer->driverdata; | 
|  | if (blendMode != data-> currentBlendMode) { | 
|  | switch (blendMode) { | 
|  | case SDL_BLENDMODE_NONE: | 
|  | sceGuTexFunc(GU_TFX_REPLACE, GU_TCC_RGBA); | 
|  | sceGuDisable(GU_BLEND); | 
|  | break; | 
|  | case SDL_BLENDMODE_BLEND: | 
|  | sceGuTexFunc(GU_TFX_MODULATE , GU_TCC_RGBA); | 
|  | sceGuEnable(GU_BLEND); | 
|  | sceGuBlendFunc(GU_ADD, GU_SRC_ALPHA, GU_ONE_MINUS_SRC_ALPHA, 0, 0 ); | 
|  | break; | 
|  | case SDL_BLENDMODE_ADD: | 
|  | sceGuTexFunc(GU_TFX_MODULATE , GU_TCC_RGBA); | 
|  | sceGuEnable(GU_BLEND); | 
|  | sceGuBlendFunc(GU_ADD, GU_SRC_ALPHA, GU_FIX, 0, 0x00FFFFFF ); | 
|  | break; | 
|  | case SDL_BLENDMODE_MOD: | 
|  | sceGuTexFunc(GU_TFX_MODULATE , GU_TCC_RGBA); | 
|  | sceGuEnable(GU_BLEND); | 
|  | sceGuBlendFunc(GU_ADD, GU_FIX, GU_SRC_COLOR, 0, 0); | 
|  | break; | 
|  | case SDL_BLENDMODE_MUL: | 
|  | sceGuTexFunc(GU_TFX_MODULATE , GU_TCC_RGBA); | 
|  | sceGuEnable(GU_BLEND); | 
|  | sceGuBlendFunc(GU_ADD, GU_DST_COLOR, GU_ONE_MINUS_SRC_ALPHA, 0, 0); | 
|  | break; | 
|  | } | 
|  | data->currentBlendMode = blendMode; | 
|  | } | 
|  | } | 
|  |  | 
|  | static int | 
|  | PSP_RunCommandQueue(SDL_Renderer * renderer, SDL_RenderCommand *cmd, void *vertices, size_t vertsize) | 
|  | { | 
|  | PSP_RenderData *data = (PSP_RenderData *) renderer->driverdata; | 
|  | size_t i; | 
|  |  | 
|  | StartDrawing(renderer); | 
|  |  | 
|  | /* note that before the renderer interface change, this would do extrememly small | 
|  | batches with sceGuGetMemory()--a few vertices at a time--and it's not clear that | 
|  | this won't fail if you try to push 100,000 draw calls in a single batch. | 
|  | I don't know what the limits on PSP hardware are. It might be useful to have | 
|  | rendering backends report a reasonable maximum, so the higher level can flush | 
|  | if we appear to be exceeding that. */ | 
|  | Uint8 *gpumem = (Uint8 *) sceGuGetMemory(vertsize); | 
|  | if (!gpumem) { | 
|  | return SDL_SetError("Couldn't obtain a %d-byte vertex buffer!", (int) vertsize); | 
|  | } | 
|  | SDL_memcpy(gpumem, vertices, vertsize); | 
|  |  | 
|  | while (cmd) { | 
|  | switch (cmd->command) { | 
|  | case SDL_RENDERCMD_SETDRAWCOLOR: { | 
|  | break;  /* !!! FIXME: we could cache drawstate like color */ | 
|  | } | 
|  |  | 
|  | case SDL_RENDERCMD_SETVIEWPORT: { | 
|  | SDL_Rect *viewport = &data->drawstate.viewport; | 
|  | if (SDL_memcmp(viewport, &cmd->data.viewport.rect, sizeof (SDL_Rect)) != 0) { | 
|  | SDL_memcpy(viewport, &cmd->data.viewport.rect, sizeof (SDL_Rect)); | 
|  | data->drawstate.viewport_dirty = SDL_TRUE; | 
|  | } | 
|  | break; | 
|  | } | 
|  |  | 
|  | case SDL_RENDERCMD_SETCLIPRECT: { | 
|  | const SDL_Rect *rect = &cmd->data.cliprect.rect; | 
|  | if (data->drawstate.cliprect_enabled != cmd->data.cliprect.enabled) { | 
|  | data->drawstate.cliprect_enabled = cmd->data.cliprect.enabled; | 
|  | data->drawstate.cliprect_enabled_dirty = SDL_TRUE; | 
|  | } | 
|  | if (SDL_memcmp(&data->drawstate.cliprect, rect, sizeof (SDL_Rect)) != 0) { | 
|  | SDL_memcpy(&data->drawstate.cliprect, rect, sizeof (SDL_Rect)); | 
|  | data->drawstate.cliprect_dirty = SDL_TRUE; | 
|  | } | 
|  | break; | 
|  | } | 
|  |  | 
|  | case SDL_RENDERCMD_CLEAR: { | 
|  | const Uint8 r = cmd->data.color.r; | 
|  | const Uint8 g = cmd->data.color.g; | 
|  | const Uint8 b = cmd->data.color.b; | 
|  | const Uint8 a = cmd->data.color.a; | 
|  | const Uint32 color = ((a << 24) | (b << 16) | (g << 8) | r); | 
|  | /* !!! FIXME: we could cache drawstate like clear color */ | 
|  | sceGuClearColor(color); | 
|  | sceGuClearDepth(0); | 
|  | sceGuClear(GU_COLOR_BUFFER_BIT|GU_DEPTH_BUFFER_BIT|GU_FAST_CLEAR_BIT); | 
|  | break; | 
|  | } | 
|  |  | 
|  | case SDL_RENDERCMD_DRAW_POINTS: { | 
|  | const size_t count = cmd->data.draw.count; | 
|  | const VertV *verts = (VertV *) (gpumem + cmd->data.draw.first); | 
|  | const Uint8 r = cmd->data.draw.r; | 
|  | const Uint8 g = cmd->data.draw.g; | 
|  | const Uint8 b = cmd->data.draw.b; | 
|  | const Uint8 a = cmd->data.draw.a; | 
|  | const Uint32 color = ((a << 24) | (b << 16) | (g << 8) | r); | 
|  | /* !!! FIXME: we could cache draw state like color, texturing, etc */ | 
|  | sceGuColor(color); | 
|  | sceGuDisable(GU_TEXTURE_2D); | 
|  | sceGuShadeModel(GU_FLAT); | 
|  | sceGuDrawArray(GU_POINTS, GU_VERTEX_32BITF|GU_TRANSFORM_2D, count, 0, verts); | 
|  | sceGuShadeModel(GU_SMOOTH); | 
|  | sceGuEnable(GU_TEXTURE_2D); | 
|  | break; | 
|  | } | 
|  |  | 
|  | case SDL_RENDERCMD_DRAW_LINES: { | 
|  | const size_t count = cmd->data.draw.count; | 
|  | const VertV *verts = (VertV *) (gpumem + cmd->data.draw.first); | 
|  | const Uint8 r = cmd->data.draw.r; | 
|  | const Uint8 g = cmd->data.draw.g; | 
|  | const Uint8 b = cmd->data.draw.b; | 
|  | const Uint8 a = cmd->data.draw.a; | 
|  | const Uint32 color = ((a << 24) | (b << 16) | (g << 8) | r); | 
|  | /* !!! FIXME: we could cache draw state like color, texturing, etc */ | 
|  | sceGuColor(color); | 
|  | sceGuDisable(GU_TEXTURE_2D); | 
|  | sceGuShadeModel(GU_FLAT); | 
|  | sceGuDrawArray(GU_LINE_STRIP, GU_VERTEX_32BITF|GU_TRANSFORM_2D, count, 0, verts); | 
|  | sceGuShadeModel(GU_SMOOTH); | 
|  | sceGuEnable(GU_TEXTURE_2D); | 
|  | break; | 
|  | } | 
|  |  | 
|  | case SDL_RENDERCMD_FILL_RECTS: { | 
|  | const size_t count = cmd->data.draw.count; | 
|  | const VertV *verts = (VertV *) (gpumem + cmd->data.draw.first); | 
|  | const Uint8 r = cmd->data.draw.r; | 
|  | const Uint8 g = cmd->data.draw.g; | 
|  | const Uint8 b = cmd->data.draw.b; | 
|  | const Uint8 a = cmd->data.draw.a; | 
|  | const Uint32 color = ((a << 24) | (b << 16) | (g << 8) | r); | 
|  | /* !!! FIXME: we could cache draw state like color, texturing, etc */ | 
|  | sceGuColor(color); | 
|  | sceGuDisable(GU_TEXTURE_2D); | 
|  | sceGuShadeModel(GU_FLAT); | 
|  | sceGuDrawArray(GU_SPRITES, GU_VERTEX_32BITF|GU_TRANSFORM_2D, 2 * count, 0, verts); | 
|  | sceGuShadeModel(GU_SMOOTH); | 
|  | sceGuEnable(GU_TEXTURE_2D); | 
|  | break; | 
|  | } | 
|  |  | 
|  | case SDL_RENDERCMD_COPY: { | 
|  | const size_t count = cmd->data.draw.count; | 
|  | const VertTV *verts = (VertTV *) (gpumem + cmd->data.draw.first); | 
|  | const Uint8 alpha = cmd->data.draw.a; | 
|  | TextureActivate(cmd->data.draw.texture); | 
|  | PSP_SetBlendMode(renderer, cmd->data.draw.blend); | 
|  |  | 
|  | if(alpha != 255) {  /* !!! FIXME: is this right? */ | 
|  | sceGuTexFunc(GU_TFX_MODULATE, GU_TCC_RGBA); | 
|  | sceGuColor(GU_RGBA(255, 255, 255, alpha)); | 
|  | } else { | 
|  | sceGuTexFunc(GU_TFX_REPLACE, GU_TCC_RGBA); | 
|  | sceGuColor(0xFFFFFFFF); | 
|  | } | 
|  |  | 
|  | sceGuDrawArray(GU_SPRITES, GU_TEXTURE_32BITF|GU_VERTEX_32BITF|GU_TRANSFORM_2D, 2 * count, 0, verts); | 
|  |  | 
|  | if(alpha != 255) { | 
|  | sceGuTexFunc(GU_TFX_REPLACE, GU_TCC_RGBA); | 
|  | } | 
|  | break; | 
|  | } | 
|  |  | 
|  | case SDL_RENDERCMD_COPY_EX: { | 
|  | const VertTV *verts = (VertTV *) (gpumem + cmd->data.draw.first); | 
|  | const Uint8 alpha = cmd->data.draw.a; | 
|  | TextureActivate(cmd->data.draw.texture); | 
|  | PSP_SetBlendMode(renderer, cmd->data.draw.blend); | 
|  |  | 
|  | if(alpha != 255) {  /* !!! FIXME: is this right? */ | 
|  | sceGuTexFunc(GU_TFX_MODULATE, GU_TCC_RGBA); | 
|  | sceGuColor(GU_RGBA(255, 255, 255, alpha)); | 
|  | } else { | 
|  | sceGuTexFunc(GU_TFX_REPLACE, GU_TCC_RGBA); | 
|  | sceGuColor(0xFFFFFFFF); | 
|  | } | 
|  |  | 
|  | sceGuDrawArray(GU_TRIANGLE_FAN, GU_TEXTURE_32BITF|GU_VERTEX_32BITF|GU_TRANSFORM_2D, 4, 0, verts); | 
|  |  | 
|  | if(alpha != 255) { | 
|  | sceGuTexFunc(GU_TFX_REPLACE, GU_TCC_RGBA); | 
|  | } | 
|  | break; | 
|  | } | 
|  |  | 
|  | case SDL_RENDERCMD_NO_OP: | 
|  | break; | 
|  | } | 
|  |  | 
|  | cmd = cmd->next; | 
|  | } | 
|  |  | 
|  | return 0; | 
|  | } | 
|  |  | 
|  | static int | 
|  | PSP_RenderReadPixels(SDL_Renderer * renderer, const SDL_Rect * rect, | 
|  | Uint32 pixel_format, void * pixels, int pitch) | 
|  | { | 
|  | return SDL_Unsupported(); | 
|  | } | 
|  |  | 
|  | static void | 
|  | PSP_RenderPresent(SDL_Renderer * renderer) | 
|  | { | 
|  | PSP_RenderData *data = (PSP_RenderData *) renderer->driverdata; | 
|  | if(!data->displayListAvail) | 
|  | return; | 
|  |  | 
|  | data->displayListAvail = SDL_FALSE; | 
|  | sceGuFinish(); | 
|  | sceGuSync(0,0); | 
|  |  | 
|  | /*  if(data->vsync) */ | 
|  | sceDisplayWaitVblankStart(); | 
|  |  | 
|  | data->backbuffer = data->frontbuffer; | 
|  | data->frontbuffer = vabsptr(sceGuSwapBuffers()); | 
|  |  | 
|  | } | 
|  |  | 
|  | static void | 
|  | PSP_DestroyTexture(SDL_Renderer * renderer, SDL_Texture * texture) | 
|  | { | 
|  | PSP_RenderData *renderdata = (PSP_RenderData *) renderer->driverdata; | 
|  | PSP_TextureData *psp_texture = (PSP_TextureData *) texture->driverdata; | 
|  |  | 
|  | if (renderdata == 0) | 
|  | return; | 
|  |  | 
|  | if(psp_texture == 0) | 
|  | return; | 
|  |  | 
|  | SDL_free(psp_texture->data); | 
|  | SDL_free(psp_texture); | 
|  | texture->driverdata = NULL; | 
|  | } | 
|  |  | 
|  | static void | 
|  | PSP_DestroyRenderer(SDL_Renderer * renderer) | 
|  | { | 
|  | PSP_RenderData *data = (PSP_RenderData *) renderer->driverdata; | 
|  | if (data) { | 
|  | if (!data->initialized) | 
|  | return; | 
|  |  | 
|  | StartDrawing(renderer); | 
|  |  | 
|  | sceGuTerm(); | 
|  | /*      vfree(data->backbuffer); */ | 
|  | /*      vfree(data->frontbuffer); */ | 
|  |  | 
|  | data->initialized = SDL_FALSE; | 
|  | data->displayListAvail = SDL_FALSE; | 
|  | SDL_free(data); | 
|  | } | 
|  | SDL_free(renderer); | 
|  | } | 
|  |  | 
|  | SDL_Renderer * | 
|  | PSP_CreateRenderer(SDL_Window * window, Uint32 flags) | 
|  | { | 
|  |  | 
|  | SDL_Renderer *renderer; | 
|  | PSP_RenderData *data; | 
|  | int pixelformat; | 
|  | renderer = (SDL_Renderer *) SDL_calloc(1, sizeof(*renderer)); | 
|  | if (!renderer) { | 
|  | SDL_OutOfMemory(); | 
|  | return NULL; | 
|  | } | 
|  |  | 
|  | data = (PSP_RenderData *) SDL_calloc(1, sizeof(*data)); | 
|  | if (!data) { | 
|  | PSP_DestroyRenderer(renderer); | 
|  | SDL_OutOfMemory(); | 
|  | return NULL; | 
|  | } | 
|  |  | 
|  |  | 
|  | renderer->WindowEvent = PSP_WindowEvent; | 
|  | renderer->CreateTexture = PSP_CreateTexture; | 
|  | renderer->SetTextureColorMod = PSP_SetTextureColorMod; | 
|  | renderer->UpdateTexture = PSP_UpdateTexture; | 
|  | renderer->LockTexture = PSP_LockTexture; | 
|  | renderer->UnlockTexture = PSP_UnlockTexture; | 
|  | renderer->SetTextureScaleMode = PSP_SetTextureScaleMode; | 
|  | renderer->SetRenderTarget = PSP_SetRenderTarget; | 
|  | renderer->QueueSetViewport = PSP_QueueSetViewport; | 
|  | renderer->QueueSetDrawColor = PSP_QueueSetViewport;  /* SetViewport and SetDrawColor are (currently) no-ops. */ | 
|  | renderer->QueueDrawPoints = PSP_QueueDrawPoints; | 
|  | renderer->QueueDrawLines = PSP_QueueDrawPoints;  /* lines and points queue vertices the same way. */ | 
|  | renderer->QueueFillRects = PSP_QueueFillRects; | 
|  | renderer->QueueCopy = PSP_QueueCopy; | 
|  | renderer->QueueCopyEx = PSP_QueueCopyEx; | 
|  | renderer->RunCommandQueue = PSP_RunCommandQueue; | 
|  | renderer->RenderReadPixels = PSP_RenderReadPixels; | 
|  | renderer->RenderPresent = PSP_RenderPresent; | 
|  | renderer->DestroyTexture = PSP_DestroyTexture; | 
|  | renderer->DestroyRenderer = PSP_DestroyRenderer; | 
|  | renderer->info = PSP_RenderDriver.info; | 
|  | renderer->info.flags = (SDL_RENDERER_ACCELERATED | SDL_RENDERER_TARGETTEXTURE); | 
|  | renderer->driverdata = data; | 
|  | renderer->window = window; | 
|  |  | 
|  | if (data->initialized != SDL_FALSE) | 
|  | return 0; | 
|  | data->initialized = SDL_TRUE; | 
|  |  | 
|  | if (flags & SDL_RENDERER_PRESENTVSYNC) { | 
|  | data->vsync = SDL_TRUE; | 
|  | } else { | 
|  | data->vsync = SDL_FALSE; | 
|  | } | 
|  |  | 
|  | pixelformat=PixelFormatToPSPFMT(SDL_GetWindowPixelFormat(window)); | 
|  | switch(pixelformat) | 
|  | { | 
|  | case GU_PSM_4444: | 
|  | case GU_PSM_5650: | 
|  | case GU_PSM_5551: | 
|  | data->frontbuffer = (unsigned int *)(PSP_FRAME_BUFFER_SIZE<<1); | 
|  | data->backbuffer =  (unsigned int *)(0); | 
|  | data->bpp = 2; | 
|  | data->psm = pixelformat; | 
|  | break; | 
|  | default: | 
|  | data->frontbuffer = (unsigned int *)(PSP_FRAME_BUFFER_SIZE<<2); | 
|  | data->backbuffer =  (unsigned int *)(0); | 
|  | data->bpp = 4; | 
|  | data->psm = GU_PSM_8888; | 
|  | break; | 
|  | } | 
|  |  | 
|  | sceGuInit(); | 
|  | /* setup GU */ | 
|  | sceGuStart(GU_DIRECT, DisplayList); | 
|  | sceGuDrawBuffer(data->psm, data->frontbuffer, PSP_FRAME_BUFFER_WIDTH); | 
|  | sceGuDispBuffer(PSP_SCREEN_WIDTH, PSP_SCREEN_HEIGHT, data->backbuffer, PSP_FRAME_BUFFER_WIDTH); | 
|  |  | 
|  |  | 
|  | sceGuOffset(2048 - (PSP_SCREEN_WIDTH>>1), 2048 - (PSP_SCREEN_HEIGHT>>1)); | 
|  | sceGuViewport(2048, 2048, PSP_SCREEN_WIDTH, PSP_SCREEN_HEIGHT); | 
|  |  | 
|  | data->frontbuffer = vabsptr(data->frontbuffer); | 
|  | data->backbuffer = vabsptr(data->backbuffer); | 
|  |  | 
|  | /* Scissoring */ | 
|  | sceGuScissor(0, 0, PSP_SCREEN_WIDTH, PSP_SCREEN_HEIGHT); | 
|  | sceGuEnable(GU_SCISSOR_TEST); | 
|  |  | 
|  | /* Backface culling */ | 
|  | sceGuFrontFace(GU_CCW); | 
|  | sceGuEnable(GU_CULL_FACE); | 
|  |  | 
|  | /* Texturing */ | 
|  | sceGuEnable(GU_TEXTURE_2D); | 
|  | sceGuShadeModel(GU_SMOOTH); | 
|  | sceGuTexWrap(GU_REPEAT, GU_REPEAT); | 
|  |  | 
|  | /* Blending */ | 
|  | sceGuEnable(GU_BLEND); | 
|  | sceGuBlendFunc(GU_ADD, GU_SRC_ALPHA, GU_ONE_MINUS_SRC_ALPHA, 0, 0); | 
|  |  | 
|  | sceGuTexFilter(GU_LINEAR,GU_LINEAR); | 
|  |  | 
|  | sceGuFinish(); | 
|  | sceGuSync(0,0); | 
|  | sceDisplayWaitVblankStartCB(); | 
|  | sceGuDisplay(GU_TRUE); | 
|  |  | 
|  | return renderer; | 
|  | } | 
|  |  | 
|  | SDL_RenderDriver PSP_RenderDriver = { | 
|  | .CreateRenderer = PSP_CreateRenderer, | 
|  | .info = { | 
|  | .name = "PSP", | 
|  | .flags = SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC | SDL_RENDERER_TARGETTEXTURE, | 
|  | .num_texture_formats = 4, | 
|  | .texture_formats = { [0] = SDL_PIXELFORMAT_BGR565, | 
|  | [1] = SDL_PIXELFORMAT_ABGR1555, | 
|  | [2] = SDL_PIXELFORMAT_ABGR4444, | 
|  | [3] = SDL_PIXELFORMAT_ABGR8888, | 
|  | }, | 
|  | .max_texture_width = 512, | 
|  | .max_texture_height = 512, | 
|  | } | 
|  | }; | 
|  |  | 
|  | #endif /* SDL_VIDEO_RENDER_PSP */ | 
|  |  | 
|  | /* vi: set ts=4 sw=4 expandtab: */ | 
|  |  |