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..f58f55ac95 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) override {
+ renderManager_.SetInvalidationCallback(callback);
+ }
+
private:
void ApplySamplers();
diff --git a/Common/GPU/Vulkan/VulkanDebug.cpp b/Common/GPU/Vulkan/VulkanDebug.cpp
index 12bd0f4d6f..7a3d3a079f 100644
--- a/Common/GPU/Vulkan/VulkanDebug.cpp
+++ b/Common/GPU/Vulkan/VulkanDebug.cpp
@@ -42,7 +42,7 @@ VKAPI_ATTR VkBool32 VKAPI_CALL VulkanDebugUtilsCallback(
// UNASSIGNED
return false;
}
- if (messageCode == 606910136 || messageCode == -392708513) {
+ if (messageCode == 606910136 || messageCode == -392708513 || messageCode == -384083808) {
// VUID-vkCmdDraw-None-02686
// Kinda false positive, or at least very unnecessary, now that I solved the real issue.
// See https://github.com/hrydgard/ppsspp/pull/16354
@@ -92,6 +92,7 @@ VKAPI_ATTR VkBool32 VKAPI_CALL VulkanDebugUtilsCallback(
} else {
WARN_LOG(G3D, "VKDEBUG: %s", msg.c_str());
}
+
// false indicates that layer should not bail-out of an
// API call that had validation failures. This may mean that the
// app dies inside the driver due to invalid parameter(s).
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..dc1b0738e0 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) override {
+ 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..3d66b8c37d 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,17 @@ 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);
+ }
+}
+
// 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..e250da4be2 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,21 @@ 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);
+ }
+}
+
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..8256ea8916 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,19 @@ 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.
+ //
+ // 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 +566,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 +794,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 +927,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;
};