From d7298ec2626f013167ea254276259ac7b39f46be Mon Sep 17 00:00:00 2001 From: Markus Wick Date: Thu, 9 Aug 2018 21:31:46 +0200 Subject: [PATCH 1/6] Update the stream_buffer helper from Citra. Please see https://github.com/citra-emu/citra/pull/3666 for more details. --- .../renderer_opengl/gl_rasterizer.cpp | 36 ++- .../renderer_opengl/gl_rasterizer.h | 3 +- .../renderer_opengl/gl_stream_buffer.cpp | 227 ++++++------------ .../renderer_opengl/gl_stream_buffer.h | 42 ++-- 4 files changed, 111 insertions(+), 197 deletions(-) diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index 38a7b14137..94e3f59a7b 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp @@ -36,7 +36,8 @@ MICROPROFILE_DEFINE(OpenGL_Drawing, "OpenGL", "Drawing", MP_RGB(128, 128, 192)); MICROPROFILE_DEFINE(OpenGL_Blits, "OpenGL", "Blits", MP_RGB(100, 100, 255)); MICROPROFILE_DEFINE(OpenGL_CacheManagement, "OpenGL", "Cache Mgmt", MP_RGB(100, 255, 100)); -RasterizerOpenGL::RasterizerOpenGL(Core::Frontend::EmuWindow& window) : emu_window{window} { +RasterizerOpenGL::RasterizerOpenGL(Core::Frontend::EmuWindow& window) + : emu_window{window}, stream_buffer(GL_ARRAY_BUFFER, STREAM_BUFFER_SIZE) { // Create sampler objects for (size_t i = 0; i < texture_samplers.size(); ++i) { texture_samplers[i].Create(); @@ -57,9 +58,7 @@ RasterizerOpenGL::RasterizerOpenGL(Core::Frontend::EmuWindow& window) : emu_wind const std::string_view extension{ reinterpret_cast(glGetStringi(GL_EXTENSIONS, i))}; - if (extension == "GL_ARB_buffer_storage") { - has_ARB_buffer_storage = true; - } else if (extension == "GL_ARB_direct_state_access") { + if (extension == "GL_ARB_direct_state_access") { has_ARB_direct_state_access = true; } else if (extension == "GL_ARB_separate_shader_objects") { has_ARB_separate_shader_objects = true; @@ -86,16 +85,14 @@ RasterizerOpenGL::RasterizerOpenGL(Core::Frontend::EmuWindow& window) : emu_wind hw_vao.Create(); - stream_buffer = OGLStreamBuffer::MakeBuffer(has_ARB_buffer_storage, GL_ARRAY_BUFFER); - stream_buffer->Create(STREAM_BUFFER_SIZE, STREAM_BUFFER_SIZE / 2); - state.draw.vertex_buffer = stream_buffer->GetHandle(); + state.draw.vertex_buffer = stream_buffer.GetHandle(); shader_program_manager = std::make_unique(); state.draw.shader_program = 0; state.draw.vertex_array = hw_vao.handle; state.Apply(); - glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, stream_buffer->GetHandle()); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, stream_buffer.GetHandle()); for (unsigned index = 0; index < uniform_buffers.size(); ++index) { auto& buffer = uniform_buffers[index]; @@ -111,13 +108,7 @@ RasterizerOpenGL::RasterizerOpenGL(Core::Frontend::EmuWindow& window) : emu_wind LOG_CRITICAL(Render_OpenGL, "Sync fixed function OpenGL state here!"); } -RasterizerOpenGL::~RasterizerOpenGL() { - if (stream_buffer != nullptr) { - state.draw.vertex_buffer = stream_buffer->GetHandle(); - state.Apply(); - stream_buffer->Release(); - } -} +RasterizerOpenGL::~RasterizerOpenGL() {} std::pair RasterizerOpenGL::SetupVertexArrays(u8* array_ptr, GLintptr buffer_offset) { @@ -126,7 +117,7 @@ std::pair RasterizerOpenGL::SetupVertexArrays(u8* array_ptr, const auto& memory_manager = Core::System::GetInstance().GPU().memory_manager; state.draw.vertex_array = hw_vao.handle; - state.draw.vertex_buffer = stream_buffer->GetHandle(); + state.draw.vertex_buffer = stream_buffer.GetHandle(); state.Apply(); // Upload all guest vertex arrays sequentially to our buffer @@ -145,7 +136,7 @@ std::pair RasterizerOpenGL::SetupVertexArrays(u8* array_ptr, Memory::ReadBlock(*memory_manager->GpuToCpuAddress(start), array_ptr, size); // Bind the vertex array to the buffer at the current offset. - glBindVertexBuffer(index, stream_buffer->GetHandle(), buffer_offset, vertex_array.stride); + glBindVertexBuffer(index, stream_buffer.GetHandle(), buffer_offset, vertex_array.stride); ASSERT_MSG(vertex_array.divisor == 0, "Vertex buffer divisor unimplemented"); @@ -205,7 +196,7 @@ void RasterizerOpenGL::SetupShaders(u8* buffer_ptr, GLintptr buffer_offset) { // Helper function for uploading uniform data const auto copy_buffer = [&](GLuint handle, GLintptr offset, GLsizeiptr size) { if (has_ARB_direct_state_access) { - glCopyNamedBufferSubData(stream_buffer->GetHandle(), handle, offset, 0, size); + glCopyNamedBufferSubData(stream_buffer.GetHandle(), handle, offset, 0, size); } else { glBindBuffer(GL_COPY_WRITE_BUFFER, handle); glCopyBufferSubData(GL_ARRAY_BUFFER, GL_COPY_WRITE_BUFFER, offset, 0, size); @@ -456,7 +447,7 @@ void RasterizerOpenGL::DrawArrays() { const u64 index_buffer_size{regs.index_array.count * regs.index_array.FormatSizeInBytes()}; const unsigned vertex_num{is_indexed ? regs.index_array.count : regs.vertex_buffer.count}; - state.draw.vertex_buffer = stream_buffer->GetHandle(); + state.draw.vertex_buffer = stream_buffer.GetHandle(); state.Apply(); size_t buffer_size = CalculateVertexArraysSize(); @@ -471,8 +462,8 @@ void RasterizerOpenGL::DrawArrays() { u8* buffer_ptr; GLintptr buffer_offset; - std::tie(buffer_ptr, buffer_offset) = - stream_buffer->Map(static_cast(buffer_size), 4); + std::tie(buffer_ptr, buffer_offset, std::ignore) = + stream_buffer.Map(static_cast(buffer_size), 4); u8* offseted_buffer; std::tie(offseted_buffer, buffer_offset) = SetupVertexArrays(buffer_ptr, buffer_offset); @@ -500,7 +491,8 @@ void RasterizerOpenGL::DrawArrays() { SetupShaders(offseted_buffer, buffer_offset); - stream_buffer->Unmap(); + // TODO: Don't use buffer_size here, use the updated buffer_offset. + stream_buffer.Unmap(buffer_size); shader_program_manager->ApplyTo(state); state.Apply(); diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h index bd01dc0ae2..19146777c8 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.h +++ b/src/video_core/renderer_opengl/gl_rasterizer.h @@ -139,7 +139,6 @@ private: /// Syncs the blend state to match the guest state void SyncBlendState(); - bool has_ARB_buffer_storage = false; bool has_ARB_direct_state_access = false; bool has_ARB_separate_shader_objects = false; bool has_ARB_vertex_attrib_binding = false; @@ -160,7 +159,7 @@ private: ssbos; static constexpr size_t STREAM_BUFFER_SIZE = 128 * 1024 * 1024; - std::unique_ptr stream_buffer; + OGLStreamBuffer stream_buffer; OGLBuffer uniform_buffer; OGLFramebuffer framebuffer; diff --git a/src/video_core/renderer_opengl/gl_stream_buffer.cpp b/src/video_core/renderer_opengl/gl_stream_buffer.cpp index a2713e9f0f..03a8ed8b7f 100644 --- a/src/video_core/renderer_opengl/gl_stream_buffer.cpp +++ b/src/video_core/renderer_opengl/gl_stream_buffer.cpp @@ -9,174 +9,91 @@ #include "video_core/renderer_opengl/gl_state.h" #include "video_core/renderer_opengl/gl_stream_buffer.h" -class OrphanBuffer : public OGLStreamBuffer { -public: - explicit OrphanBuffer(GLenum target) : OGLStreamBuffer(target) {} - ~OrphanBuffer() override; +OGLStreamBuffer::OGLStreamBuffer(GLenum target, GLsizeiptr size, bool prefer_coherent) + : gl_target(target), buffer_size(size) { + gl_buffer.Create(); + glBindBuffer(gl_target, gl_buffer.handle); -private: - void Create(size_t size, size_t sync_subdivide) override; - void Release() override; + GLsizeiptr allocate_size = size; + if (target == GL_ARRAY_BUFFER) { + // On AMD GPU there is a strange crash in indexed drawing. The crash happens when the buffer + // read position is near the end and is an out-of-bound access to the vertex buffer. This is + // probably a bug in the driver and is related to the usage of vec3 attributes in the + // vertex array. Doubling the allocation size for the vertex buffer seems to avoid the + // crash. + allocate_size *= 2; + } - std::pair Map(size_t size, size_t alignment) override; - void Unmap() override; + if (GLAD_GL_ARB_buffer_storage) { + persistent = true; + coherent = prefer_coherent; + GLbitfield flags = + GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT | (coherent ? GL_MAP_COHERENT_BIT : 0); + glBufferStorage(gl_target, allocate_size, nullptr, flags); + mapped_ptr = static_cast(glMapBufferRange( + gl_target, 0, buffer_size, flags | (coherent ? 0 : GL_MAP_FLUSH_EXPLICIT_BIT))); + } else { + glBufferData(gl_target, allocate_size, nullptr, GL_STREAM_DRAW); + } +} - std::vector data; -}; - -class StorageBuffer : public OGLStreamBuffer { -public: - explicit StorageBuffer(GLenum target) : OGLStreamBuffer(target) {} - ~StorageBuffer() override; - -private: - void Create(size_t size, size_t sync_subdivide) override; - void Release() override; - - std::pair Map(size_t size, size_t alignment) override; - void Unmap() override; - - struct Fence { - OGLSync sync; - size_t offset; - }; - std::deque head; - std::deque tail; - - u8* mapped_ptr; -}; - -OGLStreamBuffer::OGLStreamBuffer(GLenum target) { - gl_target = target; +OGLStreamBuffer::~OGLStreamBuffer() { + if (persistent) { + glBindBuffer(gl_target, gl_buffer.handle); + glUnmapBuffer(gl_target); + } + gl_buffer.Release(); } GLuint OGLStreamBuffer::GetHandle() const { return gl_buffer.handle; } -std::unique_ptr OGLStreamBuffer::MakeBuffer(bool storage_buffer, GLenum target) { - if (storage_buffer) { - return std::make_unique(target); - } - return std::make_unique(target); +GLsizeiptr OGLStreamBuffer::GetSize() const { + return buffer_size; } -OrphanBuffer::~OrphanBuffer() { - Release(); -} - -void OrphanBuffer::Create(size_t size, size_t /*sync_subdivide*/) { - buffer_pos = 0; - buffer_size = size; - data.resize(buffer_size); - - if (gl_buffer.handle == 0) { - gl_buffer.Create(); - glBindBuffer(gl_target, gl_buffer.handle); - } - - glBufferData(gl_target, static_cast(buffer_size), nullptr, GL_STREAM_DRAW); -} - -void OrphanBuffer::Release() { - gl_buffer.Release(); -} - -std::pair OrphanBuffer::Map(size_t size, size_t alignment) { - buffer_pos = Common::AlignUp(buffer_pos, alignment); - - if (buffer_pos + size > buffer_size) { - Create(std::max(buffer_size, size), 0); - } - - mapped_size = size; - return std::make_pair(&data[buffer_pos], static_cast(buffer_pos)); -} - -void OrphanBuffer::Unmap() { - glBufferSubData(gl_target, static_cast(buffer_pos), - static_cast(mapped_size), &data[buffer_pos]); - buffer_pos += mapped_size; -} - -StorageBuffer::~StorageBuffer() { - Release(); -} - -void StorageBuffer::Create(size_t size, size_t sync_subdivide) { - if (gl_buffer.handle != 0) - return; - - buffer_pos = 0; - buffer_size = size; - buffer_sync_subdivide = std::max(sync_subdivide, 1); - - gl_buffer.Create(); - glBindBuffer(gl_target, gl_buffer.handle); - - glBufferStorage(gl_target, static_cast(buffer_size), nullptr, - GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT); - mapped_ptr = reinterpret_cast( - glMapBufferRange(gl_target, 0, static_cast(buffer_size), - GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT | GL_MAP_FLUSH_EXPLICIT_BIT)); -} - -void StorageBuffer::Release() { - if (gl_buffer.handle == 0) - return; - - glUnmapBuffer(gl_target); - - gl_buffer.Release(); - head.clear(); - tail.clear(); -} - -std::pair StorageBuffer::Map(size_t size, size_t alignment) { +std::tuple OGLStreamBuffer::Map(GLsizeiptr size, GLintptr alignment) { ASSERT(size <= buffer_size); - - OGLSync sync; - - buffer_pos = Common::AlignUp(buffer_pos, alignment); - size_t effective_offset = Common::AlignDown(buffer_pos, buffer_sync_subdivide); - - if (!head.empty() && - (effective_offset > head.back().offset || buffer_pos + size > buffer_size)) { - ASSERT(head.back().sync.handle == 0); - head.back().sync.Create(); - } - - if (buffer_pos + size > buffer_size) { - if (!tail.empty()) { - std::swap(sync, tail.back().sync); - tail.clear(); - } - std::swap(tail, head); - buffer_pos = 0; - effective_offset = 0; - } - - while (!tail.empty() && buffer_pos + size > tail.front().offset) { - std::swap(sync, tail.front().sync); - tail.pop_front(); - } - - if (sync.handle != 0) { - glClientWaitSync(sync.handle, GL_SYNC_FLUSH_COMMANDS_BIT, GL_TIMEOUT_IGNORED); - sync.Release(); - } - - if (head.empty() || effective_offset > head.back().offset) { - head.emplace_back(); - head.back().offset = effective_offset; - } - + ASSERT(alignment <= buffer_size); mapped_size = size; - return std::make_pair(&mapped_ptr[buffer_pos], static_cast(buffer_pos)); + + if (alignment > 0) { + buffer_pos = Common::AlignUp(buffer_pos, alignment); + } + + bool invalidate = false; + if (buffer_pos + size > buffer_size) { + buffer_pos = 0; + invalidate = true; + + if (persistent) { + glUnmapBuffer(gl_target); + } + } + + if (invalidate | !persistent) { + GLbitfield flags = GL_MAP_WRITE_BIT | (persistent ? GL_MAP_PERSISTENT_BIT : 0) | + (coherent ? GL_MAP_COHERENT_BIT : GL_MAP_FLUSH_EXPLICIT_BIT) | + (invalidate ? GL_MAP_INVALIDATE_BUFFER_BIT : GL_MAP_UNSYNCHRONIZED_BIT); + mapped_ptr = static_cast( + glMapBufferRange(gl_target, buffer_pos, buffer_size - buffer_pos, flags)); + mapped_offset = buffer_pos; + } + + return std::make_tuple(mapped_ptr + buffer_pos - mapped_offset, buffer_pos, invalidate); } -void StorageBuffer::Unmap() { - glFlushMappedBufferRange(gl_target, static_cast(buffer_pos), - static_cast(mapped_size)); - buffer_pos += mapped_size; +void OGLStreamBuffer::Unmap(GLsizeiptr size) { + ASSERT(size <= mapped_size); + + if (!coherent && size > 0) { + glFlushMappedBufferRange(gl_target, buffer_pos - mapped_offset, size); + } + + if (!persistent) { + glUnmapBuffer(gl_target); + } + + buffer_pos += size; } diff --git a/src/video_core/renderer_opengl/gl_stream_buffer.h b/src/video_core/renderer_opengl/gl_stream_buffer.h index e78dc5784e..45592daaf8 100644 --- a/src/video_core/renderer_opengl/gl_stream_buffer.h +++ b/src/video_core/renderer_opengl/gl_stream_buffer.h @@ -2,35 +2,41 @@ // Licensed under GPLv2 or any later version // Refer to the license.txt file included. -#pragma once - -#include +#include #include #include "common/common_types.h" #include "video_core/renderer_opengl/gl_resource_manager.h" class OGLStreamBuffer : private NonCopyable { public: - explicit OGLStreamBuffer(GLenum target); - virtual ~OGLStreamBuffer() = default; - -public: - static std::unique_ptr MakeBuffer(bool storage_buffer, GLenum target); - - virtual void Create(size_t size, size_t sync_subdivide) = 0; - virtual void Release() {} + explicit OGLStreamBuffer(GLenum target, GLsizeiptr size, bool prefer_coherent = false); + ~OGLStreamBuffer(); GLuint GetHandle() const; + GLsizeiptr GetSize() const; - virtual std::pair Map(size_t size, size_t alignment) = 0; - virtual void Unmap() = 0; + /* + * Allocates a linear chunk of memory in the GPU buffer with at least "size" bytes + * and the optional alignment requirement. + * If the buffer is full, the whole buffer is reallocated which invalidates old chunks. + * The return values are the pointer to the new chunk, the offset within the buffer, + * and the invalidation flag for previous chunks. + * The actual used size must be specified on unmapping the chunk. + */ + std::tuple Map(GLsizeiptr size, GLintptr alignment = 0); -protected: + void Unmap(GLsizeiptr size); + +private: OGLBuffer gl_buffer; GLenum gl_target; - size_t buffer_pos = 0; - size_t buffer_size = 0; - size_t buffer_sync_subdivide = 0; - size_t mapped_size = 0; + bool coherent = false; + bool persistent = false; + + GLintptr buffer_pos = 0; + GLsizeiptr buffer_size = 0; + GLintptr mapped_offset = 0; + GLsizeiptr mapped_size = 0; + u8* mapped_ptr = nullptr; }; From 6f6bba3ff16641f92fbc70b31e41be80d4a3c5c8 Mon Sep 17 00:00:00 2001 From: Markus Wick Date: Fri, 10 Aug 2018 09:30:35 +0200 Subject: [PATCH 2/6] gl_rasterizer: Use a helper for aligning the buffer. --- .../renderer_opengl/gl_rasterizer.cpp | 33 +++++++++++-------- .../renderer_opengl/gl_rasterizer.h | 4 ++- 2 files changed, 22 insertions(+), 15 deletions(-) diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index 94e3f59a7b..0a62b7383a 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp @@ -192,7 +192,7 @@ static GLShader::ProgramCode GetShaderProgramCode(Maxwell::ShaderProgram program return program_code; } -void RasterizerOpenGL::SetupShaders(u8* buffer_ptr, GLintptr buffer_offset) { +std::pair RasterizerOpenGL::SetupShaders(u8* buffer_ptr, GLintptr buffer_offset) { // Helper function for uploading uniform data const auto copy_buffer = [&](GLuint handle, GLintptr offset, GLsizeiptr size) { if (has_ARB_direct_state_access) { @@ -290,6 +290,8 @@ void RasterizerOpenGL::SetupShaders(u8* buffer_ptr, GLintptr buffer_offset) { } shader_program_manager->UseTrivialGeometryShader(); + + return {buffer_ptr, buffer_offset}; } size_t RasterizerOpenGL::CalculateVertexArraysSize() const { @@ -423,6 +425,14 @@ void RasterizerOpenGL::Clear() { } } +std::pair RasterizerOpenGL::AlignBuffer(u8* buffer_ptr, GLintptr buffer_offset, + size_t alignment) { + // Align the offset, not the mapped pointer + GLintptr offset_aligned = + static_cast(Common::AlignUp(static_cast(buffer_offset), alignment)); + return {buffer_ptr + (offset_aligned - buffer_offset), offset_aligned}; +} + void RasterizerOpenGL::DrawArrays() { if (accelerate_draw == AccelDraw::Disabled) return; @@ -464,13 +474,11 @@ void RasterizerOpenGL::DrawArrays() { GLintptr buffer_offset; std::tie(buffer_ptr, buffer_offset, std::ignore) = stream_buffer.Map(static_cast(buffer_size), 4); + u8* buffer_ptr_base = buffer_ptr; - u8* offseted_buffer; - std::tie(offseted_buffer, buffer_offset) = SetupVertexArrays(buffer_ptr, buffer_offset); + std::tie(buffer_ptr, buffer_offset) = SetupVertexArrays(buffer_ptr, buffer_offset); - offseted_buffer = - reinterpret_cast(Common::AlignUp(reinterpret_cast(offseted_buffer), 4)); - buffer_offset = Common::AlignUp(buffer_offset, 4); + std::tie(buffer_ptr, buffer_offset) = AlignBuffer(buffer_ptr, buffer_offset, 4); // If indexed mode, copy the index buffer GLintptr index_buffer_offset = 0; @@ -478,21 +486,18 @@ void RasterizerOpenGL::DrawArrays() { const auto& memory_manager = Core::System::GetInstance().GPU().memory_manager; const boost::optional index_data_addr{ memory_manager->GpuToCpuAddress(regs.index_array.StartAddress())}; - Memory::ReadBlock(*index_data_addr, offseted_buffer, index_buffer_size); + Memory::ReadBlock(*index_data_addr, buffer_ptr, index_buffer_size); index_buffer_offset = buffer_offset; - offseted_buffer += index_buffer_size; + buffer_ptr += index_buffer_size; buffer_offset += index_buffer_size; } - offseted_buffer = - reinterpret_cast(Common::AlignUp(reinterpret_cast(offseted_buffer), 4)); - buffer_offset = Common::AlignUp(buffer_offset, 4); + std::tie(buffer_ptr, buffer_offset) = AlignBuffer(buffer_ptr, buffer_offset, 4); - SetupShaders(offseted_buffer, buffer_offset); + std::tie(buffer_ptr, buffer_offset) = SetupShaders(buffer_ptr, buffer_offset); - // TODO: Don't use buffer_size here, use the updated buffer_offset. - stream_buffer.Unmap(buffer_size); + stream_buffer.Unmap(buffer_ptr - buffer_ptr_base); shader_program_manager->ApplyTo(state); state.Apply(); diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h index 19146777c8..d9d4e04b96 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.h +++ b/src/video_core/renderer_opengl/gl_rasterizer.h @@ -169,7 +169,9 @@ private: std::array uniform_buffers; - void SetupShaders(u8* buffer_ptr, GLintptr buffer_offset); + std::pair SetupShaders(u8* buffer_ptr, GLintptr buffer_offset); + + std::pair AlignBuffer(u8* buffer_ptr, GLintptr buffer_offset, size_t alignment); enum class AccelDraw { Disabled, Arrays, Indexed }; AccelDraw accelerate_draw = AccelDraw::Disabled; From ce722e317b23a4591980fca0ffa43042d873eb2d Mon Sep 17 00:00:00 2001 From: Markus Wick Date: Fri, 10 Aug 2018 09:45:38 +0200 Subject: [PATCH 3/6] gl_rasterizer: Use the streaming buffer itself for the constant buffer. Don't emut copies, especially not for data, which is used once. They just end in a huge GPU overhead. --- .../renderer_opengl/gl_rasterizer.cpp | 45 ++++++------------- .../renderer_opengl/gl_rasterizer.h | 3 +- 2 files changed, 15 insertions(+), 33 deletions(-) diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index 0a62b7383a..5a0e337a55 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp @@ -94,17 +94,10 @@ RasterizerOpenGL::RasterizerOpenGL(Core::Frontend::EmuWindow& window) glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, stream_buffer.GetHandle()); - for (unsigned index = 0; index < uniform_buffers.size(); ++index) { - auto& buffer = uniform_buffers[index]; - buffer.Create(); - glBindBuffer(GL_UNIFORM_BUFFER, buffer.handle); - glBufferData(GL_UNIFORM_BUFFER, sizeof(GLShader::MaxwellUniformData), nullptr, - GL_STREAM_COPY); - glBindBufferBase(GL_UNIFORM_BUFFER, index, buffer.handle); - } - glEnable(GL_BLEND); + glGetIntegerv(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT, &uniform_buffer_alignment); + LOG_CRITICAL(Render_OpenGL, "Sync fixed function OpenGL state here!"); } @@ -193,21 +186,11 @@ static GLShader::ProgramCode GetShaderProgramCode(Maxwell::ShaderProgram program } std::pair RasterizerOpenGL::SetupShaders(u8* buffer_ptr, GLintptr buffer_offset) { - // Helper function for uploading uniform data - const auto copy_buffer = [&](GLuint handle, GLintptr offset, GLsizeiptr size) { - if (has_ARB_direct_state_access) { - glCopyNamedBufferSubData(stream_buffer.GetHandle(), handle, offset, 0, size); - } else { - glBindBuffer(GL_COPY_WRITE_BUFFER, handle); - glCopyBufferSubData(GL_ARRAY_BUFFER, GL_COPY_WRITE_BUFFER, offset, 0, size); - } - }; - auto& gpu = Core::System::GetInstance().GPU().Maxwell3D(); // Next available bindpoints to use when uploading the const buffers and textures to the GLSL // shaders. The constbuffer bindpoint starts after the shader stage configuration bind points. - u32 current_constbuffer_bindpoint = static_cast(uniform_buffers.size()); + u32 current_constbuffer_bindpoint = Tegra::Engines::Maxwell3D::Regs::MaxShaderStage; u32 current_texture_bindpoint = 0; for (size_t index = 0; index < Maxwell::MaxShaderProgram; ++index) { @@ -219,22 +202,21 @@ std::pair RasterizerOpenGL::SetupShaders(u8* buffer_ptr, GLintptr continue; } + std::tie(buffer_ptr, buffer_offset) = + AlignBuffer(buffer_ptr, buffer_offset, static_cast(uniform_buffer_alignment)); + const size_t stage{index == 0 ? 0 : index - 1}; // Stage indices are 0 - 5 GLShader::MaxwellUniformData ubo{}; ubo.SetFromRegs(gpu.state.shader_stages[stage]); std::memcpy(buffer_ptr, &ubo, sizeof(ubo)); - // Flush the buffer so that the GPU can see the data we just wrote. - glFlushMappedBufferRange(GL_ARRAY_BUFFER, buffer_offset, sizeof(ubo)); + // Bind the buffer + glBindBufferRange(GL_UNIFORM_BUFFER, stage, stream_buffer.GetHandle(), buffer_offset, + sizeof(ubo)); - // Upload uniform data as one UBO per stage - const GLintptr ubo_offset = buffer_offset; - copy_buffer(uniform_buffers[stage].handle, ubo_offset, - sizeof(GLShader::MaxwellUniformData)); - - buffer_ptr += sizeof(GLShader::MaxwellUniformData); - buffer_offset += sizeof(GLShader::MaxwellUniformData); + buffer_ptr += sizeof(ubo); + buffer_offset += sizeof(ubo); GLShader::ShaderSetup setup{GetShaderProgramCode(program)}; GLShader::ShaderEntries shader_resources; @@ -467,8 +449,9 @@ void RasterizerOpenGL::DrawArrays() { } // Uniform space for the 5 shader stages - buffer_size = Common::AlignUp(buffer_size, 4) + - sizeof(GLShader::MaxwellUniformData) * Maxwell::MaxShaderStage; + buffer_size = + Common::AlignUp(buffer_size, 4) + + (sizeof(GLShader::MaxwellUniformData) + uniform_buffer_alignment) * Maxwell::MaxShaderStage; u8* buffer_ptr; GLintptr buffer_offset; diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h index d9d4e04b96..6f8503703b 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.h +++ b/src/video_core/renderer_opengl/gl_rasterizer.h @@ -162,13 +162,12 @@ private: OGLStreamBuffer stream_buffer; OGLBuffer uniform_buffer; OGLFramebuffer framebuffer; + GLint uniform_buffer_alignment; size_t CalculateVertexArraysSize() const; std::pair SetupVertexArrays(u8* array_ptr, GLintptr buffer_offset); - std::array uniform_buffers; - std::pair SetupShaders(u8* buffer_ptr, GLintptr buffer_offset); std::pair AlignBuffer(u8* buffer_ptr, GLintptr buffer_offset, size_t alignment); From 6ff7906ddc89d8f970702d320f29317831de7975 Mon Sep 17 00:00:00 2001 From: Markus Wick Date: Fri, 10 Aug 2018 10:29:37 +0200 Subject: [PATCH 4/6] gl_rasterizer: Use the stream buffer for constant buffers. --- .../renderer_opengl/gl_rasterizer.cpp | 39 +++++++++---------- .../renderer_opengl/gl_rasterizer.h | 11 +++--- src/video_core/renderer_opengl/gl_state.cpp | 9 +++-- src/video_core/renderer_opengl/gl_state.h | 2 + 4 files changed, 32 insertions(+), 29 deletions(-) diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index 5a0e337a55..fd4731d808 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp @@ -44,14 +44,6 @@ RasterizerOpenGL::RasterizerOpenGL(Core::Frontend::EmuWindow& window) state.texture_units[i].sampler = texture_samplers[i].sampler.handle; } - // Create SSBOs - for (size_t stage = 0; stage < ssbos.size(); ++stage) { - for (size_t buffer = 0; buffer < ssbos[stage].size(); ++buffer) { - ssbos[stage][buffer].Create(); - state.draw.const_buffers[stage][buffer].ssbo = ssbos[stage][buffer].handle; - } - } - GLint ext_num; glGetIntegerv(GL_NUM_EXTENSIONS, &ext_num); for (GLint i = 0; i < ext_num; i++) { @@ -255,9 +247,9 @@ std::pair RasterizerOpenGL::SetupShaders(u8* buffer_ptr, GLintptr static_cast(stage)); // Configure the const buffers for this shader stage. - current_constbuffer_bindpoint = - SetupConstBuffers(static_cast(stage), gl_stage_program, - current_constbuffer_bindpoint, shader_resources.const_buffer_entries); + std::tie(buffer_ptr, buffer_offset, current_constbuffer_bindpoint) = SetupConstBuffers( + buffer_ptr, buffer_offset, static_cast(stage), gl_stage_program, + current_constbuffer_bindpoint, shader_resources.const_buffer_entries); // Configure the textures for this shader stage. current_texture_bindpoint = @@ -453,6 +445,9 @@ void RasterizerOpenGL::DrawArrays() { Common::AlignUp(buffer_size, 4) + (sizeof(GLShader::MaxwellUniformData) + uniform_buffer_alignment) * Maxwell::MaxShaderStage; + // Add space for at least 18 constant buffers + buffer_size += Maxwell::MaxConstBuffers * (MaxConstbufferSize + uniform_buffer_alignment); + u8* buffer_ptr; GLintptr buffer_offset; std::tie(buffer_ptr, buffer_offset, std::ignore) = @@ -627,9 +622,9 @@ void RasterizerOpenGL::SamplerInfo::SyncWithConfig(const Tegra::Texture::TSCEntr } } -u32 RasterizerOpenGL::SetupConstBuffers(Maxwell::ShaderStage stage, GLuint program, - u32 current_bindpoint, - const std::vector& entries) { +std::tuple RasterizerOpenGL::SetupConstBuffers( + u8* buffer_ptr, GLintptr buffer_offset, Maxwell::ShaderStage stage, GLuint program, + u32 current_bindpoint, const std::vector& entries) { const auto& gpu = Core::System::GetInstance().GPU(); const auto& maxwell3d = gpu.Maxwell3D(); @@ -678,12 +673,16 @@ u32 RasterizerOpenGL::SetupConstBuffers(Maxwell::ShaderStage stage, GLuint progr size = Common::AlignUp(size, sizeof(GLvec4)); ASSERT_MSG(size <= MaxConstbufferSize, "Constbuffer too big"); - std::vector data(size); - Memory::ReadBlock(*addr, data.data(), data.size()); + std::tie(buffer_ptr, buffer_offset) = + AlignBuffer(buffer_ptr, buffer_offset, static_cast(uniform_buffer_alignment)); - glBindBuffer(GL_UNIFORM_BUFFER, buffer_draw_state.ssbo); - glBufferData(GL_UNIFORM_BUFFER, data.size(), data.data(), GL_DYNAMIC_DRAW); - glBindBuffer(GL_UNIFORM_BUFFER, 0); + buffer_draw_state.size = size; + buffer_draw_state.offset = buffer_offset; + buffer_draw_state.ssbo = stream_buffer.GetHandle(); + + Memory::ReadBlock(*addr, buffer_ptr, size); + buffer_ptr += size; + buffer_offset += size; // Now configure the bindpoint of the buffer inside the shader const std::string buffer_name = used_buffer.GetName(); @@ -696,7 +695,7 @@ u32 RasterizerOpenGL::SetupConstBuffers(Maxwell::ShaderStage stage, GLuint progr state.Apply(); - return current_bindpoint + static_cast(entries.size()); + return {buffer_ptr, buffer_offset, current_bindpoint + static_cast(entries.size())}; } u32 RasterizerOpenGL::SetupTextures(Maxwell::ShaderStage stage, GLuint program, u32 current_unit, diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h index 6f8503703b..aa6afc1de8 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.h +++ b/src/video_core/renderer_opengl/gl_rasterizer.h @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -100,9 +101,10 @@ private: * @param entries Vector describing the buffers that are actually used in the guest shader. * @returns The next available bindpoint for use in the next shader stage. */ - u32 SetupConstBuffers(Tegra::Engines::Maxwell3D::Regs::ShaderStage stage, GLuint program, - u32 current_bindpoint, - const std::vector& entries); + std::tuple SetupConstBuffers( + u8* buffer_ptr, GLintptr buffer_offset, Tegra::Engines::Maxwell3D::Regs::ShaderStage stage, + GLuint program, u32 current_bindpoint, + const std::vector& entries); /* * Configures the current textures to use for the draw command. @@ -154,9 +156,6 @@ private: OGLVertexArray hw_vao; std::array texture_samplers; - std::array, - Tegra::Engines::Maxwell3D::Regs::MaxShaderStage> - ssbos; static constexpr size_t STREAM_BUFFER_SIZE = 128 * 1024 * 1024; OGLStreamBuffer stream_buffer; diff --git a/src/video_core/renderer_opengl/gl_state.cpp b/src/video_core/renderer_opengl/gl_state.cpp index 68bacd4c5f..212c87e0c5 100644 --- a/src/video_core/renderer_opengl/gl_state.cpp +++ b/src/video_core/renderer_opengl/gl_state.cpp @@ -209,10 +209,13 @@ void OpenGLState::Apply() const { const auto& current = cur_state.draw.const_buffers[stage][buffer_id]; const auto& new_state = draw.const_buffers[stage][buffer_id]; - if (current.enabled != new_state.enabled || current.bindpoint != new_state.bindpoint || - current.ssbo != new_state.ssbo) { + if (std::tie(current.enabled, current.bindpoint, current.ssbo, current.size, + current.offset) != std::tie(new_state.enabled, new_state.bindpoint, + new_state.ssbo, new_state.size, + new_state.offset)) { if (new_state.enabled) { - glBindBufferBase(GL_UNIFORM_BUFFER, new_state.bindpoint, new_state.ssbo); + glBindBufferRange(GL_UNIFORM_BUFFER, new_state.bindpoint, new_state.ssbo, + new_state.offset, new_state.size); } } } diff --git a/src/video_core/renderer_opengl/gl_state.h b/src/video_core/renderer_opengl/gl_state.h index 5c7b636e4f..26e5460e57 100644 --- a/src/video_core/renderer_opengl/gl_state.h +++ b/src/video_core/renderer_opengl/gl_state.h @@ -123,6 +123,8 @@ public: bool enabled = false; GLuint bindpoint; GLuint ssbo; + GLsizeiptr size; + GLintptr offset; }; std::array, 5> const_buffers; } draw; From 0af7e937638c3c4b9c12d8625c108adb6ed11791 Mon Sep 17 00:00:00 2001 From: Markus Wick Date: Fri, 10 Aug 2018 11:40:16 +0200 Subject: [PATCH 5/6] gl_state: Don't track constant buffer mappings. --- .../renderer_opengl/gl_rasterizer.cpp | 18 +++--------------- src/video_core/renderer_opengl/gl_state.cpp | 18 ------------------ src/video_core/renderer_opengl/gl_state.h | 8 -------- 3 files changed, 3 insertions(+), 41 deletions(-) diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index fd4731d808..268b395a12 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp @@ -628,28 +628,17 @@ std::tuple RasterizerOpenGL::SetupConstBuffers( const auto& gpu = Core::System::GetInstance().GPU(); const auto& maxwell3d = gpu.Maxwell3D(); - // Reset all buffer draw state for this stage. - for (auto& buffer : state.draw.const_buffers[static_cast(stage)]) { - buffer.bindpoint = 0; - buffer.enabled = false; - } - // Upload only the enabled buffers from the 16 constbuffers of each shader stage const auto& shader_stage = maxwell3d.state.shader_stages[static_cast(stage)]; for (u32 bindpoint = 0; bindpoint < entries.size(); ++bindpoint) { const auto& used_buffer = entries[bindpoint]; const auto& buffer = shader_stage.const_buffers[used_buffer.GetIndex()]; - auto& buffer_draw_state = - state.draw.const_buffers[static_cast(stage)][used_buffer.GetIndex()]; if (!buffer.enabled) { continue; } - buffer_draw_state.enabled = true; - buffer_draw_state.bindpoint = current_bindpoint + bindpoint; - boost::optional addr = gpu.memory_manager->GpuToCpuAddress(buffer.address); size_t size = 0; @@ -676,9 +665,8 @@ std::tuple RasterizerOpenGL::SetupConstBuffers( std::tie(buffer_ptr, buffer_offset) = AlignBuffer(buffer_ptr, buffer_offset, static_cast(uniform_buffer_alignment)); - buffer_draw_state.size = size; - buffer_draw_state.offset = buffer_offset; - buffer_draw_state.ssbo = stream_buffer.GetHandle(); + glBindBufferRange(GL_UNIFORM_BUFFER, current_bindpoint + bindpoint, + stream_buffer.GetHandle(), buffer_offset, size); Memory::ReadBlock(*addr, buffer_ptr, size); buffer_ptr += size; @@ -689,7 +677,7 @@ std::tuple RasterizerOpenGL::SetupConstBuffers( const GLuint index = glGetProgramResourceIndex(program, GL_UNIFORM_BLOCK, buffer_name.c_str()); if (index != GL_INVALID_INDEX) { - glUniformBlockBinding(program, index, buffer_draw_state.bindpoint); + glUniformBlockBinding(program, index, current_bindpoint + bindpoint); } } diff --git a/src/video_core/renderer_opengl/gl_state.cpp b/src/video_core/renderer_opengl/gl_state.cpp index 212c87e0c5..1d19751790 100644 --- a/src/video_core/renderer_opengl/gl_state.cpp +++ b/src/video_core/renderer_opengl/gl_state.cpp @@ -203,24 +203,6 @@ void OpenGLState::Apply() const { } } - // Constbuffers - for (std::size_t stage = 0; stage < draw.const_buffers.size(); ++stage) { - for (std::size_t buffer_id = 0; buffer_id < draw.const_buffers[stage].size(); ++buffer_id) { - const auto& current = cur_state.draw.const_buffers[stage][buffer_id]; - const auto& new_state = draw.const_buffers[stage][buffer_id]; - - if (std::tie(current.enabled, current.bindpoint, current.ssbo, current.size, - current.offset) != std::tie(new_state.enabled, new_state.bindpoint, - new_state.ssbo, new_state.size, - new_state.offset)) { - if (new_state.enabled) { - glBindBufferRange(GL_UNIFORM_BUFFER, new_state.bindpoint, new_state.ssbo, - new_state.offset, new_state.size); - } - } - } - } - // Framebuffer if (draw.read_framebuffer != cur_state.draw.read_framebuffer) { glBindFramebuffer(GL_READ_FRAMEBUFFER, draw.read_framebuffer); diff --git a/src/video_core/renderer_opengl/gl_state.h b/src/video_core/renderer_opengl/gl_state.h index 26e5460e57..bdb02ba256 100644 --- a/src/video_core/renderer_opengl/gl_state.h +++ b/src/video_core/renderer_opengl/gl_state.h @@ -119,14 +119,6 @@ public: GLuint uniform_buffer; // GL_UNIFORM_BUFFER_BINDING GLuint shader_program; // GL_CURRENT_PROGRAM GLuint program_pipeline; // GL_PROGRAM_PIPELINE_BINDING - struct ConstBufferConfig { - bool enabled = false; - GLuint bindpoint; - GLuint ssbo; - GLsizeiptr size; - GLintptr offset; - }; - std::array, 5> const_buffers; } draw; struct { From 0eb39922f673e88efb90b08116293f626c0a4e62 Mon Sep 17 00:00:00 2001 From: Markus Wick Date: Fri, 10 Aug 2018 19:11:12 +0200 Subject: [PATCH 6/6] gl_rasterizer: Use a shared helper to upload from CPU memory. --- .../renderer_opengl/gl_rasterizer.cpp | 57 ++++++++++--------- .../renderer_opengl/gl_rasterizer.h | 4 ++ 2 files changed, 33 insertions(+), 28 deletions(-) diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp index 268b395a12..52a649e2f0 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.cpp +++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp @@ -99,7 +99,6 @@ std::pair RasterizerOpenGL::SetupVertexArrays(u8* array_ptr, GLintptr buffer_offset) { MICROPROFILE_SCOPE(OpenGL_VAO); const auto& regs = Core::System::GetInstance().GPU().Maxwell3D().regs; - const auto& memory_manager = Core::System::GetInstance().GPU().memory_manager; state.draw.vertex_array = hw_vao.handle; state.draw.vertex_buffer = stream_buffer.GetHandle(); @@ -117,16 +116,15 @@ std::pair RasterizerOpenGL::SetupVertexArrays(u8* array_ptr, ASSERT(end > start); u64 size = end - start + 1; - // Copy vertex array data - Memory::ReadBlock(*memory_manager->GpuToCpuAddress(start), array_ptr, size); + GLintptr vertex_buffer_offset; + std::tie(array_ptr, buffer_offset, vertex_buffer_offset) = + UploadMemory(array_ptr, buffer_offset, start, size); // Bind the vertex array to the buffer at the current offset. - glBindVertexBuffer(index, stream_buffer.GetHandle(), buffer_offset, vertex_array.stride); + glBindVertexBuffer(index, stream_buffer.GetHandle(), vertex_buffer_offset, + vertex_array.stride); ASSERT_MSG(vertex_array.divisor == 0, "Vertex buffer divisor unimplemented"); - - array_ptr += size; - buffer_offset += size; } // Use the vertex array as-is, assumes that the data is formatted correctly for OpenGL. @@ -407,6 +405,23 @@ std::pair RasterizerOpenGL::AlignBuffer(u8* buffer_ptr, GLintptr return {buffer_ptr + (offset_aligned - buffer_offset), offset_aligned}; } +std::tuple RasterizerOpenGL::UploadMemory(u8* buffer_ptr, + GLintptr buffer_offset, + Tegra::GPUVAddr gpu_addr, + size_t size, size_t alignment) { + std::tie(buffer_ptr, buffer_offset) = AlignBuffer(buffer_ptr, buffer_offset, alignment); + GLintptr uploaded_offset = buffer_offset; + + const auto& memory_manager = Core::System::GetInstance().GPU().memory_manager; + const boost::optional cpu_addr{memory_manager->GpuToCpuAddress(gpu_addr)}; + Memory::ReadBlock(*cpu_addr, buffer_ptr, size); + + buffer_ptr += size; + buffer_offset += size; + + return {buffer_ptr, buffer_offset, uploaded_offset}; +} + void RasterizerOpenGL::DrawArrays() { if (accelerate_draw == AccelDraw::Disabled) return; @@ -456,23 +471,13 @@ void RasterizerOpenGL::DrawArrays() { std::tie(buffer_ptr, buffer_offset) = SetupVertexArrays(buffer_ptr, buffer_offset); - std::tie(buffer_ptr, buffer_offset) = AlignBuffer(buffer_ptr, buffer_offset, 4); - // If indexed mode, copy the index buffer GLintptr index_buffer_offset = 0; if (is_indexed) { - const auto& memory_manager = Core::System::GetInstance().GPU().memory_manager; - const boost::optional index_data_addr{ - memory_manager->GpuToCpuAddress(regs.index_array.StartAddress())}; - Memory::ReadBlock(*index_data_addr, buffer_ptr, index_buffer_size); - - index_buffer_offset = buffer_offset; - buffer_ptr += index_buffer_size; - buffer_offset += index_buffer_size; + std::tie(buffer_ptr, buffer_offset, index_buffer_offset) = UploadMemory( + buffer_ptr, buffer_offset, regs.index_array.StartAddress(), index_buffer_size); } - std::tie(buffer_ptr, buffer_offset) = AlignBuffer(buffer_ptr, buffer_offset, 4); - std::tie(buffer_ptr, buffer_offset) = SetupShaders(buffer_ptr, buffer_offset); stream_buffer.Unmap(buffer_ptr - buffer_ptr_base); @@ -639,8 +644,6 @@ std::tuple RasterizerOpenGL::SetupConstBuffers( continue; } - boost::optional addr = gpu.memory_manager->GpuToCpuAddress(buffer.address); - size_t size = 0; if (used_buffer.IsIndirect()) { @@ -662,15 +665,13 @@ std::tuple RasterizerOpenGL::SetupConstBuffers( size = Common::AlignUp(size, sizeof(GLvec4)); ASSERT_MSG(size <= MaxConstbufferSize, "Constbuffer too big"); - std::tie(buffer_ptr, buffer_offset) = - AlignBuffer(buffer_ptr, buffer_offset, static_cast(uniform_buffer_alignment)); + GLintptr const_buffer_offset; + std::tie(buffer_ptr, buffer_offset, const_buffer_offset) = + UploadMemory(buffer_ptr, buffer_offset, buffer.address, size, + static_cast(uniform_buffer_alignment)); glBindBufferRange(GL_UNIFORM_BUFFER, current_bindpoint + bindpoint, - stream_buffer.GetHandle(), buffer_offset, size); - - Memory::ReadBlock(*addr, buffer_ptr, size); - buffer_ptr += size; - buffer_offset += size; + stream_buffer.GetHandle(), const_buffer_offset, size); // Now configure the bindpoint of the buffer inside the shader const std::string buffer_name = used_buffer.GetName(); diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h index aa6afc1de8..74307f6265 100644 --- a/src/video_core/renderer_opengl/gl_rasterizer.h +++ b/src/video_core/renderer_opengl/gl_rasterizer.h @@ -171,6 +171,10 @@ private: std::pair AlignBuffer(u8* buffer_ptr, GLintptr buffer_offset, size_t alignment); + std::tuple UploadMemory(u8* buffer_ptr, GLintptr buffer_offset, + Tegra::GPUVAddr gpu_addr, size_t size, + size_t alignment = 4); + enum class AccelDraw { Disabled, Arrays, Indexed }; AccelDraw accelerate_draw = AccelDraw::Disabled; };