Skip to content

Commit

Permalink
Some unification in DrawEngine
Browse files Browse the repository at this point in the history
  • Loading branch information
hrydgard committed Jun 3, 2017
1 parent 0ac9795 commit 240e058
Show file tree
Hide file tree
Showing 11 changed files with 236 additions and 192 deletions.
157 changes: 154 additions & 3 deletions GPU/Common/DrawEngineCommon.cpp
Expand Up @@ -131,7 +131,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.
Expand Down Expand Up @@ -195,7 +195,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;
}

Expand Down Expand Up @@ -311,7 +310,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.
Expand Down Expand Up @@ -442,3 +440,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;
}
40 changes: 34 additions & 6 deletions GPU/Common/DrawEngineCommon.h
Expand Up @@ -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;
Expand Down Expand Up @@ -81,9 +82,9 @@ class DrawEngineCommon {
}

// Vertex collector buffers
u8 *decoded;
u16 *decIndex;
u8 *splineBuffer;
u8 *decoded = nullptr;
u16 *decIndex = nullptr;
u8 *splineBuffer = nullptr;

// Cached vertex decoders
u32 lastVType_ = -1;
Expand All @@ -92,12 +93,39 @@ class DrawEngineCommon {
VertexDecoderJitCache *decJitCache_;
VertexDecoderOptions decOptions_;

// 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;
Expand Down
27 changes: 18 additions & 9 deletions GPU/Common/IndexGenerator.cpp
Expand Up @@ -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"
Expand Down Expand Up @@ -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;
}
Expand Down
12 changes: 2 additions & 10 deletions GPU/D3D11/DrawEngineD3D11.cpp
Expand Up @@ -76,16 +76,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;
Expand Down
36 changes: 3 additions & 33 deletions GPU/D3D11/DrawEngineD3D11.h
Expand Up @@ -179,24 +179,6 @@ class DrawEngineD3D11 : public DrawEngineCommon {
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;

Expand All @@ -217,26 +199,14 @@ class DrawEngineD3D11 : public DrawEngineCommon {
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_;
Expand Down

0 comments on commit 240e058

Please sign in to comment.