PSP: zero-copy WindowSurface API for direct VRAM access

if all one needs is a raw framebuffer to the PSP's vram,
instead of dealing with renderers and textures, that need to be
copied hence and forth, this method allows one to create a window,
set the pixel format using SDL_SetWindowDisplayMode() - preferably
BGR565 for optimal speed (the other possible natively supported
option is ABGR8888) - and then request SDL_GetWindowSurface(),
which provides one with a surface with direct framebuffer access.
note that the pixels pointer inside the surface will be switched
after each call because of double-buffering.

it's advisable to overwrite all pixels of the PSP visible area
(480x272) to not encounter old data.

after writing the pixels, a call to SDL_UpdateWindowSurface()
sends the changes to the graphics chip.

the result is a raw framerate of 250 fps with BGR565 mode, under
optimal circumstances - i.e. nothing else is done than drawing,
and the drawing loop is as simple as possible.
that leaves about 12 ms per frame for other tasks and still allow
a fluent 60 fps.
This commit is contained in:
rofl0r
2024-03-14 02:58:44 +00:00
committed by Sam Lantinga
parent 37c664a13d
commit 7b6695f4d4
4 changed files with 180 additions and 0 deletions

View File

@@ -36,6 +36,7 @@
#include <stdarg.h>
#include <stdlib.h>
#include <vram.h>
#include "SDL_render_psp.h"
/* PSP renderer implementation, based on the PGE */
@@ -124,6 +125,24 @@ typedef struct
float x, y, z;
} VertTCV;
int SDL_PSP_RenderGetProp(SDL_Renderer *r, enum SDL_PSP_RenderProps which, void** out)
{
PSP_RenderData *rd;
if (r == NULL) {
return -1;
}
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;
}
return -1;
}
#define PI 3.14159265358979f
#define radToDeg(x) ((x)*180.f / PI)

View File

@@ -0,0 +1,32 @@
/*
Simple DirectMedia Layer
Copyright (C) 1997-2024 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.
*/
/* this header is meant to be included after the other related internal SDL
headers. it's the interface between psp renderer and video driver code. */
enum SDL_PSP_RenderProps
{
SDL_PSP_RENDERPROPS_FRONTBUFFER,
SDL_PSP_RENDERPROPS_BACKBUFFER,
};
int SDL_PSP_RenderGetProp(SDL_Renderer *r, enum SDL_PSP_RenderProps which, void** out);

View File

@@ -29,14 +29,128 @@
#include "SDL_syswm.h"
#include "SDL_loadso.h"
#include "SDL_events.h"
#include "SDL_render.h"
#include "../../events/SDL_mouse_c.h"
#include "../../events/SDL_keyboard_c.h"
#include "../../render/SDL_sysrender.h"
/* PSP declarations */
#include "SDL_pspvideo.h"
#include "SDL_pspevents_c.h"
#include "SDL_pspgl_c.h"
#include "../../render/psp/SDL_render_psp.h"
#define SDL_WINDOWTEXTUREDATA "_SDL_WindowTextureData"
typedef struct
{
SDL_Renderer *renderer;
SDL_Texture *texture;
void *pixels;
int pitch;
int bytes_per_pixel;
} SDL_WindowTextureData;
int PSP_CreateWindowFramebuffer(SDL_VideoDevice *_this, SDL_Window *window, Uint32 *format, void **pixels, int *pitch)
{
SDL_RendererInfo info;
SDL_WindowTextureData *data = SDL_GetWindowData(window, SDL_WINDOWTEXTUREDATA);
int i, w, h;
SDL_GetWindowSizeInPixels(window, &w, &h);
if (w != 480) {
return SDL_SetError("Unexpected window size");
}
if (!data) {
SDL_Renderer *renderer = NULL;
for (i = 0; i < SDL_GetNumRenderDrivers(); ++i) {
SDL_GetRenderDriverInfo(i, &info);
if (SDL_strcmp(info.name, "software") != 0) {
renderer = SDL_CreateRenderer(window, i, 0);
if (renderer && (SDL_GetRendererInfo(renderer, &info) == 0) && (info.flags & SDL_RENDERER_ACCELERATED)) {
break; /* this will work. */
}
if (renderer) { /* wasn't accelerated, etc, skip it. */
SDL_DestroyRenderer(renderer);
renderer = NULL;
}
}
}
if (!renderer) {
return SDL_SetError("No hardware accelerated renderers available");
}
SDL_assert(renderer != NULL); /* should have explicitly checked this above. */
/* Create the data after we successfully create the renderer (bug #1116) */
data = (SDL_WindowTextureData *)SDL_calloc(1, sizeof(*data));
if (!data) {
SDL_DestroyRenderer(renderer);
return SDL_OutOfMemory();
}
SDL_SetWindowData(window, SDL_WINDOWTEXTUREDATA, data);
data->renderer = renderer;
} else {
if (SDL_GetRendererInfo(data->renderer, &info) == -1) {
return -1;
}
}
/* Find the first format without an alpha channel */
*format = info.texture_formats[0];
for (i = 0; i < (int)info.num_texture_formats; ++i) {
if (!SDL_ISPIXELFORMAT_FOURCC(info.texture_formats[i]) &&
!SDL_ISPIXELFORMAT_ALPHA(info.texture_formats[i])) {
*format = info.texture_formats[i];
break;
}
}
/* get the PSP renderer's "private" data */
SDL_PSP_RenderGetProp(data->renderer, SDL_PSP_RENDERPROPS_FRONTBUFFER, &data->pixels);
/* Create framebuffer data */
data->bytes_per_pixel = SDL_BYTESPERPIXEL(*format);
/* since we point directly to VRAM's frontbuffer, we have to use
the upscaled pitch of 512 width - since PSP requires all textures to be
powers of 2. */
data->pitch = 512 * data->bytes_per_pixel;
*pixels = data->pixels;
*pitch = data->pitch;
/* Make sure we're not double-scaling the viewport */
SDL_RenderSetViewport(data->renderer, NULL);
return 0;
}
int PSP_UpdateWindowFramebuffer(_THIS, SDL_Window *window, const SDL_Rect *rects, int numrects)
{
SDL_WindowTextureData *data;
data = SDL_GetWindowData(window, SDL_WINDOWTEXTUREDATA);
if (!data || !data->renderer || !window->surface) {
return -1;
}
data->renderer->RenderPresent(data->renderer);
SDL_PSP_RenderGetProp(data->renderer, SDL_PSP_RENDERPROPS_BACKBUFFER, &window->surface->pixels);
return 0;
}
void PSP_DestroyWindowFramebuffer(_THIS, SDL_Window *window)
{
SDL_WindowTextureData *data = SDL_GetWindowData(window, SDL_WINDOWTEXTUREDATA);
if (!data || !data->renderer) {
return;
}
SDL_DestroyRenderer(data->renderer);
data->renderer = NULL;
}
/* unused
static SDL_bool PSP_initialized = SDL_FALSE;
@@ -128,6 +242,13 @@ static SDL_VideoDevice *PSP_Create()
device->PumpEvents = PSP_PumpEvents;
/* backend to use VRAM directly as a framebuffer using
SDL_GetWindowSurface() API. */
device->checked_texture_framebuffer = 1;
device->CreateWindowFramebuffer = PSP_CreateWindowFramebuffer;
device->UpdateWindowFramebuffer = PSP_UpdateWindowFramebuffer;
device->DestroyWindowFramebuffer = PSP_DestroyWindowFramebuffer;
return device;
}

View File

@@ -68,6 +68,14 @@ void PSP_MinimizeWindow(_THIS, SDL_Window *window);
void PSP_RestoreWindow(_THIS, SDL_Window *window);
void PSP_DestroyWindow(_THIS, SDL_Window *window);
/* "methods" aka callbacks for SDL_WindowSurface API */
int PSP_CreateWindowFramebuffer(_THIS, SDL_Window *window, Uint32 *format,
void **pixels, int *pitch);
int PSP_UpdateWindowFramebuffer(_THIS, SDL_Window *window,
const SDL_Rect *rects, int numrects);
void PSP_DestroyWindowFramebuffer(_THIS, SDL_Window *window);
/* Window manager function */
SDL_bool PSP_GetWindowWMInfo(_THIS, SDL_Window * window,
struct SDL_SysWMinfo *info);