Просмотр исходного кода

renderer_gl: Port over gl_stream_buffer module from Citra.

bunnei 8 лет назад
Родитель
Сommit
0e4b9cdde4

+ 2 - 0
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

+ 182 - 0
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;
+}

+ 34 - 0
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 <memory>
+#include <glad/glad.h>
+#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<OGLStreamBuffer> 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<u8*, GLintptr> 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;
+};