| /* |
| Simple DirectMedia Layer |
| Copyright (C) 1997-2022 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)) |
| |
| /** |
| * Holds psp specific texture data |
| * |
| * Part of a hot-list of textures that are used as render targets |
| * When short of vram we spill Least-Recently-Used render targets to system memory |
| */ |
| typedef struct PSP_TextureData |
| { |
| 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. */ |
| struct PSP_TextureData* prevhotw; /**< More recently used render target */ |
| struct PSP_TextureData* nexthotw; /**< Less recently used render target */ |
| } PSP_TextureData; |
| |
| typedef struct |
| { |
| SDL_BlendMode mode; |
| unsigned int color; |
| int shadeModel; |
| SDL_Texture* texture; |
| } PSP_BlendState; |
| |
| typedef struct |
| { |
| void* frontbuffer; /**< main screen buffer */ |
| void* backbuffer; /**< buffer presented to display */ |
| SDL_Texture* boundTarget; /**< currently bound rendertarget */ |
| SDL_bool initialized; /**< is driver initialized */ |
| SDL_bool displayListAvail; /**< is the display list already initialized for this frame */ |
| unsigned int psm; /**< format of the display buffers */ |
| unsigned int bpp; /**< bits per pixel of the main display */ |
| |
| SDL_bool vsync; /**< wether we do vsync */ |
| PSP_BlendState blendState; /**< current blend mode */ |
| PSP_TextureData* most_recent_target; /**< start of render target LRU double linked list */ |
| PSP_TextureData* least_recent_target; /**< end of the LRU list */ |
| |
| SDL_bool vblank_not_reached; /**< wether vblank wasn't reached */ |
| } PSP_RenderData; |
| |
| |
| typedef struct |
| { |
| float x, y, z; |
| } VertV; |
| |
| |
| typedef struct |
| { |
| float u, v; |
| float x, y, z; |
| } VertTV; |
| |
| typedef struct |
| { |
| SDL_Color col; |
| float x, y, z; |
| } VertCV; |
| |
| |
| typedef struct |
| { |
| float u, v; |
| SDL_Color col; |
| float x, y, z; |
| } VertTCV; |
| |
| #define PI 3.14159265358979f |
| |
| #define radToDeg(x) ((x)*180.f/PI) |
| #define degToRad(x) ((x)*PI/180.f) |
| |
| static 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; |
| } |
| |
| static 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)); |
| } |
| |
| static void |
| Swap(float *a, float *b) |
| { |
| float n=*a; |
| *a = *b; |
| *b = n; |
| } |
| |
| static inline int |
| InVram(void* data) |
| { |
| return data < (void*)0x04200000; |
| } |
| |
| /* Return next power of 2 */ |
| static int |
| TextureNextPow2(unsigned int w) |
| { |
| unsigned int n = 2; |
| if(w == 0) |
| return 0; |
| |
| while(w > n) |
| n <<= 1; |
| |
| return n; |
| } |
| |
| static void psp_on_vblank(u32 sub, PSP_RenderData *data) |
| { |
| if (data) |
| data->vblank_not_reached = SDL_FALSE; |
| } |
| |
| |
| 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; |
| } |
| } |
| |
| ///SECTION render target LRU management |
| static void |
| LRUTargetRelink(PSP_TextureData* psp_texture) { |
| if(psp_texture->prevhotw) { |
| psp_texture->prevhotw->nexthotw = psp_texture->nexthotw; |
| } |
| if(psp_texture->nexthotw) { |
| psp_texture->nexthotw->prevhotw = psp_texture->prevhotw; |
| } |
| } |
| |
| static void |
| LRUTargetPushFront(PSP_RenderData* data, PSP_TextureData* psp_texture) { |
| psp_texture->nexthotw = data->most_recent_target; |
| if(data->most_recent_target) { |
| data->most_recent_target->prevhotw = psp_texture; |
| } |
| data->most_recent_target = psp_texture; |
| if(!data->least_recent_target) { |
| data->least_recent_target = psp_texture; |
| } |
| } |
| |
| static void |
| LRUTargetRemove(PSP_RenderData* data, PSP_TextureData* psp_texture) { |
| LRUTargetRelink(psp_texture); |
| if(data->most_recent_target == psp_texture) { |
| data->most_recent_target = psp_texture->nexthotw; |
| } |
| if(data->least_recent_target == psp_texture) { |
| data->least_recent_target = psp_texture->prevhotw; |
| } |
| psp_texture->prevhotw = NULL; |
| psp_texture->nexthotw = NULL; |
| } |
| |
| static void |
| LRUTargetBringFront(PSP_RenderData* data, PSP_TextureData* psp_texture) { |
| if(data->most_recent_target == psp_texture) { |
| return; //nothing to do |
| } |
| LRUTargetRemove(data, psp_texture); |
| LRUTargetPushFront(data, psp_texture); |
| } |
| |
| static void |
| TextureStorageFree(void* storage) { |
| if(InVram(storage)) { |
| vfree(storage); |
| } else { |
| SDL_free(storage); |
| } |
| } |
| |
| static int |
| TextureSwizzle(PSP_TextureData *psp_texture, void* dst) |
| { |
| int bytewidth, height; |
| int rowblocks, rowblocksadd; |
| int i, j; |
| unsigned int blockaddress = 0; |
| unsigned int *src = NULL; |
| unsigned char *data = NULL; |
| |
| if(psp_texture->swizzled) |
| return 1; |
| |
| bytewidth = psp_texture->textureWidth*(psp_texture->bits>>3); |
| height = psp_texture->size / bytewidth; |
| |
| rowblocks = (bytewidth>>4); |
| rowblocksadd = (rowblocks-1)<<7; |
| |
| src = (unsigned int*) psp_texture->data; |
| |
| data = dst; |
| if(!data) { |
| data = SDL_malloc(psp_texture->size); |
| } |
| |
| if(!data) { |
| return SDL_OutOfMemory(); |
| } |
| |
| for(j = 0; j < height; j++, blockaddress += 16) |
| { |
| unsigned int *block; |
| |
| block = (unsigned int*)&data[blockaddress]; |
| |
| for(i = 0; i < rowblocks; i++) |
| { |
| *block++ = *src++; |
| *block++ = *src++; |
| *block++ = *src++; |
| *block++ = *src++; |
| block += 28; |
| } |
| |
| if((j & 0x7) == 0x7) |
| blockaddress += rowblocksadd; |
| } |
| |
| TextureStorageFree(psp_texture->data); |
| psp_texture->data = data; |
| psp_texture->swizzled = SDL_TRUE; |
| |
| sceKernelDcacheWritebackRange(psp_texture->data, psp_texture->size); |
| return 1; |
| } |
| |
| static int |
| TextureUnswizzle(PSP_TextureData *psp_texture, void* dst) |
| { |
| int bytewidth, height; |
| int widthblocks, heightblocks; |
| int dstpitch, dstrow; |
| int blockx, blocky; |
| int j; |
| unsigned int *src = NULL; |
| unsigned char *data = NULL; |
| unsigned char *ydst = NULL; |
| |
| if(!psp_texture->swizzled) |
| return 1; |
| |
| bytewidth = psp_texture->textureWidth*(psp_texture->bits>>3); |
| height = psp_texture->size / bytewidth; |
| |
| widthblocks = bytewidth/16; |
| heightblocks = height/8; |
| |
| dstpitch = (bytewidth - 16)/4; |
| dstrow = bytewidth * 8; |
| |
| src = (unsigned int*) psp_texture->data; |
| |
| data = dst; |
| |
| if(!data) { |
| data = SDL_malloc(psp_texture->size); |
| } |
| |
| if(!data) |
| return SDL_OutOfMemory(); |
| |
| 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; |
| } |
| |
| TextureStorageFree(psp_texture->data); |
| |
| psp_texture->data = data; |
| |
| psp_texture->swizzled = SDL_FALSE; |
| |
| sceKernelDcacheWritebackRange(psp_texture->data, psp_texture->size); |
| return 1; |
| } |
| |
| static int |
| TextureSpillToSram(PSP_RenderData* data, PSP_TextureData* psp_texture) |
| { |
| // Assumes the texture is in VRAM |
| if(psp_texture->swizzled) { |
| //Texture was swizzled in vram, just copy to system memory |
| void* data = SDL_malloc(psp_texture->size); |
| if(!data) { |
| return SDL_OutOfMemory(); |
| } |
| |
| SDL_memcpy(data, psp_texture->data, psp_texture->size); |
| vfree(psp_texture->data); |
| psp_texture->data = data; |
| return 0; |
| } else { |
| return TextureSwizzle(psp_texture, NULL); //Will realloc in sysram |
| } |
| } |
| |
| static int |
| TexturePromoteToVram(PSP_RenderData* data, PSP_TextureData* psp_texture, SDL_bool target) |
| { |
| // Assumes texture in sram and a large enough continuous block in vram |
| void* tdata = valloc(psp_texture->size); |
| if(psp_texture->swizzled && target) { |
| return TextureUnswizzle(psp_texture, tdata); |
| } else { |
| SDL_memcpy(tdata, psp_texture->data, psp_texture->size); |
| SDL_free(psp_texture->data); |
| psp_texture->data = tdata; |
| return 0; |
| } |
| } |
| |
| static int |
| TextureSpillLRU(PSP_RenderData* data, size_t wanted) { |
| PSP_TextureData* lru = data->least_recent_target; |
| if(lru) { |
| if(TextureSpillToSram(data, lru) < 0) { |
| return -1; |
| } |
| LRUTargetRemove(data, lru); |
| } else { |
| // Asked to spill but there nothing to spill |
| return SDL_SetError("Could not spill more VRAM to system memory. VRAM : %dKB,(%dKB), wanted %dKB", vmemavail()/1024, vlargestblock()/1024, wanted/1024); |
| } |
| return 0; |
| } |
| |
| static int |
| TextureSpillTargetsForSpace(PSP_RenderData* data, size_t size) |
| { |
| while(vlargestblock() < size) { |
| if(TextureSpillLRU(data, size) < 0) { |
| return -1; |
| } |
| } |
| return 0; |
| } |
| |
| static int |
| TextureBindAsTarget(PSP_RenderData* data, PSP_TextureData* psp_texture) { |
| unsigned int dstFormat; |
| |
| if(!InVram(psp_texture->data)) { |
| // Bring back the texture in vram |
| if(TextureSpillTargetsForSpace(data, psp_texture->size) < 0) { |
| return -1; |
| } |
| if(TexturePromoteToVram(data, psp_texture, SDL_TRUE) < 0) { |
| return -1; |
| } |
| } |
| LRUTargetBringFront(data, psp_texture); |
| sceGuDrawBufferList(psp_texture->format, vrelptr(psp_texture->data), psp_texture->textureWidth); |
| |
| // Stencil alpha dst hack |
| dstFormat = psp_texture->format; |
| if(dstFormat == GU_PSM_5551) { |
| sceGuEnable(GU_STENCIL_TEST); |
| sceGuStencilOp(GU_REPLACE, GU_REPLACE, GU_REPLACE); |
| sceGuStencilFunc(GU_GEQUAL, 0xff, 0xff); |
| sceGuEnable(GU_ALPHA_TEST); |
| sceGuAlphaFunc(GU_GREATER, 0x00, 0xff); |
| } else { |
| sceGuDisable(GU_STENCIL_TEST); |
| sceGuDisable(GU_ALPHA_TEST); |
| } |
| return 0; |
| } |
| |
| static void |
| PSP_WindowEvent(SDL_Renderer * renderer, const SDL_WindowEvent *event) |
| { |
| } |
| |
| |
| static int |
| PSP_CreateTexture(SDL_Renderer * renderer, SDL_Texture * texture) |
| { |
| PSP_RenderData *data = renderer->driverdata; |
| PSP_TextureData* psp_texture = (PSP_TextureData*) SDL_calloc(1, sizeof(*psp_texture)); |
| |
| if(!psp_texture) |
| return SDL_OutOfMemory(); |
| |
| 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; |
| if(texture->access & SDL_TEXTUREACCESS_TARGET) { |
| if(TextureSpillTargetsForSpace(renderer->driverdata, psp_texture->size) < 0){ |
| return -1; |
| } |
| psp_texture->data = valloc(psp_texture->size); |
| if(psp_texture->data) { |
| LRUTargetPushFront(data, psp_texture); |
| } |
| } else { |
| 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 |
| TextureShouldSwizzle(PSP_TextureData* psp_texture, SDL_Texture *texture) |
| { |
| return !((texture->access == SDL_TEXTUREACCESS_TARGET) && InVram(psp_texture->data)) |
| && texture->access != SDL_TEXTUREACCESS_STREAMING |
| && (texture->w >= 16 || texture->h >= 16); |
| } |
| |
| static 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 (TextureShouldSwizzle(psp_texture, texture)) |
| { |
| TextureSwizzle(psp_texture, NULL); |
| } |
| |
| 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); |
| } |
| |
| static int |
| PSP_LockTexture(SDL_Renderer * renderer, SDL_Texture * texture, |
| const SDL_Rect * rect, void **pixels, int *pitch); |
| |
| 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_QueueGeometry(SDL_Renderer *renderer, SDL_RenderCommand *cmd, SDL_Texture *texture, |
| const float *xy, int xy_stride, const SDL_Color *color, int color_stride, const float *uv, int uv_stride, |
| int num_vertices, const void *indices, int num_indices, int size_indices, |
| float scale_x, float scale_y) |
| { |
| int i; |
| int count = indices ? num_indices : num_vertices; |
| |
| cmd->data.draw.count = count; |
| size_indices = indices ? size_indices : 0; |
| |
| if (texture == NULL) { |
| VertCV *verts; |
| verts = (VertCV *) SDL_AllocateRenderVertices(renderer, count * sizeof (VertCV), 4, &cmd->data.draw.first); |
| if (!verts) { |
| return -1; |
| } |
| |
| for (i = 0; i < count; i++) { |
| int j; |
| float *xy_; |
| SDL_Color col_; |
| if (size_indices == 4) { |
| j = ((const Uint32 *)indices)[i]; |
| } else if (size_indices == 2) { |
| j = ((const Uint16 *)indices)[i]; |
| } else if (size_indices == 1) { |
| j = ((const Uint8 *)indices)[i]; |
| } else { |
| j = i; |
| } |
| |
| xy_ = (float *)((char*)xy + j * xy_stride); |
| col_ = *(SDL_Color *)((char*)color + j * color_stride); |
| |
| verts->x = xy_[0] * scale_x; |
| verts->y = xy_[1] * scale_y; |
| verts->z = 0; |
| |
| verts->col = col_; |
| |
| verts++; |
| } |
| } else { |
| PSP_TextureData *psp_texture = (PSP_TextureData *) texture->driverdata; |
| VertTCV *verts; |
| verts = (VertTCV *) SDL_AllocateRenderVertices(renderer, count * sizeof (VertTCV), 4, &cmd->data.draw.first); |
| if (!verts) { |
| return -1; |
| } |
| |
| for (i = 0; i < count; i++) { |
| int j; |
| float *xy_; |
| SDL_Color col_; |
| float *uv_; |
| |
| if (size_indices == 4) { |
| j = ((const Uint32 *)indices)[i]; |
| } else if (size_indices == 2) { |
| j = ((const Uint16 *)indices)[i]; |
| } else if (size_indices == 1) { |
| j = ((const Uint8 *)indices)[i]; |
| } else { |
| j = i; |
| } |
| |
| xy_ = (float *)((char*)xy + j * xy_stride); |
| col_ = *(SDL_Color *)((char*)color + j * color_stride); |
| uv_ = (float *)((char*)uv + j * uv_stride); |
| |
| verts->x = xy_[0] * scale_x; |
| verts->y = xy_[1] * scale_y; |
| verts->z = 0; |
| |
| verts->col = col_; |
| |
| verts->u = uv_[0] * psp_texture->textureWidth; |
| verts->v = uv_[1] * psp_texture->textureHeight; |
| |
| verts++; |
| } |
| } |
| |
| return 0; |
| } |
| |
| static int |
| PSP_QueueFillRects(SDL_Renderer * renderer, SDL_RenderCommand *cmd, const SDL_FRect * rects, int count) |
| { |
| VertV *verts = (VertV *) 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++) { |
| verts->x = rects->x; |
| verts->y = rects->y; |
| verts->z = 0.0f; |
| verts++; |
| |
| verts->x = rects->x + rects->w + 0.5f; |
| verts->y = rects->y + rects->h + 0.5f; |
| 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 * 2 * 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; |
| verts++; |
| |
| curU += sourceWidth; |
| curX += polyWidth; |
| |
| verts->u = curU; |
| verts->v = v1; |
| verts->x = curX; |
| verts->y = (y + height); |
| verts->z = 0; |
| verts++; |
| } |
| } |
| |
| 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, float scale_x, float scale_y) |
| { |
| 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 cw1, sw1, ch1, sh1, cw2, sw2, ch2, sh2; |
| |
| 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(360-angle), &s, &c); |
| |
| cw1 = c * -centerx; |
| sw1 = s * -centerx; |
| ch1 = c * -centery; |
| sh1 = s * -centery; |
| cw2 = c * width; |
| sw2 = s * width; |
| ch2 = c * height; |
| sh2 = 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 + cw1 + sh1; |
| verts->y = y - sw1 + ch1; |
| verts->z = 0; |
| verts++; |
| |
| verts->u = u0; |
| verts->v = v1; |
| verts->x = x + cw1 + sh2; |
| verts->y = y - sw1 + ch2; |
| verts->z = 0; |
| verts++; |
| |
| verts->u = u1; |
| verts->v = v1; |
| verts->x = x + cw2 + sh2; |
| verts->y = y - sw2 + ch2; |
| verts->z = 0; |
| verts++; |
| |
| verts->u = u1; |
| verts->v = v0; |
| verts->x = x + cw2 + sh1; |
| verts->y = y - sw2 + ch1; |
| verts->z = 0; |
| |
| if (scale_x != 1.0f || scale_y != 1.0f) { |
| verts->x *= scale_x; |
| verts->y *= scale_y; |
| verts--; |
| verts->x *= scale_x; |
| verts->y *= scale_y; |
| verts--; |
| verts->x *= scale_x; |
| verts->y *= scale_y; |
| verts--; |
| verts->x *= scale_x; |
| verts->y *= scale_y; |
| } |
| |
| return 0; |
| } |
| |
| static void |
| ResetBlendState(PSP_BlendState* state) { |
| sceGuColor(0xffffffff); |
| state->color = 0xffffffff; |
| state->mode = SDL_BLENDMODE_INVALID; |
| state->texture = NULL; |
| sceGuDisable(GU_TEXTURE_2D); |
| sceGuShadeModel(GU_SMOOTH); |
| state->shadeModel = GU_SMOOTH; |
| } |
| |
| static void |
| StartDrawing(SDL_Renderer * renderer) |
| { |
| PSP_RenderData *data = (PSP_RenderData *) renderer->driverdata; |
| |
| // Check if we need to start GU displaylist |
| if(!data->displayListAvail) { |
| sceGuStart(GU_DIRECT, DisplayList); |
| data->displayListAvail = SDL_TRUE; |
| //ResetBlendState(&data->blendState); |
| } |
| |
| // Check if we need a draw buffer change |
| if(renderer->target != data->boundTarget) { |
| SDL_Texture* texture = renderer->target; |
| if(texture) { |
| PSP_TextureData* psp_texture = (PSP_TextureData*) texture->driverdata; |
| // Set target, registering LRU |
| TextureBindAsTarget(data, psp_texture); |
| } else { |
| // Set target back to screen |
| sceGuDrawBufferList(data->psm, vrelptr(data->frontbuffer), PSP_FRAME_BUFFER_WIDTH); |
| } |
| data->boundTarget = texture; |
| } |
| } |
| |
| |
| static void |
| PSP_SetBlendState(PSP_RenderData* data, PSP_BlendState* state) |
| { |
| PSP_BlendState* current = &data->blendState; |
| |
| if (state->mode != current->mode) { |
| switch (state->mode) { |
| 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); |
| sceGuBlendFunc(GU_ADD, GU_SRC_ALPHA, GU_ONE_MINUS_SRC_ALPHA, 0, 0 ); |
| sceGuEnable(GU_BLEND); |
| break; |
| case SDL_BLENDMODE_ADD: |
| sceGuTexFunc(GU_TFX_MODULATE , GU_TCC_RGBA); |
| sceGuBlendFunc(GU_ADD, GU_SRC_ALPHA, GU_FIX, 0, 0x00FFFFFF ); |
| sceGuEnable(GU_BLEND); |
| break; |
| case SDL_BLENDMODE_MOD: |
| sceGuTexFunc(GU_TFX_MODULATE , GU_TCC_RGBA); |
| sceGuBlendFunc(GU_ADD, GU_FIX, GU_SRC_COLOR, 0, 0); |
| sceGuEnable(GU_BLEND); |
| break; |
| case SDL_BLENDMODE_MUL: |
| sceGuTexFunc(GU_TFX_MODULATE , GU_TCC_RGBA); |
| sceGuBlendFunc(GU_ADD, GU_DST_COLOR, GU_ONE_MINUS_SRC_ALPHA, 0, 0); |
| sceGuEnable(GU_BLEND); |
| break; |
| case SDL_BLENDMODE_INVALID: |
| break; |
| } |
| } |
| |
| if(state->color != current->color) { |
| sceGuColor(state->color); |
| } |
| |
| if(state->shadeModel != current->shadeModel) { |
| sceGuShadeModel(state->shadeModel); |
| } |
| |
| if(state->texture != current->texture) { |
| if(state->texture != NULL) { |
| TextureActivate(state->texture); |
| sceGuEnable(GU_TEXTURE_2D); |
| } else { |
| sceGuDisable(GU_TEXTURE_2D); |
| } |
| } |
| |
| *current = *state; |
| } |
| |
| static int |
| PSP_RunCommandQueue(SDL_Renderer * renderer, SDL_RenderCommand *cmd, void *vertices, size_t vertsize) |
| { |
| PSP_RenderData *data = (PSP_RenderData *) renderer->driverdata; |
| Uint8 *gpumem = NULL; |
| 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. */ |
| 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 = &cmd->data.viewport.rect; |
| sceGuOffset(2048 - (viewport->w >> 1), 2048 - (viewport->h >> 1)); |
| sceGuViewport(2048, 2048, viewport->w, viewport->h); |
| sceGuScissor(viewport->x, viewport->y, viewport->w, viewport->h); |
| break; |
| } |
| |
| case SDL_RENDERCMD_SETCLIPRECT: { |
| const SDL_Rect *rect = &cmd->data.cliprect.rect; |
| if(cmd->data.cliprect.enabled){ |
| sceGuEnable(GU_SCISSOR_TEST); |
| sceGuScissor(rect->x, rect->y, rect->w, rect->h); |
| } else { |
| sceGuDisable(GU_SCISSOR_TEST); |
| } |
| 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; |
| sceGuClearColor(GU_RGBA(r,g,b,a)); |
| sceGuClearStencil(a); |
| sceGuClear(GU_COLOR_BUFFER_BIT | GU_STENCIL_BUFFER_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; |
| PSP_BlendState state = { |
| .color = GU_RGBA(r,g,b,a), |
| .texture = NULL, |
| .mode = cmd->data.draw.blend, |
| .shadeModel = GU_FLAT |
| }; |
| PSP_SetBlendState(data, &state); |
| sceGuDrawArray(GU_POINTS, GU_VERTEX_32BITF|GU_TRANSFORM_2D, count, 0, verts); |
| 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; |
| PSP_BlendState state = { |
| .color = GU_RGBA(r,g,b,a), |
| .texture = NULL, |
| .mode = cmd->data.draw.blend, |
| .shadeModel = GU_FLAT |
| }; |
| PSP_SetBlendState(data, &state); |
| sceGuDrawArray(GU_LINE_STRIP, GU_VERTEX_32BITF|GU_TRANSFORM_2D, count, 0, verts); |
| 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; |
| PSP_BlendState state = { |
| .color = GU_RGBA(r,g,b,a), |
| .texture = NULL, |
| .mode = cmd->data.draw.blend, |
| .shadeModel = GU_FLAT |
| }; |
| PSP_SetBlendState(data, &state); |
| sceGuDrawArray(GU_SPRITES, GU_VERTEX_32BITF|GU_TRANSFORM_2D, 2 * count, 0, verts); |
| break; |
| } |
| |
| case SDL_RENDERCMD_COPY: { |
| const size_t count = cmd->data.draw.count; |
| const VertTV *verts = (VertTV *) (gpumem + cmd->data.draw.first); |
| const Uint8 a = cmd->data.draw.a; |
| const Uint8 r = cmd->data.draw.r; |
| const Uint8 g = cmd->data.draw.g; |
| const Uint8 b = cmd->data.draw.b; |
| PSP_BlendState state = { |
| .color = GU_RGBA(r,g,b,a), |
| .texture = cmd->data.draw.texture, |
| .mode = cmd->data.draw.blend, |
| .shadeModel = GU_SMOOTH |
| }; |
| PSP_SetBlendState(data, &state); |
| sceGuDrawArray(GU_SPRITES, GU_TEXTURE_32BITF|GU_VERTEX_32BITF|GU_TRANSFORM_2D, 2 * count, 0, verts); |
| break; |
| } |
| |
| case SDL_RENDERCMD_COPY_EX: { |
| const VertTV *verts = (VertTV *) (gpumem + cmd->data.draw.first); |
| const Uint8 a = cmd->data.draw.a; |
| const Uint8 r = cmd->data.draw.r; |
| const Uint8 g = cmd->data.draw.g; |
| const Uint8 b = cmd->data.draw.b; |
| PSP_BlendState state = { |
| .color = GU_RGBA(r,g,b,a), |
| .texture = cmd->data.draw.texture, |
| .mode = cmd->data.draw.blend, |
| .shadeModel = GU_SMOOTH |
| }; |
| PSP_SetBlendState(data, &state); |
| sceGuDrawArray(GU_TRIANGLE_FAN, GU_TEXTURE_32BITF|GU_VERTEX_32BITF|GU_TRANSFORM_2D, 4, 0, verts); |
| break; |
| } |
| |
| case SDL_RENDERCMD_GEOMETRY: { |
| const size_t count = cmd->data.draw.count; |
| if (cmd->data.draw.texture == NULL) { |
| const VertCV *verts = (VertCV *) (gpumem + cmd->data.draw.first); |
| sceGuDisable(GU_TEXTURE_2D); |
| /* In GU_SMOOTH mode */ |
| sceGuDrawArray(GU_TRIANGLES, GU_COLOR_8888|GU_VERTEX_32BITF|GU_TRANSFORM_2D, count, 0, verts); |
| sceGuEnable(GU_TEXTURE_2D); |
| } else { |
| const VertTCV *verts = (VertTCV *) (gpumem + cmd->data.draw.first); |
| const Uint8 a = cmd->data.draw.a; |
| const Uint8 r = cmd->data.draw.r; |
| const Uint8 g = cmd->data.draw.g; |
| const Uint8 b = cmd->data.draw.b; |
| PSP_BlendState state = { |
| .color = GU_RGBA(r,g,b,a), |
| .texture = NULL, |
| .mode = cmd->data.draw.blend, |
| .shadeModel = GU_FLAT |
| }; |
| TextureActivate(cmd->data.draw.texture); |
| PSP_SetBlendState(data, &state); |
| sceGuDrawArray(GU_TRIANGLES, GU_TEXTURE_32BITF|GU_COLOR_8888|GU_VERTEX_32BITF|GU_TRANSFORM_2D, count, 0, verts); |
| } |
| 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) && (data->vblank_not_reached)) |
| sceDisplayWaitVblankStart(); |
| data->vblank_not_reached = SDL_TRUE; |
| |
| 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; |
| |
| LRUTargetRemove(renderdata, psp_texture); |
| TextureStorageFree(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); |
| |
| sceKernelDisableSubIntr(PSP_VBLANK_INT, 0); |
| sceKernelReleaseSubIntrHandler(PSP_VBLANK_INT,0); |
| sceDisplayWaitVblankStart(); |
| sceGuDisplay(GU_FALSE); |
| sceGuTerm(); |
| vfree(data->backbuffer); |
| vfree(data->frontbuffer); |
| |
| data->initialized = SDL_FALSE; |
| data->displayListAvail = SDL_FALSE; |
| SDL_free(data); |
| } |
| SDL_free(renderer); |
| } |
| |
| static int |
| PSP_SetVSync(SDL_Renderer * renderer, const int vsync) |
| { |
| PSP_RenderData *data = renderer->driverdata; |
| data->vsync = vsync; |
| return 0; |
| } |
| |
| SDL_Renderer * |
| PSP_CreateRenderer(SDL_Window * window, Uint32 flags) |
| { |
| |
| SDL_Renderer *renderer; |
| PSP_RenderData *data; |
| int pixelformat; |
| void* doublebuffer = NULL; |
| |
| 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->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->QueueGeometry = PSP_QueueGeometry; |
| 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->SetVSync = PSP_SetVSync; |
| 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; |
| |
| data->most_recent_target = NULL; |
| data->least_recent_target = NULL; |
| |
| 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->bpp = 2; |
| data->psm = pixelformat; |
| break; |
| default: |
| data->bpp = 4; |
| data->psm = GU_PSM_8888; |
| break; |
| } |
| |
| doublebuffer = valloc(PSP_FRAME_BUFFER_SIZE*data->bpp*2); |
| data->backbuffer = doublebuffer; |
| data->frontbuffer = ((uint8_t*)doublebuffer)+PSP_FRAME_BUFFER_SIZE*data->bpp; |
| |
| sceGuInit(); |
| /* setup GU */ |
| sceGuStart(GU_DIRECT, DisplayList); |
| sceGuDrawBuffer(data->psm, vrelptr(data->frontbuffer), PSP_FRAME_BUFFER_WIDTH); |
| sceGuDispBuffer(PSP_SCREEN_WIDTH, PSP_SCREEN_HEIGHT, vrelptr(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); |
| |
| |
| sceGuDisable(GU_DEPTH_TEST); |
| |
| /* Scissoring */ |
| sceGuScissor(0, 0, PSP_SCREEN_WIDTH, PSP_SCREEN_HEIGHT); |
| sceGuEnable(GU_SCISSOR_TEST); |
| |
| /* Backface culling */ |
| /* |
| FIXME: Culling probably un-needed ? It can conflict with SDL_RENDERCMD_GEOMETRY |
| sceGuFrontFace(GU_CCW); |
| sceGuEnable(GU_CULL_FACE); |
| */ |
| |
| //Setup initial blend state |
| ResetBlendState(&data->blendState); |
| |
| sceGuFinish(); |
| sceGuSync(0,0); |
| sceDisplayWaitVblankStartCB(); |
| sceGuDisplay(GU_TRUE); |
| |
| /* Improve performance when VSYC is enabled and it is not reaching the 60 FPS */ |
| data->vblank_not_reached = SDL_TRUE; |
| sceKernelRegisterSubIntrHandler(PSP_VBLANK_INT, 0, psp_on_vblank, data); |
| sceKernelEnableSubIntr(PSP_VBLANK_INT, 0); |
| |
| 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: */ |
| |