diff --git a/GPU/Software/BinManager.cpp b/GPU/Software/BinManager.cpp index cd2c553ab1..00ce9d6779 100644 --- a/GPU/Software/BinManager.cpp +++ b/GPU/Software/BinManager.cpp @@ -209,6 +209,7 @@ void BinManager::UpdateState() { const bool hadDepth = pendingWrites_[1].base != 0; if (HasDirty(SoftDirty::BINNER_RANGE)) { + drawTargetAddr_ = gstate.getFrameBufAddress(); DrawingCoords scissorTL(gstate.getScissorX1(), gstate.getScissorY1()); DrawingCoords scissorBR(std::min(gstate.getScissorX2(), gstate.getRegionX2()), std::min(gstate.getScissorY2(), gstate.getRegionY2())); ScreenCoords screenScissorTL = TransformUnit::DrawingToScreen(scissorTL, 0); @@ -292,14 +293,14 @@ bool BinManager::HasTextureWrite(const RasterizerState &state) { return false; } -bool BinManager::IsExactSelfRender(const Rasterizer::RasterizerState &state, const BinItem &item) { +bool BinManager::IsExactSelfRender(const Rasterizer::RasterizerState &state, const BinItem &item) const { if (item.type != BinItemType::SPRITE && item.type != BinItemType::RECT) return false; if (state.textureProj || state.maxTexLevel > 0) return false; // Only possible if the texture is 1:1. - if ((state.texaddr[0] & 0x0F1FFFFF) != (gstate.getFrameBufAddress() & 0x0F1FFFFF)) + if ((state.texaddr[0] & 0x0F1FFFFF) != (drawTargetAddr_ & 0x0F1FFFFF)) return false; int bufferPixelWidth = BufferFormatBytesPerPixel(state.pixelID.FBFormat()); int texturePixelWidth = textureBitsPerPixel[state.samplerID.texfmt] / 8; @@ -361,8 +362,13 @@ void BinManager::MarkPendingWrites(const Rasterizer::RasterizerState &state) { constexpr uint32_t mirrorMask = 0x041FFFFF; const uint32_t bpp = state.pixelID.FBFormat() == GE_FORMAT_8888 ? 4 : 2; pendingWrites_[0].Expand(gstate.getFrameBufAddress() & mirrorMask, bpp, gstate.FrameBufStride(), scissorTL, scissorBR); - if (state.pixelID.depthWrite) + if (state.pixelID.depthWrite) { pendingWrites_[1].Expand(gstate.getDepthBufAddress() & mirrorMask, 2, gstate.DepthBufStride(), scissorTL, scissorBR); + } else if (gstate.isDepthTestEnabled() && !gstate.isModeClear()) { + // Testing without writing still reads the depth buffer, so a transfer into it has to wait. + const uint32_t depthAddr = gstate.getDepthBufAddress() & mirrorMask; + pendingReads_[depthAddr].Expand(depthAddr, 2, gstate.DepthBufStride(), scissorTL, scissorBR); + } } inline void BinDirtyRange::Expand(uint32_t newBase, uint32_t bpp, uint32_t stride, const DrawingCoords &tl, const DrawingCoords &br) { @@ -584,8 +590,17 @@ void BinManager::Drain(bool flushing) { } void BinManager::Flush(const char *reason) { - if (queueRange_.x1 == 0x7FFFFFFF) + if (queueRange_.x1 == 0x7FFFFFFF) { + // Nothing queued, so nothing refers to the older states and CLUTs. Trim them anyway: callers + // flush because one of these rings is full, and push into it right after. + while (states_.Size() > 1) { + states_.SkipNext(); + } + while (cluts_.Size() > 1) { + cluts_.SkipNext(); + } return; + } double st = 0.0; const bool collectDebugStats = g_coreCollectDebugStats; diff --git a/GPU/Software/BinManager.h b/GPU/Software/BinManager.h index 8f453251a4..afff6b7e66 100644 --- a/GPU/Software/BinManager.h +++ b/GPU/Software/BinManager.h @@ -132,7 +132,7 @@ struct BinQueue { } bool Full() const { - return size_ == N - 1; + return size_ >= N - 1; } bool NearFull() const { @@ -278,13 +278,16 @@ private: const char *slowestFlushReason_ = nullptr; double slowestFlushTime_ = 0.0; int lastFlipstats_ = 0; + // The framebuffer the queued draws render to. A framebuffer change flushes first, so it's one for all + // of them, and during that flush gstate already has the new one. + u32 drawTargetAddr_ = 0; int enqueues_ = 0; int mostThreads_ = 0; void MarkPendingReads(const Rasterizer::RasterizerState &state); void MarkPendingWrites(const Rasterizer::RasterizerState &state); bool HasTextureWrite(const Rasterizer::RasterizerState &state); - static bool IsExactSelfRender(const Rasterizer::RasterizerState &state, const BinItem &item); + bool IsExactSelfRender(const Rasterizer::RasterizerState &state, const BinItem &item) const; void OptimizePendingStates(uint16_t first, uint16_t last); BinCoords Scissor(BinCoords range); BinCoords Range(const VertexData &v0, const VertexData &v1, const VertexData &v2); diff --git a/GPU/Software/RasterizerRectangle.cpp b/GPU/Software/RasterizerRectangle.cpp index 2b1d7dd4e9..74c9d66e3c 100644 --- a/GPU/Software/RasterizerRectangle.cpp +++ b/GPU/Software/RasterizerRectangle.cpp @@ -506,11 +506,16 @@ bool RectangleFastPath(const VertexData &v0, const VertexData &v1, BinManager &b if (g_needsClearAfterDialog) { g_needsClearAfterDialog = false; // Afterwards, we also need to clear the actual destination. Can do a fast rectfill. + // The binner's state was computed with texturing on, so recompute it around the sprite. + const SoftDirty texDirty = SoftDirty::SAMPLER_BASIC | SoftDirty::SAMPLER_TEXLIST | SoftDirty::RAST_TEX | SoftDirty::BINNER_OVERLAP; gstate.textureMapEnable &= ~1; + binner.SetDirty(texDirty); + binner.UpdateState(); VertexData newV1 = v1; newV1.color0 = 0xFF000000; binner.AddSprite(v0, newV1); gstate.textureMapEnable |= 1; + binner.SetDirty(texDirty); } return true; } else {