diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt index 2f946e7be..7b26fe180 100644 --- a/src/video_core/CMakeLists.txt +++ b/src/video_core/CMakeLists.txt @@ -18,6 +18,8 @@ add_library(video_core STATIC renderer_opengl/gl_shader_util.h renderer_opengl/gl_state.cpp renderer_opengl/gl_state.h + renderer_opengl/gl_stream_buffer.cpp + renderer_opengl/gl_stream_buffer.h renderer_opengl/renderer_opengl.cpp renderer_opengl/renderer_opengl.h utils.h diff --git a/src/video_core/renderer_opengl/gl_stream_buffer.cpp b/src/video_core/renderer_opengl/gl_stream_buffer.cpp new file mode 100644 index 000000000..a2713e9f0 --- /dev/null +++ b/src/video_core/renderer_opengl/gl_stream_buffer.cpp @@ -0,0 +1,182 @@ +// Copyright 2018 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#include +#include +#include "common/alignment.h" +#include "common/assert.h" +#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; + +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; + + 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; +} + +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); +} + +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) { + 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; + } + + mapped_size = size; + return std::make_pair(&mapped_ptr[buffer_pos], static_cast(buffer_pos)); +} + +void StorageBuffer::Unmap() { + glFlushMappedBufferRange(gl_target, static_cast(buffer_pos), + static_cast(mapped_size)); + buffer_pos += mapped_size; +} diff --git a/src/video_core/renderer_opengl/gl_stream_buffer.h b/src/video_core/renderer_opengl/gl_stream_buffer.h new file mode 100644 index 000000000..4bc2f52e0 --- /dev/null +++ b/src/video_core/renderer_opengl/gl_stream_buffer.h @@ -0,0 +1,34 @@ +// Copyright 2018 Citra Emulator Project +// Licensed under GPLv2 or any later version +// Refer to the license.txt file included. + +#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() {} + + GLuint GetHandle() const; + + virtual std::pair Map(size_t size, size_t alignment) = 0; + virtual void Unmap() = 0; + +protected: + 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; +};