diff --git a/GPU/GLES/TransformPipeline.cpp b/GPU/GLES/TransformPipeline.cpp index edd8a8657..f577ddd77 100644 --- a/GPU/GLES/TransformPipeline.cpp +++ b/GPU/GLES/TransformPipeline.cpp @@ -121,14 +121,14 @@ TransformDrawEngine::TransformDrawEngine() : decodedVerts_(0), prevPrim_(GE_PRIM_INVALID), lastVType_(-1), - shaderManager_(0), - textureCache_(0), - framebufferManager_(0), + shaderManager_(nullptr), + textureCache_(nullptr), + framebufferManager_(nullptr), numDrawCalls(0), vertexCountInDrawCalls(0), decodeCounter_(0), dcid_(0), - uvScale(0), + uvScale(nullptr), fboTexNeedBind_(false), fboTexBound_(false) { decimationCounter_ = VERTEXCACHE_DECIMATION_INTERVAL; diff --git a/GPU/Vulkan/DrawEngineVulkan.cpp b/GPU/Vulkan/DrawEngineVulkan.cpp index 41bb562d2..41574e150 100644 --- a/GPU/Vulkan/DrawEngineVulkan.cpp +++ b/GPU/Vulkan/DrawEngineVulkan.cpp @@ -71,6 +71,7 @@ DrawEngineVulkan::DrawEngineVulkan(VulkanContext *vulkan) framebufferManager_(nullptr), numDrawCalls(0), vertexCountInDrawCalls(0), + uvScale(nullptr), fboTexNeedBind_(false), fboTexBound_(false), curFrame_(0), @@ -92,6 +93,10 @@ DrawEngineVulkan::DrawEngineVulkan(VulkanContext *vulkan) indexGen.Setup(decIndex); + if (g_Config.bPrescaleUV) { + uvScale = new UVScale[MAX_DEFERRED_DRAW_CALLS]; + } + // All resources we need for PSP drawing. Usually only bindings 0 and 2-4 are populated. VkDescriptorSetLayoutBinding bindings[5]; bindings[0].descriptorCount = 1; @@ -202,6 +207,7 @@ DrawEngineVulkan::~DrawEngineVulkan() { nullTexture_->Destroy(); delete nullTexture_; } + delete[] uvScale; } void DrawEngineVulkan::BeginFrame() { @@ -312,6 +318,10 @@ void DrawEngineVulkan::SubmitPrim(void *verts, void *inds, GEPrimitiveType prim, dc.indexUpperBound = vertexCount - 1; } + if (uvScale) { + uvScale[numDrawCalls] = gstate_c.uv; + } + numDrawCalls++; vertexCountInDrawCalls += vertexCount; @@ -324,6 +334,70 @@ void DrawEngineVulkan::SubmitPrim(void *verts, void *inds, GEPrimitiveType prim, } } +void DrawEngineVulkan::DecodeVertsStep(u8 *dest, int &i, int &decodedVerts) { + const DeferredDrawCall &dc = drawCalls[i]; + + indexGen.SetIndex(decodedVerts); + int indexLowerBound = dc.indexLowerBound, indexUpperBound = dc.indexUpperBound; + + void *inds = dc.inds; + if (dc.indexType == GE_VTYPE_IDX_NONE >> GE_VTYPE_IDX_SHIFT) { + // Decode the verts and apply morphing. Simple. + dec_->DecodeVerts(dest + decodedVerts * (int)dec_->GetDecVtxFmt().stride, + dc.verts, indexLowerBound, indexUpperBound); + decodedVerts += indexUpperBound - indexLowerBound + 1; + indexGen.AddPrim(dc.prim, dc.vertexCount); + } else { + // It's fairly common that games issue long sequences of PRIM calls, with differing + // inds pointer but the same base vertex pointer. We'd like to reuse vertices between + // these as much as possible, so we make sure here to combine as many as possible + // into one nice big drawcall, sharing data. + + // 1. Look ahead to find the max index, only looking as "matching" drawcalls. + // Expand the lower and upper bounds as we go. + int lastMatch = i; + const int total = numDrawCalls; + for (int j = i + 1; j < total; ++j) { + if (drawCalls[j].verts != dc.verts) + break; + + indexLowerBound = std::min(indexLowerBound, (int)drawCalls[j].indexLowerBound); + indexUpperBound = std::max(indexUpperBound, (int)drawCalls[j].indexUpperBound); + lastMatch = j; + } + + // 2. Loop through the drawcalls, translating indices as we go. + switch (dc.indexType) { + case GE_VTYPE_IDX_8BIT >> GE_VTYPE_IDX_SHIFT: + for (int j = i; j <= lastMatch; j++) { + indexGen.TranslatePrim(drawCalls[j].prim, drawCalls[j].vertexCount, (const u8 *)drawCalls[j].inds, indexLowerBound); + } + break; + case GE_VTYPE_IDX_16BIT >> GE_VTYPE_IDX_SHIFT: + for (int j = i; j <= lastMatch; j++) { + indexGen.TranslatePrim(drawCalls[j].prim, drawCalls[j].vertexCount, (const u16 *)drawCalls[j].inds, indexLowerBound); + } + break; + } + + const int vertexCount = indexUpperBound - indexLowerBound + 1; + + // This check is a workaround for Pangya Fantasy Golf, which sends bogus index data when switching items in "My Room" sometimes. + if (decodedVerts + vertexCount > VERTEX_BUFFER_MAX) { + return; + } + + // 3. Decode that range of vertex data. + dec_->DecodeVerts(dest + decodedVerts * (int)dec_->GetDecVtxFmt().stride, + dc.verts, indexLowerBound, indexUpperBound); + decodedVerts += vertexCount; + + // 4. Advance indexgen vertex counter. + indexGen.Advance(vertexCount); + i = lastMatch; + } +} + void DrawEngineVulkan::DecodeVerts(VulkanPushBuffer *push, uint32_t *bindOffset, VkBuffer *vkbuf) { int decodedVerts = 0; @@ -343,69 +417,19 @@ void DrawEngineVulkan::DecodeVerts(VulkanPushBuffer *push, uint32_t *bindOffset, dest = (u8 *)push->Push(vertsToDecode * dec_->GetDecVtxFmt().stride, bindOffset, vkbuf); } - for (int i = 0; i < numDrawCalls; i++) { - const DeferredDrawCall &dc = drawCalls[i]; - - indexGen.SetIndex(decodedVerts); - int indexLowerBound = dc.indexLowerBound, indexUpperBound = dc.indexUpperBound; - - void *inds = dc.inds; - if (dc.indexType == GE_VTYPE_IDX_NONE >> GE_VTYPE_IDX_SHIFT) { - // Decode the verts and apply morphing. Simple. - dec_->DecodeVerts(dest + decodedVerts * (int)dec_->GetDecVtxFmt().stride, - dc.verts, indexLowerBound, indexUpperBound); - decodedVerts += indexUpperBound - indexLowerBound + 1; - indexGen.AddPrim(dc.prim, dc.vertexCount); - } else { - // It's fairly common that games issue long sequences of PRIM calls, with differing - // inds pointer but the same base vertex pointer. We'd like to reuse vertices between - // these as much as possible, so we make sure here to combine as many as possible - // into one nice big drawcall, sharing data. - - // 1. Look ahead to find the max index, only looking as "matching" drawcalls. - // Expand the lower and upper bounds as we go. - int lastMatch = i; - const int total = numDrawCalls; - for (int j = i + 1; j < total; ++j) { - if (drawCalls[j].verts != dc.verts) - break; - - indexLowerBound = std::min(indexLowerBound, (int)drawCalls[j].indexLowerBound); - indexUpperBound = std::max(indexUpperBound, (int)drawCalls[j].indexUpperBound); - lastMatch = j; - } - - // 2. Loop through the drawcalls, translating indices as we go. - switch (dc.indexType) { - case GE_VTYPE_IDX_8BIT >> GE_VTYPE_IDX_SHIFT: - for (int j = i; j <= lastMatch; j++) { - indexGen.TranslatePrim(drawCalls[j].prim, drawCalls[j].vertexCount, (const u8 *)drawCalls[j].inds, indexLowerBound); - } - break; - case GE_VTYPE_IDX_16BIT >> GE_VTYPE_IDX_SHIFT: - for (int j = i; j <= lastMatch; j++) { - indexGen.TranslatePrim(drawCalls[j].prim, drawCalls[j].vertexCount, (const u16 *)drawCalls[j].inds, indexLowerBound); - } - break; - } - - const int vertexCount = indexUpperBound - indexLowerBound + 1; - - // This check is a workaround for Pangya Fantasy Golf, which sends bogus index data when switching items in "My Room" sometimes. - if (decodedVerts + vertexCount > VERTEX_BUFFER_MAX) { - return; - } - - // 3. Decode that range of vertex data. - dec_->DecodeVerts(dest + decodedVerts * (int)dec_->GetDecVtxFmt().stride, - dc.verts, indexLowerBound, indexUpperBound); - decodedVerts += vertexCount; - - // 4. Advance indexgen vertex counter. - indexGen.Advance(vertexCount); - i = lastMatch; + if (uvScale) { + const UVScale origUV = gstate_c.uv; + for (int i = 0; i < numDrawCalls; i++) { + gstate_c.uv = uvScale[i]; + DecodeVertsStep(dest, i, decodedVerts); // Note that this can modify i + } + gstate_c.uv = origUV; + } else { + for (int i = 0; i < numDrawCalls; i++) { + DecodeVertsStep(dest, i, decodedVerts); // Note that this can modify i } } + // Sanity check if (indexGen.Prim() < 0) { ERROR_LOG_REPORT(G3D, "DecodeVerts: Failed to deduce prim: %i", indexGen.Prim()); diff --git a/GPU/Vulkan/DrawEngineVulkan.h b/GPU/Vulkan/DrawEngineVulkan.h index 9a54647fd..4bcaf8964 100644 --- a/GPU/Vulkan/DrawEngineVulkan.h +++ b/GPU/Vulkan/DrawEngineVulkan.h @@ -147,6 +147,8 @@ private: struct FrameData; void DecodeVerts(VulkanPushBuffer *push, uint32_t *bindOffset, VkBuffer *vkbuf); + void DecodeVertsStep(u8 *dest, int &i, int &decodedVerts); + void DoFlush(VkCommandBuffer cmd); void UpdateUBOs(FrameData *frame); @@ -244,6 +246,7 @@ private: DeferredDrawCall drawCalls[MAX_DEFERRED_DRAW_CALLS]; int numDrawCalls; int vertexCountInDrawCalls; + UVScale *uvScale; bool fboTexNeedBind_; bool fboTexBound_; diff --git a/UI/GameSettingsScreen.cpp b/UI/GameSettingsScreen.cpp index afccb8c55..3651c0bf6 100644 --- a/UI/GameSettingsScreen.cpp +++ b/UI/GameSettingsScreen.cpp @@ -311,6 +311,9 @@ void GameSettingsScreen::CreateViews() { CheckBox *depthWrite = graphicsSettings->Add(new CheckBox(&g_Config.bAlwaysDepthWrite, gr->T("Always Depth Write"))); depthWrite->SetDisabledPtr(&g_Config.bSoftwareRendering); + graphicsSettings->Add(new CheckBox(&g_Config.bPrescaleUV, gr->T("Texture Coord Speedhack"))); + depthWrite->SetDisabledPtr(&g_Config.bSoftwareRendering); + static const char *bloomHackOptions[] = { "Off", "Safe", "Balanced", "Aggressive" }; PopupMultiChoice *bloomHack = graphicsSettings->Add(new PopupMultiChoice(&g_Config.iBloomHack, gr->T("Lower resolution for effects (reduces artifacts)"), bloomHackOptions, 0, ARRAY_SIZE(bloomHackOptions), gr->GetName(), screenManager())); bloomHackEnable_ = !g_Config.bSoftwareRendering && (g_Config.iInternalResolution != 1);