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; +};