diff --git a/GPU/Common/PresentationCommon.cpp b/GPU/Common/PresentationCommon.cpp index 2064f6ef17..d11bf72b95 100644 --- a/GPU/Common/PresentationCommon.cpp +++ b/GPU/Common/PresentationCommon.cpp @@ -539,8 +539,7 @@ void PresentationCommon::UpdateUniforms(bool hasVideo) { } void PresentationCommon::CopyToOutput(OutputFlags flags, int uvRotation, float u0, float v0, float u1, float v1) { - // Make sure Direct3D 11 clears state, since we set shaders outside Draw. - draw_->BindPipeline(nullptr); + draw_->InvalidateCachedState(); // TODO: If shader objects have been created by now, we might have received errors. // GLES can have the shader fail later, shader->failed / shader->error. @@ -649,6 +648,7 @@ void PresentationCommon::CopyToOutput(OutputFlags flags, int uvRotation, float u Draw::Framebuffer *postShaderFramebuffer = postShaderFramebuffers_[i]; draw_->BindFramebufferAsRenderTarget(postShaderFramebuffer, { Draw::RPAction::DONT_CARE, Draw::RPAction::DONT_CARE, Draw::RPAction::DONT_CARE }, "PostShader"); + if (usePostShaderOutput) { draw_->BindFramebufferAsTexture(postShaderFramebuffers_[i - 1], 0, Draw::FB_COLOR_BIT, 0); } else { diff --git a/GPU/D3D11/GPU_D3D11.cpp b/GPU/D3D11/GPU_D3D11.cpp index fcd1fa0d8d..3d8a463838 100644 --- a/GPU/D3D11/GPU_D3D11.cpp +++ b/GPU/D3D11/GPU_D3D11.cpp @@ -119,7 +119,6 @@ GPU_D3D11::~GPU_D3D11() { shaderManagerD3D11_->ClearShaders(); delete shaderManagerD3D11_; delete textureCacheD3D11_; - draw_->BindPipeline(nullptr); stockD3D11.Destroy(); } @@ -233,8 +232,8 @@ void GPU_D3D11::ReapplyGfxState() { } void GPU_D3D11::EndHostFrame() { - // Tell the DrawContext that it's time to reset everything. - draw_->BindPipeline(nullptr); + // Probably not really necessary. + draw_->InvalidateCachedState(); } void GPU_D3D11::BeginFrame() { diff --git a/GPU/D3D11/TextureCacheD3D11.cpp b/GPU/D3D11/TextureCacheD3D11.cpp index 17cc43472e..47a1a95535 100644 --- a/GPU/D3D11/TextureCacheD3D11.cpp +++ b/GPU/D3D11/TextureCacheD3D11.cpp @@ -389,7 +389,9 @@ void TextureCacheD3D11::ApplyTextureFramebuffer(TexCacheEntry *entry, VirtualFra Draw::Framebuffer *depalFBO = framebufferManagerD3D11_->GetTempFBO(TempFBO::DEPAL, framebuffer->renderWidth, framebuffer->renderHeight, Draw::FBO_8888); shaderManager_->DirtyLastShader(); - draw_->BindPipeline(nullptr); + + // Not sure why or if we need this here - we're not about to actually draw using draw_, just use its framebuffer binds. + draw_->InvalidateCachedState(); float xoff = -0.5f / framebuffer->renderWidth; float yoff = 0.5f / framebuffer->renderHeight; diff --git a/ext/native/thin3d/thin3d.h b/ext/native/thin3d/thin3d.h index 88001c4aa3..e3f6390afe 100644 --- a/ext/native/thin3d/thin3d.h +++ b/ext/native/thin3d/thin3d.h @@ -646,7 +646,11 @@ public: BindTextures(stage, 1, textures); } // from sampler 0 and upwards - // Call this with nullptr to signal that you're done with the stuff you've bound, like textures and samplers and stuff. + // Clear state cached within thin3d. Must be called after directly calling API functions. + // Note that framebuffer state (which framebuffer is bounds) may not be cached. + // Must not actually perform any API calls itself since this can be called when no framebuffer is bound for rendering. + virtual void InvalidateCachedState() = 0; + virtual void BindPipeline(Pipeline *pipeline) = 0; virtual void Draw(int vertexCount, int offset) = 0; diff --git a/ext/native/thin3d/thin3d_d3d11.cpp b/ext/native/thin3d/thin3d_d3d11.cpp index 1f589e3856..1457e79af1 100644 --- a/ext/native/thin3d/thin3d_d3d11.cpp +++ b/ext/native/thin3d/thin3d_d3d11.cpp @@ -77,6 +77,8 @@ public: void GetFramebufferDimensions(Framebuffer *fbo, int *w, int *h) override; + void InvalidateCachedState() override; + void BindTextures(int start, int count, Texture **textures) override; void BindSamplerStates(int start, int count, SamplerState **states) override; void BindVertexBuffers(int start, int count, Buffer **buffers, int *offsets) override; @@ -1019,19 +1021,20 @@ void D3D11DrawContext::UpdateDynamicUniformBuffer(const void *ub, size_t size) { context_->Unmap(curPipeline_->dynamicUniforms, 0); } -void D3D11DrawContext::BindPipeline(Pipeline *pipeline) { - if (pipeline == nullptr) { - // This is a signal to forget all our caching. - curBlend_ = nullptr; - curDepth_ = nullptr; - curRaster_ = nullptr; - curPS_ = nullptr; - curVS_ = nullptr; - curGS_ = nullptr; - curInputLayout_ = nullptr; - curTopology_ = D3D11_PRIMITIVE_TOPOLOGY_UNDEFINED; - } +void D3D11DrawContext::InvalidateCachedState() { + // This is a signal to forget all our state caching. + curBlend_ = nullptr; + curDepth_ = nullptr; + curRaster_ = nullptr; + curPS_ = nullptr; + curVS_ = nullptr; + curGS_ = nullptr; + curInputLayout_ = nullptr; + curTopology_ = D3D11_PRIMITIVE_TOPOLOGY_UNDEFINED; + curPipeline_ = nullptr; +} +void D3D11DrawContext::BindPipeline(Pipeline *pipeline) { D3D11Pipeline *dPipeline = (D3D11Pipeline *)pipeline; if (curPipeline_ == dPipeline) return; diff --git a/ext/native/thin3d/thin3d_d3d9.cpp b/ext/native/thin3d/thin3d_d3d9.cpp index 2b629149a8..9843af9358 100644 --- a/ext/native/thin3d/thin3d_d3d9.cpp +++ b/ext/native/thin3d/thin3d_d3d9.cpp @@ -606,6 +606,8 @@ public: return stepId_; } + void InvalidateCachedState() override; + private: LPDIRECT3D9 d3d_; LPDIRECT3D9EX d3dEx_; @@ -631,6 +633,10 @@ private: bool supportsINTZ = false; }; +void D3D9Context::InvalidateCachedState() { + curPipeline_ = nullptr; +} + #define FB_DIV 1 #define FOURCC_INTZ ((D3DFORMAT)(MAKEFOURCC('I', 'N', 'T', 'Z'))) diff --git a/ext/native/thin3d/thin3d_gl.cpp b/ext/native/thin3d/thin3d_gl.cpp index 3a7c0f958c..abcf53dc3e 100644 --- a/ext/native/thin3d/thin3d_gl.cpp +++ b/ext/native/thin3d/thin3d_gl.cpp @@ -481,9 +481,10 @@ public: return renderManager_.GetCurrentStepId(); } + void InvalidateCachedState() override; + private: void ApplySamplers(); - void Unbind(); GLRenderManager renderManager_; @@ -612,14 +613,14 @@ void OpenGLContext::BeginFrame() { } void OpenGLContext::EndFrame() { - Unbind(); - FrameData &frameData = frameData_[renderManager_.GetCurFrame()]; renderManager_.EndPushBuffer(frameData.push); // upload the data! renderManager_.Finish(); + + InvalidateCachedState(); } -void OpenGLContext::Unbind() { +void OpenGLContext::InvalidateCachedState() { // Unbind stuff. for (auto &texture : boundTextures_) { texture = nullptr; @@ -627,9 +628,6 @@ void OpenGLContext::Unbind() { for (auto &sampler : boundSamplers_) { sampler = nullptr; } - for (int i = 0; i < ARRAY_SIZE(boundTextures_); i++) { - renderManager_.BindTexture(i, nullptr); - } curPipeline_ = nullptr; } @@ -1107,15 +1105,13 @@ bool OpenGLPipeline::LinkShaders() { void OpenGLContext::BindPipeline(Pipeline *pipeline) { curPipeline_ = (OpenGLPipeline *)pipeline; - if (curPipeline_) { - curPipeline_->blend->Apply(&renderManager_); - curPipeline_->depthStencil->Apply(&renderManager_, stencilRef_); - curPipeline_->raster->Apply(&renderManager_); - renderManager_.BindProgram(curPipeline_->program_); - } else { - // Wipe bound textures and samplers. - Unbind(); + if (!curPipeline_) { + return; } + curPipeline_->blend->Apply(&renderManager_); + curPipeline_->depthStencil->Apply(&renderManager_, stencilRef_); + curPipeline_->raster->Apply(&renderManager_); + renderManager_.BindProgram(curPipeline_->program_); } void OpenGLContext::UpdateDynamicUniformBuffer(const void *ub, size_t size) { diff --git a/ext/native/thin3d/thin3d_vulkan.cpp b/ext/native/thin3d/thin3d_vulkan.cpp index d31620e655..e74f5f1615 100644 --- a/ext/native/thin3d/thin3d_vulkan.cpp +++ b/ext/native/thin3d/thin3d_vulkan.cpp @@ -408,10 +408,6 @@ public: void BindPipeline(Pipeline *pipeline) override { curPipeline_ = (VKPipeline *)pipeline; - - if (!pipeline) { - UnbindBoundState(); - } } // TODO: Make VKBuffers proper buffers, and do a proper binding model. This is just silly. @@ -497,8 +493,9 @@ public: return renderManager_.GetCurrentStepId(); } + void InvalidateCachedState() override; + private: - void UnbindBoundState(); VulkanTexture *GetNullTexture(); VulkanContext *vulkan_ = nullptr; @@ -923,10 +920,10 @@ void VKContext::EndFrame() { push_ = nullptr; // Unbind stuff, to avoid accidentally relying on it across frames (and provide some protection against forgotten unbinds of deleted things). - UnbindBoundState(); + InvalidateCachedState(); } -void VKContext::UnbindBoundState() { +void VKContext::InvalidateCachedState() { curPipeline_ = nullptr; for (auto &view : boundImageView_) {