|
|
|
@ -9,174 +9,91 @@ |
|
|
|
#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; |
|
|
|
} |
|
|
|
OGLStreamBuffer::OGLStreamBuffer(GLenum target, GLsizeiptr size, bool prefer_coherent) |
|
|
|
: gl_target(target), buffer_size(size) { |
|
|
|
gl_buffer.Create(); |
|
|
|
glBindBuffer(gl_target, gl_buffer.handle); |
|
|
|
|
|
|
|
std::unique_ptr<OGLStreamBuffer> OGLStreamBuffer::MakeBuffer(bool storage_buffer, GLenum target) { |
|
|
|
if (storage_buffer) { |
|
|
|
return std::make_unique<StorageBuffer>(target); |
|
|
|
GLsizeiptr allocate_size = size; |
|
|
|
if (target == GL_ARRAY_BUFFER) { |
|
|
|
// On AMD GPU there is a strange crash in indexed drawing. The crash happens when the buffer
|
|
|
|
// read position is near the end and is an out-of-bound access to the vertex buffer. This is
|
|
|
|
// probably a bug in the driver and is related to the usage of vec3<byte> attributes in the
|
|
|
|
// vertex array. Doubling the allocation size for the vertex buffer seems to avoid the
|
|
|
|
// crash.
|
|
|
|
allocate_size *= 2; |
|
|
|
} |
|
|
|
return std::make_unique<OrphanBuffer>(target); |
|
|
|
} |
|
|
|
|
|
|
|
OrphanBuffer::~OrphanBuffer() { |
|
|
|
Release(); |
|
|
|
if (GLAD_GL_ARB_buffer_storage) { |
|
|
|
persistent = true; |
|
|
|
coherent = prefer_coherent; |
|
|
|
GLbitfield flags = |
|
|
|
GL_MAP_WRITE_BIT | GL_MAP_PERSISTENT_BIT | (coherent ? GL_MAP_COHERENT_BIT : 0); |
|
|
|
glBufferStorage(gl_target, allocate_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, allocate_size, nullptr, GL_STREAM_DRAW); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
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(); |
|
|
|
OGLStreamBuffer::~OGLStreamBuffer() { |
|
|
|
if (persistent) { |
|
|
|
glBindBuffer(gl_target, gl_buffer.handle); |
|
|
|
glUnmapBuffer(gl_target); |
|
|
|
} |
|
|
|
|
|
|
|
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(); |
|
|
|
GLuint OGLStreamBuffer::GetHandle() const { |
|
|
|
return gl_buffer.handle; |
|
|
|
} |
|
|
|
|
|
|
|
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)); |
|
|
|
GLsizeiptr OGLStreamBuffer::GetSize() const { |
|
|
|
return buffer_size; |
|
|
|
} |
|
|
|
|
|
|
|
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) { |
|
|
|
std::tuple<u8*, GLintptr, bool> OGLStreamBuffer::Map(GLsizeiptr size, GLintptr alignment) { |
|
|
|
ASSERT(size <= buffer_size); |
|
|
|
ASSERT(alignment <= buffer_size); |
|
|
|
mapped_size = 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 (alignment > 0) { |
|
|
|
buffer_pos = Common::AlignUp<size_t>(buffer_pos, alignment); |
|
|
|
} |
|
|
|
|
|
|
|
bool invalidate = false; |
|
|
|
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; |
|
|
|
} |
|
|
|
invalidate = true; |
|
|
|
|
|
|
|
while (!tail.empty() && buffer_pos + size > tail.front().offset) { |
|
|
|
std::swap(sync, tail.front().sync); |
|
|
|
tail.pop_front(); |
|
|
|
if (persistent) { |
|
|
|
glUnmapBuffer(gl_target); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
if (sync.handle != 0) { |
|
|
|
glClientWaitSync(sync.handle, GL_SYNC_FLUSH_COMMANDS_BIT, GL_TIMEOUT_IGNORED); |
|
|
|
sync.Release(); |
|
|
|
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; |
|
|
|
} |
|
|
|
|
|
|
|
if (head.empty() || effective_offset > head.back().offset) { |
|
|
|
head.emplace_back(); |
|
|
|
head.back().offset = effective_offset; |
|
|
|
return std::make_tuple(mapped_ptr + buffer_pos - mapped_offset, buffer_pos, invalidate); |
|
|
|
} |
|
|
|
|
|
|
|
void OGLStreamBuffer::Unmap(GLsizeiptr size) { |
|
|
|
ASSERT(size <= mapped_size); |
|
|
|
|
|
|
|
if (!coherent && size > 0) { |
|
|
|
glFlushMappedBufferRange(gl_target, buffer_pos - mapped_offset, size); |
|
|
|
} |
|
|
|
|
|
|
|
mapped_size = size; |
|
|
|
return std::make_pair(&mapped_ptr[buffer_pos], static_cast<GLintptr>(buffer_pos)); |
|
|
|
} |
|
|
|
if (!persistent) { |
|
|
|
glUnmapBuffer(gl_target); |
|
|
|
} |
|
|
|
|
|
|
|
void StorageBuffer::Unmap() { |
|
|
|
glFlushMappedBufferRange(gl_target, static_cast<GLintptr>(buffer_pos), |
|
|
|
static_cast<GLsizeiptr>(mapped_size)); |
|
|
|
buffer_pos += mapped_size; |
|
|
|
buffer_pos += size; |
|
|
|
} |