#include "d3drmrenderer_gxm.h" #include "meshutils.h" #include #include #include #include #include #include #include #include "utils.h" #include "memory.h" #define INCBIN_PREFIX _inc_ #include "incbin.h" bool with_razor = false; #define VITA_GXM_SCREEN_WIDTH 960 #define VITA_GXM_SCREEN_HEIGHT 544 #define VITA_GXM_SCREEN_STRIDE 960 #define VITA_GXM_PENDING_SWAPS 3 #define VITA_GXM_COLOR_FORMAT SCE_GXM_COLOR_FORMAT_A8B8G8R8 #define VITA_GXM_PIXEL_FORMAT SCE_DISPLAY_PIXELFORMAT_A8B8G8R8 INCBIN(main_vert_gxp, "shaders/main.vert.gxp"); INCBIN(main_frag_gxp, "shaders/main.frag.gxp"); INCBIN(color_frag_gxp, "shaders/color.frag.gxp"); INCBIN(image_frag_gxp, "shaders/image.frag.gxp"); const SceGxmProgram* mainVertexProgramGxp = (const SceGxmProgram*)_inc_main_vert_gxpData; const SceGxmProgram* mainFragmentProgramGxp = (const SceGxmProgram*)_inc_main_frag_gxpData; const SceGxmProgram* colorFragmentProgramGxp = (const SceGxmProgram*)_inc_color_frag_gxpData; const SceGxmProgram* imageFragmentProgramGxp = (const SceGxmProgram*)_inc_image_frag_gxpData; extern "C" int sceRazorGpuCaptureSetTrigger(int frames, const char* path); extern "C" int sceRazorGpuCaptureEnableSalvage(const char* path); extern "C" int sceRazorGpuCaptureSetTriggerNextFrame(const char* path); static GXMRendererContext gxm_renderer_context; static void display_callback(const void *callback_data) { const GXMDisplayData *display_data = (const GXMDisplayData *)callback_data; SceDisplayFrameBuf framebuf; SDL_memset(&framebuf, 0x00, sizeof(SceDisplayFrameBuf)); framebuf.size = sizeof(SceDisplayFrameBuf); framebuf.base = display_data->address; framebuf.pitch = VITA_GXM_SCREEN_STRIDE; framebuf.pixelformat = VITA_GXM_PIXEL_FORMAT; framebuf.width = VITA_GXM_SCREEN_WIDTH; framebuf.height = VITA_GXM_SCREEN_HEIGHT; sceDisplaySetFrameBuf(&framebuf, SCE_DISPLAY_SETBUF_NEXTFRAME); } static void load_razor() { int mod_id = _sceKernelLoadModule("app0:librazorcapture_es4.suprx", 0, nullptr); int status; if(!SCE_ERR(sceKernelStartModule, mod_id, 0, nullptr, 0, nullptr, &status)) { with_razor = true; } if(with_razor) { sceRazorGpuCaptureEnableSalvage("ux0:data/gpu_crash.sgx"); } } bool gxm_initialized = false; bool gxm_init() { if(gxm_initialized) { return true; } load_razor(); SceGxmInitializeParams initializeParams; SDL_memset(&initializeParams, 0, sizeof(SceGxmInitializeParams)); initializeParams.flags = 0; initializeParams.displayQueueMaxPendingCount = VITA_GXM_PENDING_SWAPS; initializeParams.displayQueueCallback = display_callback; initializeParams.displayQueueCallbackDataSize = sizeof(GXMDisplayData); initializeParams.parameterBufferSize = SCE_GXM_DEFAULT_PARAMETER_BUFFER_SIZE; int err = sceGxmInitialize(&initializeParams); if (err != 0) { SDL_LogError(SDL_LOG_CATEGORY_RENDER, "gxm init failed: %d", err); return err; } gxm_initialized = true; return cdramPool_init(); } static bool create_gxm_context() { GXMRendererContext* data = &gxm_renderer_context; if(data->context) { return true; } if(!gxm_init()) { return false; } const unsigned int patcherBufferSize = 64 * 1024; const unsigned int patcherVertexUsseSize = 64 * 1024; const unsigned int patcherFragmentUsseSize = 64 * 1024; data->cdramPool = cdramPool_get(); if(!data->cdramPool) { SDL_Log("failed to allocate cdramPool"); return false; } // allocate buffers data->vdmRingBuffer = vita_mem_alloc( SCE_KERNEL_MEMBLOCK_TYPE_USER_RW_UNCACHE, SCE_GXM_DEFAULT_VDM_RING_BUFFER_SIZE, 4, SCE_GXM_MEMORY_ATTRIB_READ, &data->vdmRingBufferUid, "vdmRingBuffer", nullptr ); data->vertexRingBuffer = vita_mem_alloc( SCE_KERNEL_MEMBLOCK_TYPE_USER_RW_UNCACHE, SCE_GXM_DEFAULT_VERTEX_RING_BUFFER_SIZE, 4, SCE_GXM_MEMORY_ATTRIB_READ, &data->vertexRingBufferUid, "vertexRingBuffer", nullptr ); data->fragmentRingBuffer = vita_mem_alloc( SCE_KERNEL_MEMBLOCK_TYPE_USER_RW_UNCACHE, SCE_GXM_DEFAULT_FRAGMENT_RING_BUFFER_SIZE, 4, SCE_GXM_MEMORY_ATTRIB_READ, &data->fragmentRingBufferUid, "fragmentRingBuffer", nullptr ); data->fragmentUsseRingBuffer = vita_mem_fragment_usse_alloc( SCE_GXM_DEFAULT_FRAGMENT_USSE_RING_BUFFER_SIZE, &data->fragmentUsseRingBufferUid, &data->fragmentUsseRingBufferOffset); // create context SceGxmContextParams contextParams; memset(&contextParams, 0, sizeof(SceGxmContextParams)); contextParams.hostMem = SDL_malloc(SCE_GXM_MINIMUM_CONTEXT_HOST_MEM_SIZE); contextParams.hostMemSize = SCE_GXM_MINIMUM_CONTEXT_HOST_MEM_SIZE; contextParams.vdmRingBufferMem = data->vdmRingBuffer; contextParams.vdmRingBufferMemSize = SCE_GXM_DEFAULT_VDM_RING_BUFFER_SIZE; contextParams.vertexRingBufferMem = data->vertexRingBuffer; contextParams.vertexRingBufferMemSize = SCE_GXM_DEFAULT_VERTEX_RING_BUFFER_SIZE; contextParams.fragmentRingBufferMem = data->fragmentRingBuffer; contextParams.fragmentRingBufferMemSize = SCE_GXM_DEFAULT_FRAGMENT_RING_BUFFER_SIZE; contextParams.fragmentUsseRingBufferMem = data->fragmentUsseRingBuffer; contextParams.fragmentUsseRingBufferMemSize = SCE_GXM_DEFAULT_FRAGMENT_USSE_RING_BUFFER_SIZE; contextParams.fragmentUsseRingBufferOffset = data->fragmentUsseRingBufferOffset; if(SCE_ERR(sceGxmCreateContext, &contextParams, &data->context)) { return false; } data->contextHostMem = contextParams.hostMem; // shader patcher data->patcherBuffer = vita_mem_alloc( SCE_KERNEL_MEMBLOCK_TYPE_USER_RW_UNCACHE, patcherBufferSize, 4, SCE_GXM_MEMORY_ATTRIB_READ | SCE_GXM_MEMORY_ATTRIB_WRITE, &data->patcherBufferUid, "patcherBuffer", nullptr ); data->patcherVertexUsse = vita_mem_vertex_usse_alloc( patcherVertexUsseSize, &data->patcherVertexUsseUid, &data->patcherVertexUsseOffset); data->patcherFragmentUsse = vita_mem_fragment_usse_alloc( patcherFragmentUsseSize, &data->patcherFragmentUsseUid, &data->patcherFragmentUsseOffset); SceGxmShaderPatcherParams patcherParams; memset(&patcherParams, 0, sizeof(SceGxmShaderPatcherParams)); patcherParams.userData = NULL; patcherParams.hostAllocCallback = &patcher_host_alloc; patcherParams.hostFreeCallback = &patcher_host_free; patcherParams.bufferAllocCallback = NULL; patcherParams.bufferFreeCallback = NULL; patcherParams.bufferMem = data->patcherBuffer; patcherParams.bufferMemSize = patcherBufferSize; patcherParams.vertexUsseAllocCallback = NULL; patcherParams.vertexUsseFreeCallback = NULL; patcherParams.vertexUsseMem = data->patcherVertexUsse; patcherParams.vertexUsseMemSize = patcherVertexUsseSize; patcherParams.vertexUsseOffset = data->patcherVertexUsseOffset; patcherParams.fragmentUsseAllocCallback = NULL; patcherParams.fragmentUsseFreeCallback = NULL; patcherParams.fragmentUsseMem = data->patcherFragmentUsse; patcherParams.fragmentUsseMemSize = patcherFragmentUsseSize; patcherParams.fragmentUsseOffset = data->patcherFragmentUsseOffset; if(SCE_ERR(sceGxmShaderPatcherCreate, &patcherParams, &data->shaderPatcher)) { return false; } return true; } static void destroy_gxm_context() { sceGxmShaderPatcherDestroy(gxm_renderer_context.shaderPatcher); sceGxmDestroyContext(gxm_renderer_context.context); vita_mem_fragment_usse_free(gxm_renderer_context.fragmentUsseRingBufferUid); vita_mem_free(gxm_renderer_context.fragmentRingBufferUid); vita_mem_free(gxm_renderer_context.vertexRingBufferUid); vita_mem_free(gxm_renderer_context.vdmRingBufferUid); SDL_free(gxm_renderer_context.contextHostMem); } bool get_gxm_context(SceGxmContext** context, SceGxmShaderPatcher** shaderPatcher, SceClibMspace* cdramPool) { if(!create_gxm_context()) { return false; } *context = gxm_renderer_context.context; *shaderPatcher = gxm_renderer_context.shaderPatcher; *cdramPool = gxm_renderer_context.cdramPool; return true; } static bool create_gxm_renderer(int width, int height, GXMRendererData* data) { const unsigned int alignedWidth = ALIGN(VITA_GXM_SCREEN_WIDTH, SCE_GXM_TILE_SIZEX); const unsigned int alignedHeight = ALIGN(VITA_GXM_SCREEN_HEIGHT, SCE_GXM_TILE_SIZEY); const unsigned int sampleCount = alignedWidth * alignedHeight; const unsigned int depthStrideInSamples = alignedWidth; if(!get_gxm_context(&data->context, &data->shaderPatcher, &data->cdramPool)) { return false; } static const SceGxmBlendInfo blendInfoOpaque = { .colorMask = SCE_GXM_COLOR_MASK_ALL, .colorFunc = SCE_GXM_BLEND_FUNC_NONE, .alphaFunc = SCE_GXM_BLEND_FUNC_NONE, .colorSrc = SCE_GXM_BLEND_FACTOR_ZERO, .colorDst = SCE_GXM_BLEND_FACTOR_ZERO, .alphaSrc = SCE_GXM_BLEND_FACTOR_ZERO, .alphaDst = SCE_GXM_BLEND_FACTOR_ZERO, }; static const SceGxmBlendInfo blendInfoTransparent = { .colorMask = SCE_GXM_COLOR_MASK_ALL, .colorFunc = SCE_GXM_BLEND_FUNC_ADD, .alphaFunc = SCE_GXM_BLEND_FUNC_ADD, .colorSrc = SCE_GXM_BLEND_FACTOR_SRC_ALPHA, .colorDst = SCE_GXM_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA, .alphaSrc = SCE_GXM_BLEND_FACTOR_ONE, .alphaDst = SCE_GXM_BLEND_FACTOR_ONE_MINUS_SRC_ALPHA, }; // render target SceGxmRenderTargetParams renderTargetParams; memset(&renderTargetParams, 0, sizeof(SceGxmRenderTargetParams)); renderTargetParams.flags = 0; renderTargetParams.width = VITA_GXM_SCREEN_WIDTH; renderTargetParams.height = VITA_GXM_SCREEN_HEIGHT; renderTargetParams.scenesPerFrame = 1; renderTargetParams.multisampleMode = 0; renderTargetParams.multisampleLocations = 0; renderTargetParams.driverMemBlock = -1; // Invalid UID if(SCE_ERR(sceGxmCreateRenderTarget, &renderTargetParams, &data->renderTarget)) { return false; } for(int i = 0; i < VITA_GXM_DISPLAY_BUFFER_COUNT; i++) { data->displayBuffers[i] = vita_mem_alloc( SCE_KERNEL_MEMBLOCK_TYPE_USER_CDRAM_RW, 4 * VITA_GXM_SCREEN_STRIDE * VITA_GXM_SCREEN_HEIGHT, SCE_GXM_COLOR_SURFACE_ALIGNMENT, SCE_GXM_MEMORY_ATTRIB_READ | SCE_GXM_MEMORY_ATTRIB_WRITE, &data->displayBuffersUid[i], "display", nullptr); if(SCE_ERR(sceGxmColorSurfaceInit, &data->displayBuffersSurface[i], SCE_GXM_COLOR_FORMAT_A8B8G8R8, SCE_GXM_COLOR_SURFACE_LINEAR, SCE_GXM_COLOR_SURFACE_SCALE_NONE, SCE_GXM_OUTPUT_REGISTER_SIZE_32BIT, VITA_GXM_SCREEN_WIDTH, VITA_GXM_SCREEN_HEIGHT, VITA_GXM_SCREEN_STRIDE, data->displayBuffers[i] )) { return false; }; if(SCE_ERR(sceGxmSyncObjectCreate, &data->displayBuffersSync[i])) { return false; } } // depth & stencil data->depthBufferData = vita_mem_alloc( SCE_KERNEL_MEMBLOCK_TYPE_USER_RW_UNCACHE, 4 * sampleCount, SCE_GXM_DEPTHSTENCIL_SURFACE_ALIGNMENT, SCE_GXM_MEMORY_ATTRIB_READ | SCE_GXM_MEMORY_ATTRIB_WRITE, &data->depthBufferUid, "depthBufferData", nullptr ); data->stencilBufferData = vita_mem_alloc( SCE_KERNEL_MEMBLOCK_TYPE_USER_RW_UNCACHE, 4 * sampleCount, SCE_GXM_DEPTHSTENCIL_SURFACE_ALIGNMENT, SCE_GXM_MEMORY_ATTRIB_READ | SCE_GXM_MEMORY_ATTRIB_WRITE, &data->stencilBufferUid, "stencilBufferData", nullptr ); if(SCE_ERR(sceGxmDepthStencilSurfaceInit, &data->depthSurface, SCE_GXM_DEPTH_STENCIL_FORMAT_S8D24, SCE_GXM_DEPTH_STENCIL_SURFACE_TILED, depthStrideInSamples, data->depthBufferData, data->stencilBufferData )) { return false; } // register shader programs if(SCE_ERR(sceGxmShaderPatcherRegisterProgram, data->shaderPatcher, colorFragmentProgramGxp, &data->colorFragmentProgramId)) { return false; } if(SCE_ERR(sceGxmShaderPatcherRegisterProgram, data->shaderPatcher, mainVertexProgramGxp, &data->mainVertexProgramId)) { return false; } if(SCE_ERR(sceGxmShaderPatcherRegisterProgram, data->shaderPatcher, mainFragmentProgramGxp, &data->mainFragmentProgramId)) { return false; } if(SCE_ERR(sceGxmShaderPatcherRegisterProgram, data->shaderPatcher, imageFragmentProgramGxp, &data->imageFragmentProgramId)) { return false; } // main shader { GET_SHADER_PARAM(positionAttribute, mainVertexProgramGxp, "aPosition", false); GET_SHADER_PARAM(normalAttribute, mainVertexProgramGxp, "aNormal", false); GET_SHADER_PARAM(texCoordAttribute, mainVertexProgramGxp, "aTexCoord", false); SceGxmVertexAttribute vertexAttributes[3]; SceGxmVertexStream vertexStreams[1]; // position vertexAttributes[0].streamIndex = 0; vertexAttributes[0].offset = 0; vertexAttributes[0].format = SCE_GXM_ATTRIBUTE_FORMAT_F32; vertexAttributes[0].componentCount = 3; vertexAttributes[0].regIndex = sceGxmProgramParameterGetResourceIndex(positionAttribute); // normal vertexAttributes[1].streamIndex = 0; vertexAttributes[1].offset = 12; vertexAttributes[1].format = SCE_GXM_ATTRIBUTE_FORMAT_F32; vertexAttributes[1].componentCount = 3; vertexAttributes[1].regIndex = sceGxmProgramParameterGetResourceIndex(normalAttribute); vertexAttributes[2].streamIndex = 0; vertexAttributes[2].offset = 24; vertexAttributes[2].format = SCE_GXM_ATTRIBUTE_FORMAT_F32; vertexAttributes[2].componentCount = 2; vertexAttributes[2].regIndex = sceGxmProgramParameterGetResourceIndex(texCoordAttribute); vertexStreams[0].stride = sizeof(Vertex); vertexStreams[0].indexSource = SCE_GXM_INDEX_SOURCE_INDEX_16BIT; if(SCE_ERR(sceGxmShaderPatcherCreateVertexProgram, data->shaderPatcher, data->mainVertexProgramId, vertexAttributes, 3, vertexStreams, 1, &data->mainVertexProgram )) return false; } // main opaque if(SCE_ERR(sceGxmShaderPatcherCreateFragmentProgram, data->shaderPatcher, data->mainFragmentProgramId, SCE_GXM_OUTPUT_REGISTER_FORMAT_UCHAR4, SCE_GXM_MULTISAMPLE_NONE, &blendInfoOpaque, mainVertexProgramGxp, &data->opaqueFragmentProgram )) return false; // main transparent if(SCE_ERR(sceGxmShaderPatcherCreateFragmentProgram, data->shaderPatcher, data->mainFragmentProgramId, SCE_GXM_OUTPUT_REGISTER_FORMAT_UCHAR4, SCE_GXM_MULTISAMPLE_NONE, &blendInfoTransparent, mainVertexProgramGxp, &data->transparentFragmentProgram )) return false; // image if(SCE_ERR(sceGxmShaderPatcherCreateFragmentProgram, data->shaderPatcher, data->imageFragmentProgramId, SCE_GXM_OUTPUT_REGISTER_FORMAT_UCHAR4, SCE_GXM_MULTISAMPLE_NONE, &blendInfoTransparent, mainVertexProgramGxp, &data->imageFragmentProgram )) return false; // color if(SCE_ERR(sceGxmShaderPatcherCreateFragmentProgram, data->shaderPatcher, data->colorFragmentProgramId, SCE_GXM_OUTPUT_REGISTER_FORMAT_UCHAR4, SCE_GXM_MULTISAMPLE_NONE, NULL, mainVertexProgramGxp, &data->colorFragmentProgram )) { return false; } // vertex uniforms data->uModelViewMatrix = sceGxmProgramFindParameterByName(mainVertexProgramGxp, "uModelViewMatrix"); data->uNormalMatrix = sceGxmProgramFindParameterByName(mainVertexProgramGxp, "uNormalMatrix"); data->uProjectionMatrix = sceGxmProgramFindParameterByName(mainVertexProgramGxp, "uProjectionMatrix"); // fragment uniforms data->uLights = sceGxmProgramFindParameterByName(mainFragmentProgramGxp, "uLights"); // SceneLight[3] data->uLightCount = sceGxmProgramFindParameterByName(mainFragmentProgramGxp, "uLightCount"); // int data->uShininess = sceGxmProgramFindParameterByName(mainFragmentProgramGxp, "uShininess"); // float data->uColor = sceGxmProgramFindParameterByName(mainFragmentProgramGxp, "uColor"); // vec4 data->uUseTexture = sceGxmProgramFindParameterByName(mainFragmentProgramGxp, "uUseTexture"); // int // clear uniforms data->colorShader_uColor = sceGxmProgramFindParameterByName(colorFragmentProgramGxp, "uColor"); // vec4 // light uniforms buffer data->lightDataBuffer = sceClibMspaceMalloc(data->cdramPool, 3 * sizeof(SceneLightGXM) + 4 // 3 lights + light count ); // quad mesh buffer data->quadMeshBuffer = sceClibMspaceMalloc(data->cdramPool, 4*sizeof(uint16_t) + 4 * 100 * sizeof(Vertex)); data->quadIndices = (uint16_t*)data->quadMeshBuffer; data->quadIndices[0] = 0; data->quadIndices[1] = 1; data->quadIndices[2] = 2; data->quadIndices[3] = 3; data->quadVertices = (Vertex*)(data->quadIndices + 4); return true; } static void CreateOrthoMatrix(float left, float right, float bottom, float top, D3DRMMATRIX4D& outMatrix) { float near = -1.0f; float far = 1.0f; float rl = right - left; float tb = top - bottom; float fn = far - near; outMatrix[0][0] = 2.0f / rl; outMatrix[0][1] = 0.0f; outMatrix[0][2] = 0.0f; outMatrix[0][3] = 0.0f; outMatrix[1][0] = 0.0f; outMatrix[1][1] = 2.0f / tb; outMatrix[1][2] = 0.0f; outMatrix[1][3] = 0.0f; outMatrix[2][0] = 0.0f; outMatrix[2][1] = 0.0f; outMatrix[2][2] = -2.0f / fn; outMatrix[2][3] = 0.0f; outMatrix[3][0] = -(right + left) / rl; outMatrix[3][1] = -(top + bottom) / tb; outMatrix[3][2] = -(far + near) / fn; outMatrix[3][3] = 1.0f; } Direct3DRMRenderer* GXMRenderer::Create(DWORD width, DWORD height) { SDL_Log("GXMRenderer::Create width=%d height=%d", width, height); bool success = gxm_init(); if(!success) { return nullptr; } GXMRendererData gxm_data; success = create_gxm_renderer(width, height, &gxm_data); if(!success) { return nullptr; } return new GXMRenderer(width, height, gxm_data); } GXMRenderer::GXMRenderer( DWORD width, DWORD height, GXMRendererData data ) : m_data(data) { m_width = width; m_height = height; m_virtualWidth = width; m_virtualHeight = height; m_initialized = true; } GXMRenderer::~GXMRenderer() { if(!m_initialized) { return; } // todo free stuff vita_mem_free(this->m_data.depthBufferUid); this->m_data.depthBufferData = nullptr; vita_mem_free(this->m_data.stencilBufferUid); this->m_data.stencilBufferData = nullptr; } void GXMRenderer::PushLights(const SceneLight* lightsArray, size_t count) { if (count > 3) { SDL_Log("Unsupported number of lights (%d)", static_cast(count)); count = 3; } m_lights.assign(lightsArray, lightsArray + count); } void GXMRenderer::SetFrustumPlanes(const Plane* frustumPlanes) { } void GXMRenderer::SetProjection(const D3DRMMATRIX4D& projection, D3DVALUE front, D3DVALUE back) { memcpy(&m_projection, projection, sizeof(D3DRMMATRIX4D)); } struct TextureDestroyContextGXM { GXMRenderer* renderer; Uint32 textureId; }; void GXMRenderer::AddTextureDestroyCallback(Uint32 id, IDirect3DRMTexture* texture) { auto* ctx = new TextureDestroyContextGXM{this, id}; texture->AddDestroyCallback( [](IDirect3DRMObject* obj, void* arg) { auto* ctx = static_cast(arg); auto& cache = ctx->renderer->m_textures[ctx->textureId]; void* textureData = sceGxmTextureGetData(&cache.gxmTexture); sceClibMspaceFree(ctx->renderer->m_data.cdramPool, textureData); delete ctx; }, ctx ); } static void convertTextureMetadata(SDL_Surface* surface, bool* supportedFormat, SceGxmTextureFormat* textureFormat, size_t* textureSize, size_t* textureAlignment, size_t* textureStride) { *supportedFormat = true; *textureAlignment = SCE_GXM_TEXTURE_ALIGNMENT; switch(surface->format) { case SDL_PIXELFORMAT_ABGR8888: { *textureFormat = SCE_GXM_TEXTURE_FORMAT_U8U8U8U8_ABGR; *textureSize = surface->h * surface->pitch; *textureStride = surface->pitch; break; } /* case SDL_PIXELFORMAT_INDEX8: { *textureFormat = SCE_GXM_TEXTURE_FORMAT_P8_ABGR; int pixelsSize = surface->h * surface->pitch; int alignBytes = ALIGNMENT(pixelsSize, SCE_GXM_PALETTE_ALIGNMENT); *textureSize = pixelsSize + alignBytes + 0xff; *textureAlignment = SCE_GXM_PALETTE_ALIGNMENT; *textureStride = surface->pitch; break; } */ default: { *supportedFormat = false; } } } void copySurfaceTo(SDL_Surface* src, void* dstData, size_t textureStride) { SDL_Surface* dst = SDL_CreateSurfaceFrom(src->w, src->h, SDL_PIXELFORMAT_ABGR8888, dstData, textureStride); SDL_BlitSurface(src, nullptr, dst, nullptr); SDL_DestroySurface(dst); } Uint32 GXMRenderer::GetTextureId(IDirect3DRMTexture* iTexture) { auto texture = static_cast(iTexture); auto surface = static_cast(texture->m_surface); bool supportedFormat; size_t textureSize; size_t textureAlignment; size_t textureStride; SceGxmTextureFormat textureFormat; int textureWidth = surface->m_surface->w; int textureHeight = surface->m_surface->h; convertTextureMetadata(surface->m_surface, &supportedFormat, &textureFormat, &textureSize, &textureAlignment, &textureStride); if(!supportedFormat) { textureAlignment = SCE_GXM_TEXTURE_ALIGNMENT; textureStride = textureWidth * 4; textureSize = textureHeight * textureStride; textureFormat = SCE_GXM_TEXTURE_FORMAT_U8U8U8U8_ABGR; } for (Uint32 i = 0; i < m_textures.size(); ++i) { auto& tex = m_textures[i]; if (tex.texture == texture) { if (tex.version != texture->m_version) { void* textureData = sceGxmTextureGetData(&tex.gxmTexture); if(!supportedFormat) { copySurfaceTo(surface->m_surface, textureData, textureStride); } else { memcpy(textureData, surface->m_surface->pixels, textureSize); } tex.version = texture->m_version; } return i; } } SDL_Log("Create Texture %s w=%d h=%d s=%d", SDL_GetPixelFormatName(surface->m_surface->format), textureWidth, textureHeight, textureStride); // allocate gpu memory void* textureData = sceClibMspaceMemalign(this->m_data.cdramPool, textureAlignment, textureSize); uint8_t* paletteData = nullptr; if(!supportedFormat) { SDL_Log("unsupported SDL texture format %s, falling back on SDL_PIXELFORMAT_ABGR8888", SDL_GetPixelFormatName(surface->m_surface->format)); copySurfaceTo(surface->m_surface, textureData, textureStride); } else if(surface->m_surface->format == SDL_PIXELFORMAT_INDEX8) { LPDIRECTDRAWPALETTE _palette; surface->GetPalette(&_palette); auto palette = static_cast(_palette); int pixelsSize = surface->m_surface->w * surface->m_surface->h; int alignBytes = ALIGNMENT(pixelsSize, SCE_GXM_PALETTE_ALIGNMENT); SDL_Log("copying indexed texture data from=%p to=%p", surface->m_surface->pixels, textureData); memcpy(textureData, surface->m_surface->pixels, pixelsSize); paletteData = (uint8_t*)textureData + pixelsSize + alignBytes; memcpy(paletteData, palette->m_palette->colors, palette->m_palette->ncolors*sizeof(SDL_Color)); } else { SDL_Log("copying texture data from=%p to=%p", surface->m_surface->pixels, textureData); memcpy(textureData, surface->m_surface->pixels, textureSize); } SceGxmTexture gxmTexture; SCE_ERR(sceGxmTextureInitLinearStrided, &gxmTexture, textureData, textureFormat, textureWidth, textureHeight, textureStride); //sceGxmTextureSetMinFilter(&gxmTexture, SCE_GXM_TEXTURE_FILTER_LINEAR); sceGxmTextureSetMagFilter(&gxmTexture, SCE_GXM_TEXTURE_FILTER_LINEAR); if(paletteData) { sceGxmTextureSetPalette(&gxmTexture, paletteData); } for (Uint32 i = 0; i < m_textures.size(); ++i) { auto& tex = m_textures[i]; if (!tex.texture) { tex.texture = texture; tex.version = texture->m_version; tex.gxmTexture = gxmTexture; tex.textureSize = textureSize; AddTextureDestroyCallback(i, texture); return i; } } m_textures.push_back({texture, texture->m_version, gxmTexture}); Uint32 textureId = (Uint32) (m_textures.size() - 1); AddTextureDestroyCallback(textureId, texture); return textureId; } GXMMeshCacheEntry GXMRenderer::GXMUploadMesh(const MeshGroup& meshGroup) { GXMMeshCacheEntry cache{&meshGroup, meshGroup.version}; cache.flat = meshGroup.quality == D3DRMRENDER_FLAT || meshGroup.quality == D3DRMRENDER_UNLITFLAT; std::vector vertices; std::vector indices; if (cache.flat) { FlattenSurfaces( meshGroup.vertices.data(), meshGroup.vertices.size(), meshGroup.indices.data(), meshGroup.indices.size(), meshGroup.texture != nullptr, vertices, indices ); } else { vertices = meshGroup.vertices; indices.resize(meshGroup.indices.size()); std::transform(meshGroup.indices.begin(), meshGroup.indices.end(), indices.begin(), [](DWORD index) { return static_cast(index); }); } size_t vertexBufferSize = sizeof(Vertex)*vertices.size(); size_t indexBufferSize = sizeof(uint16_t)*indices.size(); void* meshData = sceClibMspaceMemalign(this->m_data.cdramPool, 4, vertexBufferSize+indexBufferSize); Vertex* vertexBuffer = (Vertex*)meshData; uint16_t* indexBuffer = (uint16_t*)((uint8_t*)meshData + vertexBufferSize); for(int i = 0; i < vertices.size(); i++) { D3DRMVERTEX vertex = vertices.data()[i]; vertexBuffer[i] = Vertex{ .position = { vertex.position.x, vertex.position.y, vertex.position.z, }, .normal = { vertex.normal.x, vertex.normal.y, vertex.normal.z, }, .texCoord = { vertex.tu, vertex.tv, } }; } memcpy(indexBuffer, indices.data(), indices.size()*sizeof(uint16_t)); cache.meshData = meshData; cache.vertexBuffer = vertexBuffer; cache.indexBuffer = indexBuffer; cache.indexCount = indices.size(); return cache; } struct GXMMeshDestroyContext { GXMRenderer* renderer; Uint32 id; }; void GXMRenderer::AddMeshDestroyCallback(Uint32 id, IDirect3DRMMesh* mesh) { auto* ctx = new GXMMeshDestroyContext{this, id}; mesh->AddDestroyCallback( [](IDirect3DRMObject*, void* arg) { auto* ctx = static_cast(arg); auto& cache = ctx->renderer->m_meshes[ctx->id]; cache.meshGroup = nullptr; sceClibMspaceFree(ctx->renderer->m_data.cdramPool, cache.meshData); delete ctx; }, ctx ); } Uint32 GXMRenderer::GetMeshId(IDirect3DRMMesh* mesh, const MeshGroup* meshGroup) { for (Uint32 i = 0; i < m_meshes.size(); ++i) { auto& cache = m_meshes[i]; if (cache.meshGroup == meshGroup) { if (cache.version != meshGroup->version) { cache = std::move(this->GXMUploadMesh(*meshGroup)); } return i; } } auto newCache = this->GXMUploadMesh(*meshGroup); for (Uint32 i = 0; i < m_meshes.size(); ++i) { auto& cache = m_meshes[i]; if (!cache.meshGroup) { cache = std::move(newCache); AddMeshDestroyCallback(i, mesh); return i; } } m_meshes.push_back(std::move(newCache)); AddMeshDestroyCallback((Uint32) (m_meshes.size() - 1), mesh); return (Uint32) (m_meshes.size() - 1); } void GXMRenderer::GetDesc(D3DDEVICEDESC* halDesc, D3DDEVICEDESC* helDesc) { halDesc->dcmColorModel = D3DCOLORMODEL::RGB; halDesc->dwFlags = D3DDD_DEVICEZBUFFERBITDEPTH; halDesc->dwDeviceZBufferBitDepth = DDBD_16; halDesc->dwDeviceZBufferBitDepth |= DDBD_32; helDesc->dwDeviceRenderBitDepth = DDBD_32; halDesc->dpcTriCaps.dwTextureCaps = D3DPTEXTURECAPS_PERSPECTIVE; halDesc->dpcTriCaps.dwShadeCaps = D3DPSHADECAPS_ALPHAFLATBLEND; halDesc->dpcTriCaps.dwTextureFilterCaps = D3DPTFILTERCAPS_LINEAR; memset(helDesc, 0, sizeof(D3DDEVICEDESC)); } const char* GXMRenderer::GetName() { return "GXM"; } bool razor_triggered = false; void GXMRenderer::StartScene() { if(sceneStarted) return; sceGxmBeginScene( this->m_data.context, 0, this->m_data.renderTarget, nullptr, nullptr, this->m_data.displayBuffersSync[this->backBufferIndex], &this->m_data.displayBuffersSurface[this->backBufferIndex], &this->m_data.depthSurface ); this->sceneStarted = true; // reset quad vertices buffer this->m_data.quadVertices = (Vertex*)(this->m_data.quadIndices + 4); } int frames = 0; HRESULT GXMRenderer::BeginFrame() { frames++; if(with_razor) { if(!razor_triggered && frames == 10) { SDL_Log("trigger razor"); sceRazorGpuCaptureSetTriggerNextFrame("ux0:/data/capture.sgx"); razor_triggered = true; } } this->transparencyEnabled = false; this->StartScene(); // set light data int lightCount = std::min(static_cast(m_lights.size()), 3); SceneLightGXM* lightData = (SceneLightGXM*)this->m_data.lightDataBuffer; int* pLightCount = (int*)((uint8_t*)(this->m_data.lightDataBuffer)+sizeof(SceneLightGXM)*3); *pLightCount = lightCount; for (int i = 0; i < lightCount; ++i) { const auto& src = m_lights[i]; lightData[i].color[0] = src.color.r; lightData[i].color[1] = src.color.g; lightData[i].color[2] = src.color.b; lightData[i].color[3] = src.color.a; lightData[i].position[0] = src.position.x; lightData[i].position[1] = src.position.y; lightData[i].position[2] = src.position.z; lightData[i].position[3] = src.positional; lightData[i].direction[0] = src.direction.x; lightData[i].direction[1] = src.direction.y; lightData[i].direction[2] = src.direction.z; lightData[i].direction[3] = src.directional; } sceGxmSetFragmentUniformBuffer(this->m_data.context, 0, this->m_data.lightDataBuffer); return DD_OK; } void GXMRenderer::EnableTransparency() { this->transparencyEnabled = true; } static void transpose4x4(const float src[4][4], float dst[4][4]) { for (int i = 0; i < 4; ++i) for (int j = 0; j < 4; ++j) dst[j][i] = src[i][j]; } static const D3DRMMATRIX4D identity4x4 = { {1.0, 0.0, 0.0, 0.0}, {0.0, 1.0, 0.0, 0.0}, {0.0, 0.0, 1.0, 0.0}, {0.0, 0.0, 0.0, 1.0}, }; void GXMRenderer::SubmitDraw( DWORD meshId, const D3DRMMATRIX4D& modelViewMatrix, const D3DRMMATRIX4D& worldMatrix, const D3DRMMATRIX4D& viewMatrix, const Matrix3x3& normalMatrix, const Appearance& appearance ) { auto& mesh = m_meshes[meshId]; char marker[256]; snprintf(marker, sizeof(marker), "SubmitDraw: %d", meshId); sceGxmPushUserMarker(this->m_data.context, marker); sceGxmSetVertexProgram(this->m_data.context, this->m_data.mainVertexProgram); if(this->transparencyEnabled) { sceGxmSetFragmentProgram(this->m_data.context, this->m_data.transparentFragmentProgram); } else { sceGxmSetFragmentProgram(this->m_data.context, this->m_data.opaqueFragmentProgram); } void* vertUniforms; void* fragUniforms; sceGxmReserveVertexDefaultUniformBuffer(this->m_data.context, &vertUniforms); sceGxmReserveFragmentDefaultUniformBuffer(this->m_data.context, &fragUniforms); /* D3DRMMATRIX4D modelViewMatrixTrans; D3DRMMATRIX4D projectionTrans; transpose4x4(modelViewMatrix, modelViewMatrixTrans); transpose4x4(m_projection, projectionTrans); */ SET_UNIFORM(vertUniforms, this->m_data.uModelViewMatrix, modelViewMatrix); SET_UNIFORM(vertUniforms, this->m_data.uProjectionMatrix, m_projection); SET_UNIFORM(vertUniforms, this->m_data.uNormalMatrix, normalMatrix); float color[4] = { appearance.color.r / 255.0f, appearance.color.g / 255.0f, appearance.color.b / 255.0f, appearance.color.a / 255.0f }; SET_UNIFORM(fragUniforms, this->m_data.uColor, color); SET_UNIFORM(fragUniforms, this->m_data.uShininess, appearance.shininess); int useTexture = appearance.textureId != NO_TEXTURE_ID ? 1 : 0; SET_UNIFORM(fragUniforms, this->m_data.uUseTexture, useTexture); if(useTexture) { auto& texture = m_textures[appearance.textureId]; sceGxmSetFragmentTexture(this->m_data.context, 0, &texture.gxmTexture); } sceGxmSetVertexStream(this->m_data.context, 0, mesh.vertexBuffer); sceGxmDraw( this->m_data.context, SCE_GXM_PRIMITIVE_TRIANGLES, SCE_GXM_INDEX_FORMAT_U16, mesh.indexBuffer, mesh.indexCount ); sceGxmPopUserMarker(this->m_data.context); } HRESULT GXMRenderer::FinalizeFrame() { return DD_OK; } Vertex* GXMRenderer::GetQuadVertices() { Vertex* verts = this->m_data.quadVertices; this->m_data.quadVertices += 4; return verts; } void GXMRenderer::Resize(int width, int height, const ViewportTransform& viewportTransform) { m_width = width; m_height = height; m_viewportTransform = viewportTransform; } void GXMRenderer::Clear(float r, float g, float b) { this->StartScene(); char marker[256]; snprintf(marker, sizeof(marker), "Clear"); sceGxmPushUserMarker(this->m_data.context, marker); sceGxmSetVertexProgram(this->m_data.context, this->m_data.mainVertexProgram); sceGxmSetFragmentProgram(this->m_data.context, this->m_data.colorFragmentProgram); void* vertUniforms; void* fragUniforms; sceGxmReserveVertexDefaultUniformBuffer(this->m_data.context, &vertUniforms); sceGxmReserveFragmentDefaultUniformBuffer(this->m_data.context, &fragUniforms); D3DRMMATRIX4D projection; CreateOrthoMatrix(0.0, 1.0, 1.0, 0.0, projection); Matrix3x3 normal = {{1.f, 0.f, 0.f}, {0.f, 1.f, 0.f}, {0.f, 0.f, 1.f}}; SET_UNIFORM(vertUniforms, this->m_data.uModelViewMatrix, identity4x4); // float4x4 SET_UNIFORM(vertUniforms, this->m_data.uNormalMatrix, normal); // float3x3 SET_UNIFORM(vertUniforms, this->m_data.uProjectionMatrix, projection); // float4x4 float color[] = {r,g,b,1}; SET_UNIFORM(fragUniforms, this->m_data.colorShader_uColor, color); float x1 = 0; float y1 = 0; float x2 = x1 + 1.0; float y2 = y1 + 1.0; Vertex* quadVertices = this->GetQuadVertices(); quadVertices[0] = Vertex{ .position = {x1, y1, -1.0}, .normal = {0,0,0}, .texCoord = {0,0}}; quadVertices[1] = Vertex{ .position = {x2, y1, -1.0}, .normal = {0,0,0}, .texCoord = {0,0}}; quadVertices[2] = Vertex{ .position = {x1, y2, -1.0}, .normal = {0,0,0}, .texCoord = {0,0}}; quadVertices[3] = Vertex{ .position = {x2, y2, -1.0}, .normal = {0,0,0}, .texCoord = {0,0}}; sceGxmSetVertexStream(this->m_data.context, 0, quadVertices); sceGxmDraw( this->m_data.context, SCE_GXM_PRIMITIVE_TRIANGLE_STRIP, SCE_GXM_INDEX_FORMAT_U16, this->m_data.quadIndices, 4 ); sceGxmPopUserMarker(this->m_data.context); } void GXMRenderer::Flip() { if(!this->sceneStarted) { this->Clear(0,0,0); } // end scene sceGxmEndScene( this->m_data.context, nullptr, nullptr ); sceGxmPadHeartbeat( &this->m_data.displayBuffersSurface[this->backBufferIndex], this->m_data.displayBuffersSync[this->backBufferIndex] ); this->sceneStarted = false; // display GXMDisplayData displayData; displayData.address = this->m_data.displayBuffers[this->backBufferIndex]; sceGxmDisplayQueueAddEntry( this->m_data.displayBuffersSync[this->frontBufferIndex], this->m_data.displayBuffersSync[this->backBufferIndex], &displayData ); this->frontBufferIndex = this->backBufferIndex; this->backBufferIndex = (this->backBufferIndex + 1) % VITA_GXM_DISPLAY_BUFFER_COUNT; } void GXMRenderer::Draw2DImage(Uint32 textureId, const SDL_Rect& srcRect, const SDL_Rect& dstRect) { this->StartScene(); char marker[256]; snprintf(marker, sizeof(marker), "Draw2DImage: %d", textureId); sceGxmPushUserMarker(this->m_data.context, marker); sceGxmSetVertexProgram(this->m_data.context, this->m_data.mainVertexProgram); sceGxmSetFragmentProgram(this->m_data.context, this->m_data.imageFragmentProgram); void* vertUniforms; void* fragUniforms; sceGxmReserveVertexDefaultUniformBuffer(this->m_data.context, &vertUniforms); sceGxmReserveFragmentDefaultUniformBuffer(this->m_data.context, &fragUniforms); float left = -this->m_viewportTransform.offsetX / this->m_viewportTransform.scale; float right = (this->m_width - this->m_viewportTransform.offsetX) / this->m_viewportTransform.scale; float top = -this->m_viewportTransform.offsetY / this->m_viewportTransform.scale; float bottom = (this->m_height - this->m_viewportTransform.offsetY) / this->m_viewportTransform.scale; #define virtualToNDCX(x) (((x - left) / (right - left))); #define virtualToNDCY(y) (((y - top) / (bottom - top))); float x1_virtual = static_cast(dstRect.x); float y1_virtual = static_cast(dstRect.y); float x2_virtual = x1_virtual + dstRect.w; float y2_virtual = y1_virtual + dstRect.h; float x1 = virtualToNDCX(x1_virtual); float y1 = virtualToNDCY(y1_virtual); float x2 = virtualToNDCX(x2_virtual); float y2 = virtualToNDCY(y2_virtual); D3DRMMATRIX4D projection; CreateOrthoMatrix(0.0, 1.0, 1.0, 0.0, projection); static const Matrix3x3 normal = {{1.f, 0.f, 0.f}, {0.f, 1.f, 0.f}, {0.f, 0.f, 1.f}}; D3DRMMATRIX4D identity; memset(identity, 0, sizeof(identity)); identity[0][0] = 1.0f; identity[1][1] = 1.0f; identity[2][2] = 1.0f; identity[3][3] = 1.0f; SET_UNIFORM(vertUniforms, this->m_data.uModelViewMatrix, identity); // float4x4 SET_UNIFORM(vertUniforms, this->m_data.uNormalMatrix, normal); // float3x3 SET_UNIFORM(vertUniforms, this->m_data.uProjectionMatrix, projection); // float4x4 const GXMTextureCacheEntry& texture = m_textures[textureId]; sceGxmSetFragmentTexture(this->m_data.context, 0, &texture.gxmTexture); float texW = sceGxmTextureGetWidth(&texture.gxmTexture); float texH = sceGxmTextureGetHeight(&texture.gxmTexture); float u1 = static_cast(srcRect.x) / texW; float v1 = static_cast(srcRect.y) / texH; float u2 = static_cast(srcRect.x + srcRect.w) / texW; float v2 = static_cast(srcRect.y + srcRect.h) / texH; Vertex* quadVertices = this->GetQuadVertices(); quadVertices[0] = Vertex{ .position = {x1, y1, 0}, .normal = {0,0,0}, .texCoord = {u1, v1}}; quadVertices[1] = Vertex{ .position = {x2, y1, 0}, .normal = {0,0,0}, .texCoord = {u2, v1}}; quadVertices[2] = Vertex{ .position = {x1, y2, 0}, .normal = {0,0,0}, .texCoord = {u1, v2}}; quadVertices[3] = Vertex{ .position = {x2, y2, 0}, .normal = {0,0,0}, .texCoord = {u2, v2}}; sceGxmSetVertexStream(this->m_data.context, 0, quadVertices); sceGxmSetFrontDepthWriteEnable(this->m_data.context, SCE_GXM_DEPTH_WRITE_DISABLED); sceGxmDraw( this->m_data.context, SCE_GXM_PRIMITIVE_TRIANGLE_STRIP, SCE_GXM_INDEX_FORMAT_U16, this->m_data.quadIndices, 4 ); sceGxmSetFrontDepthWriteEnable(this->m_data.context, SCE_GXM_DEPTH_WRITE_ENABLED); sceGxmPopUserMarker(this->m_data.context); } void GXMRenderer::Download(SDL_Surface* target) { SDL_Rect srcRect = { static_cast(m_viewportTransform.offsetX), static_cast(m_viewportTransform.offsetY), static_cast(target->w * m_viewportTransform.scale), static_cast(target->h * m_viewportTransform.scale), }; SDL_Surface* src = SDL_CreateSurfaceFrom( this->m_width, this->m_height, SDL_PIXELFORMAT_RGBA32, this->m_data.displayBuffers[this->frontBufferIndex], VITA_GXM_SCREEN_STRIDE ); SDL_BlitSurfaceScaled(src, &srcRect, target, nullptr, SDL_SCALEMODE_NEAREST); SDL_DestroySurface(src); }