summaryrefslogtreecommitdiffstats
path: root/src/video_core/renderer_opengl/gl_stream_buffer.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/video_core/renderer_opengl/gl_stream_buffer.cpp')
-rw-r--r--src/video_core/renderer_opengl/gl_stream_buffer.cpp182
1 files changed, 182 insertions, 0 deletions
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 <deque>
+#include <vector>
+#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<u8*, GLintptr> Map(size_t size, size_t alignment) override;
+ void Unmap() override;
+
+ std::vector<u8> 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<u8*, GLintptr> Map(size_t size, size_t alignment) override;
+ void Unmap() override;
+
+ struct Fence {
+ OGLSync sync;
+ size_t offset;
+ };
+ std::deque<Fence> head;
+ std::deque<Fence> tail;
+
+ u8* mapped_ptr;
+};
+
+OGLStreamBuffer::OGLStreamBuffer(GLenum target) {
+ gl_target = target;
+}
+
+GLuint OGLStreamBuffer::GetHandle() const {
+ return gl_buffer.handle;
+}
+
+std::unique_ptr<OGLStreamBuffer> OGLStreamBuffer::MakeBuffer(bool storage_buffer, GLenum target) {
+ if (storage_buffer) {
+ return std::make_unique<StorageBuffer>(target);
+ }
+ return std::make_unique<OrphanBuffer>(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<GLsizeiptr>(buffer_size), nullptr, GL_STREAM_DRAW);
+}
+
+void OrphanBuffer::Release() {
+ gl_buffer.Release();
+}
+
+std::pair<u8*, GLintptr> 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<GLintptr>(buffer_pos));
+}
+
+void OrphanBuffer::Unmap() {
+ glBufferSubData(gl_target, static_cast<GLintptr>(buffer_pos),
+ static_cast<GLsizeiptr>(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<size_t>(sync_subdivide, 1);
+
+ gl_buffer.Create();
+ glBindBuffer(gl_target, gl_buffer.handle);
+
+ glBufferStorage(gl_target, static_cast<GLsizeiptr>(buffer_size), nullptr,
+ GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT);
+ mapped_ptr = reinterpret_cast<u8*>(
+ glMapBufferRange(gl_target, 0, static_cast<GLsizeiptr>(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<u8*, GLintptr> 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<GLintptr>(buffer_pos));
+}
+
+void StorageBuffer::Unmap() {
+ glFlushMappedBufferRange(gl_target, static_cast<GLintptr>(buffer_pos),
+ static_cast<GLsizeiptr>(mapped_size));
+ buffer_pos += mapped_size;
+}