diff --git a/CMakeLists.txt b/CMakeLists.txt index ea2368cdfe9aa..ebf15d6b8bf70 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2666,6 +2666,7 @@ elseif(VITA) # CheckPTHREAD() elseif(PSP) + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsingle-precision-constant -ffast-math") file(GLOB PSP_MAIN_SOURCES ${SDL2_SOURCE_DIR}/src/main/psp/*.c) list(APPEND SDLMAIN_SOURCES ${PSP_MAIN_SOURCES}) diff --git a/include/SDL_hints.h b/include/SDL_hints.h index 6713d01fea204..f603975a1ff57 100644 --- a/include/SDL_hints.h +++ b/include/SDL_hints.h @@ -2038,6 +2038,17 @@ extern "C" { */ #define SDL_HINT_PS2_DYNAMIC_VSYNC "SDL_PS2_DYNAMIC_VSYNC" +/** + * \brief A variable controlling if VSYNC is automatically disable if doesn't reach the enough FPS + * + * This variable can be set to the following values: + * "0" - It will be using VSYNC as defined in the main flag. Default + * "1" - If VSYNC was previously enabled, then it will disable VSYNC if doesn't reach enough speed + * + * By default SDL does not enable the automatic VSYNC + */ +#define SDL_HINT_PSP_DYNAMIC_VSYNC "SDL_PSP_DYNAMIC_VSYNC" + /** * A variable to control whether the return key on the soft keyboard should * hide the soft keyboard on Android and iOS. diff --git a/src/render/psp/SDL_render_psp.c b/src/render/psp/SDL_render_psp.c index 2b85309d81260..38ab727bf9d19 100644 --- a/src/render/psp/SDL_render_psp.c +++ b/src/render/psp/SDL_render_psp.c @@ -22,103 +22,89 @@ #if SDL_VIDEO_RENDER_PSP -#include "SDL_hints.h" #include "../SDL_sysrender.h" +#include "SDL_hints.h" +#include "SDL_render_psp.h" -#include #include #include -#include -#include -#include -#include -#include -#include -#include +#include +#include +#include #include -#include "SDL_render_psp.h" -/* PSP renderer implementation, based on the PGE */ -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; +#define GPU_LIST_SIZE 256 * 1024 typedef struct { - SDL_BlendMode mode; - unsigned int color; - int shadeModel; - SDL_Texture *texture; -} PSP_BlendState; + uint8_t shade; /**< GU_FLAT or GU_SMOOTH */ + uint8_t mode; /**< GU_ADD, GU_SUBTRACT, GU_REVERSE_SUBTRACT, GU_MIN, GU_MAX */ +} PSP_BlendInfo; 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; /**< whether 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 */ - + uint32_t __attribute__((aligned(16))) guList[3][GPU_LIST_SIZE]; + void *frontbuffer; /**< main screen buffer */ + void *backbuffer; /**< buffer presented to display */ + PSP_BlendInfo blendInfo; /**< current blend info */ + uint8_t drawBufferFormat; /**< GU_PSM_8888 or GU_PSM_5650 or GU_PSM_4444 */ + uint8_t currentDrawBufferFormat; /**< GU_PSM_8888 or GU_PSM_5650 or GU_PSM_4444 */ + uint8_t vsync; /* 0 (Disabled), 1 (Enabled), 2 (Dynamic) */ + uint8_t list_idx; SDL_bool vblank_not_reached; /**< whether vblank wasn't reached */ } PSP_RenderData; typedef struct { - float x, y, z; -} VertV; + void *data; /**< Image data. */ + void *swizzledData; /**< Swizzled image data. */ + uint32_t semaphore; /**< Semaphore for the texture. */ + uint32_t textureWidth; /**< Texture width (power of two). */ + uint32_t textureHeight; /**< Texture height (power of two). */ + uint32_t width; /**< Image width. */ + uint32_t height; /**< Image height. */ + uint32_t pitch; /**< Image pitch. */ + uint32_t swizzledWidth; /**< Swizzled image width. */ + uint32_t swizzledHeight; /**< Swizzled image height. */ + uint32_t swizzledPitch; /**< Swizzled image pitch. */ + uint32_t size; /**< Image size in bytes. */ + uint32_t swizzledSize; /**< Swizzled image size in bytes. */ + uint8_t format; /**< Image format - one of ::pgePixelFormat. */ + uint8_t filter; /**< Image filter - one of GU_NEAREST or GU_LINEAR. */ + uint8_t swizzled; /**< Whether the image is swizzled or not. */ +} PSP_Texture; typedef struct { - float u, v; float x, y, z; -} VertTV; +} __attribute__((packed)) VertV; typedef struct { SDL_Color col; float x, y, z; -} VertCV; +} __attribute__((packed)) VertCV; typedef struct { float u, v; SDL_Color col; float x, y, z; -} VertTCV; +} __attribute__((packed)) VertTCV; + +typedef struct +{ + float u, v; + float x, y, z; +} __attribute__((packed)) VertTV; + +typedef struct +{ + int width; + int height; +} SliceSize; -int SDL_PSP_RenderGetProp(SDL_Renderer *r, enum SDL_PSP_RenderProps which, void** out) +int SDL_PSP_RenderGetProp(SDL_Renderer *r, enum SDL_PSP_RenderProps which, void **out) { PSP_RenderData *rd; if (r == NULL) { @@ -126,83 +112,101 @@ int SDL_PSP_RenderGetProp(SDL_Renderer *r, enum SDL_PSP_RenderProps which, void* } rd = r->driverdata; switch (which) { - case SDL_PSP_RENDERPROPS_FRONTBUFFER: - *out = rd->frontbuffer; - return 0; - case SDL_PSP_RENDERPROPS_BACKBUFFER: - *out = rd->backbuffer; - return 0; + case SDL_PSP_RENDERPROPS_FRONTBUFFER: + *out = rd->frontbuffer; + return 0; + case SDL_PSP_RENDERPROPS_BACKBUFFER: + *out = rd->backbuffer; + return 0; } return -1; } -#define PI 3.14159265358979f - -#define radToDeg(x) ((x)*180.f / PI) -#define degToRad(x) ((x)*PI / 180.f) - -static float MathAbs(float x) +/* PRIVATE METHODS */ +static void psp_on_vblank(u32 sub, PSP_RenderData *data) { - float result; - - __asm__ volatile( - "mtv %1, S000\n" - "vabs.s S000, S000\n" - "mfv %0, S000\n" - : "=r"(result) - : "r"(x)); + if (data) { + data->vblank_not_reached = SDL_FALSE; + } +} - return result; +// This is a trick to be able of getting the semaphore ID from the signal callback +// From the value received on the signal callback, we can just use the lower 16 bits +// So in order to get the semaphore ID (which is 32 bits), we have done this trick. +// Where on the sceGuSignalSemaphore we send 2 consecutive signals, one with the high 16 bits +// and the other with the low 16 bits. +// Then on the psp_on_signal we identify if we are reading the high or the low 16 bits +// by using the psp_on_signal_read_high variable. +// So we can get the semaphore ID by combining the high and low 16 bits. +static uint8_t psp_on_signal_read_high = 0; +static uint16_t psp_on_signal_high = 0; +static void psp_on_signal(int id) +{ + uint16_t value = id & 0xFFFF; + if (psp_on_signal_read_high) { + uint32_t semaphore = (psp_on_signal_high << 16) | value; + sceKernelSignalSema(semaphore, 1); + psp_on_signal_read_high = 0; + } else { + psp_on_signal_high = value; + psp_on_signal_read_high = 1; + } } -static void MathSincos(float r, float *s, float *c) +static inline void sceGuSignalSemaphore(uint32_t semaphore) { - __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)); + sceGuSignal(GU_SIGNAL_NOWAIT, (semaphore >> 16) & 0xFFFF); + sceGuSignal(GU_SIGNAL_NOWAIT, (semaphore & 0xFFFF)); } -static void Swap(float *a, float *b) +static inline uint32_t createSemaphore(SDL_Texture *texture) { - float n = *a; - *a = *b; - *b = n; + uint32_t semaphore; + char semaphoreName[31]; + snprintf(semaphoreName, sizeof(semaphoreName), "PSP_Tex_Sem_%p", texture); + semaphore = sceKernelCreateSema(semaphoreName, 0, 1, 1, NULL); + if (semaphore < 0) { + SDL_SetError("Failed to create texture semaphore"); + return -1; + } + return semaphore; } -static inline int InVram(void *data) +static inline void destroySemaphore(uint32_t semaphore) { - return data < (void *)0x04200000; + if (semaphore != -1) { + // Wait for all threads to finish using the semaphore + sceKernelWaitSema(semaphore, 1, NULL); + sceKernelDeleteSema(semaphore); + semaphore = -1; + } } -/* Return next power of 2 */ -static int TextureNextPow2(unsigned int w) +static inline unsigned int getMemorySize(unsigned int width, unsigned int height, unsigned int psm) { - unsigned int n = 2; - if (w == 0) { - return 0; - } + switch (psm) { + case GU_PSM_T4: + return (width * height) >> 1; - while (w > n) { - n <<= 1; - } + case GU_PSM_T8: + return width * height; - return n; -} + case GU_PSM_5650: + case GU_PSM_5551: + case GU_PSM_4444: + case GU_PSM_T16: + return 2 * width * height; -static void psp_on_vblank(u32 sub, PSP_RenderData *data) -{ - if (data) { - data->vblank_not_reached = SDL_FALSE; + case GU_PSM_8888: + case GU_PSM_T32: + return 4 * width * height; + + default: + return 0; } } -static int PixelFormatToPSPFMT(Uint32 format) +static inline int pixelFormatToPSPFMT(Uint32 format) { switch (format) { case SDL_PIXELFORMAT_BGR565: @@ -218,273 +222,284 @@ static int PixelFormatToPSPFMT(Uint32 format) } } -/// SECTION render target LRU management -static void LRUTargetRelink(PSP_TextureData *psp_texture) +static inline int calculatePitchForTextureFormat(int width, int format) { - if (psp_texture->prevhotw) { - psp_texture->prevhotw->nexthotw = psp_texture->nexthotw; - } - if (psp_texture->nexthotw) { - psp_texture->nexthotw->prevhotw = psp_texture->prevhotw; + switch (format) { + case GU_PSM_5650: + case GU_PSM_5551: + case GU_PSM_4444: + return (width + 7) & ~7; + case GU_PSM_8888: + return (width + 3) & ~3; + default: + return width; } } -static void LRUTargetPushFront(PSP_RenderData *data, PSP_TextureData *psp_texture) +static inline int calculatePitchForTextureFormatAndAccess(int width, int format, int access) { - 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; + if (access == SDL_TEXTUREACCESS_TARGET) { + // Round up to 64 bytes because it is required to be used by sceGuDrawBufferList + return (width + 63) & ~63; + } else { + return calculatePitchForTextureFormat(width, format); } } -static void LRUTargetRemove(PSP_RenderData *data, PSP_TextureData *psp_texture) +static inline int calculateHeightForSwizzledTexture(int height, int format) { - 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; + switch (format) { + case GU_PSM_5650: + case GU_PSM_5551: + case GU_PSM_4444: + return (height + 15) & ~15; + case GU_PSM_8888: + return (height + 7) & ~7; + default: + return height; } - psp_texture->prevhotw = NULL; - psp_texture->nexthotw = NULL; } -static void LRUTargetBringFront(PSP_RenderData *data, PSP_TextureData *psp_texture) +/* Return next power of 2 */ +static inline int calculateNextPow2(int value) { - if (data->most_recent_target == psp_texture) { - return; // nothing to do + int i = 1; + while (i < value) { + i <<= 1; } - LRUTargetRemove(data, psp_texture); - LRUTargetPushFront(data, psp_texture); + return i; } -static void TextureStorageFree(void *storage) +static inline int calculateBestSliceSizeForSprite(SDL_Renderer *renderer, const SDL_FRect *dstrect, SliceSize *sliceSize, SliceSize *sliceDimension) { - if (InVram(storage)) { - vfree(storage); - } else { - SDL_free(storage); - } -} + PSP_RenderData *data = (PSP_RenderData *)renderer->driverdata; -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; + // We split in blocks of (64 x destiny height) when 16 bits per color + // or (32 x destiny height) when 32 bits per color - if (psp_texture->swizzled) { - return 1; + switch (data->currentDrawBufferFormat) { + case GU_PSM_5650: + case GU_PSM_5551: + case GU_PSM_4444: + sliceSize->width = dstrect->w > 64 ? 64 : dstrect->w; + break; + case GU_PSM_8888: + sliceSize->width = dstrect->w > 32 ? 32 : dstrect->w; + break; + default: + return -1; } + sliceSize->height = dstrect->h; - bytewidth = psp_texture->textureWidth * (psp_texture->bits >> 3); - height = psp_texture->size / bytewidth; + sliceDimension->width = SDL_ceilf(dstrect->w / sliceSize->width); + sliceDimension->height = SDL_ceilf(dstrect->h / sliceSize->height); - rowblocks = (bytewidth >> 4); - rowblocksadd = (rowblocks - 1) << 7; + return 0; +} - src = (unsigned int *)psp_texture->data; +static inline void fillSingleSliceVertices(VertTV *vertices, const SDL_Rect *srcrect, const SDL_FRect *dstrect) +{ + vertices[0].x = dstrect->x; + vertices[0].y = dstrect->y; + vertices[0].z = 0.0f; + vertices[0].u = srcrect->x; + vertices[0].v = srcrect->y; + + vertices[1].x = dstrect->x + dstrect->w; + vertices[1].y = dstrect->y + dstrect->h; + vertices[1].z = 0.0f; + vertices[1].u = srcrect->x + srcrect->w; + vertices[1].v = srcrect->y + srcrect->h; +} - data = dst; - if (!data) { - data = SDL_malloc(psp_texture->size); - } +static inline void fillSpriteVertices(VertTV *vertices, SliceSize *dimensions, SliceSize *sliceSize, + const SDL_Rect *srcrect, const SDL_FRect *dstrect) +{ - if (!data) { - return SDL_OutOfMemory(); + int i, j; + int remainingWidth, remainingHeight; + int hasRemainingWidth, hasRemainingHeight; + float srcrectRateWidth, srcrectRateHeight; + float srcWidth, srcHeight; + float remainingSrcWidth, remainingSrcHeight; + + // For avoiding division by zero + if (dimensions->width == 1 && dimensions->height == 1) { + fillSingleSliceVertices(vertices, srcrect, dstrect); + return; } - for (j = 0; j < height; j++, blockaddress += 16) { - unsigned int *block; + remainingWidth = (int)dstrect->w % sliceSize->width; + remainingHeight = (int)dstrect->h % sliceSize->height; + hasRemainingWidth = remainingWidth > 0; + hasRemainingHeight = remainingHeight > 0; + srcrectRateWidth = (float)(abs(srcrect->w - dimensions->width)) / (float)(abs(dstrect->w - dimensions->width)); + srcrectRateHeight = (float)(abs(srcrect->h - dimensions->height)) / (float)(abs(dstrect->h - dimensions->height)); + srcWidth = sliceSize->width * srcrectRateWidth; + srcHeight = sliceSize->height * srcrectRateHeight; + remainingSrcWidth = remainingWidth * srcrectRateWidth; + remainingSrcHeight = remainingHeight * srcrectRateHeight; + + for (i = 0; i < dimensions->width; i++) { + for (j = 0; j < dimensions->height; j++) { + uint8_t currentIndex = (i * dimensions->height + j) * 2; + vertices[currentIndex].u = srcrect->x + i * srcWidth; + vertices[currentIndex].v = srcrect->y + j * srcHeight; + vertices[currentIndex].x = dstrect->x + i * sliceSize->width; + vertices[currentIndex].y = dstrect->y + j * sliceSize->height; + vertices[currentIndex].z = 0; + + if (i == dimensions->width - 1 && hasRemainingWidth) { + vertices[currentIndex + 1].u = vertices[currentIndex].u + remainingSrcWidth; + vertices[currentIndex + 1].x = vertices[currentIndex].x + remainingWidth; + } else { + vertices[currentIndex + 1].u = vertices[currentIndex].u + srcWidth; + vertices[currentIndex + 1].x = vertices[currentIndex].x + sliceSize->width; + } - block = (unsigned int *)&data[blockaddress]; + if (j == dimensions->height - 1 && hasRemainingHeight) { + vertices[currentIndex + 1].v = vertices[currentIndex].v + remainingSrcHeight; + vertices[currentIndex + 1].y = vertices[currentIndex].y + remainingHeight; + } else { + vertices[currentIndex + 1].v = vertices[currentIndex].v + srcHeight; + vertices[currentIndex + 1].y = vertices[currentIndex].y + sliceSize->height; + } - for (i = 0; i < rowblocks; i++) { - *block++ = *src++; - *block++ = *src++; - *block++ = *src++; - *block++ = *src++; - block += 28; + vertices[currentIndex + 1].z = 0; } + } +} - if ((j & 0x7) == 0x7) { - blockaddress += rowblocksadd; +// The slize when swizzling is always 16x32 bytes, no matter the texture format +// so the algorithm is the same for all formats +static inline int swizzle(PSP_Texture *psp_tex) +{ + uint32_t i, j, verticalSlice, dstOffset; + uint32_t *src, *dst, *srcBlock, *dstBlock; + uint32_t srcWidth = psp_tex->pitch >> 2; + uint32_t dstWidth = psp_tex->swizzledPitch >> 2; + uint32_t srcWidthBlocks = srcWidth >> 2; + uint8_t blockSizeBytes = 16; // 4 pixels of 32 bits + + dst = (uint32_t *)psp_tex->swizzledData; + for (j = 0; j < psp_tex->height; j++) { + src = (uint32_t *)psp_tex->data + j * srcWidth; + verticalSlice = (((j >> 3) << 3) * dstWidth) + ((j % 8) << 2); + for (i = 0; i < srcWidthBlocks; i++) { + srcBlock = src + (i << 2); + dstOffset = verticalSlice + (i << 5); + dstBlock = dst + dstOffset; + memcpy(dstBlock, srcBlock, blockSizeBytes); } } - 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) +static inline int unswizzle(PSP_Texture *psp_tex) { - 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; + uint32_t i, j, verticalSlice, srcOffset; + uint32_t *src, *dst, *srcBlock, *dstBlock; + uint32_t srcWidth = psp_tex->swizzledPitch >> 2; + uint32_t dstWidth = psp_tex->pitch >> 2; + uint32_t dstWidthBlocks = dstWidth >> 2; + uint8_t blockSizeBytes = 16; // 4 pixels of 32 bits + + src = (uint32_t *)psp_tex->swizzledData; + for (j = 0; j < psp_tex->height; j++) { + dst = (uint32_t *)psp_tex->data + j * dstWidth; + verticalSlice = (((j >> 3) << 3) * srcWidth) + ((j % 8) << 2); + for (i = 0; i < dstWidthBlocks; i++) { + dstBlock = dst + (i << 2); + srcOffset = verticalSlice + (i << 5); + srcBlock = src + srcOffset; + memcpy(dstBlock, srcBlock, blockSizeBytes); } - - 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) +static inline void prepareTextureForUpload(SDL_Texture *texture) { - // Assumes the texture is in VRAM - if (psp_texture->swizzled) { - // Texture was swizzled in vram, just copy to system memory - void *sdata = SDL_malloc(psp_texture->size); - if (!sdata) { - return SDL_OutOfMemory(); - } + PSP_Texture *psp_tex = (PSP_Texture *)texture->driverdata; + if (texture->access != SDL_TEXTUREACCESS_STATIC || psp_tex->swizzled) + return; - SDL_memcpy(sdata, psp_texture->data, psp_texture->size); - vfree(psp_texture->data); - psp_texture->data = sdata; - return 0; - } else { - return TextureSwizzle(psp_texture, NULL); // Will realloc in sysram + psp_tex->swizzledData = vramalloc(psp_tex->swizzledSize); + if (!psp_tex->swizzledData) { + sceKernelDcacheWritebackRange(psp_tex->data, psp_tex->size); + return; } -} -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 = vramalloc(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; - } -} + swizzle(psp_tex); + SDL_free(psp_tex->data); + psp_tex->swizzled = GU_TRUE; -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; + sceKernelDcacheWritebackRange(psp_tex->swizzledData, psp_tex->swizzledSize); } -static int TextureSpillTargetsForSpace(PSP_RenderData *data, size_t size) +static inline void prepareTextureForDownload(SDL_Texture *texture) { - while (vlargestblock() < size) { - if (TextureSpillLRU(data, size) < 0) { - return -1; - } + PSP_Texture *psp_tex = (PSP_Texture *)texture->driverdata; + + if (texture->access != SDL_TEXTUREACCESS_STATIC || !psp_tex->swizzled) + return; + + psp_tex->data = SDL_malloc(psp_tex->size); + if (!psp_tex->data) { + sceKernelDcacheInvalidateRange(psp_tex->swizzledData, psp_tex->swizzledSize); + return; } - return 0; + + unswizzle(psp_tex); + vfree(psp_tex->swizzledData); + psp_tex->swizzled = GU_FALSE; + + sceKernelDcacheInvalidateRange(psp_tex->data, psp_tex->size); } -static int TextureBindAsTarget(PSP_RenderData *data, PSP_TextureData *psp_texture) +static inline void PSP_SetBlendMode(PSP_RenderData *data, PSP_BlendInfo blendInfo) { - 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; + // Update the blend mode if necessary + if (data->blendInfo.mode != blendInfo.mode) { + switch (blendInfo.mode) { + case SDL_BLENDMODE_NONE: + sceGuShadeModel(GU_SMOOTH); + 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); + /* FIXME SDL_BLENDMODE_MUL is simplified, and dstA is in fact un-changed.*/ + sceGuBlendFunc(GU_ADD, GU_DST_COLOR, GU_ONE_MINUS_SRC_ALPHA, 0, 0); + sceGuEnable(GU_BLEND); + break; } + + data->blendInfo.mode = blendInfo.mode; } - 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); + + // Update shade model if needed + if (data->blendInfo.shade != blendInfo.shade) { + sceGuShadeModel(blendInfo.shade); + data->blendInfo.shade = blendInfo.shade; } - return 0; } static void PSP_WindowEvent(SDL_Renderer *renderer, const SDL_WindowEvent *event) @@ -493,89 +508,78 @@ 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)); + PSP_Texture *psp_tex = (PSP_Texture *)SDL_calloc(1, sizeof(PSP_Texture)); - if (!psp_texture) { + if (!psp_tex) { 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: - SDL_free(psp_texture); - return -1; + psp_tex->semaphore = createSemaphore(texture); + if (psp_tex->semaphore == -1) { + return SDL_OutOfMemory(); } - - 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) { - SDL_free(psp_texture); - return -1; - } - psp_texture->data = vramalloc(psp_texture->size); - if (psp_texture->data) { - LRUTargetPushFront(data, psp_texture); + psp_tex->format = pixelFormatToPSPFMT(texture->format); + psp_tex->textureWidth = calculateNextPow2(texture->w); + psp_tex->textureHeight = calculateNextPow2(texture->h); + psp_tex->width = calculatePitchForTextureFormatAndAccess(texture->w, psp_tex->format, texture->access); + psp_tex->height = texture->h; + psp_tex->pitch = psp_tex->width * SDL_BYTESPERPIXEL(texture->format); + psp_tex->swizzledWidth = psp_tex->textureWidth; + psp_tex->swizzledHeight = calculateHeightForSwizzledTexture(texture->h, psp_tex->format); + psp_tex->swizzledPitch = psp_tex->swizzledWidth * SDL_BYTESPERPIXEL(texture->format); + psp_tex->size = getMemorySize(psp_tex->width, psp_tex->height, psp_tex->format); + psp_tex->swizzledSize = getMemorySize(psp_tex->swizzledWidth, psp_tex->swizzledHeight, psp_tex->format); + + if (texture->access != SDL_TEXTUREACCESS_STATIC) { + psp_tex->data = vramalloc(psp_tex->size); + if (!psp_tex->data) { + destroySemaphore(psp_tex->semaphore); + vfree(psp_tex); + return SDL_OutOfMemory(); } } else { - psp_texture->data = SDL_calloc(1, psp_texture->size); + psp_tex->data = SDL_calloc(1, psp_tex->size); + if (!psp_tex->data) { + destroySemaphore(psp_tex->semaphore); + SDL_free(psp_tex); + return SDL_OutOfMemory(); + } } + psp_tex->swizzled = GU_FALSE; - if (!psp_texture->data) { - SDL_free(psp_texture); - return SDL_OutOfMemory(); - } - texture->driverdata = psp_texture; + texture->driverdata = psp_tex; return 0; } -static int TextureShouldSwizzle(PSP_TextureData *psp_texture, SDL_Texture *texture) +static int PSP_LockTexture(SDL_Renderer *renderer, SDL_Texture *texture, + const SDL_Rect *rect, void **pixels, int *pitch) { - return !((texture->access == SDL_TEXTUREACCESS_TARGET) && InVram(psp_texture->data)) && texture->access != SDL_TEXTUREACCESS_STREAMING && (texture->w >= 16 || texture->h >= 16); -} + PSP_Texture *psp_tex = (PSP_Texture *)texture->driverdata; -static void TextureActivate(SDL_Texture *texture) -{ - PSP_TextureData *psp_texture = (PSP_TextureData *)texture->driverdata; - int scaleMode = (texture->scaleMode == SDL_ScaleModeNearest) ? GU_NEAREST : GU_LINEAR; + // How a pointer to the texture data is returned it need to be unswizzled before it can be used + prepareTextureForDownload(texture); - /* Swizzling is useless with small textures. */ - if (TextureShouldSwizzle(psp_texture, texture)) { - TextureSwizzle(psp_texture, NULL); - } + sceKernelWaitSema(psp_tex->semaphore, 1, NULL); + *pixels = + (void *)((Uint8 *)psp_tex->data + rect->y * psp_tex->pitch + + rect->x * SDL_BYTESPERPIXEL(texture->format)); + *pitch = psp_tex->pitch; - 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); + return 0; } -static int PSP_LockTexture(SDL_Renderer *renderer, SDL_Texture *texture, - const SDL_Rect *rect, void **pixels, int *pitch); +static void PSP_UnlockTexture(SDL_Renderer *renderer, SDL_Texture *texture) +{ + PSP_Texture *psp_tex = (PSP_Texture *)texture->driverdata; + + sceKernelDcacheWritebackRange(psp_tex->data, psp_tex->size); + sceKernelSignalSema(psp_tex->semaphore, 1); +} 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; @@ -593,42 +597,57 @@ static int PSP_UpdateTexture(SDL_Renderer *renderer, SDL_Texture *texture, } } - 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; + PSP_UnlockTexture(renderer, texture); - *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 */ + PSP_Texture *psp_tex = (PSP_Texture *)texture->driverdata; + /* + set texture filtering according to scaleMode + suported hint values are nearest (0, default) or linear (1) + GU scale mode is either GU_NEAREST (good for tile-map) + or GU_LINEAR (good for scaling) + */ + uint32_t guScaleMode = (scaleMode == SDL_ScaleModeNearest + ? GU_NEAREST + : GU_LINEAR); + psp_tex->filter = guScaleMode; } static int PSP_SetRenderTarget(SDL_Renderer *renderer, SDL_Texture *texture) { + PSP_RenderData *data = (PSP_RenderData *)renderer->driverdata; + + if (texture) { + PSP_Texture *psp_tex = (PSP_Texture *)texture->driverdata; + sceKernelWaitSema(psp_tex->semaphore, 1, NULL); + sceGuDrawBufferList(psp_tex->format, vrelptr(psp_tex->data), psp_tex->width); + data->currentDrawBufferFormat = psp_tex->format; + + if (psp_tex->format == 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); + } + + // Enable scissor to avoid drawing outside viewport + sceGuEnable(GU_SCISSOR_TEST); + sceGuScissor(0, 0, psp_tex->width, psp_tex->height); + + sceGuSignalSemaphore(psp_tex->semaphore); + } else { + sceGuDrawBufferList(data->drawBufferFormat, vrelptr(data->backbuffer), PSP_FRAME_BUFFER_WIDTH); + data->currentDrawBufferFormat = data->drawBufferFormat; + } + return 0; } @@ -668,16 +687,18 @@ static int PSP_QueueGeometry(SDL_Renderer *renderer, SDL_RenderCommand *cmd, SDL cmd->data.draw.count = count; size_indices = indices ? size_indices : 0; - if (!texture) { - VertCV *verts; - verts = (VertCV *)SDL_AllocateRenderVertices(renderer, count * sizeof(VertCV), 4, &cmd->data.draw.first); - if (!verts) { + if (texture) { + VertTCV *vertices = (VertTCV *)SDL_AllocateRenderVertices(renderer, count * sizeof(VertTCV), 4, &cmd->data.draw.first); + PSP_Texture *psp_tex = (PSP_Texture *)texture->driverdata; + + if (!vertices) { return -1; } for (i = 0; i < count; i++) { int j; float *xy_; + float *uv_; SDL_Color col_; if (size_indices == 4) { j = ((const Uint32 *)indices)[i]; @@ -691,20 +712,24 @@ static int PSP_QueueGeometry(SDL_Renderer *renderer, SDL_RenderCommand *cmd, SDL xy_ = (float *)((char *)xy + j * xy_stride); col_ = *(SDL_Color *)((char *)color + j * color_stride); + uv_ = (float *)((char *)uv + j * uv_stride); + + vertices->x = xy_[0] * scale_x; + vertices->y = xy_[1] * scale_y; + vertices->z = 0; - verts->x = xy_[0] * scale_x; - verts->y = xy_[1] * scale_y; - verts->z = 0; + vertices->col = col_; - verts->col = col_; + vertices->u = uv_[0] * psp_tex->width; + vertices->v = uv_[1] * psp_tex->height; - verts++; + vertices++; } + } 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) { + VertCV *vertices = (VertCV *)SDL_AllocateRenderVertices(renderer, count * sizeof(VertCV), 4, &cmd->data.draw.first); + + if (!vertices) { return -1; } @@ -712,8 +737,6 @@ static int PSP_QueueGeometry(SDL_Renderer *renderer, SDL_RenderCommand *cmd, SDL int j; float *xy_; SDL_Color col_; - float *uv_; - if (size_indices == 4) { j = ((const Uint32 *)indices)[i]; } else if (size_indices == 2) { @@ -726,18 +749,13 @@ static int PSP_QueueGeometry(SDL_Renderer *renderer, SDL_RenderCommand *cmd, SDL 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; + vertices->x = xy_[0] * scale_x; + vertices->y = xy_[1] * scale_y; + vertices->z = 0; + vertices->col = col_; - verts->col = col_; - - verts->u = uv_[0] * psp_texture->textureWidth; - verts->v = uv_[1] * psp_texture->textureHeight; - - verts++; + vertices++; } } @@ -749,7 +767,7 @@ static int PSP_QueueFillRects(SDL_Renderer *renderer, SDL_RenderCommand *cmd, co VertV *verts = (VertV *)SDL_AllocateRenderVertices(renderer, count * 2 * sizeof(VertV), 4, &cmd->data.draw.first); int i; - if (!verts) { + if (verts == NULL) { return -1; } @@ -760,12 +778,15 @@ static int PSP_QueueFillRects(SDL_Renderer *renderer, SDL_RenderCommand *cmd, co verts->z = 0.0f; verts++; - verts->x = rects->x + rects->w + 0.5f; - verts->y = rects->y + rects->h + 0.5f; + verts->x = rects->x + rects->w; + verts->y = rects->y + rects->h; verts->z = 0.0f; verts++; } + // Update the vertex count + cmd->data.draw.count = count * 2; + return 0; } @@ -773,460 +794,300 @@ static int PSP_QueueCopy(SDL_Renderer *renderer, SDL_RenderCommand *cmd, SDL_Tex 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; - } + uint8_t verticesCount; - cmd->data.draw.count = 1; + SliceSize sliceSize, sliceDimension; - verts->u = u0; - verts->v = v0; - verts->x = x; - verts->y = y; - verts->z = 0; - verts++; + // In this function texture must be created + if (!texture) + return -1; - 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; - } + if (calculateBestSliceSizeForSprite(renderer, dstrect, &sliceSize, &sliceDimension)) + return -1; - cmd->data.draw.count = count; + verticesCount = sliceDimension.width * sliceDimension.height * 2; + verts = (VertTV *)SDL_AllocateRenderVertices(renderer, verticesCount * sizeof(VertTV), 4, &cmd->data.draw.first); + if (verts == NULL) + return -1; - verts = (VertTV *)SDL_AllocateRenderVertices(renderer, count * 2 * sizeof(VertTV), 4, &cmd->data.draw.first); - if (!verts) { - return -1; - } + fillSpriteVertices(verts, &sliceDimension, &sliceSize, srcrect, dstrect); + + cmd->data.draw.count = verticesCount; + + return 0; +} - 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; +static inline int PSP_RenderSetViewPort(SDL_Renderer *renderer, SDL_RenderCommand *cmd) +{ + const SDL_Rect *viewport = &cmd->data.viewport.rect; - SDL_assert(start < end); + 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); - verts->u = curU; - verts->v = v0; - verts->x = curX; - verts->y = y; - verts->z = 0; - verts++; + return 0; +} - curU += sourceWidth; - curX += polyWidth; +static inline int PSP_RenderSetClipRect(SDL_Renderer *renderer, SDL_RenderCommand *cmd) +{ + const SDL_Rect *rect = &cmd->data.cliprect.rect; - verts->u = curU; - verts->v = v1; - verts->x = curX; - verts->y = (y + height); - verts->z = 0; - verts++; - } + if (cmd->data.cliprect.enabled) { + sceGuEnable(GU_SCISSOR_TEST); + sceGuScissor(rect->x, rect->y, rect->w, rect->h); + } else { + sceGuDisable(GU_SCISSOR_TEST); } 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) +static inline int PSP_RenderSetDrawColor(SDL_Renderer *renderer, SDL_RenderCommand *cmd) { - 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; + uint8_t colorR, colorG, colorB, colorA; - if (!verts) { - return -1; - } + colorR = cmd->data.color.r; + colorG = cmd->data.color.g; + colorB = cmd->data.color.b; + colorA = cmd->data.color.a; + sceGuColor(GU_RGBA(colorR, colorG, colorB, colorA)); - cmd->data.draw.count = 1; + return 0; +} - MathSincos(degToRad(360 - angle), &s, &c); +static inline int PSP_RenderClear(SDL_Renderer *renderer, SDL_RenderCommand *cmd) +{ + uint8_t colorR, colorG, colorB, colorA; - cw1 = c * -centerx; - sw1 = s * -centerx; - ch1 = c * -centery; - sh1 = s * -centery; - cw2 = c * width; - sw2 = s * width; - ch2 = c * height; - sh2 = s * height; + colorR = cmd->data.color.r; + colorG = cmd->data.color.g; + colorB = cmd->data.color.b; + colorA = cmd->data.color.a; - if (flip & SDL_FLIP_VERTICAL) { - Swap(&v0, &v1); - } + sceGuClearColor(GU_RGBA(colorR, colorG, colorB, colorA)); + sceGuClear(GU_FAST_CLEAR_BIT | GU_COLOR_BUFFER_BIT); - if (flip & SDL_FLIP_HORIZONTAL) { - Swap(&u0, &u1); - } + return 0; +} - 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; +static inline int PSP_RenderGeometry(SDL_Renderer *renderer, void *vertices, SDL_RenderCommand *cmd) +{ + PSP_RenderData *data = (PSP_RenderData *)renderer->driverdata; + SDL_Texture *texture = cmd->data.draw.texture; + const size_t count = cmd->data.draw.count; + PSP_BlendInfo blendInfo = { + .mode = cmd->data.draw.blend, + .shade = GU_SMOOTH + }; + + PSP_SetBlendMode(data, blendInfo); + + if (texture) { + uint32_t tbw; + void *twp; + const VertTCV *verts = (VertTCV *)(vertices + cmd->data.draw.first); + + PSP_Texture *psp_tex = (PSP_Texture *)texture->driverdata; + + prepareTextureForUpload(texture); + + tbw = psp_tex->swizzled ? psp_tex->swizzledWidth : psp_tex->width; + twp = psp_tex->swizzled ? psp_tex->swizzledData : psp_tex->data; + + sceKernelWaitSema(psp_tex->semaphore, 1, NULL); + sceGuTexMode(psp_tex->format, 0, 0, psp_tex->swizzled); + sceGuTexImage(0, psp_tex->textureWidth, psp_tex->textureHeight, tbw, twp); + sceGuTexFilter(psp_tex->filter, psp_tex->filter); + sceGuEnable(GU_TEXTURE_2D); + sceGuDrawArray(GU_TRIANGLES, GU_TEXTURE_32BITF | GU_COLOR_8888 | GU_VERTEX_32BITF | GU_TRANSFORM_2D, count, 0, verts); + sceGuDisable(GU_TEXTURE_2D); + sceGuSignalSemaphore(psp_tex->semaphore); + } else { + const VertCV *verts = (VertCV *)(vertices + cmd->data.draw.first); + sceGuDrawArray(GU_TRIANGLES, GU_COLOR_8888 | GU_VERTEX_32BITF | GU_TRANSFORM_2D, count, 0, verts); } return 0; } -static void ResetBlendState(PSP_BlendState *state) +static inline int PSP_RenderLines(SDL_Renderer *renderer, void *vertices, SDL_RenderCommand *cmd) { - sceGuColor(0xffffffff); - state->color = 0xffffffff; - state->mode = SDL_BLENDMODE_INVALID; - state->texture = NULL; - sceGuDisable(GU_TEXTURE_2D); - sceGuShadeModel(GU_SMOOTH); - state->shadeModel = GU_SMOOTH; + PSP_RenderData *data = (PSP_RenderData *)renderer->driverdata; + const size_t count = cmd->data.draw.count; + const VertV *verts = (VertV *)(vertices + cmd->data.draw.first); + const PSP_BlendInfo blendInfo = { + .mode = cmd->data.draw.blend, + .shade = GU_FLAT + }; + + PSP_SetBlendMode(data, blendInfo); + sceGuDrawArray(GU_LINES, GU_VERTEX_32BITF | GU_TRANSFORM_2D, count, 0, verts); + + return 0; } -static void StartDrawing(SDL_Renderer *renderer) +static inline int PSP_RenderFillRects(SDL_Renderer *renderer, void *vertices, SDL_RenderCommand *cmd) { PSP_RenderData *data = (PSP_RenderData *)renderer->driverdata; + const size_t count = cmd->data.draw.count; + const VertV *verts = (VertV *)(vertices + cmd->data.draw.first); + const PSP_BlendInfo blendInfo = { + .mode = cmd->data.draw.blend, + .shade = GU_FLAT + }; - // Check if we need to start GU displaylist - if (!data->displayListAvail) { - sceGuStart(GU_DIRECT, DisplayList); - data->displayListAvail = SDL_TRUE; - // ResetBlendState(&data->blendState); - } + PSP_SetBlendMode(data, blendInfo); + sceGuDrawArray(GU_SPRITES, GU_VERTEX_32BITF | GU_TRANSFORM_2D, count, 0, verts); - // 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; - } + return 0; } -static void PSP_SetBlendState(PSP_RenderData *data, PSP_BlendState *state) +static inline int PSP_RenderPoints(SDL_Renderer *renderer, void *vertices, SDL_RenderCommand *cmd) { - PSP_BlendState *current = &data->blendState; + PSP_RenderData *data = (PSP_RenderData *)renderer->driverdata; + const size_t count = cmd->data.draw.count; + const VertV *verts = (VertV *)(vertices + cmd->data.draw.first); + const PSP_BlendInfo blendInfo = { + .mode = cmd->data.draw.blend, + .shade = GU_FLAT + }; - 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); - /* FIXME SDL_BLENDMODE_MUL is simplified, and dstA is in fact un-changed.*/ - sceGuBlendFunc(GU_ADD, GU_DST_COLOR, GU_ONE_MINUS_SRC_ALPHA, 0, 0); - sceGuEnable(GU_BLEND); - break; - case SDL_BLENDMODE_INVALID: - break; - } - } + PSP_SetBlendMode(data, blendInfo); + sceGuDrawArray(GU_POINTS, GU_VERTEX_32BITF | GU_TRANSFORM_2D, count, 0, verts); - if (state->color != current->color) { - sceGuColor(state->color); - } + return 0; +} - if (state->shadeModel != current->shadeModel) { - sceGuShadeModel(state->shadeModel); - } +static inline int PSP_RenderCopy(SDL_Renderer *renderer, void *vertices, SDL_RenderCommand *cmd) +{ + uint32_t tbw; + void *twp; + PSP_RenderData *data = (PSP_RenderData *)renderer->driverdata; + SDL_Texture *texture = cmd->data.draw.texture; + PSP_Texture *psp_tex = (PSP_Texture *)texture->driverdata; + const size_t count = cmd->data.draw.count; + const VertTV *verts = (VertTV *)(vertices + cmd->data.draw.first); + const PSP_BlendInfo blendInfo = { + .mode = cmd->data.draw.blend, + .shade = GU_FLAT + }; + + PSP_SetBlendMode(data, blendInfo); + + prepareTextureForUpload(texture); + // We can't use sceKernelWaitSema here because several consecutive SDL_RenderCopy calls + // could be performed by the user. + sceKernelPollSema(psp_tex->semaphore, 1); + + tbw = psp_tex->swizzled ? psp_tex->textureWidth : psp_tex->width; + twp = psp_tex->swizzled ? psp_tex->swizzledData : psp_tex->data; + + sceGuTexMode(psp_tex->format, 0, 0, psp_tex->swizzled); + sceGuTexImage(0, psp_tex->textureWidth, psp_tex->textureHeight, tbw, twp); + sceGuTexFilter(psp_tex->filter, psp_tex->filter); + sceGuEnable(GU_TEXTURE_2D); + sceGuDrawArray(GU_SPRITES, GU_TEXTURE_32BITF | GU_VERTEX_32BITF | GU_TRANSFORM_2D, count, 0, verts); + sceGuDisable(GU_TEXTURE_2D); + sceGuSignalSemaphore(psp_tex->semaphore); - if (state->texture != current->texture) { - if (state->texture) { - TextureActivate(state->texture); - sceGuEnable(GU_TEXTURE_2D); - } else { - sceGuDisable(GU_TEXTURE_2D); - } - } + return 0; +} + +static inline void PSP_SendQueueToGPU(SDL_Renderer *renderer) { + PSP_RenderData *data = (PSP_RenderData *)renderer->driverdata; + + int g_packet_size = sceGuFinish(); + void *pkt = data->guList[data->list_idx]; + SDL_assert(g_packet_size < GPU_LIST_SIZE); + sceKernelDcacheWritebackRange(pkt, g_packet_size); + + sceGuSync(GU_SYNC_SEND, GU_SYNC_WHAT_DONE); - *current = *state; + // Send the packet to the GPU + sceGuSendList(GU_TAIL, data->guList[data->list_idx], NULL); + + // Starting a new list + data->list_idx = (data->list_idx + 1) % 3; + + sceGuStart(GU_SEND, data->guList[data->list_idx]); } 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) { + Uint8 *gpumem = (Uint8 *)sceGuGetMemory(vertsize); + if (gpumem == NULL) { 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); - /* FIXME: We need to update the clip rect too, see https://github.com/libsdl-org/SDL/issues/9094 */ + PSP_RenderSetViewPort(renderer, cmd); 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); - } + PSP_RenderSetClipRect(renderer, cmd); + break; + } + case SDL_RENDERCMD_SETDRAWCOLOR: + { + PSP_RenderSetDrawColor(renderer, cmd); 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); + PSP_RenderClear(renderer, cmd); 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); + PSP_RenderPoints(renderer, gpumem, cmd); 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); + PSP_RenderLines(renderer, gpumem, cmd); 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); + PSP_RenderFillRects(renderer, gpumem, cmd); 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); + PSP_RenderCopy(renderer, gpumem, cmd); 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); + case SDL_RENDERCMD_COPY_EX: /* unused */ break; - } - case SDL_RENDERCMD_GEOMETRY: { - const size_t count = cmd->data.draw.count; - if (!cmd->data.draw.texture) { - 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); - } + PSP_RenderGeometry(renderer, gpumem, cmd); break; } - case SDL_RENDERCMD_NO_OP: break; } - cmd = cmd->next; } + PSP_SendQueueToGPU(renderer); + return 0; } static int PSP_RenderReadPixels(SDL_Renderer *renderer, const SDL_Rect *rect, - Uint32 pixel_format, void *pixels, int pitch) + Uint32 format, void *pixels, int pitch) { return SDL_Unsupported(); } @@ -1234,15 +1095,9 @@ static int PSP_RenderReadPixels(SDL_Renderer *renderer, const SDL_Rect *rect, static int PSP_RenderPresent(SDL_Renderer *renderer) { PSP_RenderData *data = (PSP_RenderData *)renderer->driverdata; - if (!data->displayListAvail) { - return -1; - } - data->displayListAvail = SDL_FALSE; - sceGuFinish(); - sceGuSync(0, 0); - - if ((data->vsync) && (data->vblank_not_reached)) { + if (((data->vsync == 2) && (data->vblank_not_reached)) || // Dynamic + (data->vsync == 1)) { // Normal VSync sceDisplayWaitVblankStart(); } data->vblank_not_reached = SDL_TRUE; @@ -1250,25 +1105,34 @@ static int PSP_RenderPresent(SDL_Renderer *renderer) data->backbuffer = data->frontbuffer; data->frontbuffer = vabsptr(sceGuSwapBuffers()); + sceGuDrawBufferList(data->drawBufferFormat, vrelptr(data->backbuffer), PSP_FRAME_BUFFER_WIDTH); + return 0; } 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; + PSP_Texture *psp_tex = (PSP_Texture *)texture->driverdata; + PSP_RenderData *data = (PSP_RenderData *)renderer->driverdata; - if (!renderdata) { + if (!data) { return; } - if (!psp_texture) { + if (!psp_tex) { return; } - LRUTargetRemove(renderdata, psp_texture); - TextureStorageFree(psp_texture->data); - SDL_free(psp_texture); + destroySemaphore(psp_tex->semaphore); + + if (psp_tex->swizzledData) { + vfree(psp_tex->swizzledData); + } else if (texture->access != SDL_TEXTUREACCESS_STATIC) { + vfree(psp_tex->data); + } else { + SDL_free(psp_tex->data); + } + SDL_free(psp_tex); texture->driverdata = NULL; } @@ -1276,36 +1140,32 @@ static void PSP_DestroyRenderer(SDL_Renderer *renderer) { PSP_RenderData *data = (PSP_RenderData *)renderer->driverdata; if (data) { - if (!data->initialized) { - return; - } - 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); } } static int PSP_SetVSync(SDL_Renderer *renderer, const int vsync) { - PSP_RenderData *data = renderer->driverdata; - data->vsync = vsync; + PSP_RenderData *data = (PSP_RenderData *)renderer->driverdata; + SDL_bool dynamicVsync = SDL_GetHintBoolean(SDL_HINT_PSP_DYNAMIC_VSYNC, SDL_FALSE); + data->vsync = vsync ? (dynamicVsync ? 2 : 1) : 0; return 0; } -int PSP_CreateRenderer(SDL_Renderer *renderer, SDL_Window *window, Uint32 flags) +static int PSP_CreateRenderer(SDL_Renderer *renderer, SDL_Window *window, Uint32 flags) { PSP_RenderData *data; - int pixelformat; - void *doublebuffer = NULL; + uint32_t bufferSize = 0; + SDL_bool dynamicVsync; data = (PSP_RenderData *)SDL_calloc(1, sizeof(*data)); if (!data) { @@ -1313,6 +1173,59 @@ int PSP_CreateRenderer(SDL_Renderer *renderer, SDL_Window *window, Uint32 flags) return SDL_OutOfMemory(); } + // flush cache so that no stray data remains + sceKernelDcacheWritebackAll(); + + data->drawBufferFormat = pixelFormatToPSPFMT(SDL_GetWindowPixelFormat(window)); + ; + data->currentDrawBufferFormat = data->drawBufferFormat; + + /* Specific GU init */ + bufferSize = getMemorySize(PSP_FRAME_BUFFER_WIDTH, PSP_SCREEN_HEIGHT, data->drawBufferFormat); + data->frontbuffer = vramalloc(bufferSize); + data->backbuffer = vramalloc(bufferSize); + + sceGuInit(); + sceGuStart(GU_DIRECT, data->guList[0]); + sceGuDrawBuffer(data->drawBufferFormat, 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); + + sceGuScissor(0, 0, PSP_SCREEN_WIDTH, PSP_SCREEN_HEIGHT); + sceGuEnable(GU_SCISSOR_TEST); + + sceGuFinish(); + sceGuSync(GU_SYNC_FINISH, GU_SYNC_WHAT_DONE); + + sceDisplayWaitVblankStart(); + sceGuDisplay(GU_TRUE); + + // Starting the first frame + data->list_idx = 0; + + sceGuStart(GU_SEND, data->guList[data->list_idx]); + sceGuDrawBufferList(data->drawBufferFormat, vrelptr(data->backbuffer), PSP_FRAME_BUFFER_WIDTH); + + // Clear the screen + sceGuClearColor(0); + sceGuClear(GU_COLOR_BUFFER_BIT); + + /* Improve performance when VSYC is enabled and it is not reaching the 60 FPS */ + dynamicVsync = SDL_GetHintBoolean(SDL_HINT_PSP_DYNAMIC_VSYNC, SDL_FALSE); + data->vsync = flags & SDL_RENDERER_PRESENTVSYNC ? (dynamicVsync ? 2 : 1) : 0; + if (data->vsync == 2) { + sceKernelRegisterSubIntrHandler(PSP_VBLANK_INT, 0, psp_on_vblank, data); + sceKernelEnableSubIntr(PSP_VBLANK_INT, 0); + } + data->vblank_not_reached = SDL_TRUE; + + // Set the callback for the texture semaphores + sceGuSetCallback(GU_CALLBACK_SIGNAL, psp_on_signal); + renderer->WindowEvent = PSP_WindowEvent; renderer->CreateTexture = PSP_CreateTexture; renderer->UpdateTexture = PSP_UpdateTexture; @@ -1321,13 +1234,12 @@ int PSP_CreateRenderer(SDL_Renderer *renderer, SDL_Window *window, Uint32 flags) renderer->SetTextureScaleMode = PSP_SetTextureScaleMode; renderer->SetRenderTarget = PSP_SetRenderTarget; renderer->QueueSetViewport = PSP_QueueSetViewport; - renderer->QueueSetDrawColor = PSP_QueueSetViewport; /* SetViewport and SetDrawColor are (currently) no-ops. */ + renderer->QueueSetDrawColor = PSP_QueueSetViewport; renderer->QueueDrawPoints = PSP_QueueDrawPoints; - renderer->QueueDrawLines = PSP_QueueDrawPoints; /* lines and points queue vertices the same way. */ + renderer->QueueDrawLines = PSP_QueueDrawPoints; 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; @@ -1335,80 +1247,16 @@ int PSP_CreateRenderer(SDL_Renderer *renderer, SDL_Window *window, Uint32 flags) 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; - 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 = vramalloc(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 0; } SDL_RenderDriver PSP_RenderDriver = { .CreateRenderer = PSP_CreateRenderer, .info = { - .name = "PSP", + .name = "PSP_GU", .flags = SDL_RENDERER_ACCELERATED | SDL_RENDERER_PRESENTVSYNC | SDL_RENDERER_TARGETTEXTURE, .num_texture_formats = 4, .texture_formats = {