mirror of
https://github.com/hrydgard/ppsspp.git
synced 2026-11-12 17:19:28 +01:00
Merge pull request #22906 from hrydgard/bitplane-copy
Keep one framebuffer copy for a run of draws with the same write mask
This commit is contained in:
11 files changed
+64
-6
No files matched your search
@@ -30,6 +30,7 @@
|
||||
#include "Core/Config.h"
|
||||
#include "GPU/GPUCommon.h"
|
||||
#include "GPU/Common/DrawEngineCommon.h"
|
||||
#include "GPU/Common/FramebufferManagerCommon.h"
|
||||
#include "GPU/GPUStateSIMDUtil.h"
|
||||
#include "GPU/Common/SplineCommon.h"
|
||||
#include "GPU/Common/DepthRaster.h"
|
||||
@@ -775,6 +776,13 @@ void DrawEngineCommon::ApplyFramebufferRead(FBOTexState *fboTexState) {
|
||||
gstate_c.Dirty(DIRTY_SHADERBLEND);
|
||||
}
|
||||
|
||||
int DrawEngineCommon::ShaderReadBindFlags() const {
|
||||
// The mask forces blending and the logic op into the shader too, so check what the GE asks for.
|
||||
const bool maskOnly = pipelineState_.maskState.applyFramebufferRead && !gstate.isAlphaBlendEnabled() &&
|
||||
!(gstate.isLogicOpEnabled() && gstate.getLogicOp() != GE_LOGIC_COPY);
|
||||
return BINDFBCOLOR_MAY_COPY | (maskOnly ? BINDFBCOLOR_MASK_READ : 0) | BINDFBCOLOR_UNCACHED;
|
||||
}
|
||||
|
||||
int DrawEngineCommon::ComputeNumVertsToDecode() const {
|
||||
int sum = 0;
|
||||
for (int i = 0; i < numDrawVerts_; i++) {
|
||||
|
||||
@@ -195,6 +195,8 @@ protected:
|
||||
int ComputeNumVertsToDecode() const;
|
||||
|
||||
void ApplyFramebufferRead(FBOTexState *fboTexState);
|
||||
// For BindFramebufferAsColorTexture, when the draw reads the target for shader blending.
|
||||
int ShaderReadBindFlags() const;
|
||||
|
||||
void InitDepthRaster();
|
||||
void ShutdownDepthRaster();
|
||||
|
||||
@@ -119,6 +119,7 @@ void FramebufferManagerCommon::BeginFrame(const DisplayLayoutConfig &config) {
|
||||
DecimateFBOs();
|
||||
presentation_->BeginFrame(config);
|
||||
currentRenderVfb_ = nullptr;
|
||||
DiscardFramebufferCopy();
|
||||
|
||||
// Hack.
|
||||
displayLayoutConfigCopy_ = config;
|
||||
@@ -1420,6 +1421,11 @@ bool FramebufferManagerCommon::BindFramebufferAsColorTexture(int stage, VirtualF
|
||||
draw_->BindFramebufferAsTexture(currentFramebufferCopy_, stage, Draw::Aspect::COLOR_BIT, layer);
|
||||
return true;
|
||||
}
|
||||
const bool maskRead = (flags & BINDFBCOLOR_MASK_READ) != 0;
|
||||
if (maskRead && maskCopy_ && maskCopyVfb_ == framebuffer && maskCopyMask_ == gstate.getColorMask()) {
|
||||
draw_->BindFramebufferAsTexture(maskCopy_, stage, Draw::Aspect::COLOR_BIT, layer);
|
||||
return true;
|
||||
}
|
||||
|
||||
// There's a special case we can handle here, where the game is texturing from the same pixels being read, in order to
|
||||
// implement a DST*DST blending function, which the PSP can't do. However on the PC we can absolutely do this!
|
||||
@@ -1436,7 +1442,7 @@ bool FramebufferManagerCommon::BindFramebufferAsColorTexture(int stage, VirtualF
|
||||
return true;
|
||||
}
|
||||
|
||||
Draw::Framebuffer *renderCopy = GetTempFBO(TempFBO::COPY, framebuffer->renderWidth, framebuffer->renderHeight);
|
||||
Draw::Framebuffer *renderCopy = GetTempFBO(maskRead ? TempFBO::MASK_COPY : TempFBO::COPY, framebuffer->renderWidth, framebuffer->renderHeight);
|
||||
if (renderCopy) {
|
||||
VirtualFramebuffer copyInfo = *framebuffer;
|
||||
copyInfo.fbo = renderCopy;
|
||||
@@ -1451,6 +1457,11 @@ bool FramebufferManagerCommon::BindFramebufferAsColorTexture(int stage, VirtualF
|
||||
if (!partial && (flags & BINDFBCOLOR_UNCACHED) == 0) {
|
||||
currentFramebufferCopy_ = renderCopy;
|
||||
}
|
||||
if (!partial && maskRead) {
|
||||
maskCopy_ = renderCopy;
|
||||
maskCopyVfb_ = framebuffer;
|
||||
maskCopyMask_ = gstate.getColorMask();
|
||||
}
|
||||
gpuStats.perFrame.numCopiesForSelfTex++;
|
||||
} else {
|
||||
// Failed to get temp FBO? Weird.
|
||||
@@ -3208,6 +3219,14 @@ void FramebufferManagerCommon::DestroyAllFBOs() {
|
||||
drawPixelsCache_.clear();
|
||||
}
|
||||
|
||||
void FramebufferManagerCommon::CheckMaskCopyStillValid(const VirtualFramebuffer *vfb) {
|
||||
// Anything but a draw with the same write mask, without blending or a logic op, can change the bits the copy holds.
|
||||
if (vfb != maskCopyVfb_ || gstate.isModeClear() || gstate.getColorMask() != maskCopyMask_ ||
|
||||
gstate.isAlphaBlendEnabled() || (gstate.isLogicOpEnabled() && gstate.getLogicOp() != GE_LOGIC_COPY)) {
|
||||
maskCopy_ = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
static const char *TempFBOReasonToString(TempFBO reason) {
|
||||
switch (reason) {
|
||||
case TempFBO::DEPAL: return "depal";
|
||||
@@ -3216,6 +3235,7 @@ static const char *TempFBOReasonToString(TempFBO reason) {
|
||||
case TempFBO::STENCIL: return "stencil";
|
||||
case TempFBO::DEPTH_SHIFT: return "depth_shift";
|
||||
case TempFBO::SAMPLE0: return "sample0";
|
||||
case TempFBO::MASK_COPY: return "mask_copy";
|
||||
default: break;
|
||||
}
|
||||
return "";
|
||||
|
||||
@@ -214,6 +214,8 @@ enum BindFramebufferColorFlags {
|
||||
// Used when rendering to a temporary surface (e.g. not the current render target.)
|
||||
BINDFBCOLOR_FORCE_SELF = 8,
|
||||
BINDFBCOLOR_UNCACHED = 16,
|
||||
// The draw reads only the bits its write mask keeps. See maskCopy_.
|
||||
BINDFBCOLOR_MASK_READ = 32,
|
||||
};
|
||||
|
||||
enum DrawTextureFlags {
|
||||
@@ -241,6 +243,8 @@ enum class TempFBO {
|
||||
DEPTH_SHIFT,
|
||||
// The first samples of a multisampled framebuffer.
|
||||
SAMPLE0,
|
||||
// A copy for draws that read only the bits outside their write mask.
|
||||
MASK_COPY,
|
||||
};
|
||||
|
||||
inline Draw::DataFormat GEFormatToThin3D(GEBufferFormat geFormat) {
|
||||
@@ -522,7 +526,19 @@ public:
|
||||
|
||||
void DiscardFramebufferCopy() {
|
||||
currentFramebufferCopy_ = nullptr;
|
||||
maskCopy_ = nullptr;
|
||||
}
|
||||
// The target hasn't changed, but texturing from it should see the latest drawing.
|
||||
void DiscardSelfTextureCopy() {
|
||||
currentFramebufferCopy_ = nullptr;
|
||||
}
|
||||
// Call before a draw to the current render target.
|
||||
void CheckMaskCopy(const VirtualFramebuffer *vfb) {
|
||||
if (maskCopy_) {
|
||||
CheckMaskCopyStillValid(vfb);
|
||||
}
|
||||
}
|
||||
void CheckMaskCopyStillValid(const VirtualFramebuffer *vfb);
|
||||
|
||||
bool PresentedThisFrame() const;
|
||||
void SetPresentationOutput(const OutputParams *output);
|
||||
@@ -640,6 +656,12 @@ protected:
|
||||
VirtualFramebuffer *currentRenderVfb_ = nullptr;
|
||||
|
||||
Draw::Framebuffer *currentFramebufferCopy_ = nullptr;
|
||||
// A draw with a write mask only reads the bits the mask keeps, and doesn't change them. So while the same
|
||||
// mask draws on, without blending or a logic op, one copy serves every draw: DBZ Tenkaichi Tag Team draws
|
||||
// its shadows one bit at a time, which took a copy per flush.
|
||||
Draw::Framebuffer *maskCopy_ = nullptr;
|
||||
const VirtualFramebuffer *maskCopyVfb_ = nullptr;
|
||||
u32 maskCopyMask_ = 0;
|
||||
|
||||
// The range of PSP memory that may contain FBOs. So we can skip iterating.
|
||||
u32 framebufColorRangeEnd_ = 0;
|
||||
|
||||
@@ -348,6 +348,8 @@ bool FramebufferManagerCommon::PerformWriteStencilFromMemory(u32 addr, int size,
|
||||
if (!CheckStencilBits(src, dstBuffer, values, usedBits))
|
||||
return false;
|
||||
|
||||
DiscardFramebufferCopy();
|
||||
|
||||
if (usedBits == 0) {
|
||||
if (flags & WriteStencil::STENCIL_IS_ZERO) {
|
||||
// Common when creating buffers, it's already 0.
|
||||
|
||||
@@ -156,7 +156,7 @@ void DrawEngineD3D11::ApplyDrawState(int prim) {
|
||||
ApplyStencilReplaceAndLogicOpIgnoreBlend(blendState.replaceAlphaWithStencil, blendState);
|
||||
|
||||
if (fboTexBindState == FBO_TEX_COPY_BIND_TEX) {
|
||||
framebufferManager_->BindFramebufferAsColorTexture(1, framebufferManager_->GetCurrentRenderVFB(), BINDFBCOLOR_MAY_COPY | BINDFBCOLOR_UNCACHED, 0);
|
||||
framebufferManager_->BindFramebufferAsColorTexture(1, framebufferManager_->GetCurrentRenderVFB(), ShaderReadBindFlags(), 0);
|
||||
// No sampler required, we do a plain Load in the pixel shader.
|
||||
fboTexBound_ = true;
|
||||
fboTexBindState = FBO_TEX_NONE;
|
||||
|
||||
@@ -148,7 +148,7 @@ void DrawEngineGLES::ApplyDrawState(int prim) {
|
||||
// fboTexNeedsBind_ won't be set if we can read directly from the target.
|
||||
if (fboTexBindState == FBO_TEX_COPY_BIND_TEX) {
|
||||
// Note that this is positions, not UVs, that we need the copy from.
|
||||
framebufferManager_->BindFramebufferAsColorTexture(1, framebufferManager_->GetCurrentRenderVFB(), BINDFBCOLOR_MAY_COPY | BINDFBCOLOR_UNCACHED, 0);
|
||||
framebufferManager_->BindFramebufferAsColorTexture(1, framebufferManager_->GetCurrentRenderVFB(), ShaderReadBindFlags(), 0);
|
||||
// If we are rendering at a higher resolution, linear is probably best for the dest color.
|
||||
renderManager->SetTextureSampler(1, GL_CLAMP_TO_EDGE, GL_CLAMP_TO_EDGE, GL_LINEAR, GL_LINEAR, 0.0f);
|
||||
fboTexBound_ = true;
|
||||
|
||||
@@ -1607,6 +1607,7 @@ void GPUCommon::FlushImm() {
|
||||
}
|
||||
if (vfb) {
|
||||
CheckDepthUsage(vfb);
|
||||
framebufferManager_->CheckMaskCopy(vfb);
|
||||
}
|
||||
|
||||
bool antialias = (immFlags_ & GE_IMM_ANTIALIAS) != 0;
|
||||
|
||||
+4
-1
@@ -956,6 +956,7 @@ void GPUCommonHW::Execute_Prim(u32 op, u32 diff) {
|
||||
}
|
||||
|
||||
CheckDepthUsage(vfb);
|
||||
framebufferManager_->CheckMaskCopy(vfb);
|
||||
|
||||
const void *verts = Memory::GetPointerUnchecked(gstate_c.vertexAddr);
|
||||
const void *inds = nullptr;
|
||||
@@ -1334,6 +1335,7 @@ void GPUCommonHW::Execute_Bezier(u32 op, u32 diff) {
|
||||
}
|
||||
|
||||
CheckDepthUsage(vfb);
|
||||
framebufferManager_->CheckMaskCopy(vfb);
|
||||
|
||||
if (!Memory::IsValidAddress(gstate_c.vertexAddr)) {
|
||||
ERROR_LOG(Log::G3D, "Bad vertex address %08x!", gstate_c.vertexAddr);
|
||||
@@ -1409,6 +1411,7 @@ void GPUCommonHW::Execute_Spline(u32 op, u32 diff) {
|
||||
}
|
||||
|
||||
CheckDepthUsage(vfb);
|
||||
framebufferManager_->CheckMaskCopy(vfb);
|
||||
|
||||
if (!Memory::IsValidAddress(gstate_c.vertexAddr)) {
|
||||
ERROR_LOG(Log::G3D, "Bad vertex address %08x!", gstate_c.vertexAddr);
|
||||
@@ -1522,7 +1525,7 @@ void GPUCommonHW::Execute_TexFlush(u32 op, u32 diff) {
|
||||
// Games call this when they need the effect of drawing to be visible to texturing.
|
||||
// And for a bunch of other reasons, but either way, this is what we need to do.
|
||||
// It's possible we could also use this as a hint for the texture cache somehow.
|
||||
framebufferManager_->DiscardFramebufferCopy();
|
||||
framebufferManager_->DiscardSelfTextureCopy();
|
||||
}
|
||||
|
||||
void GPUCommonHW::FlushPendingDrawing() {
|
||||
|
||||
@@ -552,7 +552,7 @@ void DrawEngineMetal::ConvertStateToKeys(int prim) {
|
||||
void DrawEngineMetal::BindShaderBlendTex() {
|
||||
if (!gstate.isModeClear() && fboTexBindState_ == FBO_TEX_COPY_BIND_TEX) {
|
||||
VirtualFramebuffer *curRenderVfb = framebufferManager_->GetCurrentRenderVFB();
|
||||
bool bindResult = framebufferManager_->BindFramebufferAsColorTexture(1, curRenderVfb, BINDFBCOLOR_MAY_COPY | BINDFBCOLOR_UNCACHED, Draw::ALL_LAYERS);
|
||||
bool bindResult = framebufferManager_->BindFramebufferAsColorTexture(1, curRenderVfb, ShaderReadBindFlags(), Draw::ALL_LAYERS);
|
||||
_dbg_assert_(bindResult);
|
||||
boundSecondary_ = (__bridge id<MTLTexture>)(void *)draw_->GetNativeObject(Draw::NativeObject::BOUND_TEXTURE1_IMAGEVIEW);
|
||||
fboTexBound_ = true;
|
||||
|
||||
@@ -352,7 +352,7 @@ void DrawEngineVulkan::BindShaderBlendTex() {
|
||||
if (!gstate.isModeClear()) {
|
||||
if (fboTexBindState_ == FBO_TEX_COPY_BIND_TEX) {
|
||||
VirtualFramebuffer *curRenderVfb = framebufferManager_->GetCurrentRenderVFB();
|
||||
bool bindResult = framebufferManager_->BindFramebufferAsColorTexture(1, curRenderVfb, BINDFBCOLOR_MAY_COPY | BINDFBCOLOR_UNCACHED, Draw::ALL_LAYERS);
|
||||
bool bindResult = framebufferManager_->BindFramebufferAsColorTexture(1, curRenderVfb, ShaderReadBindFlags(), Draw::ALL_LAYERS);
|
||||
_dbg_assert_(bindResult);
|
||||
boundSecondary_ = (VkImageView)draw_->GetNativeObject(Draw::NativeObject::BOUND_TEXTURE1_IMAGEVIEW);
|
||||
fboTexBound_ = true;
|
||||
|
||||
Reference in new issue
Block a user