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