From 94e68b118d51c3ed31fd18c75e3f1408caa3971a Mon Sep 17 00:00:00 2001 From: twinaphex Date: Thu, 22 Nov 2012 23:25:43 +0100 Subject: [PATCH] (PSP2) Add preliminary video driver --- psp2/psp2_video.c | 538 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 538 insertions(+) create mode 100644 psp2/psp2_video.c diff --git a/psp2/psp2_video.c b/psp2/psp2_video.c new file mode 100644 index 0000000000..e28cb502da --- /dev/null +++ b/psp2/psp2_video.c @@ -0,0 +1,538 @@ +/* RetroArch - A frontend for libretro. + * Copyright (C) 2010-2012 - Hans-Kristian Arntzen + * + * RetroArch is free software: you can redistribute it and/or modify it under the terms + * of the GNU General Public License as published by the Free Software Found- + * ation, either version 3 of the License, or (at your option) any later version. + * + * RetroArch is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; + * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR + * PURPOSE. See the GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along with RetroArch. + * If not, see . + */ + +/* DONE: + * - Context creation (mostly) + * TODO: + * - Shader code + * - Texture reinitialization (16bpp support, etc) + * - Viewports + * - Implement video frame logic inbetween Begin/End + * - Actually run and test this to make sure it does work + */ + +#include "../general.h" +#include "../driver.h" + +#define MALLOC_PARAMS_FRAGMENT_FLAG (1 << 0) +#define MALLOC_PARAMS_VERTEX_FLAG (1 << 1) + +#define PSP2_WIDTH 960 +#define PSP2_HEIGHT 544 +#define PSP2_PITCH_PIXELS 1024 + +#define TEX_FMT_32BPP SCE_GXM_COLOR_FORMAT_A8R8G8B8 +#define PIX_FMT_32BPP SCE_DISPLAY_PIXELFORMAT_A8R8G8B8 + +#define GXM_ALIGN(x, a) (((x) + ((a) - 1)) & ~((a) - 1)) + +#define DISPLAY_BUFFER_COUNT 3 +#define DISPLAY_BUFFER_SIZE (GXM_ALIGN(PSP2_PITCH_PIXELS * PSP2_HEIGHT * 4, 1024 * 1024)) +#define DISPLAY_MAX_PENDING_SWAPS 2 + +typedef struct psp2_video +{ + SceGxmContext *gxm_ctx; + void *context_host_mem; + void *disp_buf_data[DISPLAY_BUFFER_COUNT]; + SceUID disp_buf_uid[DISPLAY_BUFFER_COUNT]; + SceGxmColorSurface disp_surface[DISPLAY_BUFFER_COUNT]; + SceGxmSyncObject *disp_buf_sync[DISPLAY_BUFFER_COUNT]; + SceGxmShaderPatcher *shader_patcher; + SceGxmRenderTarget *rt; + SceUID vid_rb_uid; + SceUID vtx_rb_uid; + SceUID fp_rb_uid; + SceUID patcher_buf_id; + SceUID patcher_vertex_usse_uid; + SceUID patcher_fragment_usse_uid; + SceUID shader_patcher; + SceUID fp_usse_rb_uid; + SceUID patcher_buf_uid; + unsigned disp_back_buf_index; + unsigned disp_front_buf_index; +} psp2_video_t; + +typedef struct +{ + uint32_t *address; +} DisplayData; + +static void *malloc_gpu(SceKernelMemBlockType type, uint32_t size, + uint32_t attribs, SceUID *uid, uint32_t params, uint32_t *offset) +{ + int ret = SCE_OK; + + if (type == SCE_KERNEL_MEMBLOCK_TYPE_USER_CDRAM_RWDATA) + size = GXM_ALIGN(size, 262144); + else + size = GXM_ALIGN(size, 4096); + + if(((params & MALLOC_PARAMS_FRAGMENT_FLAG) == MALLOC_PARAMS_FRAGMENT_FLAG) || + ((params & MALLOC_PARAMS_VERTEX_FLAG) == MALLOC_PARAMS_VERTEX_FLAG)) + type = SCE_KERNEL_MEMBLOCK_TYPE_USER_RWDATA_UNCACHE; + + *uid = sceKernelAllocMemBlock("basic", type, size, NULL); + + if (uid != SCE_OK) + goto error; + + void *mem = NULL; + + ret = sceKernelGetMemBlockBase(*uid, &mem); + + if (ret != SCE_OK) + goto error; + + if((params & MALLOC_PARAMS_FRAGMENT_FLAG) == MALLOC_PARAMS_FRAGMENT_FLAG) + ret = sceGxmMapFragmentUsseMemory(mem, size, offset); + else if((params & MALLOC_PARAMS_VERTEX_FLAG) == MALLOC_PARAMS_VERTEX_FLAG) + ret = sceGxmMapVertexUsseMemory(mem, size, offset); + else + ret = sceGxmMapMemory(mem, size, attribs); + + if (ret != SCE_OK) + goto error; + + return mem; +error: + RARCH_ERR("Error during GPU memory allocation.\n"); + return NULL; +} + +static void free_gpu(SceUID uid, uint32_t params) +{ + int ret = SCE_OK; + + void *mem = NULL; + ret = sceKernelGetMemBlockBase(uid, &mem); + + if (ret != SCE_OK) + goto error; + + if((params & MALLOC_PARAMS_FRAGMENT_FLAG) == MALLOC_PARAMS_FRAGMENT_FLAG) + ret = sceGxmUnmapFragmentUsseMemory(mem); + else if((params & MALLOC_PARAMS_VERTEX_FLAG) == MALLOC_PARAMS_VERTEX_FLAG) + ret = sceGxmUnmapVertexUsseMemory(mem); + else + ret = sceGxmUnmapMemory(mem); + + if (ret != SCE_OK) + goto error; + + ret = sceKernelFreeMemBlock(uid); + + if (ret != SCE_OK) + goto error; + +error: + RARCH_ERR("Error during GPU memory deallocation.\n"); +} + +static void psp2_gfx_init_fbo(void *data, const video_info_t *video) +{ + psp2_video_t *vid = (psp2_video_t*)driver.video_data; + + SceGxmRenderTargetParams rtparams; + memset(&rtparams, 0, sizeof(SceGxmRenderTargetParams)); + + rtparams.flags = 0; + rtparams.width = PSP2_WIDTH; + rtparams.height = PSP2_HEIGHT; + rtparams.scenesPerFrame = 1; + rtparams.multisampleMode = SCE_GXM_MULTISAMPLE_NONE; + rtparams.multisampleLocations = 0; + rtparams.hostMem = NULL; + rtparams.hostMemSize = 0; + rtparams.driverMemBlock = -1; + + // compute size + uint32_t host_mem_size, driver_mem_size; + sceGxmGetRenderTargetMemSizes(&rtparams, &host_mem_size, &driver_mem_size); + + rtparams.hostMem = malloc(host_mem_size); + rtparams.hostMemSize = host_mem_size; + rtparams.driverMemBlock = sceKernelAllocMemBlock( + "SampleRT", + SCE_KERNEL_MEMBLOCK_TYPE_USER_RWDATA_UNCACHE, + driver_mem_sie, NULL); + + int ret = sceGxmCreateRenderTarget(&rtparams, &vid->rt); + + return ret; +} + +static void disp_callback(const void *callback_data) +{ + int ret = SCE_OK; + + SceDisplyFrameBuf framebuf; + + const DisplayData *display_data = (const DisplayData*)callback_data; + + memset(&framebuf, 0, sizeof(SceDisplayFrameBuf)); + + framebuf.size = sizeof(SceDisplayFrameBuf); + framebuf.base = display_data->address; + framebuf.pitch = PSP2_PITCH_PIXELS; + framebuf.pixelformat = SCE_DISPLAY_PIXELFORMAT_A8B8G8R8; + framebuf.width = PSP2_WIDTH; + framebuf.height = PSP2_HEIGHT; + + /* TODO - Don't bother with error checking for now in here */ + + ret = sceDisplaySetFrameBuf(&framebuf, SCE_DISPLAY_UPDATETIMING_NEXTVSYNC); + + // Block until swap has occurred and the old buffer is no longer displayed + ret = sceDisplayWaitSetFrameBuf(); +} + +static void *patcher_host_alloc(void *user_data, uint32_t size) +{ + (void)user_data; + return malloc(size); +} + +static void patcher_host_free(void *user_data, void *mem) +{ + (void)user_data; + free(mem); +} + +static int psp2_gfx_init_shader_patcher(const video_info_t *video) +{ + ps2p_video_t *vid = (psp2_video_t*)driver.video_data; + + SceGxmShaderPatcherParams patcher_params; + uint32_t patcherVertexUsseOffset, patcherFragmentUsseOffset; + + memset(&patcher_params, 0, sizeof(SceGxmShaderPatcherParams)); + patcher_params.userData = NULL; + patcher_params.hostAllocCallback = &patcher_host_alloc; + patcher_params.hostFreeCallback = &patcher_host_free; + patcher_params.bufferAllocCallback = NULL; + patcher_params.bufferreeCallback = NULL; + patcher_params.bufferMem = malloc_gpu( + SCE_KERNEL_MEMBLOCK_TYPE_USER_RWDATA_UNCACHE, + 64 * 1024, + SCE_GXM_MEMORY_ATTRIB_READ | SCE_GXM_MEMORY_ATTRIB_WRITE, + &vid->patcher_buf_uid, 0, NULL); + patcher_params.bufferMemSize = 64 * 1024; + patcher_params.vertexUsseAllocCallback = NULL; + patcher_params.vertexUsseFreeCallback = NULL; + patcher_params.vertexUsseMem = malloc_gpu( + 0, + 64 * 1024, + 0, + &vid->patcher_vertex_usse_uid, + MALLOC_PARAMS_VERTEX_FLAG, + &patcherVertexUsseOffset); + patcher_params.vertexUsseMemSize = 64 * 1024; + patcher_params.vertexUsseOffset = patcherVertexUsseOffset; + patcher_params.fragmentUsseAllocCallback = NULL; + patcher_params.fragmentUsseFreeCallback = NULL; + patcher_params.fragmentUsseMem = malloc_gpu( + 0, + 64 * 1024, + 0, + &vid->patcher_fragment_usse_uid, + MALLOC_PARAMS_FRAGMENT_FLAG, + &patcherFragmentUsseOffset); + patcher_params.fragmentUsseMemSize = 64 * 1024; + patcher_params.fragmentUsseOffset = patcherFragmentUsseOffset; + + int ret = sceGxmShaderPatcherCreate(&patcher_params, &vid->shader_patcher); + + return ret; +} + +static void psp2_gfx_init_sync_objects(const video_info_t *video) +{ + psp2_video_t *vid = (psp2_video_t*)driver.video_data; + + for (unsigned i = 0; i < DISPLAY_BUFFER_COUNT; ++i) + { + vid->disp_buf_data[i] = malloc_gpu( + SCE_KERNEL_MEMBLOCK_TYPE_USER_RWDATA_UNCACHE, + DISPLAY_BUFFER_SIZE, + SCE_GXM_MEMORY_ATTRIB_READ | SCE_GXM_MEMORY_ATTRIB_WRITE, + &vid->disp_buf_uid[i], 0, NULL); + + int ret = sceGxmColorSurfaceInit( + &vid->disp_surface[i], + SCE_GXM_COLOR_FORMAT_A8B8G8R8, //TODO - Add toggle between 16bpp and 32bpp here + SCE_GXM_COLOR_SURFACE_LINEAR, + SCE_GXM_COLOR_SURFACE_SCALE_NONE, + SCE_GXM_OUTPUT_REGISTER_SIZE_32BIT, + PSP2_WIDTH, + PS2P_HEIGHT, + PSP2_PITCH_PIXELS, + vid->disp_buf_data[i]); + + if(ret != SCE_OK) + { + RARCH_ERR("Initialization of color surface %d failed.\n", i); + } + else + { + ret = sceGxmSyncObjectCreate(&vid->disp_buffer_sync[i]); + + if(ret != SCE_OK) + RARCH_ERR("Initialization of sync object %d failed.\n"); + } + } +} + +static void *psp2_gfx_init(const video_info_t *video, + const input_driver_t **input, void **input_data) +{ + *input = NULL; + *input_data = NULL; + (void)video; + + if (driver.video_data) + { + psp2_video_t *vid = (psp2_video_t*)driver.video_data; + + /* TODO - Reinitialize textures here */ + + return driver.video_data; + } + + psp2_video_t *vid = (psp2_video_t*)calloc(1, sizeof(psp2_video_t)); + + if (!vid) + goto error; + + driver.video_data = vid; + + int ret; + SceGxmInitializeParams params; + memset(¶ms, 0, sizeof(SceGxmInitializeParams)); + + params.flags = 0; + params.displayQueueMaxPendingCount = DISPLAY_MAX_PENDING_SWAPS; + params.displayQueueCallback = disp_callback; + params.displayQueueCallbackDataSize = sizeof(DisplayData); + params.parameterBufferSize = SCE_GXM_DEFAULT_PARAMETER_BUFFER_SIZE; + + ret = sceGxmInitialize(¶ms); + + if(ret != SCE_OK) + goto error; + + SceGxmContextParams ctx_params; + memset(&ctx_params, 0, sizeof(SceGxmContextParams)); + + uint32_t fp_usse_ring_buffer_offset; + + vid->context_host_mem = malloc(SCE_GXM_MINIMUM_CONTEXT_HOST_MEM_SIZE); + + ctx_params.hostMem = vid->context_host_mem; + ctx_params.hostMemSize = SCE_GXM_MINIMUM_CONTEXT_HOST_MEM_SIZE; + ctx_params.vdmRingBufferMem = malloc_gpu( + SCE_KERNEL_MEMBLOCK_TYPE_USER_RWDATA_UNCACHE, + SCE_GXM_DEFAULT_VDM_RING_BUFFER_SIZE, + SCE_GXM_MEMORY_ATTRIB_READ, + &vid->rb_uid, 0, NULL); + ctx_params.vdmRingBufferMemSize = SCE_GXM_DEFAULT_VDM_RING_BUFFER_SIZE; + ctx_params.vertexRingBufferMem = malloc_gpu( + SCE_KERNEL_MEMBLOCK_TYPE_USER_RWDATA_UNCACHE, + SCE_GXM_DEFAULT_VERTEX_RING_BUFFER_SIZE, + SCE_GXM_MEMORY_ATTRIB_READ, + &vid->vtx_rb_uid, 0, NULL); + ctx_params.vertexRingBufferMemSize = SCE_GXM_DEFAULT_VERTEX_RING_BUFFER_SIZE; + ctx_params.fragmentRingBufferMem = malloc_gpu( + SCE_KERNEL_MEMBLOCK_TYPE_USER_RWDATA_UNCACHE, + SCE_GXM_DEFAULT_FRAGMENT_RING_BUFFER_SIZE, + SCE_GXM_MEMORY_ATTRIB_READ, + &vid->fp_rb_uid, 0, NULL); + ctx_params.fragmentRingBufferMemSize = SCE_GXM_DEFAULT_FRAGMENT_RING_BUFFER_SIZE; + ctx_params.fragmentUsseRingBufferMem = malloc_gpu( + 0, + SCE_GXM_DEFAULT_FRAGMENT_USSE_RING_BUFFER_SIZE, + 0, + &vid->fp_usse_rb_uid, + MALLOC_PARAMS_FRAGMENT_FLAG, + &fp_usse_ring_buffer_offset); + ctx_params.fragmentUsseRingBufferMemSize = SCE_GXM_DEFAULT_FRAGMENT_USSE_RING_BUFFER_SIZE; + ctx_params.fragmentUsseRingBufferOffset = vid->fp_rb_uid; + + vid->gxm_ctx = NULL; + + ret = sceGxmCreateContext(&ctx_params, &vid->gxm_ctx); + + if (ret != SCE_OK) + goto error; + + if((psp2_gfx_init_fbo()) != SCE_OK) + goto error; + else + RARCH_LOG("FBO initialized successfully.\n"); + + if((psp2_gfx_init_shader_patcher()) != SCE_OK) + goto error; + else + RARCH_LOG("Shader patcher initialized successfully.\n"); + + psp2_gfx_init_sync_objects(video); + + /* Clear display buffer for first swap */ + memset(vid->disp_buf_data[vid>disp_front_buf_index], 0x00, DISPLAY_BUFFER_SIZE); + + /* Swap to the current front buffer with Vsync */ + disp_callback(NULL); + + return vid; +error: + RARCH_ERR("PSP2 libgxm video could not be initialized.\n"); + return (void*)-1; +} + +static inline void psp2_gfx_swap(void) +{ + psp2_video_t *vid = (psp2_video_t*)driver.video_data; + + DisplayData display_data; + + display_data.address = vid->disp_buf_data[vid->disp_back_buf_index]; + + /* queue swap for this frame */ + + int ret = sceGxmDisplayQueueAddEntry( + vid->disp_buffer_sync[vid->disp_front_buf_index], + vid->disp_buffer_sync[vid->disp_back_buf_index], + &display_data); + + vid->disp_front_buf_index = vid->disp_back_buf_index; + vid->disp_back_buf_index = (vid->disp_back_buf_index + 1) & DISPLAY_BUFFER_COUNT; +} + +static bool psp2_gfx_frame(void *data, const void *frame, + unsigned width, unsigned height, unsigned pitch, const char *msg) +{ + (void)data; + (void)frame; + (void)width; + (void)height; + (void)pitch; + (void)msg; + + psp2_video_t *vid = (psp2_video_t*)data; + + sceGxmBeginScene(vid->gcm_ctx, 0, vid->rt, NULL, + NULL, vid->disp_buf_sync[vid->disp_back_buf_index]); + + /* TODO - code comes inbetween */ + + sceGxmEndScene(vid->gxm_ctx, NULL, NULL); + + /* notify end of frame */ + sceGxmPadHeartBeat(&vid->disp_surface[vid->disp_back_buf_index], vid->disp_buf_sync[vid->disp_back_buf_index]); + + psp2_gfx_swap(); + + return true; +} + +static void psp2_gfx_set_nonblock_state(void *data, bool toggle) +{ + (void)data; + (void)toggle; +} + +static bool psp2_gfx_alive(void *data) +{ + (void)data; + return true; +} + +static bool psp2_gfx_focus(void *data) +{ + (void)data; + return true; +} + +static void psp2_gfx_free(void *data) +{ + (void)data; + void *hostmem; + int ret; + SceUID drivermemblock; + + /* TDO: error checking */ + + psp2_video_t *vid = (psp2_video_t*)driver.video_data; + + sceGxmFinish(vid->gxm_ctx); + + ret = sceGxmRenderTargetGetHostMem(vid->rt, &hostmem); + + ret = sceGxmRenderTargetGetDriverMemBlock(vid->rt, &drivermemblock); + + ret = sceGxmDestroyRenderTarget(vid->rt); + + sceKernelFreeMemBlock(drivermemblock); + free(hostmem); + + // wait until display queue is finished before deallocating display buffers + int ret = sceGxmDisplayQueueFinish(); + + for (int i = 0; i < DISPLAY_BUFFER_COUNT; ++i) + { + free_gpu(vid->disp_buf_uid[i], 0); + ret = sceGxmSyncObjectDestroy(vid->disp_buf_sync[i]); + } + + ret = sceGxmShaderPatcherDestroy(vid->shader_patcher); + + free_gpu(vid->patcher_fragment_usse_uid, MALLOC_PARAMS_FRAGMENT_FLAG); + free_gpu(vid->patcher_vertex_usse_uid, MALLOC_PARAMS_VERTEX_FLAG); + free_gpu(vid->patcher_buf_uid, 0); + + ret = sceGxmDestroyContext(vid->gxm_ctx); + + free_gpu(vid->fp_rb_uid, MALLOC_PARAMS_FRAGMENT_FLAG); + free_gpu(vid->vtx_rb_uid, MALLOC_PARAMS_VERTEX_FLAG); + free_gpu(vid->vid_rb_uid, 0); + + free(vid->context_host_mem, 0); + + sceGxmTerminate(); +} + +#ifdef RARCH_CONSOLE +static void psp2_gfx_start(void) {} +static void psp2_gfx_restart(void) {} +static void psp2_gfx_stop(void) {} +#endif + +const video_driver_t video_null = { + psp2_gfx_init, + psp2_gfx_frame, + psp2_gfx_set_nonblock_state, + psp2_gfx_alive, + psp2_gfx_focus, + NULL, + psp2_gfx_free, + "null", + +#ifdef RARCH_CONSOLE + psp2_gfx_start, + psp2_gfx_stop, + psp2_gfx_restart, +#endif +}; +