diff --git a/CMakeLists.txt b/CMakeLists.txt
index 4d96d72320..a40d4334ec 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1070,6 +1070,7 @@ add_library(native STATIC
ext/native/util/text/wrap_text.h
ext/native/util/text/wrap_text.cpp
ext/native/util/const_map.h
+ ext/native/util/tiny_set.h
ext/native/ext/jpge/jpgd.cpp
ext/native/ext/jpge/jpgd.h
ext/native/ext/jpge/jpge.cpp
diff --git a/GPU/GLES/FramebufferManagerGLES.cpp b/GPU/GLES/FramebufferManagerGLES.cpp
index fe597a4d49..266b4fde93 100644
--- a/GPU/GLES/FramebufferManagerGLES.cpp
+++ b/GPU/GLES/FramebufferManagerGLES.cpp
@@ -317,7 +317,7 @@ void FramebufferManagerGLES::UpdateDownloadTempBuffer(VirtualFramebuffer *nvfb)
_assert_msg_(G3D, nvfb->fbo, "Expecting a valid nvfb in UpdateDownloadTempBuffer");
// Discard the previous contents of this buffer where possible.
- if (gl_extensions.GLES3 && glInvalidateFramebuffer != nullptr) {
+ if (gl_extensions.GLES3) {
draw_->BindFramebufferAsRenderTarget(nvfb->fbo, { Draw::RPAction::DONT_CARE, Draw::RPAction::DONT_CARE, Draw::RPAction::DONT_CARE });
} else if (gl_extensions.IsGLES) {
draw_->BindFramebufferAsRenderTarget(nvfb->fbo, { Draw::RPAction::CLEAR, Draw::RPAction::CLEAR, Draw::RPAction::CLEAR });
diff --git a/ext/native/native.vcxproj b/ext/native/native.vcxproj
index 80b0f68e59..60f361b959 100644
--- a/ext/native/native.vcxproj
+++ b/ext/native/native.vcxproj
@@ -489,6 +489,7 @@
+
diff --git a/ext/native/native.vcxproj.filters b/ext/native/native.vcxproj.filters
index 82e64872aa..9c6af13184 100644
--- a/ext/native/native.vcxproj.filters
+++ b/ext/native/native.vcxproj.filters
@@ -350,6 +350,9 @@
ui
+
+ util
+
diff --git a/ext/native/thin3d/GLQueueRunner.cpp b/ext/native/thin3d/GLQueueRunner.cpp
index 20c5bf7800..78289b0fcb 100644
--- a/ext/native/thin3d/GLQueueRunner.cpp
+++ b/ext/native/thin3d/GLQueueRunner.cpp
@@ -827,13 +827,15 @@ void GLQueueRunner::PerformRenderPass(const GLRStep &step) {
{
GLenum attachments[3];
int count = 0;
+ bool hasDepth = step.render.framebuffer ? step.render.framebuffer->z_stencil_ : false;
if (c.clear.clearMask & GL_COLOR_BUFFER_BIT)
attachments[count++] = GL_COLOR_ATTACHMENT0;
- if (c.clear.clearMask & GL_DEPTH_BUFFER_BIT)
+ if (hasDepth && (c.clear.clearMask & GL_DEPTH_BUFFER_BIT))
attachments[count++] = GL_DEPTH_ATTACHMENT;
- if (c.clear.clearMask & GL_STENCIL_BUFFER_BIT)
+ if (hasDepth && (c.clear.clearMask & GL_STENCIL_BUFFER_BIT))
attachments[count++] = GL_STENCIL_BUFFER_BIT;
- glInvalidateFramebuffer(GL_FRAMEBUFFER, count, attachments);
+ if (glInvalidateFramebuffer != nullptr && count != 0)
+ glInvalidateFramebuffer(GL_FRAMEBUFFER, count, attachments);
CHECK_GL_ERROR_IF_DEBUG();
break;
}
diff --git a/ext/native/thin3d/GLQueueRunner.h b/ext/native/thin3d/GLQueueRunner.h
index 41bceedb20..813f8c020e 100644
--- a/ext/native/thin3d/GLQueueRunner.h
+++ b/ext/native/thin3d/GLQueueRunner.h
@@ -6,6 +6,7 @@
#include "gfx/gl_common.h"
#include "thin3d/DataFormat.h"
+#include "util/tiny_set.h"
struct GLRViewport {
float x, y, w, h, minZ, maxZ;
@@ -295,9 +296,14 @@ struct GLRStep {
GLRStep(GLRStepType _type) : stepType(_type) {}
GLRStepType stepType;
std::vector commands;
+ TinySet dependencies;
union {
struct {
GLRFramebuffer *framebuffer;
+ GLRRenderPassAction color;
+ GLRRenderPassAction depth;
+ GLRRenderPassAction stencil;
+ // Note: not accurate.
int numDraws;
} render;
struct {
diff --git a/ext/native/thin3d/GLRenderManager.cpp b/ext/native/thin3d/GLRenderManager.cpp
index 670021cf93..e1d8393162 100644
--- a/ext/native/thin3d/GLRenderManager.cpp
+++ b/ext/native/thin3d/GLRenderManager.cpp
@@ -279,6 +279,9 @@ void GLRenderManager::BindFramebufferAsRenderTarget(GLRFramebuffer *fb, GLRRende
GLRStep *step = new GLRStep{ GLRStepType::RENDER };
// This is what queues up new passes, and can end previous ones.
step->render.framebuffer = fb;
+ step->render.color = color;
+ step->render.depth = depth;
+ step->render.stencil = stencil;
step->render.numDraws = 0;
steps_.push_back(step);
@@ -308,6 +311,12 @@ void GLRenderManager::BindFramebufferAsRenderTarget(GLRFramebuffer *fb, GLRRende
}
curRenderStep_ = step;
+ if (fb) {
+ if (color == GLRRenderPassAction::KEEP || depth == GLRRenderPassAction::KEEP || stencil == GLRRenderPassAction::KEEP) {
+ step->dependencies.insert(fb);
+ }
+ }
+
// Every step clears this state.
gstate_c.Dirty(DIRTY_BLEND_STATE | DIRTY_DEPTHSTENCIL_STATE | DIRTY_RASTER_STATE);
}
@@ -319,6 +328,7 @@ void GLRenderManager::BindFramebufferAsTexture(GLRFramebuffer *fb, int binding,
data.bind_fb_texture.framebuffer = fb;
data.bind_fb_texture.aspect = aspectBit;
curRenderStep_->commands.push_back(data);
+ curRenderStep_->dependencies.insert(fb);
}
void GLRenderManager::CopyFramebuffer(GLRFramebuffer *src, GLRect2D srcRect, GLRFramebuffer *dst, GLOffset2D dstPos, int aspectMask) {
@@ -328,6 +338,10 @@ void GLRenderManager::CopyFramebuffer(GLRFramebuffer *src, GLRect2D srcRect, GLR
step->copy.src = src;
step->copy.dst = dst;
step->copy.aspectMask = aspectMask;
+ step->dependencies.insert(src);
+ bool fillsDst = dst && srcRect.x == 0 && srcRect.y == 0 && srcRect.w == dst->width && srcRect.h == dst->height;
+ if (dstPos.x != 0 || dstPos.y != 0 || !fillsDst)
+ step->dependencies.insert(dst);
steps_.push_back(step);
// Every step clears this state.
@@ -342,6 +356,10 @@ void GLRenderManager::BlitFramebuffer(GLRFramebuffer *src, GLRect2D srcRect, GLR
step->blit.dst = dst;
step->blit.aspectMask = aspectMask;
step->blit.filter = filter;
+ step->dependencies.insert(src);
+ bool fillsDst = dst && dstRect.x == 0 && dstRect.y == 0 && dstRect.w == dst->width && dstRect.h == dst->height;
+ if (!fillsDst)
+ step->dependencies.insert(dst);
steps_.push_back(step);
// Every step clears this state.
@@ -356,6 +374,7 @@ bool GLRenderManager::CopyFramebufferToMemorySync(GLRFramebuffer *src, int aspec
step->readback.srcRect = { x, y, w, h };
step->readback.aspectMask = aspectBits;
step->readback.dstFormat = destFormat;
+ step->dependencies.insert(src);
steps_.push_back(step);
// Every step clears this state.
diff --git a/ext/native/thin3d/VulkanQueueRunner.h b/ext/native/thin3d/VulkanQueueRunner.h
index 129c441b86..1c9e46a5cf 100644
--- a/ext/native/thin3d/VulkanQueueRunner.h
+++ b/ext/native/thin3d/VulkanQueueRunner.h
@@ -6,6 +6,7 @@
#include "Common/Vulkan/VulkanContext.h"
#include "math/dataconv.h"
#include "thin3d/DataFormat.h"
+#include "util/tiny_set.h"
class VKRFramebuffer;
struct VKRImage;
@@ -17,70 +18,6 @@ enum {
QUEUE_HACK_RENDERPASS_MERGE = 8,
};
-// Insert-only small-set implementation. Performs no allocation unless MaxFastSize is exceeded.
-template
-struct TinySet {
- ~TinySet() { delete slowLookup_; }
- inline void insert(T t) {
- // Fast linear scan.
- for (int i = 0; i < fastCount; i++) {
- if (fastLookup_[i] == t)
- return; // We already have it.
- }
- // Fast insertion
- if (fastCount < MaxFastSize) {
- fastLookup_[fastCount++] = t;
- return;
- }
- // Fall back to slow path.
- insertSlow(t);
- }
- bool contains(T t) const {
- for (int i = 0; i < fastCount; i++) {
- if (fastLookup_[i] == t)
- return true;
- }
- if (slowLookup_) {
- for (auto x : *slowLookup_) {
- if (x == t)
- return true;
- }
- }
- return false;
- }
- bool contains(const TinySet &otherSet) {
- // Awkward, kind of ruins the fun.
- for (int i = 0; i < fastCount; i++) {
- if (otherSet.contains(fastLookup_[i]))
- return true;
- }
- if (slowLookup_) {
- for (auto x : *slowLookup_) {
- if (otherSet.contains(x))
- return true;
- }
- }
- return false;
- }
-
-private:
- void insertSlow(T t) {
- if (!slowLookup_) {
- slowLookup_ = new std::vector();
- } else {
- for (size_t i = 0; i < slowLookup_->size(); i++) {
- if ((*slowLookup_)[i] == t)
- return;
- }
- }
- slowLookup_->push_back(t);
- }
- T fastLookup_[MaxFastSize];
- int fastCount = 0;
- int slowCount = 0;
- std::vector *slowLookup_ = nullptr;
-};
-
enum class VKRRenderCommand : uint8_t {
REMOVED,
BIND_PIPELINE,
diff --git a/ext/native/util/tiny_set.h b/ext/native/util/tiny_set.h
new file mode 100644
index 0000000000..f82bf35633
--- /dev/null
+++ b/ext/native/util/tiny_set.h
@@ -0,0 +1,67 @@
+#pragma once
+
+#include
+
+// Insert-only small-set implementation. Performs no allocation unless MaxFastSize is exceeded.
+template
+struct TinySet {
+ ~TinySet() { delete slowLookup_; }
+ inline void insert(T t) {
+ // Fast linear scan.
+ for (int i = 0; i < fastCount; i++) {
+ if (fastLookup_[i] == t)
+ return; // We already have it.
+ }
+ // Fast insertion
+ if (fastCount < MaxFastSize) {
+ fastLookup_[fastCount++] = t;
+ return;
+ }
+ // Fall back to slow path.
+ insertSlow(t);
+ }
+ bool contains(T t) const {
+ for (int i = 0; i < fastCount; i++) {
+ if (fastLookup_[i] == t)
+ return true;
+ }
+ if (slowLookup_) {
+ for (auto x : *slowLookup_) {
+ if (x == t)
+ return true;
+ }
+ }
+ return false;
+ }
+ bool contains(const TinySet &otherSet) {
+ // Awkward, kind of ruins the fun.
+ for (int i = 0; i < fastCount; i++) {
+ if (otherSet.contains(fastLookup_[i]))
+ return true;
+ }
+ if (slowLookup_) {
+ for (auto x : *slowLookup_) {
+ if (otherSet.contains(x))
+ return true;
+ }
+ }
+ return false;
+ }
+
+private:
+ void insertSlow(T t) {
+ if (!slowLookup_) {
+ slowLookup_ = new std::vector();
+ } else {
+ for (size_t i = 0; i < slowLookup_->size(); i++) {
+ if ((*slowLookup_)[i] == t)
+ return;
+ }
+ }
+ slowLookup_->push_back(t);
+ }
+ T fastLookup_[MaxFastSize];
+ int fastCount = 0;
+ int slowCount = 0;
+ std::vector *slowLookup_ = nullptr;
+};