From 52ad0d03351cc791892cdf16abf45f76eacf5ec0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sun, 1 Oct 2023 13:57:41 +0200 Subject: [PATCH 1/5] Minor cleanup in Prim() --- GPU/GPUCommonHW.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/GPU/GPUCommonHW.cpp b/GPU/GPUCommonHW.cpp index 18efa1b9a8..f3e6fb6ea0 100644 --- a/GPU/GPUCommonHW.cpp +++ b/GPU/GPUCommonHW.cpp @@ -896,14 +896,10 @@ void GPUCommonHW::Execute_Prim(u32 op, u32 diff) { PROFILE_THIS_SCOPE("execprim"); - u32 data = op & 0xFFFFFF; - u32 count = data & 0xFFFF; - if (count == 0) - return; FlushImm(); // Upper bits are ignored. - GEPrimitiveType prim = static_cast((data >> 16) & 7); + GEPrimitiveType prim = static_cast((op >> 16) & 7); SetDrawType(DRAW_PRIM, prim); // Discard AA lines as we can't do anything that makes sense with these anyway. The SW plugin might, though. @@ -952,6 +948,10 @@ void GPUCommonHW::Execute_Prim(u32 op, u32 diff) { vfb->usageFlags |= FB_USAGE_BLUE_TO_ALPHA; } + u32 count = op & 0xFFFF; + if (count == 0) + return; + // Must check this after SetRenderFrameBuffer so we know SKIPDRAW_NON_DISPLAYED_FB. if (gstate_c.skipDrawReason & (SKIPDRAW_SKIPFRAME | SKIPDRAW_NON_DISPLAYED_FB)) { // Rough estimate, not sure what's correct. @@ -995,9 +995,9 @@ void GPUCommonHW::Execute_Prim(u32 op, u32 diff) { int totalVertCount = count; // PRIMs are often followed by more PRIMs. Save some work and submit them immediately. - const u32_le *src = (const u32_le *)Memory::GetPointerUnchecked(currentList->pc + 4); + const u32_le *start = (const u32_le *)Memory::GetPointerUnchecked(currentList->pc + 4); + const u32_le *src = start; const u32_le *stall = currentList->stall ? (const u32_le *)Memory::GetPointerUnchecked(currentList->stall) : 0; - int cmdCount = 0; // Optimized submission of sequences of PRIM. Allows us to avoid going through all the mess // above for each one. This can be expanded to support additional games that intersperse @@ -1130,12 +1130,12 @@ void GPUCommonHW::Execute_Prim(u32 op, u32 diff) { // All other commands might need a flush or something, stop this inner loop. goto bail; } - cmdCount++; src++; } bail: gstate.cmdmem[GE_CMD_VERTEXTYPE] = vertexType; + int cmdCount = src - start; // Skip over the commands we just read out manually. if (cmdCount > 0) { UpdatePC(currentList->pc, currentList->pc + cmdCount * 4); From a2fe90653474ecc865a64b1a20cfcba6fe7ca715 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sun, 1 Oct 2023 14:10:02 +0200 Subject: [PATCH 2/5] Micro-optimization: Don't need to check drawcalls for 0. Extract shared expression. Yes I checked assembly. --- GPU/GPUCommon.cpp | 2 +- GPU/GPUCommonHW.cpp | 12 +++--------- 2 files changed, 4 insertions(+), 10 deletions(-) diff --git a/GPU/GPUCommon.cpp b/GPU/GPUCommon.cpp index 9dfd2cdbbf..0a3083346c 100644 --- a/GPU/GPUCommon.cpp +++ b/GPU/GPUCommon.cpp @@ -1944,7 +1944,7 @@ bool GPUCommon::DescribeCodePtr(const u8 *ptr, std::string &name) { void GPUCommon::UpdateUVScaleOffset() { #ifdef _M_SSE - __m128i values = _mm_slli_epi32(_mm_load_si128((const __m128i *) & gstate.texscaleu), 8); + __m128i values = _mm_slli_epi32(_mm_load_si128((const __m128i *)&gstate.texscaleu), 8); _mm_storeu_si128((__m128i *)&gstate_c.uv, values); #elif PPSSPP_ARCH(ARM_NEON) const uint32x4_t values = vshlq_n_u32(vld1q_u32((const u32 *)&gstate.texscaleu), 8); diff --git a/GPU/GPUCommonHW.cpp b/GPU/GPUCommonHW.cpp index f3e6fb6ea0..617caaea82 100644 --- a/GPU/GPUCommonHW.cpp +++ b/GPU/GPUCommonHW.cpp @@ -949,8 +949,6 @@ void GPUCommonHW::Execute_Prim(u32 op, u32 diff) { } u32 count = op & 0xFFFF; - if (count == 0) - return; // Must check this after SetRenderFrameBuffer so we know SKIPDRAW_NON_DISPLAYED_FB. if (gstate_c.skipDrawReason & (SKIPDRAW_SKIPFRAME | SKIPDRAW_NON_DISPLAYED_FB)) { @@ -1017,11 +1015,6 @@ void GPUCommonHW::Execute_Prim(u32 op, u32 diff) { case GE_CMD_PRIM: { u32 count = data & 0xFFFF; - if (count == 0) { - // Ignore. - break; - } - GEPrimitiveType newPrim = static_cast((data >> 16) & 7); SetDrawType(DRAW_PRIM, newPrim); // TODO: more efficient updating of verts/inds @@ -1151,8 +1144,9 @@ bail: } } - gpuStats.vertexGPUCycles += vertexCost_ * totalVertCount; - cyclesExecuted += vertexCost_ * totalVertCount; + int cycles = vertexCost_ * totalVertCount; + gpuStats.vertexGPUCycles += cycles; + cyclesExecuted += cycles; } void GPUCommonHW::Execute_Bezier(u32 op, u32 diff) { From 3cef04f8858e1fd437f2e9762554d111c62efd0a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sun, 1 Oct 2023 14:23:11 +0200 Subject: [PATCH 3/5] Fix incorrect flushing behavior in the prim sequencer, small optimization --- GPU/GPUCommon.h | 5 ++++- GPU/GPUCommonHW.cpp | 7 ++++--- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/GPU/GPUCommon.h b/GPU/GPUCommon.h index 8b3f59b9dd..2ad73539dd 100644 --- a/GPU/GPUCommon.h +++ b/GPU/GPUCommon.h @@ -224,7 +224,10 @@ protected: virtual void CheckRenderResized() {} inline bool IsTrianglePrim(GEPrimitiveType prim) const { - return prim != GE_PRIM_RECTANGLES && prim > GE_PRIM_LINE_STRIP; + // TODO: KEEP_PREVIOUS is mistakenly treated as TRIANGLE here... This isn't new. + // static const bool p[8] = { false, false, false, true, true, true, false, true }; + // 10111000 = 0xB8; + return (0xB8 >> (int)prim) & 1; } void SetDrawType(DrawType type, GEPrimitiveType prim) { diff --git a/GPU/GPUCommonHW.cpp b/GPU/GPUCommonHW.cpp index 617caaea82..5e7c1575bf 100644 --- a/GPU/GPUCommonHW.cpp +++ b/GPU/GPUCommonHW.cpp @@ -1009,21 +1009,22 @@ void GPUCommonHW::Execute_Prim(u32 op, u32 diff) { if (debugRecording_) goto bail; + bool isTriangle = IsTrianglePrim(prim); while (src != stall) { uint32_t data = *src; switch (data >> 24) { case GE_CMD_PRIM: { - u32 count = data & 0xFFFF; GEPrimitiveType newPrim = static_cast((data >> 16) & 7); - SetDrawType(DRAW_PRIM, newPrim); + 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 verts = Memory::GetPointerUnchecked(gstate_c.vertexAddr); inds = nullptr; if ((vertexType & GE_VTYPE_IDX_MASK) != GE_VTYPE_IDX_NONE) { inds = Memory::GetPointerUnchecked(gstate_c.indexAddr); } - + u32 count = data & 0xFFFF; drawEngineCommon_->SubmitPrim(verts, inds, newPrim, count, vertTypeID, cullMode, &bytesRead); AdvanceVerts(vertexType, count, bytesRead); totalVertCount += count; From bd931f9cbe189171f0fa7e10107b12b43180ba42 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sun, 1 Oct 2023 14:31:46 +0200 Subject: [PATCH 4/5] Additional minor cleanups --- GPU/GPUCommon.h | 2 +- GPU/GPUCommonHW.cpp | 15 ++++++--------- 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/GPU/GPUCommon.h b/GPU/GPUCommon.h index 2ad73539dd..e6e2c0774f 100644 --- a/GPU/GPUCommon.h +++ b/GPU/GPUCommon.h @@ -227,7 +227,7 @@ protected: // TODO: KEEP_PREVIOUS is mistakenly treated as TRIANGLE here... This isn't new. // static const bool p[8] = { false, false, false, true, true, true, false, true }; // 10111000 = 0xB8; - return (0xB8 >> (int)prim) & 1; + return (0xB8U >> (u8)prim) & 1; } void SetDrawType(DrawType type, GEPrimitiveType prim) { diff --git a/GPU/GPUCommonHW.cpp b/GPU/GPUCommonHW.cpp index 5e7c1575bf..40bdcad2c5 100644 --- a/GPU/GPUCommonHW.cpp +++ b/GPU/GPUCommonHW.cpp @@ -948,12 +948,15 @@ void GPUCommonHW::Execute_Prim(u32 op, u32 diff) { vfb->usageFlags |= FB_USAGE_BLUE_TO_ALPHA; } - u32 count = op & 0xFFFF; + if (gstate_c.dirty & DIRTY_VERTEXSHADER_STATE) { + vertexCost_ = EstimatePerVertexCost(); + } + u32 count = op & 0xFFFF; // Must check this after SetRenderFrameBuffer so we know SKIPDRAW_NON_DISPLAYED_FB. if (gstate_c.skipDrawReason & (SKIPDRAW_SKIPFRAME | SKIPDRAW_NON_DISPLAYED_FB)) { // Rough estimate, not sure what's correct. - cyclesExecuted += EstimatePerVertexCost() * count; + cyclesExecuted += vertexCost_ * count; if (gstate.isModeClear()) { gpuStats.numClears++; } @@ -974,10 +977,6 @@ void GPUCommonHW::Execute_Prim(u32 op, u32 diff) { inds = Memory::GetPointerUnchecked(indexAddr); } - if (gstate_c.dirty & DIRTY_VERTEXSHADER_STATE) { - vertexCost_ = EstimatePerVertexCost(); - } - int bytesRead = 0; UpdateUVScaleOffset(); @@ -1002,9 +1001,7 @@ void GPUCommonHW::Execute_Prim(u32 op, u32 diff) { // PRIM commands with other commands. A special case is Earth Defence Force 2 that changes culling mode // between each prim, we just change the triangle winding right here to still be able to join draw calls. - uint32_t vtypeCheckMask = ~GE_VTYPE_WEIGHTCOUNT_MASK; - if (!g_Config.bSoftwareSkinning) - vtypeCheckMask = 0xFFFFFFFF; + uint32_t vtypeCheckMask = g_Config.bSoftwareSkinning ? (~GE_VTYPE_WEIGHTCOUNT_MASK) : 0xFFFFFFFF; if (debugRecording_) goto bail; From 76ad3dec4da65060c44190b8ae45157a5d6ac327 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Sun, 1 Oct 2023 16:39:20 +0200 Subject: [PATCH 5/5] Revert unclear optimization --- GPU/GPUCommon.h | 6 +++++- GPU/GPUCommonHW.cpp | 3 ++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/GPU/GPUCommon.h b/GPU/GPUCommon.h index e6e2c0774f..85cec6f52b 100644 --- a/GPU/GPUCommon.h +++ b/GPU/GPUCommon.h @@ -225,9 +225,13 @@ protected: inline bool IsTrianglePrim(GEPrimitiveType prim) const { // TODO: KEEP_PREVIOUS is mistakenly treated as TRIANGLE here... This isn't new. + // + // Interesting optimization, but not confident in performance: // static const bool p[8] = { false, false, false, true, true, true, false, true }; // 10111000 = 0xB8; - return (0xB8U >> (u8)prim) & 1; + // return (0xB8U >> (u8)prim) & 1; + + return prim > GE_PRIM_LINE_STRIP && prim != GE_PRIM_RECTANGLES; } void SetDrawType(DrawType type, GEPrimitiveType prim) { diff --git a/GPU/GPUCommonHW.cpp b/GPU/GPUCommonHW.cpp index 40bdcad2c5..a9b5ded875 100644 --- a/GPU/GPUCommonHW.cpp +++ b/GPU/GPUCommonHW.cpp @@ -1003,10 +1003,11 @@ void GPUCommonHW::Execute_Prim(u32 op, u32 diff) { uint32_t vtypeCheckMask = g_Config.bSoftwareSkinning ? (~GE_VTYPE_WEIGHTCOUNT_MASK) : 0xFFFFFFFF; + bool isTriangle = IsTrianglePrim(prim); + if (debugRecording_) goto bail; - bool isTriangle = IsTrianglePrim(prim); while (src != stall) { uint32_t data = *src; switch (data >> 24) {