diff options
| author | bunnei <bunneidev@gmail.com> | 2018-03-19 17:10:59 -0400 | 
|---|---|---|
| committer | bunnei <bunneidev@gmail.com> | 2018-03-19 23:14:02 -0400 | 
| commit | 0e4b9cdde408871f542a2a3093963ebbaa007d33 (patch) | |
| tree | c183a90182c5a0ba32b5787bdbc1dfc35a4f6ffb | |
| parent | a1cf5020e6a0b5c3b815b9dfa430ef03915495ed (diff) | |
renderer_gl: Port over gl_stream_buffer module from Citra.
| -rw-r--r-- | src/video_core/CMakeLists.txt | 2 | ||||
| -rw-r--r-- | src/video_core/renderer_opengl/gl_stream_buffer.cpp | 182 | ||||
| -rw-r--r-- | src/video_core/renderer_opengl/gl_stream_buffer.h | 34 | 
3 files changed, 218 insertions, 0 deletions
| 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 <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; +} 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 <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; +}; | 
