Merge pull request #18286 from hrydgard/drawengine-refactor

DrawEngine refactor, quickly merge non-indexed consecutive draws
This commit is contained in:
Henrik Rydgård authored and GitHub committed 2023-10-03 12:17:34 +02:00
commit 4f43dac04d
16 files changed
+389 -345

No files matched your search

+178 -211
View File
@@ -72,52 +72,6 @@ VertexDecoder *DrawEngineCommon::GetVertexDecoder(u32 vtype) {
return dec;
}
int DrawEngineCommon::ComputeNumVertsToDecode() const {
int vertsToDecode = 0;
int numDrawCalls = numDrawCalls_;
if (drawCalls_[0].indexType == GE_VTYPE_IDX_NONE >> GE_VTYPE_IDX_SHIFT) {
for (int i = 0; i < numDrawCalls; i++) {
const DeferredDrawCall &dc = drawCalls_[i];
vertsToDecode += dc.vertexCount;
}
} else {
// TODO: Share this computation with DecodeVertsStep?
for (int i = 0; i < numDrawCalls; i++) {
const DeferredDrawCall &dc = drawCalls_[i];
int lastMatch = i;
const int total = numDrawCalls;
int indexLowerBound = dc.indexLowerBound;
int indexUpperBound = dc.indexUpperBound;
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;
}
vertsToDecode += indexUpperBound - indexLowerBound + 1;
i = lastMatch;
}
}
return vertsToDecode;
}
void DrawEngineCommon::DecodeVerts(u8 *dest) {
int decodeCounter = decodeCounter_;
for (; decodeCounter < numDrawCalls_; decodeCounter++) {
DecodeVertsStep(dest, decodeCounter, decodedVerts_, &drawCalls_[decodeCounter].uvScale); // NOTE! DecodeVertsStep can modify the decodeCounter parameter!
}
decodeCounter_ = decodeCounter;
// Sanity check
if (indexGen.Prim() < 0) {
ERROR_LOG_REPORT(G3D, "DecodeVerts: Failed to deduce prim: %i", indexGen.Prim());
// Force to points (0)
indexGen.AddPrim(GE_PRIM_POINTS, 0, true);
}
}
std::vector<std::string> DrawEngineCommon::DebugGetVertexLoaderIDs() {
std::vector<std::string> ids;
decoderMap_.Iterate([&](const uint32_t vtype, VertexDecoder *decoder) {
@@ -619,94 +573,6 @@ void DrawEngineCommon::ApplyFramebufferRead(FBOTexState *fboTexState) {
gstate_c.Dirty(DIRTY_SHADERBLEND);
}
void DrawEngineCommon::DecodeVertsStep(u8 *dest, int &i, int &decodedVerts, const UVScale *uvScale) {
PROFILE_THIS_SCOPE("vertdec");
const DeferredDrawCall &dc = drawCalls_[i];
indexGen.SetIndex(decodedVerts);
int indexLowerBound = dc.indexLowerBound;
int indexUpperBound = dc.indexUpperBound;
if (dc.indexType == GE_VTYPE_IDX_NONE >> GE_VTYPE_IDX_SHIFT) {
// Decode the verts (and at the same time apply morphing/skinning). Simple.
dec_->DecodeVerts(dest + decodedVerts * (int)dec_->GetDecVtxFmt().stride,
dc.verts, uvScale, indexLowerBound, indexUpperBound);
decodedVerts += indexUpperBound - indexLowerBound + 1;
bool clockwise = true;
if (gstate.isCullEnabled() && gstate.getCullMode() != dc.cullMode) {
clockwise = false;
}
indexGen.AddPrim(dc.prim, dc.vertexCount, clockwise);
} 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;
// TODO: What if UV scale/offset changes between drawcalls here?
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++) {
bool clockwise = true;
if (gstate.isCullEnabled() && gstate.getCullMode() != drawCalls_[j].cullMode) {
clockwise = false;
}
indexGen.TranslatePrim(drawCalls_[j].prim, drawCalls_[j].vertexCount, (const u8 *)drawCalls_[j].inds, indexLowerBound, clockwise);
}
break;
case GE_VTYPE_IDX_16BIT >> GE_VTYPE_IDX_SHIFT:
for (int j = i; j <= lastMatch; j++) {
bool clockwise = true;
if (gstate.isCullEnabled() && gstate.getCullMode() != drawCalls_[j].cullMode) {
clockwise = false;
}
indexGen.TranslatePrim(drawCalls_[j].prim, drawCalls_[j].vertexCount, (const u16_le *)drawCalls_[j].inds, indexLowerBound, clockwise);
}
break;
case GE_VTYPE_IDX_32BIT >> GE_VTYPE_IDX_SHIFT:
for (int j = i; j <= lastMatch; j++) {
bool clockwise = true;
if (gstate.isCullEnabled() && gstate.getCullMode() != drawCalls_[j].cullMode) {
clockwise = false;
}
indexGen.TranslatePrim(drawCalls_[j].prim, drawCalls_[j].vertexCount, (const u32_le *)drawCalls_[j].inds, indexLowerBound, clockwise);
}
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, uvScale, 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, and round up to avoid unaligned accesses.
// Probably doesn't matter if we skip the first few bytes in some cases.
@@ -731,69 +597,121 @@ u32 DrawEngineCommon::ComputeMiniHash() {
const int indexSize = IndexSize(dec_->VertexType());
int step;
if (numDrawCalls_ < 3) {
if (numDrawVerts_ < 3) {
step = 1;
} else if (numDrawCalls_ < 8) {
} else if (numDrawVerts_ < 8) {
step = 4;
} else {
step = numDrawCalls_ / 8;
step = numDrawVerts_ / 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);
for (int i = 0; i < numDrawVerts_; i += step) {
const DeferredVerts &dc = drawVerts_[i];
fullhash += ComputeMiniHashRange((const u8 *)dc.verts + vertexSize * dc.indexLowerBound, vertexSize * (dc.indexUpperBound - dc.indexLowerBound));
}
for (int i = 0; i < numDrawInds_; i += step) {
const DeferredInds &di = drawInds_[i];
if (di.inds) {
fullhash += ComputeMiniHashRange(di.inds, indexSize * di.vertexCount);
}
}
return fullhash;
}
// Cheap bit scrambler from https://nullprogram.com/blog/2018/07/31/
inline uint32_t lowbias32_r(uint32_t x) {
x ^= x >> 16;
x *= 0x43021123U;
x ^= x >> 15 ^ x >> 30;
x *= 0x1d69e2a5U;
x ^= x >> 16;
return x;
}
uint32_t DrawEngineCommon::ComputeDrawcallsHash() const {
uint32_t dcid = 0;
for (int i = 0; i < numDrawVerts_; i++) {
u32 dhash = dcid;
dhash = __rotl(dhash ^ (u32)(uintptr_t)drawVerts_[i].verts, 13);
dhash = __rotl(dhash ^ (u32)drawInds_[i].vertexCount, 11);
dcid = lowbias32_r(dhash ^ (u32)drawInds_[i].prim);
}
for (int j = 0; j < numDrawInds_; j++) {
u32 dhash = dcid;
dhash = __rotl(dhash ^ (u32)(uintptr_t)drawInds_[j].inds, 19);
dcid = lowbias32_r(__rotl(dhash ^ (u32)drawInds_[j].indexType, 7));
}
return dcid;
}
int DrawEngineCommon::ComputeNumVertsToDecode() const {
int sum = 0;
for (int i = 0; i < numDrawVerts_; i++) {
sum += drawVerts_[i].indexUpperBound + 1 - drawVerts_[i].indexLowerBound;
}
return sum;
}
uint64_t DrawEngineCommon::ComputeHash() {
uint64_t 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 += XXH3_64bits((const char *)dc.verts, vertexSize * dc.vertexCount);
} 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 += XXH3_64bits((const char *)dc.verts + vertexSize * indexLowerBound,
vertexSize * (indexUpperBound - indexLowerBound));
for (int i = 0; i < numDrawVerts_; i++) {
const DeferredVerts &dv = drawVerts_[i];
int indexLowerBound = dv.indexLowerBound, indexUpperBound = dv.indexUpperBound;
fullhash += XXH3_64bits((const char *)dv.verts + vertexSize * indexLowerBound, vertexSize * (indexUpperBound - indexLowerBound));
}
for (int i = 0; i < numDrawInds_; i++) {
const DeferredInds &di = drawInds_[i];
if (di.indexType != 0) {
int indexSize = IndexSize(di.indexType << GE_VTYPE_IDX_SHIFT);
// Hm, we will miss some indices when combining above, but meh, it should be fine.
fullhash += XXH3_64bits((const char *)dc.inds, indexSize * dc.vertexCount);
i = lastMatch;
fullhash += XXH3_64bits((const char *)di.inds, indexSize * di.vertexCount);
}
}
fullhash += XXH3_64bits(&drawCalls_[0].uvScale, sizeof(drawCalls_[0].uvScale) * numDrawCalls_);
// this looks utterly broken??
// fullhash += XXH3_64bits(&drawCalls_[0].uvScale, sizeof(drawCalls_[0].uvScale) * numDrawCalls_);
return fullhash;
}
bool DrawEngineCommon::ExtendNonIndexedPrim(GEPrimitiveType prim, int vertexCount, u32 vertTypeID, int cullMode, int *bytesRead) {
if (numDrawInds_ >= MAX_DEFERRED_DRAW_INDS || vertexCountInDrawCalls_ + vertexCount > VERTEX_BUFFER_MAX) {
return false;
}
_dbg_assert_(numDrawInds_ < MAX_DEFERRED_DRAW_INDS);
_dbg_assert_(numDrawVerts_ > 0);
*bytesRead = vertexCount * dec_->VertexSize();
DeferredInds &di = drawInds_[numDrawInds_++];
di.inds = nullptr;
di.indexType = 0;
di.prim = prim;
di.cullMode = cullMode;
di.vertexCount = vertexCount;
di.vertDecodeIndex = numDrawVerts_ - 1;
DeferredVerts &dv = drawVerts_[numDrawVerts_ - 1];
int offset = dv.vertexCount;
di.offset = offset;
dv.vertexCount += vertexCount;
dv.indexUpperBound = dv.vertexCount - 1;
vertexCountInDrawCalls_ += vertexCount;
return true;
}
// vertTypeID is the vertex type but with the UVGen mode smashed into the top bits.
void DrawEngineCommon::SubmitPrim(const void *verts, const void *inds, GEPrimitiveType prim, int vertexCount, u32 vertTypeID, int cullMode, int *bytesRead) {
if (!indexGen.PrimCompatible(prevPrim_, prim) || numDrawCalls_ >= MAX_DEFERRED_DRAW_CALLS || vertexCountInDrawCalls_ + vertexCount > VERTEX_BUFFER_MAX) {
if (!indexGen.PrimCompatible(prevPrim_, prim) || numDrawVerts_ >= MAX_DEFERRED_DRAW_VERTS || numDrawInds_ >= MAX_DEFERRED_DRAW_INDS || vertexCountInDrawCalls_ + vertexCount > VERTEX_BUFFER_MAX) {
DispatchFlush();
}
_dbg_assert_(numDrawVerts_ < MAX_DEFERRED_DRAW_VERTS);
_dbg_assert_(numDrawInds_ < MAX_DEFERRED_DRAW_INDS);
// This isn't exactly right, if we flushed, since prims can straddle previous calls.
// But it generally works for common usage.
@@ -818,29 +736,47 @@ void DrawEngineCommon::SubmitPrim(const void *verts, const void *inds, GEPrimiti
if (vertexCount < 3 && ((vertexCount < 2 && prim > 0) || (prim > GE_PRIM_LINE_STRIP && prim != GE_PRIM_RECTANGLES)))
return;
DeferredDrawCall &dc = drawCalls_[numDrawCalls_];
dc.verts = verts;
dc.inds = inds;
dc.vertexCount = vertexCount;
dc.indexType = (vertTypeID & GE_VTYPE_IDX_MASK) >> GE_VTYPE_IDX_SHIFT;
dc.prim = prim;
dc.cullMode = cullMode;
dc.uvScale = gstate_c.uv;
if (inds) {
GetIndexBounds(inds, vertexCount, vertTypeID, &dc.indexLowerBound, &dc.indexUpperBound);
bool applySkin = (vertTypeID & GE_VTYPE_WEIGHT_MASK) && decOptions_.applySkinInDecode;
DeferredInds &di = drawInds_[numDrawInds_++];
di.inds = inds;
di.indexType = (vertTypeID & GE_VTYPE_IDX_MASK) >> GE_VTYPE_IDX_SHIFT;
di.prim = prim;
di.cullMode = cullMode;
di.vertexCount = vertexCount;
di.vertDecodeIndex = numDrawVerts_;
di.offset = 0;
_dbg_assert_(numDrawVerts_ <= MAX_DEFERRED_DRAW_VERTS);
_dbg_assert_(numDrawInds_ <= MAX_DEFERRED_DRAW_INDS);
if (inds && numDrawVerts_ > decodeVertsCounter_ && drawVerts_[numDrawVerts_ - 1].verts == verts && !applySkin) {
// Same vertex pointer as a previous un-decoded draw call - let's just extend the decode!
di.vertDecodeIndex = numDrawVerts_ - 1;
DeferredVerts &dv = drawVerts_[numDrawVerts_ - 1];
u16 lb;
u16 ub;
GetIndexBounds(inds, vertexCount, vertTypeID, &lb, &ub);
if (lb < dv.indexLowerBound)
dv.indexLowerBound = lb;
if (ub > dv.indexUpperBound)
dv.indexUpperBound = ub;
} else {
dc.indexLowerBound = 0;
dc.indexUpperBound = vertexCount - 1;
// Record a new draw, and a new index gen.
DeferredVerts &dv = drawVerts_[numDrawVerts_++];
dv.verts = verts;
dv.vertexCount = vertexCount;
dv.uvScale = gstate_c.uv;
if (inds) {
GetIndexBounds(inds, vertexCount, vertTypeID, &dv.indexLowerBound, &dv.indexUpperBound);
} else {
dv.indexLowerBound = 0;
dv.indexUpperBound = vertexCount - 1;
}
}
numDrawCalls_++;
vertexCountInDrawCalls_ += vertexCount;
if ((vertTypeID & GE_VTYPE_WEIGHT_MASK) && decOptions_.applySkinInDecode) {
DecodeVertsStep(decoded_, decodeCounter_, decodedVerts_, &dc.uvScale);
decodeCounter_++;
}
if (prim == GE_PRIM_RECTANGLES && (gstate.getTextureAddress(0) & 0x3FFFFFFF) == (gstate.getFrameBufAddress() & 0x3FFFFFFF)) {
// This prevents issues with consecutive self-renders in Ridge Racer.
gstate_c.Dirty(DIRTY_TEXTURE_PARAMS);
@@ -848,6 +784,60 @@ void DrawEngineCommon::SubmitPrim(const void *verts, const void *inds, GEPrimiti
}
}
void DrawEngineCommon::DecodeVerts(u8 *dest) {
int i = decodeVertsCounter_;
int stride = (int)dec_->GetDecVtxFmt().stride;
for (; i < numDrawVerts_; i++) {
DeferredVerts &dv = drawVerts_[i];
int indexLowerBound = dv.indexLowerBound;
drawVertexOffsets_[i] = decodedVerts_ - indexLowerBound;
int indexUpperBound = dv.indexUpperBound;
// Decode the verts (and at the same time apply morphing/skinning). Simple.
dec_->DecodeVerts(dest + decodedVerts_ * stride, dv.verts, &dv.uvScale, indexLowerBound, indexUpperBound);
decodedVerts_ += indexUpperBound - indexLowerBound + 1;
}
decodeVertsCounter_ = i;
}
void DrawEngineCommon::DecodeInds() {
int i = decodeIndsCounter_;
for (; i < numDrawInds_; i++) {
const DeferredInds &di = drawInds_[i];
int indexOffset = drawVertexOffsets_[di.vertDecodeIndex] + di.offset;
bool clockwise = true;
if (gstate.isCullEnabled() && gstate.getCullMode() != di.cullMode) {
clockwise = false;
}
// We've already collapsed subsequent draws with the same vertex pointer, so no tricky logic here anymore.
// 2. Loop through the drawcalls, translating indices as we go.
switch (di.indexType) {
case GE_VTYPE_IDX_NONE >> GE_VTYPE_IDX_SHIFT:
indexGen.AddPrim(di.prim, di.vertexCount, indexOffset, clockwise);
break;
case GE_VTYPE_IDX_8BIT >> GE_VTYPE_IDX_SHIFT:
indexGen.TranslatePrim(di.prim, di.vertexCount, (const u8 *)di.inds, indexOffset, clockwise);
break;
case GE_VTYPE_IDX_16BIT >> GE_VTYPE_IDX_SHIFT:
indexGen.TranslatePrim(di.prim, di.vertexCount, (const u16_le *)di.inds, indexOffset, clockwise);
break;
case GE_VTYPE_IDX_32BIT >> GE_VTYPE_IDX_SHIFT:
indexGen.TranslatePrim(di.prim, di.vertexCount, (const u32_le *)di.inds, indexOffset, clockwise);
break;
}
}
decodeIndsCounter_ = i;
// Sanity check
if (indexGen.Prim() < 0) {
ERROR_LOG_REPORT(G3D, "DecodeVerts: Failed to deduce prim: %i", indexGen.Prim());
// Force to points (0)
indexGen.AddPrim(GE_PRIM_POINTS, 0, 0, true);
}
}
bool DrawEngineCommon::CanUseHardwareTransform(int prim) {
if (!useHWTransform_)
return false;
@@ -861,29 +851,6 @@ bool DrawEngineCommon::CanUseHardwareTessellation(GEPatchPrimType prim) {
return false;
}
// Cheap bit scrambler from https://nullprogram.com/blog/2018/07/31/
inline uint32_t lowbias32_r(uint32_t x) {
x ^= x >> 16;
x *= 0x43021123U;
x ^= x >> 15 ^ x >> 30;
x *= 0x1d69e2a5U;
x ^= x >> 16;
return x;
}
uint32_t DrawEngineCommon::ComputeDrawcallsHash() const {
uint32_t dcid = 0;
for (int i = 0; i < numDrawCalls_; i++) {
u32 dhash = dcid;
dhash = __rotl(dhash ^ (u32)(uintptr_t)drawCalls_[i].verts, 13);
dhash = __rotl(dhash ^ (u32)(uintptr_t)drawCalls_[i].inds, 19);
dhash = __rotl(dhash ^ (u32)drawCalls_[i].indexType, 7);
dhash = __rotl(dhash ^ (u32)drawCalls_[i].vertexCount, 11);
dcid = lowbias32_r(dhash ^ (u32)drawCalls_[i].prim);
}
return dcid;
}
void TessellationDataTransfer::CopyControlPoints(float *pos, float *tex, float *col, int posStride, int texStride, int colStride, const SimpleVertex *const *points, int size, u32 vertType) {
bool hasColor = (vertType & GE_VTYPE_COL_MASK) != 0;
bool hasTexCoord = (vertType & GE_VTYPE_TC_MASK) != 0;
+36 -13
View File
@@ -104,6 +104,14 @@ public:
bool TestBoundingBox(const void *control_points, const void *inds, int vertexCount, u32 vertType);
void FlushSkin() {
bool applySkin = (lastVType_ & GE_VTYPE_WEIGHT_MASK) && decOptions_.applySkinInDecode;
if (applySkin) {
DecodeVerts(decoded_);
}
}
bool ExtendNonIndexedPrim(GEPrimitiveType prim, int vertexCount, u32 vertTypeID, int cullMode, int *bytesRead);
void SubmitPrim(const void *verts, const void *inds, GEPrimitiveType prim, int vertexCount, u32 vertTypeID, int cullMode, int *bytesRead);
template<class Surface>
void SubmitCurve(const void *control_points, const void *indices, Surface &surface, u32 vertType, int *bytesRead, const char *scope);
@@ -130,7 +138,7 @@ public:
return false;
}
int GetNumDrawCalls() const {
return numDrawCalls_;
return numDrawVerts_;
}
VertexDecoder *GetVertexDecoder(u32 vtype);
@@ -141,8 +149,12 @@ protected:
virtual bool UpdateUseHWTessellation(bool enabled) const { return enabled; }
void UpdatePlanes();
int ComputeNumVertsToDecode() const;
void DecodeVerts(u8 *dest);
void DecodeInds();
int MaxIndex() const {
return decodedVerts_;
}
// Preprocessing for spline/bezier
u32 NormalizeVertices(u8 *outPtr, u8 *bufPtr, const u8 *inPtr, int lowerBound, int upperBound, u32 vertType, int *vertexSize = nullptr);
@@ -151,8 +163,7 @@ protected:
u32 ComputeMiniHash();
uint64_t ComputeHash();
// Vertex decoding
void DecodeVertsStep(u8 *dest, int &i, int &decodedVerts, const UVScale *uvScale);
int ComputeNumVertsToDecode() const;
void ApplyFramebufferRead(FBOTexState *fboTexState);
@@ -210,25 +221,37 @@ protected:
TransformedVertex *transformedExpanded_ = nullptr;
// Defer all vertex decoding to a "Flush" (except when software skinning)
struct DeferredDrawCall {
struct DeferredVerts {
const void *verts;
const void *inds;
u32 vertexCount;
u8 indexType;
s8 prim;
u8 cullMode;
u16 indexLowerBound;
u16 indexUpperBound;
UVScale uvScale;
};
enum { MAX_DEFERRED_DRAW_CALLS = 128 };
DeferredDrawCall drawCalls_[MAX_DEFERRED_DRAW_CALLS];
int numDrawCalls_ = 0;
struct DeferredInds {
const void *inds;
u32 vertexCount;
u8 vertDecodeIndex; // index into the drawVerts_ array to look up the vertexOffset.
u8 indexType;
s8 prim;
u8 cullMode;
u16 offset;
};
enum { MAX_DEFERRED_DRAW_VERTS = 128 }; // If you change this to more than 256, change type of DeferredInds::vertDecodeIndex.
enum { MAX_DEFERRED_DRAW_INDS = 512 }; // Monster Hunter spams indexed calls that we end up merging.
DeferredVerts drawVerts_[MAX_DEFERRED_DRAW_VERTS];
uint32_t drawVertexOffsets_[MAX_DEFERRED_DRAW_VERTS];
DeferredInds drawInds_[MAX_DEFERRED_DRAW_INDS];
int numDrawVerts_ = 0;
int numDrawInds_ = 0;
int vertexCountInDrawCalls_ = 0;
int decimationCounter_ = 0;
int decodeCounter_ = 0;
int decodeVertsCounter_ = 0;
int decodeIndsCounter_ = 0;
// Vertex collector state
IndexGenerator indexGen;
+32 -51
View File
@@ -50,44 +50,40 @@ void IndexGenerator::Setup(u16 *inds) {
Reset();
}
void IndexGenerator::AddPrim(int prim, int vertexCount, bool clockwise) {
void IndexGenerator::AddPrim(int prim, int vertexCount, int indexOffset, bool clockwise) {
switch (prim) {
case GE_PRIM_POINTS: AddPoints(vertexCount); break;
case GE_PRIM_LINES: AddLineList(vertexCount); break;
case GE_PRIM_LINE_STRIP: AddLineStrip(vertexCount); break;
case GE_PRIM_TRIANGLES: AddList(vertexCount, clockwise); break;
case GE_PRIM_TRIANGLE_STRIP: AddStrip(vertexCount, clockwise); break;
case GE_PRIM_TRIANGLE_FAN: AddFan(vertexCount, clockwise); break;
case GE_PRIM_RECTANGLES: AddRectangles(vertexCount); break; // Same
case GE_PRIM_POINTS: AddPoints(vertexCount, indexOffset); break;
case GE_PRIM_LINES: AddLineList(vertexCount, indexOffset); break;
case GE_PRIM_LINE_STRIP: AddLineStrip(vertexCount, indexOffset); break;
case GE_PRIM_TRIANGLES: AddList(vertexCount, indexOffset, clockwise); break;
case GE_PRIM_TRIANGLE_STRIP: AddStrip(vertexCount, indexOffset, clockwise); break;
case GE_PRIM_TRIANGLE_FAN: AddFan(vertexCount, indexOffset, clockwise); break;
case GE_PRIM_RECTANGLES: AddRectangles(vertexCount, indexOffset); break; // Same
}
}
void IndexGenerator::AddPoints(int numVerts) {
void IndexGenerator::AddPoints(int numVerts, int indexOffset) {
u16 *outInds = inds_;
const int startIndex = index_;
for (int i = 0; i < numVerts; i++)
*outInds++ = startIndex + i;
*outInds++ = indexOffset + i;
inds_ = outInds;
// ignore overflow verts
index_ += numVerts;
count_ += numVerts;
prim_ = GE_PRIM_POINTS;
seenPrims_ |= 1 << GE_PRIM_POINTS;
}
void IndexGenerator::AddList(int numVerts, bool clockwise) {
void IndexGenerator::AddList(int numVerts, int indexOffset, bool clockwise) {
u16 *outInds = inds_;
const int startIndex = index_;
const int v1 = clockwise ? 1 : 2;
const int v2 = clockwise ? 2 : 1;
for (int i = 0; i < numVerts; i += 3) {
*outInds++ = startIndex + i;
*outInds++ = startIndex + i + v1;
*outInds++ = startIndex + i + v2;
*outInds++ = indexOffset + i;
*outInds++ = indexOffset + i + v1;
*outInds++ = indexOffset + i + v2;
}
inds_ = outInds;
// ignore overflow verts
index_ += numVerts;
count_ += numVerts;
prim_ = GE_PRIM_TRIANGLES;
seenPrims_ |= 1 << GE_PRIM_TRIANGLES;
@@ -119,7 +115,7 @@ alignas(16) static const uint16_t offsets_counter_clockwise[24] = {
7, (u16)(7 + 1), (u16)(7 + 2),
};
void IndexGenerator::AddStrip(int numVerts, bool clockwise) {
void IndexGenerator::AddStrip(int numVerts, int indexOffset, bool clockwise) {
int numTris = numVerts - 2;
#ifdef _M_SSE
// In an SSE2 register we can fit 8 16-bit integers.
@@ -130,7 +126,7 @@ void IndexGenerator::AddStrip(int numVerts, bool clockwise) {
// We allow ourselves to write some extra indices to avoid the fallback loop.
// That's alright as we're appending to a buffer - they will get overwritten anyway.
int numChunks = (numTris + 7) >> 3;
__m128i ibase8 = _mm_set1_epi16(index_);
__m128i ibase8 = _mm_set1_epi16(indexOffset);
const __m128i *offsets = (const __m128i *)(clockwise ? offsets_clockwise : offsets_counter_clockwise);
__m128i *dst = (__m128i *)inds_;
__m128i offsets0 = _mm_add_epi16(ibase8, _mm_load_si128(offsets));
@@ -158,7 +154,7 @@ void IndexGenerator::AddStrip(int numVerts, bool clockwise) {
// wind doesn't need to be updated, an even number of triangles have been drawn.
#elif PPSSPP_ARCH(ARM_NEON)
int numChunks = (numTris + 7) >> 3;
uint16x8_t ibase8 = vdupq_n_u16(index_);
uint16x8_t ibase8 = vdupq_n_u16(indexOffset);
const u16 *offsets = clockwise ? offsets_clockwise : offsets_counter_clockwise;
u16 *dst = inds_;
uint16x8_t offsets0 = vaddq_u16(ibase8, vld1q_u16(offsets));
@@ -185,7 +181,7 @@ void IndexGenerator::AddStrip(int numVerts, bool clockwise) {
#else
// Slow fallback loop.
int wind = clockwise ? 1 : 2;
int ibase = index_;
int ibase = indexOffset;
size_t numPairs = numTris / 2;
u16 *outInds = inds_;
while (numPairs > 0) {
@@ -207,7 +203,6 @@ void IndexGenerator::AddStrip(int numVerts, bool clockwise) {
inds_ = outInds;
#endif
index_ += numVerts;
if (numTris > 0)
count_ += numTris * 3;
// This is so we can detect one single strip by just looking at seenPrims_.
@@ -222,19 +217,17 @@ void IndexGenerator::AddStrip(int numVerts, bool clockwise) {
}
}
void IndexGenerator::AddFan(int numVerts, bool clockwise) {
void IndexGenerator::AddFan(int numVerts, int indexOffset, bool clockwise) {
const int numTris = numVerts - 2;
u16 *outInds = inds_;
const int startIndex = index_;
const int v1 = clockwise ? 1 : 2;
const int v2 = clockwise ? 2 : 1;
for (int i = 0; i < numTris; i++) {
*outInds++ = startIndex;
*outInds++ = startIndex + i + v1;
*outInds++ = startIndex + i + v2;
*outInds++ = indexOffset;
*outInds++ = indexOffset + i + v1;
*outInds++ = indexOffset + i + v2;
}
inds_ = outInds;
index_ += numVerts;
count_ += numTris * 3;
prim_ = GE_PRIM_TRIANGLES;
seenPrims_ |= 1 << GE_PRIM_TRIANGLE_FAN;
@@ -245,46 +238,40 @@ void IndexGenerator::AddFan(int numVerts, bool clockwise) {
}
//Lines
void IndexGenerator::AddLineList(int numVerts) {
void IndexGenerator::AddLineList(int numVerts, int indexOffset) {
u16 *outInds = inds_;
const int startIndex = index_;
for (int i = 0; i < numVerts; i += 2) {
*outInds++ = startIndex + i;
*outInds++ = startIndex + i + 1;
*outInds++ = indexOffset + i;
*outInds++ = indexOffset + i + 1;
}
inds_ = outInds;
index_ += numVerts;
count_ += numVerts;
prim_ = GE_PRIM_LINES;
seenPrims_ |= 1 << prim_;
}
void IndexGenerator::AddLineStrip(int numVerts) {
void IndexGenerator::AddLineStrip(int numVerts, int indexOffset) {
const int numLines = numVerts - 1;
u16 *outInds = inds_;
const int startIndex = index_;
for (int i = 0; i < numLines; i++) {
*outInds++ = startIndex + i;
*outInds++ = startIndex + i + 1;
*outInds++ = indexOffset + i;
*outInds++ = indexOffset + i + 1;
}
inds_ = outInds;
index_ += numVerts;
count_ += numLines * 2;
prim_ = GE_PRIM_LINES;
seenPrims_ |= 1 << GE_PRIM_LINE_STRIP;
}
void IndexGenerator::AddRectangles(int numVerts) {
void IndexGenerator::AddRectangles(int numVerts, int indexOffset) {
u16 *outInds = inds_;
const int startIndex = index_;
//rectangles always need 2 vertices, disregard the last one if there's an odd number
numVerts = numVerts & ~1;
for (int i = 0; i < numVerts; i += 2) {
*outInds++ = startIndex + i;
*outInds++ = startIndex + i + 1;
*outInds++ = indexOffset + i;
*outInds++ = indexOffset + i + 1;
}
inds_ = outInds;
index_ += numVerts;
count_ += numVerts;
prim_ = GE_PRIM_RECTANGLES;
seenPrims_ |= 1 << GE_PRIM_RECTANGLES;
@@ -292,7 +279,6 @@ void IndexGenerator::AddRectangles(int numVerts) {
template <class ITypeLE, int flag>
void IndexGenerator::TranslatePoints(int numInds, const ITypeLE *inds, int indexOffset) {
indexOffset = index_ - indexOffset;
u16 *outInds = inds_;
for (int i = 0; i < numInds; i++)
*outInds++ = indexOffset + inds[i];
@@ -304,7 +290,6 @@ void IndexGenerator::TranslatePoints(int numInds, const ITypeLE *inds, int index
template <class ITypeLE, int flag>
void IndexGenerator::TranslateLineList(int numInds, const ITypeLE *inds, int indexOffset) {
indexOffset = index_ - indexOffset;
u16 *outInds = inds_;
numInds = numInds & ~1;
for (int i = 0; i < numInds; i += 2) {
@@ -319,7 +304,6 @@ void IndexGenerator::TranslateLineList(int numInds, const ITypeLE *inds, int ind
template <class ITypeLE, int flag>
void IndexGenerator::TranslateLineStrip(int numInds, const ITypeLE *inds, int indexOffset) {
indexOffset = index_ - indexOffset;
int numLines = numInds - 1;
u16 *outInds = inds_;
for (int i = 0; i < numLines; i++) {
@@ -334,7 +318,6 @@ 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, bool clockwise) {
indexOffset = index_ - indexOffset;
// 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 && clockwise) {
@@ -347,6 +330,7 @@ void IndexGenerator::TranslateList(int numInds, const ITypeLE *inds, int indexOf
numInds = numTris * 3;
const int v1 = clockwise ? 1 : 2;
const int v2 = clockwise ? 2 : 1;
// TODO: This can actually be SIMD-d, although will need complex shuffles if clockwise.
for (int i = 0; i < numInds; i += 3) {
*outInds++ = indexOffset + inds[i];
*outInds++ = indexOffset + inds[i + v1];
@@ -362,7 +346,6 @@ void IndexGenerator::TranslateList(int numInds, const ITypeLE *inds, int indexOf
template <class ITypeLE, int flag>
void IndexGenerator::TranslateStrip(int numInds, const ITypeLE *inds, int indexOffset, bool clockwise) {
int wind = clockwise ? 1 : 2;
indexOffset = index_ - indexOffset;
int numTris = numInds - 2;
u16 *outInds = inds_;
for (int i = 0; i < numTris; i++) {
@@ -380,7 +363,6 @@ void IndexGenerator::TranslateStrip(int numInds, const ITypeLE *inds, int indexO
template <class ITypeLE, int flag>
void IndexGenerator::TranslateFan(int numInds, const ITypeLE *inds, int indexOffset, bool clockwise) {
if (numInds <= 0) return;
indexOffset = index_ - indexOffset;
int numTris = numInds - 2;
u16 *outInds = inds_;
const int v1 = clockwise ? 1 : 2;
@@ -398,7 +380,6 @@ void IndexGenerator::TranslateFan(int numInds, const ITypeLE *inds, int indexOff
template <class ITypeLE, int flag>
inline void IndexGenerator::TranslateRectangles(int numInds, const ITypeLE *inds, int indexOffset) {
indexOffset = index_ - indexOffset;
u16 *outInds = inds_;
//rectangles always need 2 vertices, disregard the last one if there's an odd number
numInds = numInds & ~1;
+8 -17
View File
@@ -28,7 +28,6 @@ public:
void Reset() {
prim_ = GE_PRIM_INVALID;
count_ = 0;
index_ = 0;
seenPrims_ = 0;
pureCount_ = 0;
this->inds_ = indsBase_;
@@ -57,19 +56,12 @@ public:
}
}
void AddPrim(int prim, int vertexCount, bool clockwise);
void AddPrim(int prim, int vertexCount, int indexOffset, bool clockwise);
void TranslatePrim(int prim, int numInds, const u8 *inds, int indexOffset, bool clockwise);
void TranslatePrim(int prim, int numInds, const u16_le *inds, int indexOffset, bool clockwise);
void TranslatePrim(int prim, int numInds, const u32_le *inds, int indexOffset, bool clockwise);
void Advance(int numVerts) {
index_ += numVerts;
}
void SetIndex(int ind) { index_ = ind; }
int MaxIndex() const { return index_; } // Really NextIndex rather than MaxIndex, it's one more than the highest index generated
int VertexCount() const { return count_; }
bool Empty() const { return index_ == 0; }
int SeenPrims() const { return seenPrims_; }
int PureCount() const { return pureCount_; }
bool SeenOnlyPurePrims() const {
@@ -81,16 +73,16 @@ public:
private:
// Points (why index these? code simplicity)
void AddPoints(int numVerts);
void AddPoints(int numVerts, int indexOffset);
// Triangles
void AddList(int numVerts, bool clockwise);
void AddStrip(int numVerts, bool clockwise);
void AddFan(int numVerts, bool clockwise);
void AddList(int numVerts, int indexOffset, bool clockwise);
void AddStrip(int numVerts, int indexOffset, bool clockwise);
void AddFan(int numVerts, int indexOffset, bool clockwise);
// Lines
void AddLineList(int numVerts);
void AddLineStrip(int numVerts);
void AddLineList(int numVerts, int indexOffset);
void AddLineStrip(int numVerts, int indexOffset);
// Rectangles
void AddRectangles(int numVerts);
void AddRectangles(int numVerts, int indexOffset);
// These translate already indexed lists
template <class ITypeLE, int flag>
@@ -118,7 +110,6 @@ private:
u16 *indsBase_;
u16 *inds_;
int index_;
int count_;
int pureCount_;
GEPrimitiveType prim_;
+3
View File
@@ -1293,6 +1293,9 @@ void VertexDecoder::SetVertexType(u32 fmt, const VertexDecoderOptions &options,
}
void VertexDecoder::DecodeVerts(u8 *decodedptr, const void *verts, const UVScale *uvScaleOffset, int indexLowerBound, int indexUpperBound) const {
// A single 0 is acceptable for point lists.
_dbg_assert_(indexLowerBound <= indexUpperBound);
// Decode the vertices within the found bounds, once each
// decoded_ and ptr_ are used in the steps, so can't be turned into locals for speed.
const u8 *startPtr = (const u8*)verts + indexLowerBound * size;
+19 -9
View File
@@ -366,7 +366,7 @@ void DrawEngineD3D11::DoFlush() {
if (useCache) {
// getUVGenMode can have an effect on which UV decoder we need to use! And hence what the decoded data will look like. See #9263
u32 dcid = (u32)XXH3_64bits(&drawCalls_, sizeof(DeferredDrawCall) * numDrawCalls_) ^ gstate.getUVGenMode();
u32 dcid = ComputeDrawcallsHash() ^ gstate.getUVGenMode();
VertexArrayInfoD3D11 *vai;
if (!vai_.Get(dcid, &vai)) {
@@ -384,9 +384,10 @@ void DrawEngineD3D11::DoFlush() {
vai->status = VertexArrayInfoD3D11::VAI_HASHING;
vai->drawsUntilNextFullHash = 0;
DecodeVerts(decoded_); // writes to indexGen
DecodeInds();
vai->numVerts = indexGen.VertexCount();
vai->prim = indexGen.Prim();
vai->maxIndex = indexGen.MaxIndex();
vai->maxIndex = MaxIndex();
vai->flags = gstate_c.vertexFullAlpha ? VAI11_FLAG_VERTEXFULLALPHA : 0;
goto rotateVBO;
}
@@ -409,6 +410,7 @@ void DrawEngineD3D11::DoFlush() {
if (newMiniHash != vai->minihash || newHash != vai->hash) {
MarkUnreliable(vai);
DecodeVerts(decoded_);
DecodeInds();
goto rotateVBO;
}
if (vai->numVerts > 64) {
@@ -428,15 +430,17 @@ void DrawEngineD3D11::DoFlush() {
if (newMiniHash != vai->minihash) {
MarkUnreliable(vai);
DecodeVerts(decoded_);
DecodeInds();
goto rotateVBO;
}
}
if (vai->vbo == 0) {
DecodeVerts(decoded_);
DecodeInds();
vai->numVerts = indexGen.VertexCount();
vai->prim = indexGen.Prim();
vai->maxIndex = indexGen.MaxIndex();
vai->maxIndex = MaxIndex();
vai->flags = gstate_c.vertexFullAlpha ? VAI11_FLAG_VERTEXFULLALPHA : 0;
useElements = !indexGen.SeenOnlyPurePrims() || prim == GE_PRIM_TRIANGLE_FAN;
if (!useElements && indexGen.PureCount()) {
@@ -446,7 +450,7 @@ void DrawEngineD3D11::DoFlush() {
_dbg_assert_msg_(gstate_c.vertBounds.minV >= gstate_c.vertBounds.maxV, "Should not have checked UVs when caching.");
// TODO: Combine these two into one buffer?
u32 size = dec_->GetDecVtxFmt().stride * indexGen.MaxIndex();
u32 size = dec_->GetDecVtxFmt().stride * MaxIndex();
D3D11_BUFFER_DESC desc{ size, D3D11_USAGE_IMMUTABLE, D3D11_BIND_VERTEX_BUFFER, 0 };
D3D11_SUBRESOURCE_DATA data{ decoded_ };
ASSERT_SUCCESS(device_->CreateBuffer(&desc, &data, &vai->vbo));
@@ -500,6 +504,7 @@ void DrawEngineD3D11::DoFlush() {
vai->numFrames++;
}
DecodeVerts(decoded_);
DecodeInds();
goto rotateVBO;
}
}
@@ -507,11 +512,12 @@ void DrawEngineD3D11::DoFlush() {
vai->lastFrame = gpuStats.numFlips;
} else {
DecodeVerts(decoded_);
DecodeInds();
rotateVBO:
gpuStats.numUncachedVertsDrawn += indexGen.VertexCount();
useElements = !indexGen.SeenOnlyPurePrims() || prim == GE_PRIM_TRIANGLE_FAN;
vertexCount = indexGen.VertexCount();
maxIndex = indexGen.MaxIndex();
maxIndex = MaxIndex();
if (!useElements && indexGen.PureCount()) {
vertexCount = indexGen.PureCount();
}
@@ -584,6 +590,7 @@ rotateVBO:
dec_ = GetVertexDecoder(lastVType_);
}
DecodeVerts(decoded_);
DecodeInds();
bool hasColor = (lastVType_ & GE_VTYPE_COL_MASK) != GE_VTYPE_COL_NONE;
if (gstate.isModeThrough()) {
gstate_c.vertexFullAlpha = gstate_c.vertexFullAlpha && (hasColor || gstate.getMaterialAmbientA() == 255);
@@ -622,7 +629,7 @@ rotateVBO:
UpdateCachedViewportState(vpAndScissor);
}
int maxIndex = indexGen.MaxIndex();
int maxIndex = MaxIndex();
SoftwareTransform swTransform(params);
const Lin::Vec3 trans(gstate_c.vpXOffset, -gstate_c.vpYOffset, gstate_c.vpZOffset * 0.5f + 0.5f);
@@ -719,14 +726,17 @@ rotateVBO:
}
gpuStats.numFlushes++;
gpuStats.numDrawCalls += numDrawCalls_;
gpuStats.numDrawCalls += numDrawInds_;
gpuStats.numVertexDecodes += numDrawVerts_;
gpuStats.numVertsSubmitted += vertexCountInDrawCalls_;
indexGen.Reset();
decodedVerts_ = 0;
numDrawCalls_ = 0;
numDrawVerts_ = 0;
numDrawInds_ = 0;
vertexCountInDrawCalls_ = 0;
decodeCounter_ = 0;
decodeVertsCounter_ = 0;
decodeIndsCounter_ = 0;
gstate_c.vertexFullAlpha = true;
framebufferManager_->SetColorUpdated(gstate_c.skipDrawReason);
+3 -3
View File
@@ -138,19 +138,19 @@ public:
// So that this can be inlined
void Flush() {
if (!numDrawCalls_)
if (!numDrawVerts_)
return;
DoFlush();
}
void FinishDeferred() {
if (!numDrawCalls_)
if (!numDrawVerts_)
return;
DecodeVerts(decoded_);
}
void DispatchFlush() override {
if (!numDrawCalls_)
if (!numDrawVerts_)
return;
Flush();
}
+23 -9
View File
@@ -345,7 +345,7 @@ void DrawEngineDX9::DoFlush() {
if (useCache) {
// getUVGenMode can have an effect on which UV decoder we need to use! And hence what the decoded data will look like. See #9263
u32 dcid = (u32)XXH3_64bits(&drawCalls_, sizeof(DeferredDrawCall) * numDrawCalls_) ^ gstate.getUVGenMode();
u32 dcid = ComputeDrawcallsHash() ^ gstate.getUVGenMode();
VertexArrayInfoDX9 *vai;
if (!vai_.Get(dcid, &vai)) {
vai = new VertexArrayInfoDX9();
@@ -362,9 +362,10 @@ void DrawEngineDX9::DoFlush() {
vai->status = VertexArrayInfoDX9::VAI_HASHING;
vai->drawsUntilNextFullHash = 0;
DecodeVerts(decoded_); // writes to indexGen
DecodeInds();
vai->numVerts = indexGen.VertexCount();
vai->prim = indexGen.Prim();
vai->maxIndex = indexGen.MaxIndex();
vai->maxIndex = MaxIndex();
vai->flags = gstate_c.vertexFullAlpha ? VAI_FLAG_VERTEXFULLALPHA : 0;
goto rotateVBO;
@@ -388,6 +389,7 @@ void DrawEngineDX9::DoFlush() {
if (newMiniHash != vai->minihash || newHash != vai->hash) {
MarkUnreliable(vai);
DecodeVerts(decoded_);
DecodeInds();
goto rotateVBO;
}
if (vai->numVerts > 64) {
@@ -407,15 +409,17 @@ void DrawEngineDX9::DoFlush() {
if (newMiniHash != vai->minihash) {
MarkUnreliable(vai);
DecodeVerts(decoded_);
DecodeInds();
goto rotateVBO;
}
}
if (vai->vbo == 0) {
DecodeVerts(decoded_);
DecodeInds();
vai->numVerts = indexGen.VertexCount();
vai->prim = indexGen.Prim();
vai->maxIndex = indexGen.MaxIndex();
vai->maxIndex = MaxIndex();
vai->flags = gstate_c.vertexFullAlpha ? VAI_FLAG_VERTEXFULLALPHA : 0;
useElements = !indexGen.SeenOnlyPurePrims();
if (!useElements && indexGen.PureCount()) {
@@ -425,7 +429,7 @@ void DrawEngineDX9::DoFlush() {
_dbg_assert_msg_(gstate_c.vertBounds.minV >= gstate_c.vertBounds.maxV, "Should not have checked UVs when caching.");
void * pVb;
u32 size = dec_->GetDecVtxFmt().stride * indexGen.MaxIndex();
u32 size = dec_->GetDecVtxFmt().stride * MaxIndex();
device_->CreateVertexBuffer(size, D3DUSAGE_WRITEONLY, 0, D3DPOOL_DEFAULT, &vai->vbo, NULL);
vai->vbo->Lock(0, size, &pVb, 0);
memcpy(pVb, decoded_, size);
@@ -482,6 +486,7 @@ void DrawEngineDX9::DoFlush() {
vai->numFrames++;
}
DecodeVerts(decoded_);
DecodeInds();
goto rotateVBO;
}
}
@@ -489,17 +494,20 @@ void DrawEngineDX9::DoFlush() {
vai->lastFrame = gpuStats.numFlips;
} else {
DecodeVerts(decoded_);
DecodeInds();
rotateVBO:
gpuStats.numUncachedVertsDrawn += indexGen.VertexCount();
useElements = !indexGen.SeenOnlyPurePrims();
vertexCount = indexGen.VertexCount();
maxIndex = indexGen.MaxIndex();
maxIndex = MaxIndex();
if (!useElements && indexGen.PureCount()) {
vertexCount = indexGen.PureCount();
}
prim = indexGen.Prim();
}
_dbg_assert_((int)prim > 0);
bool hasColor = (lastVType_ & GE_VTYPE_COL_MASK) != GE_VTYPE_COL_NONE;
if (gstate.isModeThrough()) {
gstate_c.vertexFullAlpha = gstate_c.vertexFullAlpha && (hasColor || gstate.getMaterialAmbientA() == 255);
@@ -544,6 +552,7 @@ rotateVBO:
dec_ = GetVertexDecoder(lastVType_);
}
DecodeVerts(decoded_);
DecodeInds();
bool hasColor = (lastVType_ & GE_VTYPE_COL_MASK) != GE_VTYPE_COL_NONE;
if (gstate.isModeThrough()) {
gstate_c.vertexFullAlpha = gstate_c.vertexFullAlpha && (hasColor || gstate.getMaterialAmbientA() == 255);
@@ -582,7 +591,7 @@ rotateVBO:
UpdateCachedViewportState(vpAndScissor);
}
int maxIndex = indexGen.MaxIndex();
int maxIndex = MaxIndex();
SoftwareTransform swTransform(params);
// Half pixel offset hack.
@@ -658,14 +667,19 @@ rotateVBO:
}
gpuStats.numFlushes++;
gpuStats.numDrawCalls += numDrawCalls_;
gpuStats.numDrawCalls += numDrawInds_;
gpuStats.numVertexDecodes += numDrawVerts_;
gpuStats.numVertsSubmitted += vertexCountInDrawCalls_;
// TODO: The below should be shared.
indexGen.Reset();
decodedVerts_ = 0;
numDrawCalls_ = 0;
numDrawVerts_ = 0;
numDrawInds_ = 0;
vertexCountInDrawCalls_ = 0;
decodeCounter_ = 0;
decodeVertsCounter_ = 0;
decodeIndsCounter_ = 0;
gstate_c.vertexFullAlpha = true;
framebufferManager_->SetColorUpdated(gstate_c.skipDrawReason);
+3 -3
View File
@@ -128,19 +128,19 @@ public:
// So that this can be inlined
void Flush() {
if (!numDrawCalls_)
if (!numDrawVerts_)
return;
DoFlush();
}
void FinishDeferred() {
if (!numDrawCalls_)
if (!numDrawVerts_)
return;
DecodeVerts(decoded_);
}
void DispatchFlush() override {
if (!numDrawCalls_)
if (!numDrawVerts_)
return;
Flush();
}
+13 -7
View File
@@ -245,9 +245,11 @@ void DrawEngineGLES::DoFlush() {
// can't goto bail here, skips too many variable initializations. So let's wipe the most important stuff.
indexGen.Reset();
decodedVerts_ = 0;
numDrawCalls_ = 0;
numDrawVerts_ = 0;
numDrawInds_ = 0;
vertexCountInDrawCalls_ = 0;
decodeCounter_ = 0;
decodeVertsCounter_ = 0;
decodeIndsCounter_ = 0;
return;
}
@@ -284,9 +286,9 @@ void DrawEngineGLES::DoFlush() {
// Figure out how much pushbuffer space we need to allocate.
int vertsToDecode = ComputeNumVertsToDecode();
u8 *dest = (u8 *)frameData.pushVertex->Allocate(vertsToDecode * dec_->GetDecVtxFmt().stride, 4, &vertexBuffer, &vertexBufferOffset);
// Indices are decoded in here.
DecodeVerts(dest);
}
DecodeInds();
gpuStats.numUncachedVertsDrawn += indexGen.VertexCount();
@@ -343,6 +345,7 @@ void DrawEngineGLES::DoFlush() {
dec_ = GetVertexDecoder(lastVType_);
}
DecodeVerts(decoded_);
DecodeInds();
bool hasColor = (lastVType_ & GE_VTYPE_COL_MASK) != GE_VTYPE_COL_NONE;
if (gstate.isModeThrough()) {
@@ -381,7 +384,7 @@ void DrawEngineGLES::DoFlush() {
UpdateCachedViewportState(vpAndScissor_);
}
int maxIndex = indexGen.MaxIndex();
int maxIndex = MaxIndex();
int vertexCount = indexGen.VertexCount();
// TODO: Split up into multiple draw calls for GLES 2.0 where you can't guarantee support for more than 0x10000 verts.
@@ -471,7 +474,8 @@ void DrawEngineGLES::DoFlush() {
bail:
gpuStats.numFlushes++;
gpuStats.numDrawCalls += numDrawCalls_;
gpuStats.numDrawCalls += numDrawInds_;
gpuStats.numVertexDecodes += numDrawVerts_;
gpuStats.numVertsSubmitted += vertexCountInDrawCalls_;
// TODO: When the next flush has the same vertex format, we can continue with the same offset in the vertex buffer,
@@ -479,9 +483,11 @@ bail:
// wanted to avoid rebinding the vertex input every time).
indexGen.Reset();
decodedVerts_ = 0;
numDrawCalls_ = 0;
numDrawVerts_ = 0;
numDrawInds_ = 0;
vertexCountInDrawCalls_ = 0;
decodeCounter_ = 0;
decodeVertsCounter_ = 0;
decodeIndsCounter_ = 0;
gstate_c.vertexFullAlpha = true;
framebufferManager_->SetColorUpdated(gstate_c.skipDrawReason);
+3 -3
View File
@@ -86,19 +86,19 @@ public:
// So that this can be inlined
void Flush() {
if (!numDrawCalls_)
if (!numDrawVerts_)
return;
DoFlush();
}
void FinishDeferred() {
if (!numDrawCalls_)
if (!numDrawVerts_)
return;
DoFlush();
}
void DispatchFlush() override {
if (!numDrawCalls_)
if (!numDrawVerts_)
return;
Flush();
}
+2
View File
@@ -75,6 +75,7 @@ struct GPUStatistics {
void ResetFrame() {
numDrawCalls = 0;
numVertexDecodes = 0;
numDrawSyncs = 0;
numListSyncs = 0;
numCachedDrawCalls = 0;
@@ -111,6 +112,7 @@ struct GPUStatistics {
// Per frame statistics
int numDrawCalls;
int numVertexDecodes;
int numDrawSyncs;
int numListSyncs;
int numCachedDrawCalls;
+37 -6
View File
@@ -967,6 +967,8 @@ void GPUCommonHW::Execute_Prim(u32 op, u32 diff) {
const void *verts = Memory::GetPointerUnchecked(gstate_c.vertexAddr);
const void *inds = nullptr;
bool canExtend = true;
u32 vertexType = gstate.vertType;
if ((vertexType & GE_VTYPE_IDX_MASK) != GE_VTYPE_IDX_NONE) {
u32 indexAddr = gstate_c.indexAddr;
@@ -975,6 +977,7 @@ void GPUCommonHW::Execute_Prim(u32 op, u32 diff) {
return;
}
inds = Memory::GetPointerUnchecked(indexAddr);
canExtend = false;
}
int bytesRead = 0;
@@ -1017,12 +1020,28 @@ void GPUCommonHW::Execute_Prim(u32 op, u32 diff) {
if (IsTrianglePrim(newPrim) != isTriangle)
goto bail; // Can't join over this boundary. Might as well exit and get this on the next time around.
// TODO: more efficient updating of verts/inds
u32 count = data & 0xFFFF;
if (canExtend) {
// Non-indexed draws can be cheaply merged if vertexAddr hasn't changed, that means the vertices
// are consecutive in memory.
_dbg_assert_((vertexType & GE_VTYPE_IDX_MASK) == GE_VTYPE_IDX_NONE);
if (drawEngineCommon_->ExtendNonIndexedPrim(newPrim, count, vertTypeID, cullMode, &bytesRead)) {
gstate_c.vertexAddr += bytesRead;
totalVertCount += count;
break;
}
}
// Failed, or can't extend? Do a normal submit.
verts = Memory::GetPointerUnchecked(gstate_c.vertexAddr);
inds = nullptr;
if ((vertexType & GE_VTYPE_IDX_MASK) != GE_VTYPE_IDX_NONE) {
inds = Memory::GetPointerUnchecked(gstate_c.indexAddr);
} else {
// We can extend again after submitting a normal draw.
canExtend = true;
}
u32 count = data & 0xFFFF;
drawEngineCommon_->SubmitPrim(verts, inds, newPrim, count, vertTypeID, cullMode, &bytesRead);
AdvanceVerts(vertexType, count, bytesRead);
totalVertCount += count;
@@ -1032,18 +1051,26 @@ void GPUCommonHW::Execute_Prim(u32 op, u32 diff) {
{
uint32_t diff = data ^ vertexType;
// don't mask upper bits, vertexType is unmasked
if (diff & vtypeCheckMask) {
goto bail;
} else {
if (diff) {
drawEngineCommon_->FlushSkin();
if (diff & vtypeCheckMask)
goto bail;
canExtend = false; // TODO: Might support extending between some vertex types in the future.
vertexType = data;
vertTypeID = GetVertTypeID(vertexType, gstate.getUVGenMode(), g_Config.bSoftwareSkinning);
}
break;
}
case GE_CMD_VADDR:
{
gstate.cmdmem[GE_CMD_VADDR] = data;
gstate_c.vertexAddr = gstate_c.getRelativeAddress(data & 0x00FFFFFF);
uint32_t newAddr = gstate_c.getRelativeAddress(data & 0x00FFFFFF);
if (gstate_c.vertexAddr != newAddr) {
canExtend = false;
gstate_c.vertexAddr = newAddr;
}
break;
}
case GE_CMD_IADDR:
gstate.cmdmem[GE_CMD_IADDR] = data;
gstate_c.indexAddr = gstate_c.getRelativeAddress(data & 0x00FFFFFF);
@@ -1105,6 +1132,8 @@ void GPUCommonHW::Execute_Prim(u32 op, u32 diff) {
(Memory::ReadUnchecked_U32(target + 12 * 4) >> 24) == GE_CMD_RET &&
(target > currentList->stall || target + 12 * 4 < currentList->stall) &&
(gstate.boneMatrixNumber & 0x00FFFFFF) <= 96 - 12) {
drawEngineCommon_->FlushSkin();
canExtend = false;
FastLoadBoneMatrix(target);
} else {
goto bail;
@@ -1126,6 +1155,7 @@ void GPUCommonHW::Execute_Prim(u32 op, u32 diff) {
}
bail:
drawEngineCommon_->FlushSkin();
gstate.cmdmem[GE_CMD_VERTEXTYPE] = vertexType;
int cmdCount = src - start;
// Skip over the commands we just read out manually.
@@ -1647,7 +1677,7 @@ size_t GPUCommonHW::FormatGPUStatsCommon(char *buffer, size_t size) {
float vertexAverageCycles = gpuStats.numVertsSubmitted > 0 ? (float)gpuStats.vertexGPUCycles / (float)gpuStats.numVertsSubmitted : 0.0f;
return snprintf(buffer, size,
"DL processing time: %0.2f ms, %d drawsync, %d listsync\n"
"Draw calls: %d, flushes %d, clears %d, bbox jumps %d (%d updates)\n"
"Draw: %d (%d dec), flushes %d, clears %d, bbox jumps %d (%d updates)\n"
"Cached draws: %d (tracked: %d)\n"
"Vertices: %d cached: %d uncached: %d\n"
"FBOs active: %d (evaluations: %d)\n"
@@ -1660,6 +1690,7 @@ size_t GPUCommonHW::FormatGPUStatsCommon(char *buffer, size_t size) {
gpuStats.numDrawSyncs,
gpuStats.numListSyncs,
gpuStats.numDrawCalls,
gpuStats.numVertexDecodes,
gpuStats.numFlushes,
gpuStats.numClears,
gpuStats.numBBOXJumps,
+23 -9
View File
@@ -566,10 +566,11 @@ bool DrawEngineVulkan::VertexCacheLookup(int &vertexCount, GEPrimitiveType &prim
vai->minihash = ComputeMiniHash();
vai->status = VertexArrayInfoVulkan::VAI_HASHING;
vai->drawsUntilNextFullHash = 0;
DecodeVertsToPushPool(pushVertex_, &vbOffset, &vbuf); // writes to indexGen
DecodeVertsToPushPool(pushVertex_, &vbOffset, &vbuf);
DecodeInds();
vai->numVerts = indexGen.VertexCount();
vai->prim = indexGen.Prim();
vai->maxIndex = indexGen.MaxIndex();
vai->maxIndex = MaxIndex();
vai->flags = gstate_c.vertexFullAlpha ? VAIVULKAN_FLAG_VERTEXFULLALPHA : 0;
return true;
}
@@ -593,6 +594,7 @@ bool DrawEngineVulkan::VertexCacheLookup(int &vertexCount, GEPrimitiveType &prim
if (newMiniHash != vai->minihash || newHash != vai->hash) {
MarkUnreliable(vai);
DecodeVertsToPushPool(pushVertex_, &vbOffset, &vbuf);
DecodeInds();
return true;
}
if (vai->numVerts > 64) {
@@ -612,6 +614,7 @@ bool DrawEngineVulkan::VertexCacheLookup(int &vertexCount, GEPrimitiveType &prim
if (newMiniHash != vai->minihash) {
MarkUnreliable(vai);
DecodeVertsToPushPool(pushVertex_, &vbOffset, &vbuf);
DecodeInds();
return true;
}
}
@@ -619,9 +622,10 @@ bool DrawEngineVulkan::VertexCacheLookup(int &vertexCount, GEPrimitiveType &prim
if (!vai->vb) {
// Directly push to the vertex cache.
DecodeVertsToPushBuffer(vertexCache_, &vai->vbOffset, &vai->vb);
DecodeInds();
_dbg_assert_msg_(gstate_c.vertBounds.minV >= gstate_c.vertBounds.maxV, "Should not have checked UVs when caching.");
vai->numVerts = indexGen.VertexCount();
vai->maxIndex = indexGen.MaxIndex();
vai->maxIndex = MaxIndex();
vai->flags = gstate_c.vertexFullAlpha ? VAIVULKAN_FLAG_VERTEXFULLALPHA : 0;
if (forceIndexed) {
vai->prim = indexGen.GeneralPrim();
@@ -684,6 +688,7 @@ bool DrawEngineVulkan::VertexCacheLookup(int &vertexCount, GEPrimitiveType &prim
vai->numFrames++;
}
DecodeVertsToPushPool(pushVertex_, &vbOffset, &vbuf);
DecodeInds();
return true;
}
default:
@@ -748,6 +753,7 @@ void DrawEngineVulkan::DoFlush() {
// Decode directly into the pushbuffer
DecodeVertsToPushPool(pushVertex_, &vbOffset, &vbuf);
}
DecodeInds();
gpuStats.numUncachedVertsDrawn += indexGen.VertexCount();
}
@@ -845,6 +851,7 @@ void DrawEngineVulkan::DoFlush() {
dec_ = GetVertexDecoder(lastVType_);
}
DecodeVerts(decoded_);
DecodeInds();
bool hasColor = (lastVType_ & GE_VTYPE_COL_MASK) != GE_VTYPE_COL_NONE;
if (gstate.isModeThrough()) {
gstate_c.vertexFullAlpha = gstate_c.vertexFullAlpha && (hasColor || gstate.getMaterialAmbientA() == 255);
@@ -857,6 +864,7 @@ void DrawEngineVulkan::DoFlush() {
// Undo the strip optimization, not supported by the SW code yet.
if (prim == GE_PRIM_TRIANGLE_STRIP)
prim = GE_PRIM_TRIANGLES;
_dbg_assert_(prim != GE_PRIM_INVALID);
u16 *inds = decIndex_;
SoftwareTransformResult result{};
@@ -886,7 +894,7 @@ void DrawEngineVulkan::DoFlush() {
UpdateCachedViewportState(vpAndScissor);
}
int maxIndex = indexGen.MaxIndex();
int maxIndex = MaxIndex();
SoftwareTransform swTransform(params);
const Lin::Vec3 trans(gstate_c.vpXOffset, gstate_c.vpYOffset, gstate_c.vpZOffset * 0.5f + 0.5f);
@@ -1007,14 +1015,17 @@ void DrawEngineVulkan::DoFlush() {
}
gpuStats.numFlushes++;
gpuStats.numDrawCalls += numDrawCalls_;
gpuStats.numDrawCalls += numDrawInds_;
gpuStats.numVertexDecodes += numDrawVerts_;
gpuStats.numVertsSubmitted += vertexCountInDrawCalls_;
indexGen.Reset();
decodedVerts_ = 0;
numDrawCalls_ = 0;
numDrawVerts_ = 0;
numDrawInds_ = 0;
vertexCountInDrawCalls_ = 0;
decodeCounter_ = 0;
decodeIndsCounter_ = 0;
decodeVertsCounter_ = 0;
gstate_c.vertexFullAlpha = true;
framebufferManager_->SetColorUpdated(gstate_c.skipDrawReason);
@@ -1030,8 +1041,11 @@ void DrawEngineVulkan::DoFlush() {
void DrawEngineVulkan::ResetAfterDraw() {
indexGen.Reset();
decodedVerts_ = 0;
numDrawCalls_ = 0;
decodeCounter_ = 0;
numDrawVerts_ = 0;
numDrawInds_ = 0;
vertexCountInDrawCalls_ = 0;
decodeIndsCounter_ = 0;
decodeVertsCounter_ = 0;
decOptions_.applySkinInDecode = g_Config.bSoftwareSkinning;
gstate_c.vertexFullAlpha = true;
}
+4 -4
View File
@@ -170,13 +170,13 @@ public:
// So that this can be inlined
void Flush() {
if (!numDrawCalls_)
if (!numDrawInds_)
return;
DoFlush();
}
void FinishDeferred() {
if (!numDrawCalls_)
if (!numDrawInds_)
return;
// Decode any pending vertices. And also flush while we're at it, for simplicity.
// It might be possible to only decode like in the other backends, but meh, it can't matter.
@@ -185,9 +185,9 @@ public:
}
void DispatchFlush() override {
if (!numDrawCalls_)
if (!numDrawInds_)
return;
Flush();
DoFlush();
}
VkPipelineLayout GetPipelineLayout() const {
+2
View File
@@ -291,6 +291,8 @@ static VulkanPipeline *CreateVulkanPipeline(VulkanRenderManager *renderManager,
desc->geometryShaderSource = gs->GetShaderString(SHADER_STRING_SOURCE_CODE);
}
_dbg_assert_(key.topology != VK_PRIMITIVE_TOPOLOGY_POINT_LIST);
_dbg_assert_(key.topology != VK_PRIMITIVE_TOPOLOGY_LINE_LIST);
desc->topology = (VkPrimitiveTopology)key.topology;
int vertexStride = 0;