diff --git a/GPU/Common/DrawEngineCommon.cpp b/GPU/Common/DrawEngineCommon.cpp index 5152ac5a08..265f5d6e1b 100644 --- a/GPU/Common/DrawEngineCommon.cpp +++ b/GPU/Common/DrawEngineCommon.cpp @@ -17,26 +17,32 @@ #include +#include "profiler/profiler.h" #include "Common/ColorConv.h" #include "Core/Config.h" #include "GPU/Common/DrawEngineCommon.h" #include "GPU/Common/SplineCommon.h" #include "GPU/Common/VertexDecoderCommon.h" +#include "GPU/Common/TextureDecoder.h" // for ReliableHash #include "GPU/ge_constants.h" #include "GPU/GPUState.h" #define QUAD_INDICES_MAX 65536 -DrawEngineCommon::DrawEngineCommon() - : dec_(nullptr), - decOptions_{}, - fboTexNeedBind_(false), - fboTexBound_(false) { +enum { + TRANSFORMED_VERTEX_BUFFER_SIZE = VERTEX_BUFFER_MAX * sizeof(TransformedVertex) +}; + +DrawEngineCommon::DrawEngineCommon() { quadIndices_ = new u16[6 * QUAD_INDICES_MAX]; decJitCache_ = new VertexDecoderJitCache(); + transformed = (TransformedVertex *)AllocateMemoryPages(TRANSFORMED_VERTEX_BUFFER_SIZE, MEM_PROT_READ | MEM_PROT_WRITE); + transformedExpanded = (TransformedVertex *)AllocateMemoryPages(3 * TRANSFORMED_VERTEX_BUFFER_SIZE, MEM_PROT_READ | MEM_PROT_WRITE); } DrawEngineCommon::~DrawEngineCommon() { + FreeMemoryPages(transformed, TRANSFORMED_VERTEX_BUFFER_SIZE); + FreeMemoryPages(transformedExpanded, 3 * TRANSFORMED_VERTEX_BUFFER_SIZE); delete[] quadIndices_; delete decJitCache_; for (auto iter = decoderMap_.begin(); iter != decoderMap_.end(); iter++) { @@ -44,6 +50,18 @@ DrawEngineCommon::~DrawEngineCommon() { } } +void DrawEngineCommon::SetupVertexDecoder(u32 vertType) { + // As the decoder depends on the UVGenMode when we use UV prescale, we simply mash it + // into the top of the verttype where there are unused bits. + const u32 vertTypeID = (vertType & 0xFFFFFF) | (gstate.getUVGenMode() << 24); + + // If vtype has changed, setup the vertex decoder. + if (vertTypeID != lastVType_) { + dec_ = GetVertexDecoder(vertTypeID); + lastVType_ = vertTypeID; + } +} + VertexDecoder *DrawEngineCommon::GetVertexDecoder(u32 vtype) { auto iter = decoderMap_.find(vtype); if (iter != decoderMap_.end()) @@ -131,7 +149,7 @@ u32 DrawEngineCommon::NormalizeVertices(u8 *outPtr, u8 *bufPtr, const u8 *inPtr, return DrawEngineCommon::NormalizeVertices(outPtr, bufPtr, inPtr, dec, lowerBound, upperBound, vertType); } -// This code is HIGHLY unoptimized! +// This code has plenty of potential for optimization. // // It does the simplest and safest test possible: If all points of a bbox is outside a single of // our clipping planes, we reject the box. Tighter bounds would be desirable but would take more calculations. @@ -195,7 +213,6 @@ bool DrawEngineCommon::TestBoundingBox(void* control_points, int vertexCount, u3 // Any out. For testing that the planes are in the right locations. // if (out != 0) return false; } - return true; } @@ -311,7 +328,6 @@ bool DrawEngineCommon::GetCurrentSimpleVertices(int count, std::vector> 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_le *)drawCalls[j].inds, indexLowerBound); + } + break; + case GE_VTYPE_IDX_32BIT >> GE_VTYPE_IDX_SHIFT: + for (int j = i; j <= lastMatch; j++) { + indexGen.TranslatePrim(drawCalls[j].prim, drawCalls[j].vertexCount, (const u32_le *)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; + } +} + +inline u32 ComputeMiniHashRange(const void *ptr, size_t sz) { + // Switch to u32 units. + const u32 *p = (const u32 *)ptr; + sz >>= 2; + + if (sz > 100) { + size_t step = sz / 4; + u32 hash = 0; + for (size_t i = 0; i < sz; i += step) { + hash += DoReliableHash32(p + i, 100, 0x3A44B9C4); + } + return hash; + } else { + return p[0] + p[sz - 1]; + } +} + +u32 DrawEngineCommon::ComputeMiniHash() { + u32 fullhash = 0; + const int vertexSize = dec_->GetDecVtxFmt().stride; + const int indexSize = IndexSize(dec_->VertexType()); + + int step; + if (numDrawCalls < 3) { + step = 1; + } else if (numDrawCalls < 8) { + step = 4; + } else { + step = numDrawCalls / 8; + } + for (int i = 0; i < numDrawCalls; i += step) { + const DeferredDrawCall &dc = drawCalls[i]; + if (!dc.inds) { + fullhash += ComputeMiniHashRange(dc.verts, vertexSize * dc.vertexCount); + } else { + int indexLowerBound = dc.indexLowerBound, indexUpperBound = dc.indexUpperBound; + fullhash += ComputeMiniHashRange((const u8 *)dc.verts + vertexSize * indexLowerBound, vertexSize * (indexUpperBound - indexLowerBound)); + fullhash += ComputeMiniHashRange(dc.inds, indexSize * dc.vertexCount); + } + } + + return fullhash; +} + +ReliableHashType DrawEngineCommon::ComputeHash() { + ReliableHashType fullhash = 0; + const int vertexSize = dec_->GetDecVtxFmt().stride; + const int indexSize = IndexSize(dec_->VertexType()); + + // TODO: Add some caps both for numDrawCalls and num verts to check? + // It is really very expensive to check all the vertex data so often. + for (int i = 0; i < numDrawCalls; i++) { + const DeferredDrawCall &dc = drawCalls[i]; + if (!dc.inds) { + fullhash += DoReliableHash((const char *)dc.verts, vertexSize * dc.vertexCount, 0x1DE8CAC4); + } else { + int indexLowerBound = dc.indexLowerBound, indexUpperBound = dc.indexUpperBound; + int j = i + 1; + int lastMatch = i; + while (j < numDrawCalls) { + if (drawCalls[j].verts != dc.verts) + break; + indexLowerBound = std::min(indexLowerBound, (int)dc.indexLowerBound); + indexUpperBound = std::max(indexUpperBound, (int)dc.indexUpperBound); + lastMatch = j; + j++; + } + // This could get seriously expensive with sparse indices. Need to combine hashing ranges the same way + // we do when drawing. + fullhash += DoReliableHash((const char *)dc.verts + vertexSize * indexLowerBound, + vertexSize * (indexUpperBound - indexLowerBound), 0x029F3EE1); + // Hm, we will miss some indices when combining above, but meh, it should be fine. + fullhash += DoReliableHash((const char *)dc.inds, indexSize * dc.vertexCount, 0x955FD1CA); + i = lastMatch; + } + } + + fullhash += DoReliableHash(&uvScale[0], sizeof(uvScale[0]) * numDrawCalls, 0x0123e658); + return fullhash; +} diff --git a/GPU/Common/DrawEngineCommon.h b/GPU/Common/DrawEngineCommon.h index ddbdd02aee..aaf42fb4f4 100644 --- a/GPU/Common/DrawEngineCommon.h +++ b/GPU/Common/DrawEngineCommon.h @@ -24,6 +24,7 @@ #include "GPU/GPUState.h" #include "GPU/Common/GPUDebugInterface.h" +#include "GPU/Common/IndexGenerator.h" #include "GPU/Common/VertexDecoderCommon.h" class VertexDecoder; @@ -35,6 +36,13 @@ enum { SPLINE_BUFFER_SIZE = VERTEX_BUFFER_MAX * 26, // At least, this buffer needs greater than 1679616 bytes for Mist Dragon morphing in FF4CC. }; +// Avoiding the full include of TextureDecoder.h. +#if (defined(_M_SSE) && defined(_M_X64)) || defined(ARM64) +typedef u64 ReliableHashType; +#else +typedef u32 ReliableHashType; +#endif + class DrawEngineCommon { public: DrawEngineCommon(); @@ -60,12 +68,21 @@ public: virtual void Resized(); + void SetupVertexDecoder(u32 vertType); + protected: virtual void ClearTrackedVertexArrays() {} // Preprocessing for spline/bezier u32 NormalizeVertices(u8 *outPtr, u8 *bufPtr, const u8 *inPtr, int lowerBound, int upperBound, u32 vertType); + // Utility for vertex caching + u32 ComputeMiniHash(); + ReliableHashType ComputeHash(); + + // Vertex decoding + void DecodeVertsStep(u8 *dest, int &i, int &decodedVerts); + bool ApplyShaderBlending(); VertexDecoder *GetVertexDecoder(u32 vtype); @@ -81,23 +98,53 @@ protected: } // Vertex collector buffers - u8 *decoded; - u16 *decIndex; - u8 *splineBuffer; + u8 *decoded = nullptr; + u16 *decIndex = nullptr; + u8 *splineBuffer = nullptr; // Cached vertex decoders u32 lastVType_ = -1; std::unordered_map decoderMap_; VertexDecoder *dec_ = nullptr; - VertexDecoderJitCache *decJitCache_; - VertexDecoderOptions decOptions_; + VertexDecoderJitCache *decJitCache_ = nullptr; + VertexDecoderOptions decOptions_{}; + + TransformedVertex *transformed = nullptr; + TransformedVertex *transformedExpanded = nullptr; + + // Defer all vertex decoding to a "Flush" (except when software skinning) + struct DeferredDrawCall { + void *verts; + void *inds; + u32 vertType; + u8 indexType; + s8 prim; + u32 vertexCount; + u16 indexLowerBound; + u16 indexUpperBound; + }; + + enum { MAX_DEFERRED_DRAW_CALLS = 128 }; + DeferredDrawCall drawCalls[MAX_DEFERRED_DRAW_CALLS]; + int numDrawCalls = 0; + int vertexCountInDrawCalls_ = 0; + UVScale uvScale[MAX_DEFERRED_DRAW_CALLS]; + + int decimationCounter_ = 0; + int decodeCounter_ = 0; + u32 dcid_ = 0; + + // Vertex collector state + IndexGenerator indexGen; + int decodedVerts_ = 0; + GEPrimitiveType prevPrim_ = GE_PRIM_INVALID; // Fixed index buffer for easy quad generation from spline/bezier - u16 *quadIndices_; + u16 *quadIndices_ = nullptr; // Shader blending state - bool fboTexNeedBind_; - bool fboTexBound_; + bool fboTexNeedBind_ = false; + bool fboTexBound_ = false; // Hardware tessellation int numPatches; diff --git a/GPU/Common/IndexGenerator.cpp b/GPU/Common/IndexGenerator.cpp index cec83aa6bc..46f076cee1 100644 --- a/GPU/Common/IndexGenerator.cpp +++ b/GPU/Common/IndexGenerator.cpp @@ -15,6 +15,7 @@ // Official git repository and contact information can be found at // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. +#include #include "IndexGenerator.h" #include "Common/Common.h" @@ -211,16 +212,24 @@ void IndexGenerator::TranslateLineStrip(int numInds, const ITypeLE *inds, int in template void IndexGenerator::TranslateList(int numInds, const ITypeLE *inds, int indexOffset) { indexOffset = index_ - indexOffset; - u16 *outInds = inds_; - int numTris = numInds / 3; // Round to whole triangles - numInds = numTris * 3; - for (int i = 0; i < numInds; i += 3) { - *outInds++ = indexOffset + inds[i]; - *outInds++ = indexOffset + inds[i + 1]; - *outInds++ = indexOffset + inds[i + 2]; + // We only bother doing this minor optimization in triangle list, since it's by far the most + // common operation that can benefit. + if (sizeof(ITypeLE) == sizeof(inds_[0]) && indexOffset == 0) { + memcpy(inds_, inds, numInds * sizeof(ITypeLE)); + inds_ += numInds; + count_ += numInds; + } else { + u16 *outInds = inds_; + int numTris = numInds / 3; // Round to whole triangles + numInds = numTris * 3; + for (int i = 0; i < numInds; i += 3) { + *outInds++ = indexOffset + inds[i]; + *outInds++ = indexOffset + inds[i + 1]; + *outInds++ = indexOffset + inds[i + 2]; + } + inds_ = outInds; + count_ += numInds; } - inds_ = outInds; - count_ += numInds; prim_ = GE_PRIM_TRIANGLES; seenPrims_ |= (1 << GE_PRIM_TRIANGLES) | flag; } diff --git a/GPU/D3D11/DrawEngineD3D11.cpp b/GPU/D3D11/DrawEngineD3D11.cpp index e6c3cd419a..3042e6090d 100644 --- a/GPU/D3D11/DrawEngineD3D11.cpp +++ b/GPU/D3D11/DrawEngineD3D11.cpp @@ -54,10 +54,6 @@ const D3D11_PRIMITIVE_TOPOLOGY d3d11prim[8] = { D3D11_PRIMITIVE_TOPOLOGY_TRIANGLELIST, // Need expansion - though we could do it with geom shaders in most cases }; -enum { - TRANSFORMED_VERTEX_BUFFER_SIZE = VERTEX_BUFFER_MAX * sizeof(TransformedVertex) -}; - #define VERTEXCACHE_DECIMATION_INTERVAL 17 enum { VAI_KILL_AGE = 120, VAI_UNRELIABLE_KILL_AGE = 240, VAI_UNRELIABLE_KILL_MAX = 4 }; @@ -76,16 +72,8 @@ static const D3D11_INPUT_ELEMENT_DESC TransformedVertexElements[] = { DrawEngineD3D11::DrawEngineD3D11(Draw::DrawContext *draw, ID3D11Device *device, ID3D11DeviceContext *context) : draw_(draw), device_(device), - context_(context), - decodedVerts_(0), - prevPrim_(GE_PRIM_INVALID), - shaderManager_(0), - textureCache_(0), - framebufferManager_(0), - numDrawCalls(0), - vertexCountInDrawCalls_(0), - decodeCounter_(0), - dcid_(0) { + context_(context) +{ device1_ = (ID3D11Device1 *)draw->GetNativeObject(Draw::NativeObject::DEVICE_EX); context1_ = (ID3D11DeviceContext1 *)draw->GetNativeObject(Draw::NativeObject::CONTEXT_EX); decOptions_.expandAllWeightsToFloat = true; @@ -98,8 +86,6 @@ DrawEngineD3D11::DrawEngineD3D11(Draw::DrawContext *draw, ID3D11Device *device, decoded = (u8 *)AllocateMemoryPages(DECODED_VERTEX_BUFFER_SIZE, MEM_PROT_READ | MEM_PROT_WRITE); decIndex = (u16 *)AllocateMemoryPages(DECODED_INDEX_BUFFER_SIZE, MEM_PROT_READ | MEM_PROT_WRITE); splineBuffer = (u8 *)AllocateMemoryPages(SPLINE_BUFFER_SIZE, MEM_PROT_READ | MEM_PROT_WRITE); - transformed = (TransformedVertex *)AllocateMemoryPages(TRANSFORMED_VERTEX_BUFFER_SIZE, MEM_PROT_READ | MEM_PROT_WRITE); - transformedExpanded = (TransformedVertex *)AllocateMemoryPages(3 * TRANSFORMED_VERTEX_BUFFER_SIZE, MEM_PROT_READ | MEM_PROT_WRITE); indexGen.Setup(decIndex); @@ -115,8 +101,6 @@ DrawEngineD3D11::~DrawEngineD3D11() { FreeMemoryPages(decoded, DECODED_VERTEX_BUFFER_SIZE); FreeMemoryPages(decIndex, DECODED_INDEX_BUFFER_SIZE); FreeMemoryPages(splineBuffer, SPLINE_BUFFER_SIZE); - FreeMemoryPages(transformed, TRANSFORMED_VERTEX_BUFFER_SIZE); - FreeMemoryPages(transformedExpanded, 3 * TRANSFORMED_VERTEX_BUFFER_SIZE); } void DrawEngineD3D11::InitDeviceObjects() { @@ -336,7 +320,7 @@ void DrawEngineD3D11::SubmitPrim(void *verts, void *inds, GEPrimitiveType prim, vertexCountInDrawCalls_ += vertexCount; if (g_Config.bSoftwareSkinning && (vertType & GE_VTYPE_WEIGHT_MASK)) { - DecodeVertsStep(); + DecodeVertsStep(decoded, decodeCounter_, decodedVerts_); decodeCounter_++; } @@ -353,7 +337,7 @@ void DrawEngineD3D11::DecodeVerts() { const UVScale origUV = gstate_c.uv; for (; decodeCounter_ < numDrawCalls; decodeCounter_++) { gstate_c.uv = uvScale[decodeCounter_]; - DecodeVertsStep(); + DecodeVertsStep(decoded, decodeCounter_, decodedVerts_); } gstate_c.uv = origUV; @@ -365,124 +349,6 @@ void DrawEngineD3D11::DecodeVerts() { } } -void DrawEngineD3D11::DecodeVertsStep() { - const int i = decodeCounter_; - - const DeferredDrawCall &dc = drawCalls[i]; - - indexGen.SetIndex(decodedVerts_); - int indexLowerBound = dc.indexLowerBound, indexUpperBound = dc.indexUpperBound; - - u32 indexType = dc.indexType; - void *inds = dc.inds; - if (indexType == GE_VTYPE_IDX_NONE >> GE_VTYPE_IDX_SHIFT) { - // Decode the verts and apply morphing. Simple. - dec_->DecodeVerts(decoded + 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; - if (memcmp(&uvScale[j], &uvScale[i], sizeof(uvScale[0])) != 0) - 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 (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_le *)drawCalls[j].inds, indexLowerBound); - } - break; - case GE_VTYPE_IDX_32BIT >> GE_VTYPE_IDX_SHIFT: - for (int j = i; j <= lastMatch; j++) { - indexGen.TranslatePrim(drawCalls[j].prim, drawCalls[j].vertexCount, (const u32_le *)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(decoded + decodedVerts_ * (int)dec_->GetDecVtxFmt().stride, - dc.verts, indexLowerBound, indexUpperBound); - decodedVerts_ += vertexCount; - - // 4. Advance indexgen vertex counter. - indexGen.Advance(vertexCount); - decodeCounter_ = lastMatch; - } -} - -inline u32 ComputeMiniHashRange(const void *ptr, size_t sz) { - // Switch to u32 units. - const u32 *p = (const u32 *)ptr; - sz >>= 2; - - if (sz > 100) { - size_t step = sz / 4; - u32 hash = 0; - for (size_t i = 0; i < sz; i += step) { - hash += DoReliableHash32(p + i, 100, 0x3A44B9C4); - } - return hash; - } else { - return p[0] + p[sz - 1]; - } -} - -u32 DrawEngineD3D11::ComputeMiniHash() { - u32 fullhash = 0; - const int vertexSize = dec_->GetDecVtxFmt().stride; - const int indexSize = IndexSize(dec_->VertexType()); - - int step; - if (numDrawCalls < 3) { - step = 1; - } else if (numDrawCalls < 8) { - step = 4; - } else { - step = numDrawCalls / 8; - } - for (int i = 0; i < numDrawCalls; i += step) { - const DeferredDrawCall &dc = drawCalls[i]; - if (!dc.inds) { - fullhash += ComputeMiniHashRange(dc.verts, vertexSize * dc.vertexCount); - } else { - int indexLowerBound = dc.indexLowerBound, indexUpperBound = dc.indexUpperBound; - fullhash += ComputeMiniHashRange((const u8 *)dc.verts + vertexSize * indexLowerBound, vertexSize * (indexUpperBound - indexLowerBound)); - fullhash += ComputeMiniHashRange(dc.inds, indexSize * dc.vertexCount); - } - } - - return fullhash; -} - void DrawEngineD3D11::MarkUnreliable(VertexArrayInfoD3D11 *vai) { vai->status = VertexArrayInfoD3D11::VAI_UNRELIABLE; if (vai->vbo) { @@ -495,45 +361,6 @@ void DrawEngineD3D11::MarkUnreliable(VertexArrayInfoD3D11 *vai) { } } -ReliableHashType DrawEngineD3D11::ComputeHash() { - ReliableHashType fullhash = 0; - const int vertexSize = dec_->GetDecVtxFmt().stride; - const int indexSize = IndexSize(dec_->VertexType()); - - // TODO: Add some caps both for numDrawCalls and num verts to check? - // It is really very expensive to check all the vertex data so often. - for (int i = 0; i < numDrawCalls; i++) { - const DeferredDrawCall &dc = drawCalls[i]; - if (!dc.inds) { - fullhash += DoReliableHash((const char *)dc.verts, vertexSize * dc.vertexCount, 0x1DE8CAC4); - } else { - int indexLowerBound = dc.indexLowerBound, indexUpperBound = dc.indexUpperBound; - int j = i + 1; - int lastMatch = i; - while (j < numDrawCalls) { - if (drawCalls[j].verts != dc.verts) - break; - indexLowerBound = std::min(indexLowerBound, (int)dc.indexLowerBound); - indexUpperBound = std::max(indexUpperBound, (int)dc.indexUpperBound); - lastMatch = j; - j++; - } - // This could get seriously expensive with sparse indices. Need to combine hashing ranges the same way - // we do when drawing. - fullhash += DoReliableHash((const char *)dc.verts + vertexSize * indexLowerBound, - vertexSize * (indexUpperBound - indexLowerBound), 0x029F3EE1); - // Hm, we will miss some indices when combining above, but meh, it should be fine. - fullhash += DoReliableHash((const char *)dc.inds, indexSize * dc.vertexCount, 0x955FD1CA); - i = lastMatch; - } - } - if (uvScale) { - fullhash += DoReliableHash(&uvScale[0], sizeof(uvScale[0]) * numDrawCalls, 0x0123e658); - } - - return fullhash; -} - void DrawEngineD3D11::BeginFrame() { pushVerts_->Reset(); pushInds_->Reset(); diff --git a/GPU/D3D11/DrawEngineD3D11.h b/GPU/D3D11/DrawEngineD3D11.h index 64867b69a2..2d2203a5be 100644 --- a/GPU/D3D11/DrawEngineD3D11.h +++ b/GPU/D3D11/DrawEngineD3D11.h @@ -55,13 +55,6 @@ enum { VAI11_FLAG_VERTEXFULLALPHA = 1, }; -// Avoiding the full include of TextureDecoder.h. -#if (defined(_M_SSE) && defined(_M_X64)) || defined(ARM64) -typedef u64 ReliableHashType; -#else -typedef u32 ReliableHashType; -#endif - // Try to keep this POD. class VertexArrayInfoD3D11 { public: @@ -158,7 +151,6 @@ public: private: void DecodeVerts(); - void DecodeVertsStep(); void DoFlush(); void ApplyDrawState(int prim); @@ -169,8 +161,6 @@ private: ID3D11InputLayout *SetupDecFmtForDraw(D3D11VertexShader *vshader, const DecVtxFormat &decFmt, u32 pspFmt); - u32 ComputeMiniHash(); - ReliableHashType ComputeHash(); // Reads deferred vertex data. void MarkUnreliable(VertexArrayInfoD3D11 *vai); Draw::DrawContext *draw_; // Used for framebuffer related things exclusively. @@ -179,27 +169,6 @@ private: ID3D11DeviceContext *context_; ID3D11DeviceContext1 *context1_; - // Defer all vertex decoding to a Flush, so that we can hash and cache the - // generated buffers without having to redecode them every time. - struct DeferredDrawCall { - void *verts; - void *inds; - u32 vertType; - u8 indexType; - s8 prim; - u32 vertexCount; - u16 indexLowerBound; - u16 indexUpperBound; - }; - - // Vertex collector state - IndexGenerator indexGen; - int decodedVerts_; - GEPrimitiveType prevPrim_; - - TransformedVertex *transformed; - TransformedVertex *transformedExpanded; - std::unordered_map vai_; struct InputLayoutKey { @@ -217,26 +186,14 @@ private: std::map inputLayoutMap_; // Other - ShaderManagerD3D11 *shaderManager_; - TextureCacheD3D11 *textureCache_; - FramebufferManagerD3D11 *framebufferManager_; + ShaderManagerD3D11 *shaderManager_ = nullptr; + TextureCacheD3D11 *textureCache_ = nullptr; + FramebufferManagerD3D11 *framebufferManager_ = nullptr; // Pushbuffers PushBufferD3D11 *pushVerts_; PushBufferD3D11 *pushInds_; - enum { MAX_DEFERRED_DRAW_CALLS = 128 }; - - DeferredDrawCall drawCalls[MAX_DEFERRED_DRAW_CALLS]; - int numDrawCalls; - int vertexCountInDrawCalls_; - - int decimationCounter_; - int decodeCounter_; - u32 dcid_; - - UVScale uvScale[MAX_DEFERRED_DRAW_CALLS]; - // D3D11 state object caches std::map blendCache_; std::map blendCache1_; diff --git a/GPU/Directx9/DrawEngineDX9.cpp b/GPU/Directx9/DrawEngineDX9.cpp index 8339f7c1a4..6eaf513079 100644 --- a/GPU/Directx9/DrawEngineDX9.cpp +++ b/GPU/Directx9/DrawEngineDX9.cpp @@ -32,7 +32,6 @@ #include "GPU/GPUState.h" #include "GPU/ge_constants.h" -#include "GPU/Common/TextureDecoder.h" #include "GPU/Common/SplineCommon.h" #include "GPU/Common/TransformCommon.h" #include "GPU/Common/VertexDecoderCommon.h" @@ -85,16 +84,7 @@ static const D3DVERTEXELEMENT9 TransformedVertexElements[] = { D3DDECL_END() }; -DrawEngineDX9::DrawEngineDX9(Draw::DrawContext *draw) - : decodedVerts_(0), - prevPrim_(GE_PRIM_INVALID), - shaderManager_(0), - textureCache_(0), - framebufferManager_(0), - numDrawCalls(0), - vertexCountInDrawCalls(0), - decodeCounter_(0), - dcid_(0) { +DrawEngineDX9::DrawEngineDX9(Draw::DrawContext *draw) { device_ = (LPDIRECT3DDEVICE9)draw->GetNativeObject(Draw::NativeObject::DEVICE); decOptions_.expandAllWeightsToFloat = true; decOptions_.expand8BitNormalsToFloat = true; @@ -106,8 +96,6 @@ DrawEngineDX9::DrawEngineDX9(Draw::DrawContext *draw) decoded = (u8 *)AllocateMemoryPages(DECODED_VERTEX_BUFFER_SIZE, MEM_PROT_READ | MEM_PROT_WRITE); decIndex = (u16 *)AllocateMemoryPages(DECODED_INDEX_BUFFER_SIZE, MEM_PROT_READ | MEM_PROT_WRITE); splineBuffer = (u8 *)AllocateMemoryPages(SPLINE_BUFFER_SIZE, MEM_PROT_READ | MEM_PROT_WRITE); - transformed = (TransformedVertex *)AllocateMemoryPages(TRANSFORMED_VERTEX_BUFFER_SIZE, MEM_PROT_READ | MEM_PROT_WRITE); - transformedExpanded = (TransformedVertex *)AllocateMemoryPages(3 * TRANSFORMED_VERTEX_BUFFER_SIZE, MEM_PROT_READ | MEM_PROT_WRITE); indexGen.Setup(decIndex); @@ -127,8 +115,6 @@ DrawEngineDX9::~DrawEngineDX9() { FreeMemoryPages(decoded, DECODED_VERTEX_BUFFER_SIZE); FreeMemoryPages(decIndex, DECODED_INDEX_BUFFER_SIZE); FreeMemoryPages(splineBuffer, SPLINE_BUFFER_SIZE); - FreeMemoryPages(transformed, TRANSFORMED_VERTEX_BUFFER_SIZE); - FreeMemoryPages(transformedExpanded, 3 * TRANSFORMED_VERTEX_BUFFER_SIZE); for (auto decl = vertexDeclMap_.begin(); decl != vertexDeclMap_.end(); ++decl) { if (decl->second) { decl->second->Release(); @@ -250,24 +236,8 @@ IDirect3DVertexDeclaration9 *DrawEngineDX9::SetupDecFmtForDraw(VSShader *vshader } } -void DrawEngineDX9::SetupVertexDecoder(u32 vertType) { - SetupVertexDecoderInternal(vertType); -} - -inline void DrawEngineDX9::SetupVertexDecoderInternal(u32 vertType) { - // As the decoder depends on the UVGenMode when we use UV prescale, we simply mash it - // into the top of the verttype where there are unused bits. - const u32 vertTypeID = (vertType & 0xFFFFFF) | (gstate.getUVGenMode() << 24); - - // If vtype has changed, setup the vertex decoder. - if (vertTypeID != lastVType_) { - dec_ = GetVertexDecoder(vertTypeID); - lastVType_ = vertTypeID; - } -} - void DrawEngineDX9::SubmitPrim(void *verts, void *inds, GEPrimitiveType prim, int vertexCount, u32 vertType, int *bytesRead) { - if (!indexGen.PrimCompatible(prevPrim_, prim) || numDrawCalls >= MAX_DEFERRED_DRAW_CALLS || vertexCountInDrawCalls + vertexCount > VERTEX_BUFFER_MAX) + if (!indexGen.PrimCompatible(prevPrim_, prim) || numDrawCalls >= MAX_DEFERRED_DRAW_CALLS || vertexCountInDrawCalls_ + vertexCount > VERTEX_BUFFER_MAX) Flush(); // TODO: Is this the right thing to do? @@ -277,7 +247,7 @@ void DrawEngineDX9::SubmitPrim(void *verts, void *inds, GEPrimitiveType prim, in prevPrim_ = prim; } - SetupVertexDecoderInternal(vertType); + SetupVertexDecoder(vertType); *bytesRead = vertexCount * dec_->VertexSize(); @@ -314,10 +284,10 @@ void DrawEngineDX9::SubmitPrim(void *verts, void *inds, GEPrimitiveType prim, in uvScale[numDrawCalls] = gstate_c.uv; numDrawCalls++; - vertexCountInDrawCalls += vertexCount; + vertexCountInDrawCalls_ += vertexCount; if (g_Config.bSoftwareSkinning && (vertType & GE_VTYPE_WEIGHT_MASK)) { - DecodeVertsStep(); + DecodeVertsStep(decoded, decodeCounter_, decodedVerts_); decodeCounter_++; } @@ -334,7 +304,7 @@ void DrawEngineDX9::DecodeVerts() { const UVScale origUV = gstate_c.uv; for (; decodeCounter_ < numDrawCalls; decodeCounter_++) { gstate_c.uv = uvScale[decodeCounter_]; - DecodeVertsStep(); + DecodeVertsStep(decoded, decodeCounter_, decodedVerts_); } gstate_c.uv = origUV; @@ -346,124 +316,6 @@ void DrawEngineDX9::DecodeVerts() { } } -void DrawEngineDX9::DecodeVertsStep() { - const int i = decodeCounter_; - - const DeferredDrawCall &dc = drawCalls[i]; - - indexGen.SetIndex(decodedVerts_); - int indexLowerBound = dc.indexLowerBound, indexUpperBound = dc.indexUpperBound; - - u32 indexType = dc.indexType; - void *inds = dc.inds; - if (indexType == GE_VTYPE_IDX_NONE >> GE_VTYPE_IDX_SHIFT) { - // Decode the verts and apply morphing. Simple. - dec_->DecodeVerts(decoded + 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; - if (memcmp(&uvScale[j], &uvScale[i], sizeof(uvScale[0])) != 0) - 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 (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_le *)drawCalls[j].inds, indexLowerBound); - } - break; - case GE_VTYPE_IDX_32BIT >> GE_VTYPE_IDX_SHIFT: - for (int j = i; j <= lastMatch; j++) { - indexGen.TranslatePrim(drawCalls[j].prim, drawCalls[j].vertexCount, (const u32_le *)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(decoded + decodedVerts_ * (int)dec_->GetDecVtxFmt().stride, - dc.verts, indexLowerBound, indexUpperBound); - decodedVerts_ += vertexCount; - - // 4. Advance indexgen vertex counter. - indexGen.Advance(vertexCount); - decodeCounter_ = lastMatch; - } -} - -inline u32 ComputeMiniHashRange(const void *ptr, size_t sz) { - // Switch to u32 units. - const u32 *p = (const u32 *)ptr; - sz >>= 2; - - if (sz > 100) { - size_t step = sz / 4; - u32 hash = 0; - for (size_t i = 0; i < sz; i += step) { - hash += DoReliableHash32(p + i, 100, 0x3A44B9C4); - } - return hash; - } else { - return p[0] + p[sz - 1]; - } -} - -u32 DrawEngineDX9::ComputeMiniHash() { - u32 fullhash = 0; - const int vertexSize = dec_->GetDecVtxFmt().stride; - const int indexSize = IndexSize(dec_->VertexType()); - - int step; - if (numDrawCalls < 3) { - step = 1; - } else if (numDrawCalls < 8) { - step = 4; - } else { - step = numDrawCalls / 8; - } - for (int i = 0; i < numDrawCalls; i += step) { - const DeferredDrawCall &dc = drawCalls[i]; - if (!dc.inds) { - fullhash += ComputeMiniHashRange(dc.verts, vertexSize * dc.vertexCount); - } else { - int indexLowerBound = dc.indexLowerBound, indexUpperBound = dc.indexUpperBound; - fullhash += ComputeMiniHashRange((const u8 *)dc.verts + vertexSize * indexLowerBound, vertexSize * (indexUpperBound - indexLowerBound)); - fullhash += ComputeMiniHashRange(dc.inds, indexSize * dc.vertexCount); - } - } - - return fullhash; -} - void DrawEngineDX9::MarkUnreliable(VertexArrayInfoDX9 *vai) { vai->status = VertexArrayInfoDX9::VAI_UNRELIABLE; if (vai->vbo) { @@ -476,45 +328,6 @@ void DrawEngineDX9::MarkUnreliable(VertexArrayInfoDX9 *vai) { } } -ReliableHashType DrawEngineDX9::ComputeHash() { - ReliableHashType fullhash = 0; - const int vertexSize = dec_->GetDecVtxFmt().stride; - const int indexSize = IndexSize(dec_->VertexType()); - - // TODO: Add some caps both for numDrawCalls and num verts to check? - // It is really very expensive to check all the vertex data so often. - for (int i = 0; i < numDrawCalls; i++) { - const DeferredDrawCall &dc = drawCalls[i]; - if (!dc.inds) { - fullhash += DoReliableHash((const char *)dc.verts, vertexSize * dc.vertexCount, 0x1DE8CAC4); - } else { - int indexLowerBound = dc.indexLowerBound, indexUpperBound = dc.indexUpperBound; - int j = i + 1; - int lastMatch = i; - while (j < numDrawCalls) { - if (drawCalls[j].verts != dc.verts) - break; - indexLowerBound = std::min(indexLowerBound, (int)dc.indexLowerBound); - indexUpperBound = std::max(indexUpperBound, (int)dc.indexUpperBound); - lastMatch = j; - j++; - } - // This could get seriously expensive with sparse indices. Need to combine hashing ranges the same way - // we do when drawing. - fullhash += DoReliableHash((const char *)dc.verts + vertexSize * indexLowerBound, - vertexSize * (indexUpperBound - indexLowerBound), 0x029F3EE1); - // Hm, we will miss some indices when combining above, but meh, it should be fine. - fullhash += DoReliableHash((const char *)dc.inds, indexSize * dc.vertexCount, 0x955FD1CA); - i = lastMatch; - } - } - if (uvScale) { - fullhash += DoReliableHash(&uvScale[0], sizeof(uvScale[0]) * numDrawCalls, 0x0123e658); - } - - return fullhash; -} - void DrawEngineDX9::ClearTrackedVertexArrays() { for (auto vai = vai_.begin(); vai != vai_.end(); vai++) { delete vai->second; @@ -880,12 +693,12 @@ rotateVBO: } gpuStats.numDrawCalls += numDrawCalls; - gpuStats.numVertsSubmitted += vertexCountInDrawCalls; + gpuStats.numVertsSubmitted += vertexCountInDrawCalls_; indexGen.Reset(); decodedVerts_ = 0; numDrawCalls = 0; - vertexCountInDrawCalls = 0; + vertexCountInDrawCalls_ = 0; decodeCounter_ = 0; dcid_ = 0; prevPrim_ = GE_PRIM_INVALID; diff --git a/GPU/Directx9/DrawEngineDX9.h b/GPU/Directx9/DrawEngineDX9.h index 128154eab4..45a413aad8 100644 --- a/GPU/Directx9/DrawEngineDX9.h +++ b/GPU/Directx9/DrawEngineDX9.h @@ -54,13 +54,6 @@ enum { VAI_FLAG_VERTEXFULLALPHA = 1, }; -// Avoiding the full include of TextureDecoder.h. -#if (defined(_M_SSE) && defined(_M_X64)) || defined(ARM64) -typedef u64 ReliableHashType; -#else -typedef u32 ReliableHashType; -#endif - // Try to keep this POD. class VertexArrayInfoDX9 { public: @@ -129,9 +122,6 @@ public: void ClearTrackedVertexArrays() override; void DecimateTrackedVertexArrays(); - void SetupVertexDecoder(u32 vertType); - void SetupVertexDecoderInternal(u32 vertType); - // So that this can be inlined void Flush() { if (!numDrawCalls) @@ -154,7 +144,6 @@ public: private: void DecodeVerts(); - void DecodeVertsStep(); void DoFlush(); void ApplyDrawState(int prim); @@ -163,55 +152,20 @@ private: IDirect3DVertexDeclaration9 *SetupDecFmtForDraw(VSShader *vshader, const DecVtxFormat &decFmt, u32 pspFmt); - u32 ComputeMiniHash(); - ReliableHashType ComputeHash(); // Reads deferred vertex data. void MarkUnreliable(VertexArrayInfoDX9 *vai); - LPDIRECT3DDEVICE9 device_; - - // Defer all vertex decoding to a Flush, so that we can hash and cache the - // generated buffers without having to redecode them every time. - struct DeferredDrawCall { - void *verts; - void *inds; - u32 vertType; - u8 indexType; - s8 prim; - u32 vertexCount; - u16 indexLowerBound; - u16 indexUpperBound; - }; - - // Vertex collector state - IndexGenerator indexGen; - int decodedVerts_; - GEPrimitiveType prevPrim_; - - TransformedVertex *transformed; - TransformedVertex *transformedExpanded; + LPDIRECT3DDEVICE9 device_ = nullptr; std::unordered_map vai_; std::unordered_map vertexDeclMap_; // SimpleVertex - IDirect3DVertexDeclaration9* transformedVertexDecl_; + IDirect3DVertexDeclaration9* transformedVertexDecl_ = nullptr; // Other - ShaderManagerDX9 *shaderManager_; - TextureCacheDX9 *textureCache_; - FramebufferManagerDX9 *framebufferManager_; - - enum { MAX_DEFERRED_DRAW_CALLS = 128 }; - - DeferredDrawCall drawCalls[MAX_DEFERRED_DRAW_CALLS]; - int numDrawCalls; - int vertexCountInDrawCalls; - - int decimationCounter_; - int decodeCounter_; - u32 dcid_; - - UVScale uvScale[MAX_DEFERRED_DRAW_CALLS]; + ShaderManagerDX9 *shaderManager_ = nullptr; + TextureCacheDX9 *textureCache_ = nullptr; + FramebufferManagerDX9 *framebufferManager_ = nullptr; // Hardware tessellation class TessellationDataTransferDX9 : public TessellationDataTransfer { diff --git a/GPU/GLES/DrawEngineGLES.cpp b/GPU/GLES/DrawEngineGLES.cpp index cf8ebbe6d6..9fb13b9dc4 100644 --- a/GPU/GLES/DrawEngineGLES.cpp +++ b/GPU/GLES/DrawEngineGLES.cpp @@ -115,16 +115,7 @@ enum { enum { VAI_KILL_AGE = 120, VAI_UNRELIABLE_KILL_AGE = 240, VAI_UNRELIABLE_KILL_MAX = 4 }; -DrawEngineGLES::DrawEngineGLES() - : decodedVerts_(0), - prevPrim_(GE_PRIM_INVALID), - shaderManager_(nullptr), - textureCache_(nullptr), - framebufferManager_(nullptr), - numDrawCalls(0), - vertexCountInDrawCalls(0), - decodeCounter_(0), - dcid_(0) { +DrawEngineGLES::DrawEngineGLES() { decOptions_.expandAllWeightsToFloat = false; decOptions_.expand8BitNormalsToFloat = false; @@ -137,8 +128,6 @@ DrawEngineGLES::DrawEngineGLES() decoded = (u8 *)AllocateMemoryPages(DECODED_VERTEX_BUFFER_SIZE, MEM_PROT_READ | MEM_PROT_WRITE); decIndex = (u16 *)AllocateMemoryPages(DECODED_INDEX_BUFFER_SIZE, MEM_PROT_READ | MEM_PROT_WRITE); splineBuffer = (u8 *)AllocateMemoryPages(SPLINE_BUFFER_SIZE, MEM_PROT_READ | MEM_PROT_WRITE); - transformed = (TransformedVertex *)AllocateMemoryPages(TRANSFORMED_VERTEX_BUFFER_SIZE, MEM_PROT_READ | MEM_PROT_WRITE); - transformedExpanded = (TransformedVertex *)AllocateMemoryPages(3 * TRANSFORMED_VERTEX_BUFFER_SIZE, MEM_PROT_READ | MEM_PROT_WRITE); indexGen.Setup(decIndex); @@ -153,8 +142,6 @@ DrawEngineGLES::~DrawEngineGLES() { FreeMemoryPages(decoded, DECODED_VERTEX_BUFFER_SIZE); FreeMemoryPages(decIndex, DECODED_INDEX_BUFFER_SIZE); FreeMemoryPages(splineBuffer, SPLINE_BUFFER_SIZE); - FreeMemoryPages(transformed, TRANSFORMED_VERTEX_BUFFER_SIZE); - FreeMemoryPages(transformedExpanded, 3 * TRANSFORMED_VERTEX_BUFFER_SIZE); unregister_gl_resource_holder(this); @@ -265,24 +252,8 @@ static void SetupDecFmtForDraw(LinkedShader *program, const DecVtxFormat &decFmt CHECK_GL_ERROR_IF_DEBUG(); } -void DrawEngineGLES::SetupVertexDecoder(u32 vertType) { - SetupVertexDecoderInternal(vertType); -} - -inline void DrawEngineGLES::SetupVertexDecoderInternal(u32 vertType) { - // As the decoder depends on the UVGenMode when we use UV prescale, we simply mash it - // into the top of the verttype where there are unused bits. - const u32 vertTypeID = (vertType & 0xFFFFFF) | (gstate.getUVGenMode() << 24); - - // If vtype has changed, setup the vertex decoder. - if (vertTypeID != lastVType_) { - dec_ = GetVertexDecoder(vertTypeID); - lastVType_ = vertTypeID; - } -} - void DrawEngineGLES::SubmitPrim(void *verts, void *inds, GEPrimitiveType prim, int vertexCount, u32 vertType, int *bytesRead) { - if (!indexGen.PrimCompatible(prevPrim_, prim) || numDrawCalls >= MAX_DEFERRED_DRAW_CALLS || vertexCountInDrawCalls + vertexCount > VERTEX_BUFFER_MAX) + if (!indexGen.PrimCompatible(prevPrim_, prim) || numDrawCalls >= MAX_DEFERRED_DRAW_CALLS || vertexCountInDrawCalls_ + vertexCount > VERTEX_BUFFER_MAX) Flush(); // TODO: Is this the right thing to do? @@ -292,7 +263,7 @@ void DrawEngineGLES::SubmitPrim(void *verts, void *inds, GEPrimitiveType prim, i prevPrim_ = prim; } - SetupVertexDecoderInternal(vertType); + SetupVertexDecoder(vertType); *bytesRead = vertexCount * dec_->VertexSize(); @@ -329,10 +300,10 @@ void DrawEngineGLES::SubmitPrim(void *verts, void *inds, GEPrimitiveType prim, i uvScale[numDrawCalls] = gstate_c.uv; numDrawCalls++; - vertexCountInDrawCalls += vertexCount; + vertexCountInDrawCalls_ += vertexCount; if (g_Config.bSoftwareSkinning && (vertType & GE_VTYPE_WEIGHT_MASK)) { - DecodeVertsStep(); + DecodeVertsStep(decoded, decodeCounter_, decodedVerts_); decodeCounter_++; } @@ -349,7 +320,7 @@ void DrawEngineGLES::DecodeVerts() { const UVScale origUV = gstate_c.uv; for (; decodeCounter_ < numDrawCalls; decodeCounter_++) { gstate_c.uv = uvScale[decodeCounter_]; - DecodeVertsStep(); + DecodeVertsStep(decoded, decodeCounter_, decodedVerts_); } gstate_c.uv = origUV; // Sanity check @@ -360,125 +331,6 @@ void DrawEngineGLES::DecodeVerts() { } } -void DrawEngineGLES::DecodeVertsStep() { - PROFILE_THIS_SCOPE("vertdec"); - - const int i = decodeCounter_; - - const DeferredDrawCall &dc = drawCalls[i]; - - indexGen.SetIndex(decodedVerts_); - int indexLowerBound = dc.indexLowerBound, indexUpperBound = dc.indexUpperBound; - - u32 indexType = dc.indexType; - if (indexType == (GE_VTYPE_IDX_NONE >> GE_VTYPE_IDX_SHIFT)) { - // Decode the verts and apply morphing. Simple. - dec_->DecodeVerts(decoded + 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; - if (memcmp(&uvScale[j], &uvScale[i], sizeof(uvScale[0])) != 0) - 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 (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_le *)drawCalls[j].inds, indexLowerBound); - } - break; - case GE_VTYPE_IDX_32BIT >> GE_VTYPE_IDX_SHIFT: - for (int j = i; j <= lastMatch; j++) { - indexGen.TranslatePrim(drawCalls[j].prim, drawCalls[j].vertexCount, (const u32_le *)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. - int stride = (int)dec_->GetDecVtxFmt().stride; - dec_->DecodeVerts(decoded + decodedVerts_ * stride, dc.verts, indexLowerBound, indexUpperBound); - decodedVerts_ += vertexCount; - - // 4. Advance indexgen vertex counter. - indexGen.Advance(vertexCount); - decodeCounter_ = lastMatch; - } -} - -inline u32 ComputeMiniHashRange(const void *ptr, size_t sz) { - // Switch to u32 units. - const u32 *p = (const u32 *)ptr; - sz >>= 2; - - if (sz > 100) { - size_t step = sz / 4; - u32 hash = 0; - for (size_t i = 0; i < sz; i += step) { - hash += DoReliableHash32(p + i, 100, 0x3A44B9C4); - } - return hash; - } else { - return p[0] + p[sz - 1]; - } -} - -u32 DrawEngineGLES::ComputeMiniHash() { - u32 fullhash = 0; - const int vertexSize = dec_->GetDecVtxFmt().stride; - const int indexSize = IndexSize(dec_->VertexType()); - - int step; - if (numDrawCalls < 3) { - step = 1; - } else if (numDrawCalls < 8) { - step = 4; - } else { - step = numDrawCalls / 8; - } - for (int i = 0; i < numDrawCalls; i += step) { - const DeferredDrawCall &dc = drawCalls[i]; - if (!dc.inds) { - fullhash += ComputeMiniHashRange(dc.verts, vertexSize * dc.vertexCount); - } else { - int indexLowerBound = dc.indexLowerBound, indexUpperBound = dc.indexUpperBound; - fullhash += ComputeMiniHashRange((const u8 *)dc.verts + vertexSize * indexLowerBound, vertexSize * (indexUpperBound - indexLowerBound)); - fullhash += ComputeMiniHashRange(dc.inds, indexSize * dc.vertexCount); - } - } - - return fullhash; -} - void DrawEngineGLES::MarkUnreliable(VertexArrayInfo *vai) { vai->status = VertexArrayInfo::VAI_UNRELIABLE; if (vai->vbo) { @@ -491,43 +343,6 @@ void DrawEngineGLES::MarkUnreliable(VertexArrayInfo *vai) { } } -ReliableHashType DrawEngineGLES::ComputeHash() { - ReliableHashType fullhash = 0; - const int vertexSize = dec_->GetDecVtxFmt().stride; - const int indexSize = IndexSize(dec_->VertexType()); - - // TODO: Add some caps both for numDrawCalls and num verts to check? - // It is really very expensive to check all the vertex data so often. - for (int i = 0; i < numDrawCalls; i++) { - const DeferredDrawCall &dc = drawCalls[i]; - if (!dc.inds) { - fullhash += DoReliableHash((const char *)dc.verts, vertexSize * dc.vertexCount, 0x1DE8CAC4); - } else { - int indexLowerBound = dc.indexLowerBound, indexUpperBound = dc.indexUpperBound; - int j = i + 1; - int lastMatch = i; - while (j < numDrawCalls) { - if (drawCalls[j].verts != dc.verts) - break; - indexLowerBound = std::min(indexLowerBound, (int)dc.indexLowerBound); - indexUpperBound = std::max(indexUpperBound, (int)dc.indexUpperBound); - lastMatch = j; - j++; - } - // This could get seriously expensive with sparse indices. Need to combine hashing ranges the same way - // we do when drawing. - fullhash += DoReliableHash((const char *)dc.verts + vertexSize * indexLowerBound, - vertexSize * (indexUpperBound - indexLowerBound), 0x029F3EE1); - // Hm, we will miss some indices when combining above, but meh, it should be fine. - fullhash += DoReliableHash((const char *)dc.inds, indexSize * dc.vertexCount, 0x955FD1CA); - i = lastMatch; - } - } - fullhash += DoReliableHash(&uvScale[0], sizeof(uvScale[0]) * numDrawCalls, 0x0123e658); - - return fullhash; -} - void DrawEngineGLES::ClearTrackedVertexArrays() { for (auto vai = vai_.begin(); vai != vai_.end(); vai++) { FreeVertexArray(vai->second); @@ -984,12 +799,12 @@ rotateVBO: } gpuStats.numDrawCalls += numDrawCalls; - gpuStats.numVertsSubmitted += vertexCountInDrawCalls; + gpuStats.numVertsSubmitted += vertexCountInDrawCalls_; indexGen.Reset(); decodedVerts_ = 0; numDrawCalls = 0; - vertexCountInDrawCalls = 0; + vertexCountInDrawCalls_ = 0; decodeCounter_ = 0; dcid_ = 0; prevPrim_ = GE_PRIM_INVALID; diff --git a/GPU/GLES/DrawEngineGLES.h b/GPU/GLES/DrawEngineGLES.h index 71535dd2d6..c8423cb44e 100644 --- a/GPU/GLES/DrawEngineGLES.h +++ b/GPU/GLES/DrawEngineGLES.h @@ -55,13 +55,6 @@ enum { VAI_FLAG_VERTEXFULLALPHA = 1, }; -// Avoiding the full include of TextureDecoder.h. -#if (defined(_M_SSE) && defined(_M_X64)) || defined(ARM64) -typedef u64 ReliableHashType; -#else -typedef u32 ReliableHashType; -#endif - // Try to keep this POD. class VertexArrayInfo { public: @@ -135,9 +128,6 @@ public: void ClearTrackedVertexArrays() override; void DecimateTrackedVertexArrays(); - void SetupVertexDecoder(u32 vertType); - inline void SetupVertexDecoderInternal(u32 vertType); - // So that this can be inlined void Flush() { if (!numDrawCalls) @@ -165,7 +155,6 @@ public: private: void DecodeVerts(); - void DecodeVertsStep(); void DoFlush(); void ApplyDrawState(int prim); void ApplyDrawStateLate(); @@ -175,31 +164,8 @@ private: void FreeBuffer(GLuint buf); void FreeVertexArray(VertexArrayInfo *vai); - u32 ComputeMiniHash(); - ReliableHashType ComputeHash(); // Reads deferred vertex data. void MarkUnreliable(VertexArrayInfo *vai); - // Defer all vertex decoding to a Flush, so that we can hash and cache the - // generated buffers without having to redecode them every time. - struct DeferredDrawCall { - void *verts; - void *inds; - u32 vertType; - u8 indexType; - s8 prim; - u32 vertexCount; - u16 indexLowerBound; - u16 indexUpperBound; - }; - - // Vertex collector state - IndexGenerator indexGen; - int decodedVerts_; - GEPrimitiveType prevPrim_; - - TransformedVertex *transformed; - TransformedVertex *transformedExpanded; - std::unordered_map vai_; // Vertex buffer objects @@ -215,26 +181,16 @@ private: std::multimap freeSizedBuffers_; std::unordered_map bufferNameInfo_; std::vector buffersThisFrame_; - size_t bufferNameCacheSize_; - GLuint sharedVao_; + size_t bufferNameCacheSize_ = 0; + GLuint sharedVao_ = 0; // Other - ShaderManagerGLES *shaderManager_; - TextureCacheGLES *textureCache_; - FramebufferManagerGLES *framebufferManager_; - FragmentTestCacheGLES *fragmentTestCache_; + ShaderManagerGLES *shaderManager_ = nullptr; + TextureCacheGLES *textureCache_ = nullptr; + FramebufferManagerGLES *framebufferManager_ = nullptr; + FragmentTestCacheGLES *fragmentTestCache_ = nullptr; - enum { MAX_DEFERRED_DRAW_CALLS = 128 }; - DeferredDrawCall drawCalls[MAX_DEFERRED_DRAW_CALLS]; - int numDrawCalls; - int vertexCountInDrawCalls; - - int decimationCounter_; - int bufferDecimationCounter_; - int decodeCounter_; - u32 dcid_; - - UVScale uvScale[MAX_DEFERRED_DRAW_CALLS]; + int bufferDecimationCounter_ = 0; // Hardware tessellation class TessellationDataTransferGLES : public TessellationDataTransfer { diff --git a/GPU/Vulkan/DrawEngineVulkan.cpp b/GPU/Vulkan/DrawEngineVulkan.cpp index 84d0bd5fc7..82e78711a8 100644 --- a/GPU/Vulkan/DrawEngineVulkan.cpp +++ b/GPU/Vulkan/DrawEngineVulkan.cpp @@ -67,8 +67,6 @@ enum { DrawEngineVulkan::DrawEngineVulkan(VulkanContext *vulkan, Draw::DrawContext *draw) : vulkan_(vulkan), draw_(draw), - prevPrim_(GE_PRIM_INVALID), - numDrawCalls(0), curFrame_(0), stats_{} { decOptions_.expandAllWeightsToFloat = false; @@ -79,8 +77,6 @@ DrawEngineVulkan::DrawEngineVulkan(VulkanContext *vulkan, Draw::DrawContext *dra decoded = (u8 *)AllocateMemoryPages(DECODED_VERTEX_BUFFER_SIZE, MEM_PROT_READ | MEM_PROT_WRITE); decIndex = (u16 *)AllocateMemoryPages(DECODED_INDEX_BUFFER_SIZE, MEM_PROT_READ | MEM_PROT_WRITE); splineBuffer = (u8 *)AllocateMemoryPages(SPLINE_BUFFER_SIZE, MEM_PROT_READ | MEM_PROT_WRITE); - transformed = (TransformedVertex *)AllocateMemoryPages(TRANSFORMED_VERTEX_BUFFER_SIZE, MEM_PROT_READ | MEM_PROT_WRITE); - transformedExpanded = (TransformedVertex *)AllocateMemoryPages(3 * TRANSFORMED_VERTEX_BUFFER_SIZE, MEM_PROT_READ | MEM_PROT_WRITE); indexGen.Setup(decIndex); @@ -200,8 +196,6 @@ DrawEngineVulkan::~DrawEngineVulkan() { FreeMemoryPages(decoded, DECODED_VERTEX_BUFFER_SIZE); FreeMemoryPages(decIndex, DECODED_INDEX_BUFFER_SIZE); FreeMemoryPages(splineBuffer, SPLINE_BUFFER_SIZE); - FreeMemoryPages(transformed, TRANSFORMED_VERTEX_BUFFER_SIZE); - FreeMemoryPages(transformedExpanded, 3 * TRANSFORMED_VERTEX_BUFFER_SIZE); DestroyDeviceObjects(); @@ -396,76 +390,6 @@ 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; - int 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_le *)drawCalls[j].inds, indexLowerBound); - } - break; - case GE_VTYPE_IDX_32BIT >> GE_VTYPE_IDX_SHIFT: - for (int j = i; j <= lastMatch; j++) { - indexGen.TranslatePrim(drawCalls[j].prim, drawCalls[j].vertexCount, (const u32_le *)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) { u8 *dest = decoded; @@ -503,7 +427,7 @@ void DrawEngineVulkan::DecodeVerts(VulkanPushBuffer *push, uint32_t *bindOffset, const UVScale origUV = gstate_c.uv; for (; decodeCounter_ < numDrawCalls; decodeCounter_++) { gstate_c.uv = uvScale[decodeCounter_]; - DecodeVertsStep(dest, decodeCounter_, decodedVerts_); // NOTE! DecodeVertsStep can modify i! + DecodeVertsStep(dest, decodeCounter_, decodedVerts_); // NOTE! DecodeVertsStep can modify decodeCounter_! } gstate_c.uv = origUV; @@ -515,23 +439,6 @@ void DrawEngineVulkan::DecodeVerts(VulkanPushBuffer *push, uint32_t *bindOffset, } } -inline u32 ComputeMiniHashRange(const void *ptr, size_t sz) { - // Switch to u32 units. - const u32 *p = (const u32 *)ptr; - sz >>= 2; - - if (sz > 100) { - size_t step = sz / 4; - u32 hash = 0; - for (size_t i = 0; i < sz; i += step) { - hash += DoReliableHash32(p + i, 100, 0x3A44B9C4); - } - return hash; - } else { - return p[0] + p[sz - 1]; - } -} - VkDescriptorSet DrawEngineVulkan::GetDescriptorSet(VkImageView imageView, VkSampler sampler, VkBuffer base, VkBuffer light, VkBuffer bone) { DescriptorSetKey key; key.imageView_ = imageView; diff --git a/GPU/Vulkan/DrawEngineVulkan.h b/GPU/Vulkan/DrawEngineVulkan.h index 8081ae3387..b818ad8a31 100644 --- a/GPU/Vulkan/DrawEngineVulkan.h +++ b/GPU/Vulkan/DrawEngineVulkan.h @@ -134,7 +134,6 @@ private: void DestroyDeviceObjects(); void DecodeVerts(VulkanPushBuffer *push, uint32_t *bindOffset, VkBuffer *vkbuf); - void DecodeVertsStep(u8 *dest, int &i, int &decodedVerts); void DoFlush(); void UpdateUBOs(FrameData *frame); @@ -181,26 +180,6 @@ private: int curFrame_; FrameData frame_[2]; - // Defer all vertex decoding to a "Flush" (except when software skinning) - struct DeferredDrawCall { - void *verts; - void *inds; - u32 vertType; - u8 indexType; - s8 prim; - u32 vertexCount; - u16 indexLowerBound; - u16 indexUpperBound; - }; - - // Vertex collector state - IndexGenerator indexGen; - int decodedVerts_ = 0; - GEPrimitiveType prevPrim_; - - TransformedVertex *transformed = nullptr; - TransformedVertex *transformedExpanded = nullptr; - // Other ShaderManagerVulkan *shaderManager_ = nullptr; PipelineManagerVulkan *pipelineManager_ = nullptr; @@ -209,8 +188,6 @@ private: VkSampler depalSampler_; - enum { MAX_DEFERRED_DRAW_CALLS = 128 }; - // State cache uint64_t dirtyUniforms_; uint32_t baseUBOOffset; @@ -224,15 +201,6 @@ private: VulkanTexture *nullTexture_ = nullptr; VkSampler nullSampler_ = VK_NULL_HANDLE; - DeferredDrawCall drawCalls[MAX_DEFERRED_DRAW_CALLS]; - int numDrawCalls = 0; - int vertexCountInDrawCalls_ = 0; - UVScale uvScale[MAX_DEFERRED_DRAW_CALLS]; - - int decimationCounter_ = 0; - int decodeCounter_ = 0; - u32 dcid_; - DrawEngineVulkanStats stats_; VulkanPipelineRasterStateKey pipelineKey_{};