diff --git a/CMakeLists.txt b/CMakeLists.txt index 2072a5d003..0eee60a31e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -606,6 +606,7 @@ add_library(Common STATIC Common/File/FileDescriptor.cpp Common/File/FileDescriptor.h Common/GPU/DataFormat.h + Common/GPU/MiscTypes.h Common/GPU/thin3d.cpp Common/GPU/thin3d.h Common/GPU/thin3d_create.h diff --git a/Common/Common.vcxproj b/Common/Common.vcxproj index d59ae3136e..15b6197cfd 100644 --- a/Common/Common.vcxproj +++ b/Common/Common.vcxproj @@ -432,6 +432,7 @@ + diff --git a/Common/Common.vcxproj.filters b/Common/Common.vcxproj.filters index ac2fde0155..12a2b04191 100644 --- a/Common/Common.vcxproj.filters +++ b/Common/Common.vcxproj.filters @@ -452,6 +452,9 @@ Render + + GPU + diff --git a/Common/GPU/D3D11/thin3d_d3d11.cpp b/Common/GPU/D3D11/thin3d_d3d11.cpp index 46713b53f3..57afa60de2 100644 --- a/Common/GPU/D3D11/thin3d_d3d11.cpp +++ b/Common/GPU/D3D11/thin3d_d3d11.cpp @@ -163,8 +163,8 @@ public: void HandleEvent(Event ev, int width, int height, void *param1, void *param2) override; - int GetCurrentStepId() const override { - return stepId_; + void SetInvalidationCallback(InvalidationCallback callback) override { + invalidationCallback_ = callback; } private: @@ -177,7 +177,6 @@ private: ID3D11DeviceContext *context_; ID3D11Device1 *device1_; ID3D11DeviceContext1 *context1_; - int stepId_ = -1; ID3D11Texture2D *bbRenderTargetTex_ = nullptr; // NOT OWNED ID3D11RenderTargetView *bbRenderTargetView_ = nullptr; @@ -216,6 +215,8 @@ private: ID3D11Buffer *nextIndexBuffer_ = nullptr; int nextIndexBufferOffset_ = 0; + InvalidationCallback invalidationCallback_; + // Dynamic state float blendFactor_[4]{}; bool blendFactorDirty_ = false; @@ -399,7 +400,6 @@ void D3D11DrawContext::HandleEvent(Event ev, int width, int height, void *param1 // Make sure that we don't eliminate the next time the render target is set. curRenderTargetView_ = nullptr; curDepthStencilView_ = nullptr; - stepId_ = 0; break; } } @@ -1500,13 +1500,11 @@ void D3D11DrawContext::CopyFramebufferImage(Framebuffer *srcfb, int level, int x D3D11_BOX srcBox{ (UINT)x, (UINT)y, (UINT)z, (UINT)(x + width), (UINT)(y + height), (UINT)(z + depth) }; context_->CopySubresourceRegion(dstTex, dstLevel, dstX, dstY, dstZ, srcTex, level, &srcBox); } - stepId_++; } bool D3D11DrawContext::BlitFramebuffer(Framebuffer *srcfb, int srcX1, int srcY1, int srcX2, int srcY2, Framebuffer *dstfb, int dstX1, int dstY1, int dstX2, int dstY2, int channelBits, FBBlitFilter filter, const char *tag) { // Unfortunately D3D11 has no equivalent to this, gotta render a quad. Well, in some cases we can issue a copy instead. Crash(); - stepId_++; return false; } @@ -1652,7 +1650,6 @@ bool D3D11DrawContext::CopyFramebufferToMemorySync(Framebuffer *src, int channel if (!useGlobalPacktex) { packTex->Release(); } - stepId_++; return true; } @@ -1699,7 +1696,9 @@ void D3D11DrawContext::BindFramebufferAsRenderTarget(Framebuffer *fbo, const Ren context_->ClearDepthStencilView(curDepthStencilView_, mask, rp.clearDepth, rp.clearStencil); } - stepId_++; + if (invalidationCallback_) { + invalidationCallback_(InvalidationFlags::RENDER_PASS_STATE); + } } void D3D11DrawContext::BindFramebufferAsTexture(Framebuffer *fbo, int binding, FBChannel channelBit, int layer) { diff --git a/Common/GPU/D3D9/thin3d_d3d9.cpp b/Common/GPU/D3D9/thin3d_d3d9.cpp index 8de75b09d0..7b38c1a3a2 100644 --- a/Common/GPU/D3D9/thin3d_d3d9.cpp +++ b/Common/GPU/D3D9/thin3d_d3d9.cpp @@ -610,18 +610,17 @@ public: void HandleEvent(Event ev, int width, int height, void *param1, void *param2) override; - int GetCurrentStepId() const override { - return stepId_; - } - void InvalidateCachedState() override; + void SetInvalidationCallback(InvalidationCallback callback) override { + invalidationCallback_ = callback; + } + private: LPDIRECT3D9 d3d_; LPDIRECT3D9EX d3dEx_; LPDIRECT3DDEVICE9 device_; LPDIRECT3DDEVICE9EX deviceEx_; - int stepId_ = -1; int adapterId_ = -1; D3DADAPTER_IDENTIFIER9 identifier_{}; D3DCAPS9 d3dCaps_; @@ -647,6 +646,8 @@ private: // Dynamic state uint8_t stencilRef_ = 0; + + InvalidationCallback invalidationCallback_; }; void D3D9Context::InvalidateCachedState() { @@ -1319,7 +1320,10 @@ void D3D9Context::BindFramebufferAsRenderTarget(Framebuffer *fbo, const RenderPa dxstate.scissorRect.restore(); dxstate.scissorTest.restore(); dxstate.viewport.restore(); - stepId_++; + + if (invalidationCallback_) { + invalidationCallback_(InvalidationFlags::RENDER_PASS_STATE); + } } uintptr_t D3D9Context::GetFramebufferAPITexture(Framebuffer *fbo, int channelBits, int attachment) { @@ -1398,7 +1402,6 @@ bool D3D9Context::BlitFramebuffer(Framebuffer *srcfb, int srcX1, int srcY1, int } else { return false; } - stepId_++; return SUCCEEDED(device_->StretchRect(srcSurf, &srcRect, dstSurf, &dstRect, (filter == FB_BLIT_LINEAR && channelBits == FB_COLOR_BIT) ? D3DTEXF_LINEAR : D3DTEXF_POINT)); } @@ -1519,7 +1522,6 @@ void D3D9Context::HandleEvent(Event ev, int width, int height, void *param1, voi device_->GetDepthStencilSurface(&deviceDSsurf); break; case Event::PRESENTED: - stepId_ = 0; break; } } diff --git a/Common/GPU/MiscTypes.h b/Common/GPU/MiscTypes.h new file mode 100644 index 0000000000..fd3d90dbb5 --- /dev/null +++ b/Common/GPU/MiscTypes.h @@ -0,0 +1,11 @@ +#pragma once + +#include "Common/Common.h" + +enum class InvalidationFlags { + RENDER_PASS_STATE = 1, + COMMAND_BUFFER_STATE = 2, +}; +ENUM_CLASS_BITOPS(InvalidationFlags); + +typedef std::function InvalidationCallback; diff --git a/Common/GPU/OpenGL/GLRenderManager.cpp b/Common/GPU/OpenGL/GLRenderManager.cpp index f3eb5f59eb..034f4c672e 100644 --- a/Common/GPU/OpenGL/GLRenderManager.cpp +++ b/Common/GPU/OpenGL/GLRenderManager.cpp @@ -354,6 +354,10 @@ void GLRenderManager::BindFramebufferAsRenderTarget(GLRFramebuffer *fb, GLRRende step->dependencies.insert(fb); } } + + if (invalidationCallback_) { + invalidationCallback_(InvalidationFlags::RENDER_PASS_STATE); + } } void GLRenderManager::BindFramebufferAsTexture(GLRFramebuffer *fb, int binding, int aspectBit) { @@ -480,7 +484,6 @@ void GLRenderManager::BeginFrame() { // In GL, we have to do deletes on the submission thread. insideFrame_ = true; - renderStepOffset_ = 0; } void GLRenderManager::Finish() { @@ -612,9 +615,6 @@ void GLRenderManager::Run(int frame) { } void GLRenderManager::FlushSync() { - // TODO: Reset curRenderStep_? - renderStepOffset_ += (int)steps_.size(); - int curFrame = curFrame_; FrameData &frameData = frameData_[curFrame]; { diff --git a/Common/GPU/OpenGL/GLRenderManager.h b/Common/GPU/OpenGL/GLRenderManager.h index 135f776361..905fd70826 100644 --- a/Common/GPU/OpenGL/GLRenderManager.h +++ b/Common/GPU/OpenGL/GLRenderManager.h @@ -10,6 +10,7 @@ #include #include "Common/GPU/OpenGL/GLCommon.h" +#include "Common/GPU/MiscTypes.h" #include "Common/Data/Convert/SmallDataConvert.h" #include "Common/Log.h" #include "GLQueueRunner.h" @@ -368,6 +369,9 @@ public: GLRenderManager() {} ~GLRenderManager(); + void SetInvalidationCallback(InvalidationCallback callback) { + invalidationCallback_ = callback; + } void SetErrorCallback(ErrorCallbackFn callback, void *userdata) { queueRunner_.SetErrorCallback(callback, userdata); } @@ -980,12 +984,6 @@ public: skipGLCalls_ = true; } - // Gets a frame-unique ID of the current step being recorded. Can be used to figure out - // when the current step has changed, which means the caller will need to re-record its state. - int GetCurrentStepId() const { - return renderStepOffset_ + (int)steps_.size(); - } - private: void BeginSubmitFrame(int frame); void EndSubmitFrame(int frame); @@ -1033,8 +1031,7 @@ private: // Submission time state bool insideFrame_ = false; - // This is the offset within this frame, in case of a mid-frame sync. - int renderStepOffset_ = 0; + GLRStep *curRenderStep_ = nullptr; std::vector steps_; std::vector initSteps_; @@ -1074,4 +1071,6 @@ private: GLRProgram *curProgram_ = nullptr; #endif Draw::DeviceCaps caps_{}; + + InvalidationCallback invalidationCallback_; }; diff --git a/Common/GPU/OpenGL/thin3d_gl.cpp b/Common/GPU/OpenGL/thin3d_gl.cpp index 18da422ca2..3d68c9352f 100644 --- a/Common/GPU/OpenGL/thin3d_gl.cpp +++ b/Common/GPU/OpenGL/thin3d_gl.cpp @@ -466,12 +466,12 @@ public: void HandleEvent(Event ev, int width, int height, void *param1, void *param2) override {} - int GetCurrentStepId() const override { - return renderManager_.GetCurrentStepId(); - } - void InvalidateCachedState() override; + void SetInvalidationCallback(InvalidationCallback callback) { + renderManager_.SetInvalidationCallback(callback); + } + private: void ApplySamplers(); diff --git a/Common/GPU/Vulkan/VulkanRenderManager.cpp b/Common/GPU/Vulkan/VulkanRenderManager.cpp index d828c277ca..df3f5e5539 100644 --- a/Common/GPU/Vulkan/VulkanRenderManager.cpp +++ b/Common/GPU/Vulkan/VulkanRenderManager.cpp @@ -668,8 +668,6 @@ void VulkanRenderManager::BeginFrame(bool enableProfiling, bool enableLogProfile insideFrame_ = true; vulkan_->BeginFrame(enableLogProfiler ? GetInitCmd() : VK_NULL_HANDLE); - renderStepOffset_ = 0; - frameData.profile.timestampDescriptions.clear(); if (frameData.profilingEnabled_) { // For various reasons, we need to always use an init cmd buffer in this case to perform the vkCmdResetQueryPool, @@ -956,6 +954,10 @@ void VulkanRenderManager::BindFramebufferAsRenderTarget(VKRFramebuffer *fb, VKRR data.clear.clearMask = lateClearMask; curRenderStep_->commands.push_back(data); } + + if (invalidationCallback_) { + invalidationCallback_(InvalidationFlags::RENDER_PASS_STATE); + } } bool VulkanRenderManager::CopyFramebufferToMemorySync(VKRFramebuffer *src, VkImageAspectFlags aspectBits, int x, int y, int w, int h, Draw::DataFormat destFormat, uint8_t *pixels, int pixelStride, const char *tag) { @@ -1414,7 +1416,9 @@ void VulkanRenderManager::Run(VKRRenderThreadTask &task) { // Called from main thread. void VulkanRenderManager::FlushSync() { - renderStepOffset_ += (int)steps_.size(); + if (invalidationCallback_) { + invalidationCallback_(InvalidationFlags::COMMAND_BUFFER_STATE); + } int curFrame = vulkan_->GetCurFrame(); FrameData &frameData = frameData_[curFrame]; diff --git a/Common/GPU/Vulkan/VulkanRenderManager.h b/Common/GPU/Vulkan/VulkanRenderManager.h index 04daa5966e..ca22dbab29 100644 --- a/Common/GPU/Vulkan/VulkanRenderManager.h +++ b/Common/GPU/Vulkan/VulkanRenderManager.h @@ -19,6 +19,7 @@ #include "Common/Data/Convert/SmallDataConvert.h" #include "Common/Math/math_util.h" #include "Common/GPU/DataFormat.h" +#include "Common/GPU/MiscTypes.h" #include "Common/GPU/Vulkan/VulkanQueueRunner.h" // Forward declaration @@ -235,6 +236,10 @@ public: // Zaps queued up commands. Use if you know there's a risk you've queued up stuff that has already been deleted. Can happen during in-game shutdown. void Wipe(); + void SetInvalidationCallback(InvalidationCallback callback) { + invalidationCallback_ = callback; + } + // This starts a new step containing a render pass (unless it can be trivially merged into the previous one, which is pretty common). // // After a "CopyFramebuffer" or the other functions that start "steps", you need to call this beforce @@ -475,12 +480,6 @@ public: VkCommandBuffer GetInitCmd(); - // Gets a frame-unique ID of the current step being recorded. Can be used to figure out - // when the current step has changed, which means the caller will need to re-record its state. - int GetCurrentStepId() const { - return renderStepOffset_ + (int)steps_.size(); - } - bool CreateBackbuffers(); void DestroyBackbuffers(); @@ -520,7 +519,6 @@ private: void DrainCompileQueue(); void Run(VKRRenderThreadTask &task); - void BeginSubmitFrame(int frame); // Bad for performance but sometimes necessary for synchronous CPU readbacks (screenshots and whatnot). void FlushSync(); @@ -548,7 +546,6 @@ private: bool run_ = false; // This is the offset within this frame, in case of a mid-frame sync. - int renderStepOffset_ = 0; VKRStep *curRenderStep_ = nullptr; bool curStepHasViewport_ = false; bool curStepHasScissor_ = false; @@ -587,4 +584,6 @@ private: SimpleStat initTimeMs_; SimpleStat totalGPUTimeMs_; SimpleStat renderCPUTimeMs_; + + std::function invalidationCallback_; }; diff --git a/Common/GPU/Vulkan/thin3d_vulkan.cpp b/Common/GPU/Vulkan/thin3d_vulkan.cpp index f055ad9499..2af40fda3a 100644 --- a/Common/GPU/Vulkan/thin3d_vulkan.cpp +++ b/Common/GPU/Vulkan/thin3d_vulkan.cpp @@ -498,14 +498,14 @@ public: void HandleEvent(Event ev, int width, int height, void *param1, void *param2) override; - int GetCurrentStepId() const override { - return renderManager_.GetCurrentStepId(); - } - void InvalidateCachedState() override; void InvalidateFramebuffer(FBInvalidationStage stage, uint32_t channels) override; + void SetInvalidationCallback(InvalidationCallback callback) { + renderManager_.SetInvalidationCallback(callback); + } + private: VulkanTexture *GetNullTexture(); VulkanContext *vulkan_ = nullptr; diff --git a/Common/GPU/thin3d.h b/Common/GPU/thin3d.h index 0f1a8b86bb..de3c9cc49d 100644 --- a/Common/GPU/thin3d.h +++ b/Common/GPU/thin3d.h @@ -16,6 +16,7 @@ #include "Common/Common.h" #include "Common/GPU/DataFormat.h" #include "Common/GPU/Shader.h" +#include "Common/GPU/MiscTypes.h" #include "Common/Data/Collections/Slice.h" namespace Lin { @@ -769,7 +770,9 @@ public: // This is called when we launch a new game, so any collected internal stats in the backends don't carry over. virtual void ResetStats() {} - virtual int GetCurrentStepId() const = 0; + // Used by the DrawEngines to know when they have to re-apply some state. + // Not very elegant, but more elegant than the old passId hack. + virtual void SetInvalidationCallback(InvalidationCallback callback) = 0; protected: ShaderModule *vsPresets_[VS_MAX_PRESET]; diff --git a/GPU/D3D11/DrawEngineD3D11.cpp b/GPU/D3D11/DrawEngineD3D11.cpp index b702d15250..37343f852a 100644 --- a/GPU/D3D11/DrawEngineD3D11.cpp +++ b/GPU/D3D11/DrawEngineD3D11.cpp @@ -113,6 +113,8 @@ void DrawEngineD3D11::InitDeviceObjects() { tessDataTransferD3D11 = new TessellationDataTransferD3D11(context_, device_); tessDataTransfer = tessDataTransferD3D11; + + draw_->SetInvalidationCallback(std::bind(&DrawEngineD3D11::Invalidate, this, std::placeholders::_1)); } void DrawEngineD3D11::ClearTrackedVertexArrays() { @@ -136,6 +138,8 @@ void DrawEngineD3D11::NotifyConfigChanged() { } void DrawEngineD3D11::DestroyDeviceObjects() { + draw_->SetInvalidationCallback(InvalidationCallback()); + ClearTrackedVertexArrays(); ClearInputLayoutMap(); delete tessDataTransferD3D11; @@ -322,19 +326,18 @@ VertexArrayInfoD3D11::~VertexArrayInfoD3D11() { ebo->Release(); } +// In D3D, we're synchronous and state carries over so all we reset here on a new step is the viewport/scissor. +void DrawEngineD3D11::Invalidate(InvalidationFlags flags) { + if (flags & InvalidationFlags::RENDER_PASS_STATE) { + gstate_c.Dirty(DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_TEXTURE_IMAGE | DIRTY_TEXTURE_PARAMS); + textureCache_->ForgetLastTexture(); + } +} + // The inline wrapper in the header checks for numDrawCalls == 0 void DrawEngineD3D11::DoFlush() { gpuStats.numFlushes++; - // In D3D, we're synchronous and state carries over so all we reset here on a new step is the viewport/scissor. - int curRenderStepId = draw_->GetCurrentStepId(); - if (lastRenderStepId_ != curRenderStepId) { - // Dirty everything that has dynamic state that will need re-recording. - gstate_c.Dirty(DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_TEXTURE_IMAGE | DIRTY_TEXTURE_PARAMS); - textureCache_->ForgetLastTexture(); - lastRenderStepId_ = curRenderStepId; - } - bool textureNeedsApply = false; if (gstate_c.IsDirty(DIRTY_TEXTURE_IMAGE | DIRTY_TEXTURE_PARAMS) && !gstate.isModeClear() && gstate.isTextureMapEnabled()) { textureCache_->SetTexture(); diff --git a/GPU/D3D11/DrawEngineD3D11.h b/GPU/D3D11/DrawEngineD3D11.h index 03f5f47ad7..8245cae6a4 100644 --- a/GPU/D3D11/DrawEngineD3D11.h +++ b/GPU/D3D11/DrawEngineD3D11.h @@ -155,6 +155,8 @@ public: void ClearInputLayoutMap(); private: + void Invalidate(InvalidationFlags flags); + void DoFlush(); void ApplyDrawState(int prim); diff --git a/GPU/Directx9/DrawEngineDX9.cpp b/GPU/Directx9/DrawEngineDX9.cpp index 8a9de7ef79..e22d5577d2 100644 --- a/GPU/Directx9/DrawEngineDX9.cpp +++ b/GPU/Directx9/DrawEngineDX9.cpp @@ -126,10 +126,11 @@ DrawEngineDX9::~DrawEngineDX9() { } void DrawEngineDX9::InitDeviceObjects() { - + draw_->SetInvalidationCallback(std::bind(&DrawEngineDX9::Invalidate, this, std::placeholders::_1)); } void DrawEngineDX9::DestroyDeviceObjects() { + draw_->SetInvalidationCallback(InvalidationCallback()); ClearTrackedVertexArrays(); } @@ -310,18 +311,17 @@ void DrawEngineDX9::BeginFrame() { lastRenderStepId_ = -1; } +// In D3D, we're synchronous and state carries over so all we reset here on a new step is the viewport/scissor. +void DrawEngineDX9::Invalidate(InvalidationFlags flags) { + if (flags & InvalidationFlags::RENDER_PASS_STATE) { + gstate_c.Dirty(DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_TEXTURE_IMAGE | DIRTY_TEXTURE_PARAMS); + } +} + // The inline wrapper in the header checks for numDrawCalls == 0 void DrawEngineDX9::DoFlush() { gpuStats.numFlushes++; - // In D3D, we're synchronous and state carries over so all we reset here on a new step is the viewport/scissor. - int curRenderStepId = draw_->GetCurrentStepId(); - if (lastRenderStepId_ != curRenderStepId) { - // Dirty everything that has dynamic state that will need re-recording. - gstate_c.Dirty(DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_TEXTURE_IMAGE | DIRTY_TEXTURE_PARAMS); - lastRenderStepId_ = curRenderStepId; - } - bool textureNeedsApply = false; if (gstate_c.IsDirty(DIRTY_TEXTURE_IMAGE | DIRTY_TEXTURE_PARAMS) && !gstate.isModeClear() && gstate.isTextureMapEnabled()) { textureCache_->SetTexture(); diff --git a/GPU/Directx9/DrawEngineDX9.h b/GPU/Directx9/DrawEngineDX9.h index 9ef5b37c65..aa2baf5cfc 100644 --- a/GPU/Directx9/DrawEngineDX9.h +++ b/GPU/Directx9/DrawEngineDX9.h @@ -144,6 +144,7 @@ protected: void DecimateTrackedVertexArrays(); private: + void Invalidate(InvalidationFlags flags); void DoFlush(); void ApplyDrawState(int prim); diff --git a/GPU/GLES/DrawEngineGLES.cpp b/GPU/GLES/DrawEngineGLES.cpp index 8b842f1dd2..098dc4d26c 100644 --- a/GPU/GLES/DrawEngineGLES.cpp +++ b/GPU/GLES/DrawEngineGLES.cpp @@ -117,9 +117,13 @@ void DrawEngineGLES::InitDeviceObjects() { entries.push_back({ ATTR_COLOR1, 3, GL_UNSIGNED_BYTE, GL_TRUE, vertexSize, offsetof(TransformedVertex, color1) }); entries.push_back({ ATTR_NORMAL, 1, GL_FLOAT, GL_FALSE, vertexSize, offsetof(TransformedVertex, fog) }); softwareInputLayout_ = render_->CreateInputLayout(entries); + + draw_->SetInvalidationCallback(std::bind(&DrawEngineGLES::Invalidate, this, std::placeholders::_1)); } void DrawEngineGLES::DestroyDeviceObjects() { + draw_->SetInvalidationCallback(InvalidationCallback()); + // Beware: this could be called twice in a row, sometimes. for (int i = 0; i < GLRenderManager::MAX_INFLIGHT_FRAMES; i++) { if (!frameData_[i].pushVertex && !frameData_[i].pushIndex) @@ -238,22 +242,22 @@ void *DrawEngineGLES::DecodeVertsToPushBuffer(GLPushBuffer *push, uint32_t *bind return dest; } +// A new render step means we need to flush any dynamic state. Really, any state that is reset in +// GLQueueRunner::PerformRenderPass. +void DrawEngineGLES::Invalidate(InvalidationFlags flags) { + if (flags & InvalidationFlags::RENDER_PASS_STATE) { + // Dirty everything that has dynamic state that will need re-recording. + gstate_c.Dirty(DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_DEPTHSTENCIL_STATE | DIRTY_BLEND_STATE | DIRTY_RASTER_STATE | DIRTY_TEXTURE_IMAGE | DIRTY_TEXTURE_PARAMS); + textureCache_->ForgetLastTexture(); + } +} + void DrawEngineGLES::DoFlush() { PROFILE_THIS_SCOPE("flush"); FrameData &frameData = frameData_[render_->GetCurFrame()]; gpuStats.numFlushes++; - // A new render step means we need to flush any dynamic state. Really, any state that is reset in - // GLQueueRunner::PerformRenderPass. - int curRenderStepId = render_->GetCurrentStepId(); - if (lastRenderStepId_ != curRenderStepId) { - // Dirty everything that has dynamic state that will need re-recording. - gstate_c.Dirty(DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_DEPTHSTENCIL_STATE | DIRTY_BLEND_STATE | DIRTY_RASTER_STATE | DIRTY_TEXTURE_IMAGE | DIRTY_TEXTURE_PARAMS); - textureCache_->ForgetLastTexture(); - lastRenderStepId_ = curRenderStepId; - } - bool textureNeedsApply = false; if (gstate_c.IsDirty(DIRTY_TEXTURE_IMAGE | DIRTY_TEXTURE_PARAMS) && !gstate.isModeClear() && gstate.isTextureMapEnabled()) { textureCache_->SetTexture(); diff --git a/GPU/GLES/DrawEngineGLES.h b/GPU/GLES/DrawEngineGLES.h index d843c83ff4..ed8e18f292 100644 --- a/GPU/GLES/DrawEngineGLES.h +++ b/GPU/GLES/DrawEngineGLES.h @@ -84,7 +84,6 @@ public: void BeginFrame(); void EndFrame(); - // So that this can be inlined void Flush() { if (!numDrawCalls) @@ -117,6 +116,8 @@ protected: bool UpdateUseHWTessellation(bool enable) override; private: + void Invalidate(InvalidationFlags flags); + void InitDeviceObjects(); void DestroyDeviceObjects(); diff --git a/GPU/Vulkan/DrawEngineVulkan.cpp b/GPU/Vulkan/DrawEngineVulkan.cpp index a26b14320f..b4aa4532b7 100644 --- a/GPU/Vulkan/DrawEngineVulkan.cpp +++ b/GPU/Vulkan/DrawEngineVulkan.cpp @@ -16,6 +16,7 @@ // https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. #include +#include #include "Common/Data/Convert/SmallDataConvert.h" #include "Common/Profiler/Profiler.h" @@ -150,7 +151,7 @@ void DrawEngineVulkan::InitDeviceObjects() { dpTypes[2].type = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER; dpTypes[3].descriptorCount = DEFAULT_DESC_POOL_SIZE; // TODO: Use a separate layout when no spline stuff is needed to reduce the need for these. dpTypes[3].type = VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT; - dpTypes[4].descriptorCount = 1; // For the frame global uniform buffer. + dpTypes[4].descriptorCount = DEFAULT_DESC_POOL_SIZE; // For the frame global uniform buffer. Might need to allocate multiple times. dpTypes[4].type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER; VkDescriptorPoolCreateInfo dp{ VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO }; @@ -204,6 +205,8 @@ void DrawEngineVulkan::InitDeviceObjects() { tessDataTransferVulkan = new TessellationDataTransferVulkan(vulkan); tessDataTransfer = tessDataTransferVulkan; + + draw_->SetInvalidationCallback(std::bind(&DrawEngineVulkan::Invalidate, this, std::placeholders::_1)); } DrawEngineVulkan::~DrawEngineVulkan() { @@ -234,7 +237,14 @@ void DrawEngineVulkan::FrameData::Destroy(VulkanContext *vulkan) { } void DrawEngineVulkan::DestroyDeviceObjects() { - VulkanContext *vulkan = draw_ ? (VulkanContext *)draw_->GetNativeObject(Draw::NativeObject::CONTEXT) : nullptr; + if (!draw_) { + // We've already done this from LostDevice. + return; + } + + VulkanContext *vulkan = (VulkanContext *)draw_->GetNativeObject(Draw::NativeObject::CONTEXT); + + draw_->SetInvalidationCallback(InvalidationCallback()); delete tessDataTransferVulkan; tessDataTransfer = nullptr; @@ -283,8 +293,6 @@ void DrawEngineVulkan::BeginFrame() { lastPipeline_ = nullptr; - lastRenderStepId_ = -1; - FrameData *frame = &GetCurFrame(); // First reset all buffers, then begin. This is so that Reset can free memory and Begin can allocate it, @@ -536,6 +544,21 @@ void MarkUnreliable(VertexArrayInfoVulkan *vai) { // For now we just leave it in the pushbuffer. } +void DrawEngineVulkan::Invalidate(InvalidationFlags flags) { + if (flags & InvalidationFlags::COMMAND_BUFFER_STATE) { + GetCurFrame().frameDescSetUpdated = false; + } + if (flags & InvalidationFlags::RENDER_PASS_STATE) { + // If have a new render pass, dirty our dynamic state so it gets re-set. + // We have to do this again after the last possible place in DoFlush that can cause a renderpass switch + // like a shader blend blit or similar. But before we actually set the state! + // + // Dirty everything that has dynamic state that will need re-recording. + gstate_c.Dirty(DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_DEPTHSTENCIL_STATE | DIRTY_BLEND_STATE | DIRTY_TEXTURE_IMAGE | DIRTY_TEXTURE_PARAMS); + lastPipeline_ = nullptr; + } +} + // The inline wrapper in the header checks for numDrawCalls == 0 void DrawEngineVulkan::DoFlush() { VulkanRenderManager *renderManager = (VulkanRenderManager *)draw_->GetNativeObject(Draw::NativeObject::RENDER_MANAGER); @@ -545,18 +568,6 @@ void DrawEngineVulkan::DoFlush() { gpuStats.numFlushes++; - // If have a new render pass, dirty our dynamic state so it gets re-set. - // We have to do this again after the last possible place in DoFlush that can cause a renderpass switch - // like a shader blend blit or similar. But before we actually set the state! - int curRenderStepId = renderManager->GetCurrentStepId(); - if (lastRenderStepId_ != curRenderStepId) { - // Dirty everything that has dynamic state that will need re-recording. - gstate_c.Dirty(DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_DEPTHSTENCIL_STATE | DIRTY_BLEND_STATE | DIRTY_TEXTURE_IMAGE | DIRTY_TEXTURE_PARAMS); - textureCache_->ForgetLastTexture(); - lastRenderStepId_ = curRenderStepId; - lastPipeline_ = nullptr; - } - bool tess = gstate_c.submitType == SubmitType::HW_BEZIER || gstate_c.submitType == SubmitType::HW_SPLINE; bool textureNeedsApply = false; @@ -785,16 +796,6 @@ void DrawEngineVulkan::DoFlush() { } BindShaderBlendTex(); // This might cause copies so important to do before BindPipeline. - // If have a new render pass, dirty our dynamic state so it gets re-set. - // WARNING: We have to do this AFTER the last possible place in DoFlush that can cause a renderpass switch - // like a shader blend blit or similar. But before we actually set the state! - int curRenderStepId = renderManager->GetCurrentStepId(); - if (lastRenderStepId_ != curRenderStepId) { - // Dirty everything that has dynamic state that will need re-recording. - gstate_c.Dirty(DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_DEPTHSTENCIL_STATE | DIRTY_BLEND_STATE); - lastRenderStepId_ = curRenderStepId; - } - renderManager->BindPipeline(pipeline->pipeline, pipeline->pipelineFlags, pipelineLayout_); if (pipeline != lastPipeline_) { if (lastPipeline_ && !(lastPipeline_->UsesBlendConstant() && pipeline->UsesBlendConstant())) { @@ -928,16 +929,6 @@ void DrawEngineVulkan::DoFlush() { } BindShaderBlendTex(); // This might cause copies so super important to do before BindPipeline. - // If have a new render pass, dirty our dynamic state so it gets re-set. - // WARNING: We have to do this AFTER the last possible place in DoFlush that can cause a renderpass switch - // like a shader blend blit or similar. But before we actually set the state! - int curRenderStepId = renderManager->GetCurrentStepId(); - if (lastRenderStepId_ != curRenderStepId) { - // Dirty everything that has dynamic state that will need re-recording. - gstate_c.Dirty(DIRTY_VIEWPORTSCISSOR_STATE | DIRTY_DEPTHSTENCIL_STATE | DIRTY_BLEND_STATE); - lastRenderStepId_ = curRenderStepId; - } - renderManager->BindPipeline(pipeline->pipeline, pipeline->pipelineFlags, pipelineLayout_); if (pipeline != lastPipeline_) { if (lastPipeline_ && !lastPipeline_->UsesBlendConstant() && pipeline->UsesBlendConstant()) { diff --git a/GPU/Vulkan/DrawEngineVulkan.h b/GPU/Vulkan/DrawEngineVulkan.h index 7d069c4305..d9f1339e5b 100644 --- a/GPU/Vulkan/DrawEngineVulkan.h +++ b/GPU/Vulkan/DrawEngineVulkan.h @@ -43,6 +43,7 @@ #include "GPU/Common/DrawEngineCommon.h" #include "GPU/Common/GPUStateUtils.h" #include "GPU/Vulkan/StateMappingVulkan.h" +#include "GPU/Vulkan/VulkanRenderManager.h" struct DecVtxFormat; struct UVScale; @@ -208,6 +209,8 @@ public: } private: + void Invalidate(InvalidationFlags flags); + struct FrameData; void ApplyDrawStateLate(VulkanRenderManager *renderManager, bool applyStencilRef, uint8_t stencilRef, bool useBlendConstant); void ConvertStateToVulkanKey(FramebufferManagerVulkan &fbManager, ShaderManagerVulkan *shaderManager, int prim, VulkanPipelineRasterStateKey &key, VulkanDynamicState &dynState); @@ -308,6 +311,4 @@ private: // Hardware tessellation TessellationDataTransferVulkan *tessDataTransferVulkan; - - int lastRenderStepId_ = -1; };