mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-11-13 09:39:33 +01:00
Merge pull request #9770 from hrydgard/unify-drawengine
Unify some code in DrawEngine
This commit is contained in:
commit
d0aca65f5e
11 files changed
+286
-864
No files matched your search
@@ -17,26 +17,32 @@
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
#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<GPUDebugV
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
// This normalizes a set of vertices in any format to SimpleVertex format, by processing away morphing AND skinning.
|
||||
// The rest of the transform pipeline like lighting will go as normal, either hardware or software.
|
||||
// The implementation is initially a bit inefficient but shouldn't be a big deal.
|
||||
@@ -442,3 +458,156 @@ bool DrawEngineCommon::ApplyShaderBlending() {
|
||||
gstate_c.Dirty(DIRTY_SHADERBLEND);
|
||||
return true;
|
||||
}
|
||||
|
||||
void DrawEngineCommon::DecodeVertsStep(u8 *dest, int &i, int &decodedVerts) {
|
||||
PROFILE_THIS_SCOPE("vertdec");
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
@@ -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<u32, VertexDecoder *> 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;
|
||||
|
||||
@@ -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 <cstring>
|
||||
#include "IndexGenerator.h"
|
||||
|
||||
#include "Common/Common.h"
|
||||
@@ -211,16 +212,24 @@ void IndexGenerator::TranslateLineStrip(int numInds, const ITypeLE *inds, int in
|
||||
template <class ITypeLE, int flag>
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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<u32, VertexArrayInfoD3D11 *> vai_;
|
||||
|
||||
struct InputLayoutKey {
|
||||
@@ -217,26 +186,14 @@ private:
|
||||
std::map<InputLayoutKey, ID3D11InputLayout *> 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<uint64_t, ID3D11BlendState *> blendCache_;
|
||||
std::map<uint64_t, ID3D11BlendState1 *> blendCache1_;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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<u32, VertexArrayInfoDX9 *> vai_;
|
||||
std::unordered_map<u32, IDirect3DVertexDeclaration9 *> 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 {
|
||||
|
||||
+8
-193
@@ -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;
|
||||
|
||||
@@ -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<u32, VertexArrayInfo *> vai_;
|
||||
|
||||
// Vertex buffer objects
|
||||
@@ -215,26 +181,16 @@ private:
|
||||
std::multimap<size_t, GLuint> freeSizedBuffers_;
|
||||
std::unordered_map<GLuint, BufferNameInfo> bufferNameInfo_;
|
||||
std::vector<GLuint> 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 {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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_{};
|
||||
|
||||
Reference in new issue
Block a user