diff --git a/Source/Core/VideoBackends/OGL/OGLBoundingBox.cpp b/Source/Core/VideoBackends/OGL/OGLBoundingBox.cpp index 9e88767afc..cee200d4ad 100644 --- a/Source/Core/VideoBackends/OGL/OGLBoundingBox.cpp +++ b/Source/Core/VideoBackends/OGL/OGLBoundingBox.cpp @@ -2,6 +2,8 @@ // Licensed under GPLv2+ // Refer to the license.txt file included. +#include +#include #include #include "Common/GL/GLUtil.h" @@ -12,7 +14,15 @@ #include "VideoCommon/DriverDetails.h" #include "VideoCommon/VideoConfig.h" +enum : u32 +{ + NUM_BBOX_VALUES = 4, +}; + static GLuint s_bbox_buffer_id; +static std::array s_bbox_values; +static std::array s_bbox_dirty; +static bool s_bbox_valid = false; namespace OGL { @@ -21,10 +31,14 @@ void BoundingBox::Init() if (!g_ActiveConfig.backend_info.bSupportsBBox) return; - int initial_values[4] = {0, 0, 0, 0}; + const s32 initial_values[NUM_BBOX_VALUES] = {0, 0, 0, 0}; + std::memcpy(s_bbox_values.data(), initial_values, sizeof(s_bbox_values)); + s_bbox_dirty = {}; + s_bbox_valid = true; + glGenBuffers(1, &s_bbox_buffer_id); glBindBuffer(GL_SHADER_STORAGE_BUFFER, s_bbox_buffer_id); - glBufferData(GL_SHADER_STORAGE_BUFFER, 4 * sizeof(s32), initial_values, GL_DYNAMIC_DRAW); + glBufferData(GL_SHADER_STORAGE_BUFFER, sizeof(initial_values), initial_values, GL_DYNAMIC_DRAW); glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, s_bbox_buffer_id); } @@ -36,22 +50,42 @@ void BoundingBox::Shutdown() glDeleteBuffers(1, &s_bbox_buffer_id); } -void BoundingBox::Set(int index, int value) +void BoundingBox::Flush() { - if (!g_ActiveConfig.backend_info.bSupportsBBox) + s_bbox_valid = false; + + if (std::none_of(s_bbox_dirty.begin(), s_bbox_dirty.end(), [](bool dirty) { return dirty; })) return; glBindBuffer(GL_SHADER_STORAGE_BUFFER, s_bbox_buffer_id); - glBufferSubData(GL_SHADER_STORAGE_BUFFER, index * sizeof(int), sizeof(int), &value); + + for (u32 start = 0; start < NUM_BBOX_VALUES;) + { + if (!s_bbox_dirty[start]) + { + start++; + continue; + } + + u32 end = start + 1; + s_bbox_dirty[start] = false; + for (; end < NUM_BBOX_VALUES; end++) + { + if (!s_bbox_dirty[end]) + break; + + s_bbox_dirty[end] = false; + } + + glBufferSubData(GL_SHADER_STORAGE_BUFFER, start * sizeof(s32), (end - start) * sizeof(s32), + &s_bbox_values[start]); + } + glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0); } -int BoundingBox::Get(int index) +void BoundingBox::Readback() { - if (!g_ActiveConfig.backend_info.bSupportsBBox) - return 0; - - int data = 0; glBindBuffer(GL_SHADER_STORAGE_BUFFER, s_bbox_buffer_id); if (!DriverDetails::HasBug(DriverDetails::BUG_SLOW_GETBUFFERSUBDATA) && !static_cast(g_renderer.get())->IsGLES()) @@ -59,20 +93,52 @@ int BoundingBox::Get(int index) // Using glMapBufferRange to read back the contents of the SSBO is extremely slow // on nVidia drivers. This is more noticeable at higher internal resolutions. // Using glGetBufferSubData instead does not seem to exhibit this slowdown. - glGetBufferSubData(GL_SHADER_STORAGE_BUFFER, index * sizeof(int), sizeof(int), &data); + std::array gpu_values; + glGetBufferSubData(GL_SHADER_STORAGE_BUFFER, 0, sizeof(s32) * NUM_BBOX_VALUES, + gpu_values.data()); + for (u32 i = 0; i < NUM_BBOX_VALUES; i++) + { + if (!s_bbox_dirty[i]) + s_bbox_values[i] = gpu_values[i]; + } } else { // Using glMapBufferRange is faster on AMD cards by a measurable margin. - void* ptr = glMapBufferRange(GL_SHADER_STORAGE_BUFFER, index * sizeof(int), sizeof(int), + void* ptr = glMapBufferRange(GL_SHADER_STORAGE_BUFFER, 0, sizeof(s32) * NUM_BBOX_VALUES, GL_MAP_READ_BIT); if (ptr) { - memcpy(&data, ptr, sizeof(int)); + for (u32 i = 0; i < NUM_BBOX_VALUES; i++) + { + if (!s_bbox_dirty[i]) + { + std::memcpy(&s_bbox_values[i], reinterpret_cast(ptr) + sizeof(s32) * i, + sizeof(s32)); + } + } + glUnmapBuffer(GL_SHADER_STORAGE_BUFFER); } } glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0); - return data; + s_bbox_valid = true; +} + +void BoundingBox::Set(int index, int value) +{ + if (s_bbox_valid && s_bbox_values[index] == value) + return; + + s_bbox_values[index] = value; + s_bbox_dirty[index] = true; +} + +int BoundingBox::Get(int index) +{ + if (!s_bbox_valid) + Readback(); + + return s_bbox_values[index]; } }; // namespace OGL diff --git a/Source/Core/VideoBackends/OGL/OGLBoundingBox.h b/Source/Core/VideoBackends/OGL/OGLBoundingBox.h index cbf54074ab..c2bc026785 100644 --- a/Source/Core/VideoBackends/OGL/OGLBoundingBox.h +++ b/Source/Core/VideoBackends/OGL/OGLBoundingBox.h @@ -12,6 +12,9 @@ public: static void Init(); static void Shutdown(); + static void Flush(); + static void Readback(); + static void Set(int index, int value); static int Get(int index); }; diff --git a/Source/Core/VideoBackends/OGL/OGLRender.cpp b/Source/Core/VideoBackends/OGL/OGLRender.cpp index 49f848c913..d3435c51d5 100644 --- a/Source/Core/VideoBackends/OGL/OGLRender.cpp +++ b/Source/Core/VideoBackends/OGL/OGLRender.cpp @@ -882,6 +882,11 @@ void Renderer::BBoxWriteImpl(int index, u16 value) BoundingBox::Set(index, swapped_value); } +void Renderer::BBoxFlushImpl() +{ + BoundingBox::Flush(); +} + void Renderer::SetViewport(float x, float y, float width, float height, float near_depth, float far_depth) { diff --git a/Source/Core/VideoBackends/OGL/OGLRender.h b/Source/Core/VideoBackends/OGL/OGLRender.h index 9c6623f254..c9b3b04896 100644 --- a/Source/Core/VideoBackends/OGL/OGLRender.h +++ b/Source/Core/VideoBackends/OGL/OGLRender.h @@ -128,6 +128,7 @@ public: u16 BBoxReadImpl(int index) override; void BBoxWriteImpl(int index, u16 value) override; + void BBoxFlushImpl() override; void BeginUtilityDrawing() override; void EndUtilityDrawing() override;