stream buffer
This commit is contained in:
parent
935bcdbd20
commit
50598fbbf4
|
@ -34,6 +34,8 @@ add_library(video_core STATIC
|
||||||
renderer_opengl/gl_shader_util.h
|
renderer_opengl/gl_shader_util.h
|
||||||
renderer_opengl/gl_state.cpp
|
renderer_opengl/gl_state.cpp
|
||||||
renderer_opengl/gl_state.h
|
renderer_opengl/gl_state.h
|
||||||
|
renderer_opengl/gl_stream_buffer.cpp
|
||||||
|
renderer_opengl/gl_stream_buffer.h
|
||||||
renderer_opengl/pica_to_gl.h
|
renderer_opengl/pica_to_gl.h
|
||||||
renderer_opengl/renderer_opengl.cpp
|
renderer_opengl/renderer_opengl.cpp
|
||||||
renderer_opengl/renderer_opengl.h
|
renderer_opengl/renderer_opengl.h
|
||||||
|
|
|
@ -142,6 +142,39 @@ public:
|
||||||
GLuint handle = 0;
|
GLuint handle = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class OGLSync : private NonCopyable {
|
||||||
|
public:
|
||||||
|
OGLSync() = default;
|
||||||
|
|
||||||
|
OGLSync(OGLSync&& o) : handle(std::exchange(o.handle, nullptr)) {}
|
||||||
|
|
||||||
|
~OGLSync() {
|
||||||
|
Release();
|
||||||
|
}
|
||||||
|
OGLSync& operator=(OGLSync&& o) {
|
||||||
|
Release();
|
||||||
|
handle = std::exchange(o.handle, nullptr);
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a new internal OpenGL resource and stores the handle
|
||||||
|
void Create() {
|
||||||
|
if (handle != 0)
|
||||||
|
return;
|
||||||
|
handle = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Deletes the internal OpenGL resource
|
||||||
|
void Release() {
|
||||||
|
if (handle == 0)
|
||||||
|
return;
|
||||||
|
glDeleteSync(handle);
|
||||||
|
handle = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
GLsync handle = 0;
|
||||||
|
};
|
||||||
|
|
||||||
class OGLVertexArray : private NonCopyable {
|
class OGLVertexArray : private NonCopyable {
|
||||||
public:
|
public:
|
||||||
OGLVertexArray() = default;
|
OGLVertexArray() = default;
|
||||||
|
|
|
@ -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;
|
||||||
|
};
|
Reference in New Issue