3 changed files with 218 additions and 0 deletions
-
2src/video_core/CMakeLists.txt
-
182src/video_core/renderer_opengl/gl_stream_buffer.cpp
-
34src/video_core/renderer_opengl/gl_stream_buffer.h
@ -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; |
|||
} |
|||
@ -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; |
|||
}; |
|||
Write
Preview
Loading…
Cancel
Save
Reference in new issue