Merge pull request #3666 from degasus/vertex_streaming
renderer_opengl: Rewrite stream buffer.
This commit is contained in:
commit
b003e1398a
5 changed files with 99 additions and 216 deletions
|
@ -30,7 +30,8 @@ MICROPROFILE_DEFINE(OpenGL_Drawing, "OpenGL", "Drawing", MP_RGB(128, 128, 192));
|
||||||
MICROPROFILE_DEFINE(OpenGL_Blits, "OpenGL", "Blits", MP_RGB(100, 100, 255));
|
MICROPROFILE_DEFINE(OpenGL_Blits, "OpenGL", "Blits", MP_RGB(100, 100, 255));
|
||||||
MICROPROFILE_DEFINE(OpenGL_CacheManagement, "OpenGL", "Cache Mgmt", MP_RGB(100, 255, 100));
|
MICROPROFILE_DEFINE(OpenGL_CacheManagement, "OpenGL", "Cache Mgmt", MP_RGB(100, 255, 100));
|
||||||
|
|
||||||
RasterizerOpenGL::RasterizerOpenGL() : shader_dirty(true) {
|
RasterizerOpenGL::RasterizerOpenGL()
|
||||||
|
: shader_dirty(true), vertex_buffer(GL_ARRAY_BUFFER, VERTEX_BUFFER_SIZE) {
|
||||||
// Clipping plane 0 is always enabled for PICA fixed clip plane z <= 0
|
// Clipping plane 0 is always enabled for PICA fixed clip plane z <= 0
|
||||||
state.clip_distance[0] = true;
|
state.clip_distance[0] = true;
|
||||||
|
|
||||||
|
@ -46,13 +47,11 @@ RasterizerOpenGL::RasterizerOpenGL() : shader_dirty(true) {
|
||||||
texture_cube.Create();
|
texture_cube.Create();
|
||||||
|
|
||||||
// Generate VBO, VAO and UBO
|
// Generate VBO, VAO and UBO
|
||||||
vertex_buffer = OGLStreamBuffer::MakeBuffer(GLAD_GL_ARB_buffer_storage, GL_ARRAY_BUFFER);
|
|
||||||
vertex_buffer->Create(VERTEX_BUFFER_SIZE, VERTEX_BUFFER_SIZE / 2);
|
|
||||||
vertex_array.Create();
|
vertex_array.Create();
|
||||||
uniform_buffer.Create();
|
uniform_buffer.Create();
|
||||||
|
|
||||||
state.draw.vertex_array = vertex_array.handle;
|
state.draw.vertex_array = vertex_array.handle;
|
||||||
state.draw.vertex_buffer = vertex_buffer->GetHandle();
|
state.draw.vertex_buffer = vertex_buffer.GetHandle();
|
||||||
state.draw.uniform_buffer = uniform_buffer.handle;
|
state.draw.uniform_buffer = uniform_buffer.handle;
|
||||||
state.Apply();
|
state.Apply();
|
||||||
|
|
||||||
|
@ -499,14 +498,16 @@ void RasterizerOpenGL::DrawTriangles() {
|
||||||
state.Apply();
|
state.Apply();
|
||||||
|
|
||||||
// Draw the vertex batch
|
// Draw the vertex batch
|
||||||
size_t max_vertices = 3 * (VERTEX_BUFFER_SIZE / (3 * sizeof(HardwareVertex)));
|
size_t max_vertices = 3 * (vertex_buffer.GetSize() / (3 * sizeof(HardwareVertex)));
|
||||||
for (size_t base_vertex = 0; base_vertex < vertex_batch.size(); base_vertex += max_vertices) {
|
for (size_t base_vertex = 0; base_vertex < vertex_batch.size(); base_vertex += max_vertices) {
|
||||||
size_t vertices = std::min(max_vertices, vertex_batch.size() - base_vertex);
|
size_t vertices = std::min(max_vertices, vertex_batch.size() - base_vertex);
|
||||||
size_t vertex_size = vertices * sizeof(HardwareVertex);
|
size_t vertex_size = vertices * sizeof(HardwareVertex);
|
||||||
auto map = vertex_buffer->Map(vertex_size, 1);
|
u8* vbo;
|
||||||
memcpy(map.first, vertex_batch.data() + base_vertex, vertex_size);
|
GLintptr offset;
|
||||||
vertex_buffer->Unmap();
|
std::tie(vbo, offset, std::ignore) = vertex_buffer.Map(vertex_size, sizeof(HardwareVertex));
|
||||||
glDrawArrays(GL_TRIANGLES, map.second / sizeof(HardwareVertex), (GLsizei)vertices);
|
memcpy(vbo, vertex_batch.data() + base_vertex, vertex_size);
|
||||||
|
vertex_buffer.Unmap(vertex_size);
|
||||||
|
glDrawArrays(GL_TRIANGLES, offset / sizeof(HardwareVertex), (GLsizei)vertices);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Disable scissor test
|
// Disable scissor test
|
||||||
|
|
|
@ -239,8 +239,8 @@ private:
|
||||||
|
|
||||||
std::array<SamplerInfo, 3> texture_samplers;
|
std::array<SamplerInfo, 3> texture_samplers;
|
||||||
OGLVertexArray vertex_array;
|
OGLVertexArray vertex_array;
|
||||||
static constexpr size_t VERTEX_BUFFER_SIZE = 128 * 1024 * 1024;
|
static constexpr size_t VERTEX_BUFFER_SIZE = 32 * 1024 * 1024;
|
||||||
std::unique_ptr<OGLStreamBuffer> vertex_buffer;
|
OGLStreamBuffer vertex_buffer;
|
||||||
OGLBuffer uniform_buffer;
|
OGLBuffer uniform_buffer;
|
||||||
OGLFramebuffer framebuffer;
|
OGLFramebuffer framebuffer;
|
||||||
|
|
||||||
|
|
|
@ -225,39 +225,6 @@ 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;
|
||||||
|
|
|
@ -9,174 +9,81 @@
|
||||||
#include "video_core/renderer_opengl/gl_state.h"
|
#include "video_core/renderer_opengl/gl_state.h"
|
||||||
#include "video_core/renderer_opengl/gl_stream_buffer.h"
|
#include "video_core/renderer_opengl/gl_stream_buffer.h"
|
||||||
|
|
||||||
class OrphanBuffer : public OGLStreamBuffer {
|
OGLStreamBuffer::OGLStreamBuffer(GLenum target, GLsizeiptr size, bool prefer_coherent)
|
||||||
public:
|
: gl_target(target), buffer_size(size) {
|
||||||
explicit OrphanBuffer(GLenum target) : OGLStreamBuffer(target) {}
|
gl_buffer.Create();
|
||||||
~OrphanBuffer() override;
|
glBindBuffer(gl_target, gl_buffer.handle);
|
||||||
|
|
||||||
private:
|
if (GLAD_GL_ARB_buffer_storage) {
|
||||||
void Create(size_t size, size_t sync_subdivide) override;
|
persistent = true;
|
||||||
void Release() override;
|
coherent = prefer_coherent;
|
||||||
|
GLbitfield flags =
|
||||||
|
GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT | (coherent ? GL_MAP_COHERENT_BIT : 0);
|
||||||
|
glBufferStorage(gl_target, buffer_size, nullptr, flags);
|
||||||
|
mapped_ptr = static_cast<u8*>(glMapBufferRange(
|
||||||
|
gl_target, 0, buffer_size, flags | (coherent ? 0 : GL_MAP_FLUSH_EXPLICIT_BIT)));
|
||||||
|
} else {
|
||||||
|
glBufferData(gl_target, buffer_size, nullptr, GL_STREAM_DRAW);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
std::pair<u8*, GLintptr> Map(size_t size, size_t alignment) override;
|
OGLStreamBuffer::~OGLStreamBuffer() {
|
||||||
void Unmap() override;
|
if (persistent) {
|
||||||
|
glBindBuffer(gl_target, gl_buffer.handle);
|
||||||
std::vector<u8> data;
|
glUnmapBuffer(gl_target);
|
||||||
};
|
}
|
||||||
|
gl_buffer.Release();
|
||||||
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 {
|
GLuint OGLStreamBuffer::GetHandle() const {
|
||||||
return gl_buffer.handle;
|
return gl_buffer.handle;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<OGLStreamBuffer> OGLStreamBuffer::MakeBuffer(bool storage_buffer, GLenum target) {
|
GLsizeiptr OGLStreamBuffer::GetSize() const {
|
||||||
if (storage_buffer) {
|
return buffer_size;
|
||||||
return std::make_unique<StorageBuffer>(target);
|
|
||||||
}
|
|
||||||
return std::make_unique<OrphanBuffer>(target);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
OrphanBuffer::~OrphanBuffer() {
|
std::tuple<u8*, GLintptr, bool> OGLStreamBuffer::Map(GLsizeiptr size, GLintptr alignment) {
|
||||||
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);
|
ASSERT(size <= buffer_size);
|
||||||
|
ASSERT(alignment <= 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;
|
mapped_size = size;
|
||||||
return std::make_pair(&mapped_ptr[buffer_pos], static_cast<GLintptr>(buffer_pos));
|
|
||||||
|
if (alignment > 0) {
|
||||||
|
buffer_pos = Common::AlignUp<size_t>(buffer_pos, alignment);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool invalidate = false;
|
||||||
|
if (buffer_pos + size > buffer_size) {
|
||||||
|
buffer_pos = 0;
|
||||||
|
invalidate = true;
|
||||||
|
|
||||||
|
if (persistent) {
|
||||||
|
glUnmapBuffer(gl_target);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (invalidate | !persistent) {
|
||||||
|
GLbitfield flags = GL_MAP_WRITE_BIT | (persistent ? GL_MAP_PERSISTENT_BIT : 0) |
|
||||||
|
(coherent ? GL_MAP_COHERENT_BIT : GL_MAP_FLUSH_EXPLICIT_BIT) |
|
||||||
|
(invalidate ? GL_MAP_INVALIDATE_BUFFER_BIT : GL_MAP_UNSYNCHRONIZED_BIT);
|
||||||
|
mapped_ptr = static_cast<u8*>(
|
||||||
|
glMapBufferRange(gl_target, buffer_pos, buffer_size - buffer_pos, flags));
|
||||||
|
mapped_offset = buffer_pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
return std::make_tuple(mapped_ptr + buffer_pos - mapped_offset, buffer_pos, invalidate);
|
||||||
}
|
}
|
||||||
|
|
||||||
void StorageBuffer::Unmap() {
|
void OGLStreamBuffer::Unmap(GLsizeiptr size) {
|
||||||
glFlushMappedBufferRange(gl_target, static_cast<GLintptr>(buffer_pos),
|
ASSERT(size <= mapped_size);
|
||||||
static_cast<GLsizeiptr>(mapped_size));
|
|
||||||
buffer_pos += mapped_size;
|
if (!coherent) {
|
||||||
|
glFlushMappedBufferRange(gl_target, buffer_pos - mapped_offset, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!persistent) {
|
||||||
|
glUnmapBuffer(gl_target);
|
||||||
|
}
|
||||||
|
|
||||||
|
buffer_pos += size;
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,33 +2,41 @@
|
||||||
// Licensed under GPLv2 or any later version
|
// Licensed under GPLv2 or any later version
|
||||||
// Refer to the license.txt file included.
|
// Refer to the license.txt file included.
|
||||||
|
|
||||||
#include <memory>
|
#include <tuple>
|
||||||
#include <glad/glad.h>
|
#include <glad/glad.h>
|
||||||
#include "common/common_types.h"
|
#include "common/common_types.h"
|
||||||
#include "video_core/renderer_opengl/gl_resource_manager.h"
|
#include "video_core/renderer_opengl/gl_resource_manager.h"
|
||||||
|
|
||||||
class OGLStreamBuffer : private NonCopyable {
|
class OGLStreamBuffer : private NonCopyable {
|
||||||
public:
|
public:
|
||||||
explicit OGLStreamBuffer(GLenum target);
|
explicit OGLStreamBuffer(GLenum target, GLsizeiptr size, bool prefer_coherent = false);
|
||||||
virtual ~OGLStreamBuffer() = default;
|
~OGLStreamBuffer();
|
||||||
|
|
||||||
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;
|
GLuint GetHandle() const;
|
||||||
|
GLsizeiptr GetSize() const;
|
||||||
|
|
||||||
virtual std::pair<u8*, GLintptr> Map(size_t size, size_t alignment) = 0;
|
/*
|
||||||
virtual void Unmap() = 0;
|
* Allocates a linear chunk of memory in the GPU buffer with at least "size" bytes
|
||||||
|
* and the optional alignment requirement.
|
||||||
|
* If the buffer is full, the whole buffer is reallocated which invalidates old chunks.
|
||||||
|
* The return values are the pointer to the new chunk, the offset within the buffer,
|
||||||
|
* and the invalidation flag for previous chunks.
|
||||||
|
* The actual used size must be specified on unmapping the chunk.
|
||||||
|
*/
|
||||||
|
std::tuple<u8*, GLintptr, bool> Map(GLsizeiptr size, GLintptr alignment = 0);
|
||||||
|
|
||||||
protected:
|
void Unmap(GLsizeiptr size);
|
||||||
|
|
||||||
|
private:
|
||||||
OGLBuffer gl_buffer;
|
OGLBuffer gl_buffer;
|
||||||
GLenum gl_target;
|
GLenum gl_target;
|
||||||
|
|
||||||
size_t buffer_pos = 0;
|
bool coherent = false;
|
||||||
size_t buffer_size = 0;
|
bool persistent = false;
|
||||||
size_t buffer_sync_subdivide = 0;
|
|
||||||
size_t mapped_size = 0;
|
GLintptr buffer_pos = 0;
|
||||||
|
GLsizeiptr buffer_size = 0;
|
||||||
|
GLintptr mapped_offset = 0;
|
||||||
|
GLsizeiptr mapped_size = 0;
|
||||||
|
u8* mapped_ptr = nullptr;
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in a new issue