// Copyright 2014 Dolphin Emulator Project // SPDX-License-Identifier: GPL-2.0-or-later #include "VideoBackends/OGL/OGLBoundingBox.h" #include "VideoBackends/OGL/OGLGfx.h" #include "VideoCommon/DriverDetails.h" namespace OGL { OGLBoundingBox::~OGLBoundingBox() { if (m_buffer_id) glDeleteBuffers(1, &m_buffer_id); } bool OGLBoundingBox::Initialize() { const BBoxType initial_values[NUM_BBOX_VALUES] = {0, 0, 0, 0}; glGenBuffers(1, &m_buffer_id); glBindBuffer(GL_SHADER_STORAGE_BUFFER, m_buffer_id); glBufferData(GL_SHADER_STORAGE_BUFFER, sizeof(initial_values), initial_values, GL_DYNAMIC_DRAW); glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 0, m_buffer_id); return true; } std::vector OGLBoundingBox::Read(u32 index, u32 length) { std::vector values(length); glBindBuffer(GL_SHADER_STORAGE_BUFFER, m_buffer_id); // 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. if (!DriverDetails::HasBug(DriverDetails::BUG_SLOW_GETBUFFERSUBDATA) && !static_cast(g_gfx.get())->IsGLES()) { // We also need to ensure the the CPU does not receive stale values which have been updated by // the GPU. Apparently the buffer here is not coherent on NVIDIA drivers. Not sure if this is a // driver bug/spec violation or not, one would think that glGetBufferSubData() would invalidate // any caches as needed, but this path is only used on NVIDIA anyway, so it's fine. A point to // note is that according to ARB_debug_report, it's moved from video to host memory, which would // explain why it needs the cache invalidate. glMemoryBarrier(GL_SHADER_STORAGE_BARRIER_BIT); glGetBufferSubData(GL_SHADER_STORAGE_BUFFER, sizeof(BBoxType) * index, sizeof(BBoxType) * length, values.data()); } else { // Using glMapBufferRange is faster on AMD cards by a measurable margin. void* ptr = glMapBufferRange(GL_SHADER_STORAGE_BUFFER, 0, sizeof(BBoxType) * NUM_BBOX_VALUES, GL_MAP_READ_BIT); if (ptr) { std::memcpy(values.data(), reinterpret_cast(ptr) + sizeof(BBoxType) * index, sizeof(BBoxType) * length); glUnmapBuffer(GL_SHADER_STORAGE_BUFFER); } } glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0); return values; } void OGLBoundingBox::Write(u32 index, std::span values) { glBindBuffer(GL_SHADER_STORAGE_BUFFER, m_buffer_id); glBufferSubData(GL_SHADER_STORAGE_BUFFER, sizeof(BBoxType) * index, sizeof(BBoxType) * values.size(), values.data()); glBindBuffer(GL_SHADER_STORAGE_BUFFER, 0); } } // namespace OGL