Skip to content

Commit

Permalink
OpenGL: Monotonic UBO allocator (#1911)
Browse files Browse the repository at this point in the history
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Bart Louwers <[email protected]>
  • Loading branch information
3 people authored Dec 12, 2023
1 parent 9fbcb03 commit 8b2c748
Show file tree
Hide file tree
Showing 17 changed files with 907 additions and 35 deletions.
5 changes: 4 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -159,8 +159,8 @@ if(MLN_DRAWABLE_RENDERER)
${PROJECT_SOURCE_DIR}/include/mbgl/shaders/shader_program_base.hpp
${PROJECT_SOURCE_DIR}/include/mbgl/util/identity.hpp
${PROJECT_SOURCE_DIR}/include/mbgl/util/suppress_copies.hpp

${PROJECT_SOURCE_DIR}/include/mbgl/shaders/gl/shader_program_gl.hpp
${PROJECT_SOURCE_DIR}/include/mbgl/gl/buffer_allocator.hpp
${PROJECT_SOURCE_DIR}/include/mbgl/gl/drawable_gl.hpp
${PROJECT_SOURCE_DIR}/include/mbgl/gl/drawable_gl_builder.hpp
${PROJECT_SOURCE_DIR}/include/mbgl/gl/layer_group_gl.hpp
Expand Down Expand Up @@ -216,6 +216,7 @@ if(MLN_DRAWABLE_RENDERER)
${PROJECT_SOURCE_DIR}/src/mbgl/shaders/shader_program_base.cpp
${PROJECT_SOURCE_DIR}/src/mbgl/util/identity.cpp
${PROJECT_SOURCE_DIR}/src/mbgl/shaders/gl/shader_program_gl.cpp
${PROJECT_SOURCE_DIR}/src/mbgl/gl/buffer_allocator.cpp
${PROJECT_SOURCE_DIR}/src/mbgl/gl/drawable_gl.cpp
${PROJECT_SOURCE_DIR}/src/mbgl/gl/drawable_gl_builder.cpp
${PROJECT_SOURCE_DIR}/src/mbgl/gl/drawable_gl_impl.hpp
Expand Down Expand Up @@ -1067,6 +1068,8 @@ if(MLN_WITH_OPENGL)
${PROJECT_SOURCE_DIR}/src/mbgl/gl/command_encoder.hpp
${PROJECT_SOURCE_DIR}/src/mbgl/gl/context.cpp
${PROJECT_SOURCE_DIR}/src/mbgl/gl/context.hpp
${PROJECT_SOURCE_DIR}/src/mbgl/gl/fence.cpp
${PROJECT_SOURCE_DIR}/src/mbgl/gl/fence.hpp
${PROJECT_SOURCE_DIR}/src/mbgl/style/layers/custom_layer.cpp
${PROJECT_SOURCE_DIR}/src/mbgl/layermanager/custom_layer_factory.cpp
${PROJECT_SOURCE_DIR}/src/mbgl/style/layers/custom_layer_impl.cpp
Expand Down
4 changes: 4 additions & 0 deletions bazel/core.bzl
Original file line number Diff line number Diff line change
Expand Up @@ -854,6 +854,8 @@ MLN_OPENGL_SOURCE = [
"src/mbgl/gl/command_encoder.hpp",
"src/mbgl/gl/context.cpp",
"src/mbgl/gl/context.hpp",
"src/mbgl/gl/fence.cpp",
"src/mbgl/gl/fence.hpp",
"src/mbgl/gl/debugging_extension.cpp",
"src/mbgl/gl/debugging_extension.hpp",
"src/mbgl/gl/defines.hpp",
Expand Down Expand Up @@ -995,6 +997,7 @@ MLN_DRAWABLES_HEADERS = [
]

MLN_DRAWABLES_GL_SOURCE = [
"src/mbgl/gl/buffer_allocator.cpp",
"src/mbgl/gl/drawable_gl.cpp",
"src/mbgl/gl/drawable_gl_builder.cpp",
"src/mbgl/gl/drawable_gl_impl.hpp",
Expand All @@ -1007,6 +1010,7 @@ MLN_DRAWABLES_GL_SOURCE = [
]

MLN_DRAWABLES_GL_HEADERS = [
"include/mbgl/gl/buffer_allocator.hpp",
"include/mbgl/gl/drawable_gl.hpp",
"include/mbgl/gl/drawable_gl_builder.hpp",
"include/mbgl/gl/layer_group_gl.hpp",
Expand Down
177 changes: 177 additions & 0 deletions include/mbgl/gl/buffer_allocator.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
#pragma once

#include <mbgl/gl/types.hpp>
#include <type_traits>
#include <cassert>
#include <cstring>
#include <cstdint>
#include <list>
#include <vector>
#include <memory>

namespace mbgl {
namespace gl {

class Context;
class Fence;

class BufferRef {
public:
BufferRef(size_t pointer, size_t offset, size_t size_)
: bufferIndex(pointer),
bufferOffset(offset),
size(size_) {}

// Size of our allocation
size_t getSize() const noexcept { return size; }
// Index into the array of buffers indicating our current buffer
size_t getBufferIndex() const noexcept { return bufferIndex; }
// Offset into the buffer where our allocation begins
intptr_t getBufferOffset() const noexcept { return bufferOffset; }

private:
size_t bufferIndex = 0;
intptr_t bufferOffset = 0;
size_t size = 0;
};

/// @brief IBufferAllocator hides the underlying implementation of the buffer allocator scheme used.
class IBufferAllocator {
public:
virtual ~IBufferAllocator(){};

/// @brief Write data into a buffer managed by the allocator
/// @param data Pointer to data to write into the buffer
/// @param size Size in bytes of contents to copy from `data`
/// @param ref Buffer reference keeping the new allocation alive.
/// Note: The concrete type of BufferRef is assumed based on the class type of
/// IBufferAllocator.
/// @return False on allocation failure.
virtual bool write(const void* data, size_t size, BufferRef*& ref) noexcept = 0;

/// @brief Release the allocation held by the given reference
/// @param ref Allocation reference to release
virtual void release(BufferRef* ref) noexcept = 0;

/// Defragment the allocator's underlying buffer pool.
virtual void defragment(const std::shared_ptr<gl::Fence>& fence) noexcept = 0;

/// Return the buffer page size used by the allocator. This is the maximum size a
/// single buffer can possible be.
virtual size_t pageSize() const noexcept = 0;

/// Get the OpenGL object ID for the buffer at the given index.
virtual int32_t getBufferID(size_t bufferIndex) const noexcept = 0;
};

/// @brief A BufferRef holds a strong reference on a buffer sub-allocation, managed by a RelocatableBuffer.
/// @tparam OwnerClass The class type holding a reference on the buffer
template <typename OwnerClass>
class TypedBufferRef : public BufferRef {
public:
TypedBufferRef() = default;
TypedBufferRef(OwnerClass* buffer, size_t pointer, size_t offset, size_t size_)
: BufferRef(pointer, offset, size_),
ownerPtr(buffer) {}
TypedBufferRef(OwnerClass* buffer)
: ownerPtr(buffer) {}

bool operator==(const TypedBufferRef& rhs) const noexcept { return ownerPtr == rhs.ownerPtr; }

// The owner of this allocation
OwnerClass* getOwner() const noexcept { return ownerPtr; }
void setOwner(OwnerClass* owner) { ownerPtr = owner; }

private:
OwnerClass* ownerPtr = nullptr;
};

/// A RelocatableBuffer is in essence a view onto an actual buffer. These actual buffers may move around
/// over time as fragmentation is managed and buffers are recycled. A RelocatableBuffer is aware of this
/// and actively participates in this memory relocation.
template <typename OwnerClass>
class RelocatableBuffer {
public:
RelocatableBuffer(IBufferAllocator& allocator_, OwnerClass* owner_)
: allocator(allocator_),
owner(owner_) {
assert(owner);
}
RelocatableBuffer(const RelocatableBuffer<OwnerClass>& rhs)
: allocator(rhs.allocator),
owner(rhs.owner),
contents(rhs.contents) {
allocate(contents.data(), contents.size());
}
RelocatableBuffer(RelocatableBuffer<OwnerClass>&& rhs) noexcept
: allocator(rhs.allocator),
owner(rhs.owner),
ref(std::move(rhs.ref)),
contents(std::move(rhs.contents)) {}
~RelocatableBuffer() {
if (ref) {
assert(ref->getOwner());
allocator.release(ref);
}
}

// As references are added to a buffer, reallocation of the underlying reference
// storage may occur. When that happens, we will be informed of the new memory location
// of our reference here.
void relocRef(TypedBufferRef<OwnerClass>* newRef) noexcept { ref = newRef; }

// Get the current OpenGL buffer ID. Do not store this, bind it and discard after the active frame.
int32_t getBufferID() const noexcept { return ref ? allocator.getBufferID(ref->getBufferIndex()) : 0; }

intptr_t getBindingOffset() const noexcept { return ref ? ref->getBufferOffset() : 0; }

const std::vector<std::byte>& getContents() const noexcept { return contents; }

void setOwner(OwnerClass* owner_) noexcept { owner = owner_; }

/// Allocate buffer memory and copy `size` bytes into the allocation from `data`
void allocate(const void* data, size_t size) noexcept {
assert(owner);

if (ref && ref->getOwner()) {
// If we're writing new data, we need to remove our ref from our old buffer.
allocator.release(ref);
ref = nullptr;
}

BufferRef* reference = ref;
allocator.write(data, size, reference);

ref = std::move(static_cast<decltype(ref)>(reference));
ref->setOwner(owner);

contents.resize(size);
std::memcpy(contents.data(), data, size);
};

IBufferAllocator& allocator;

private:
OwnerClass* owner = nullptr;
TypedBufferRef<OwnerClass>* ref = nullptr; // A strong reference to the active allocation backing this buffer
std::vector<std::byte> contents; // CPU-side buffer contents
};

class UniformBufferAllocator : public IBufferAllocator {
public:
UniformBufferAllocator();
~UniformBufferAllocator() override;

bool write(const void* data, size_t size, BufferRef*& ref) noexcept override;
void release(BufferRef* ref) noexcept override;
void defragment(const std::shared_ptr<gl::Fence>& fence) noexcept override;
size_t pageSize() const noexcept override;
int32_t getBufferID(size_t bufferIndex) const noexcept override;

private:
class Impl;
std::unique_ptr<Impl> impl;
};

} // namespace gl
} // namespace mbgl
26 changes: 18 additions & 8 deletions include/mbgl/gl/uniform_buffer_gl.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

#include <mbgl/gfx/uniform_buffer.hpp>
#include <mbgl/gl/types.hpp>
#include <mbgl/gl/buffer_allocator.hpp>

namespace mbgl {
namespace gl {
Expand All @@ -10,19 +11,28 @@ class UniformBufferGL final : public gfx::UniformBuffer {
UniformBufferGL(const UniformBufferGL&);

public:
UniformBufferGL(const void* data, std::size_t size_);
UniformBufferGL(UniformBufferGL&& other)
: UniformBuffer(std::move(other)) {}
UniformBufferGL(const void* data, std::size_t size_, IBufferAllocator& allocator);
~UniformBufferGL() override;

BufferID getID() const { return id; }
void update(const void* data, std::size_t size_) override;
UniformBufferGL(UniformBufferGL&& rhs) noexcept;
UniformBufferGL& operator=(const UniformBufferGL& rhs) = delete;

BufferID getID() const;
gl::RelocatableBuffer<UniformBufferGL>& getManagedBuffer() noexcept { return managedBuffer; }
const gl::RelocatableBuffer<UniformBufferGL>& getManagedBuffer() const noexcept { return managedBuffer; }

UniformBufferGL clone() const { return {*this}; }

protected:
BufferID id = 0;
uint32_t hash;
// gfx::UniformBuffer
void update(const void* data, std::size_t size_) override;

private:
// If the requested UBO size is too large for the allocator, the UBO will manage its own allocation
bool isManagedAllocation = false;
BufferID localID;
gl::RelocatableBuffer<UniformBufferGL> managedBuffer;

friend class UniformBufferArrayGL;
};

/// Stores a collection of uniform buffers by name
Expand Down
3 changes: 3 additions & 0 deletions include/mbgl/mtl/context.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,9 @@ class Context final : public gfx::Context {

const RendererBackend& getBackend() const { return backend; }

void beginFrame() override;
void endFrame() override;

std::unique_ptr<gfx::CommandEncoder> createCommandEncoder() override;

/// Create a new buffer object
Expand Down
1 change: 1 addition & 0 deletions include/mbgl/shaders/gl/drawable_fill_outline.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ struct ShaderSource<BuiltIn::FillOutlineShader, gfx::Backend::Type::OpenGL> {
static constexpr const char* vertex = R"(layout (std140) uniform FillOutlineDrawableUBO {
highp mat4 u_matrix;
highp vec2 u_world;
highp vec2 pad;
};
layout (std140) uniform FillOutlineEvaluatedPropsUBO {
highp vec4 u_outline_color;
Expand Down
3 changes: 3 additions & 0 deletions src/mbgl/gfx/context.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,9 @@ class Context {
Context& operator=(const Context& other) = delete;
virtual ~Context() = default;

virtual void beginFrame() = 0;
virtual void endFrame() = 0;

/// Called at the end of a frame.
virtual void performCleanup() = 0;

Expand Down
Loading

0 comments on commit 8b2c748

Please sign in to comment.