-
-
Notifications
You must be signed in to change notification settings - Fork 333
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
OpenGL: Monotonic UBO allocator (#1911)
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
1 parent
9fbcb03
commit 8b2c748
Showing
17 changed files
with
907 additions
and
35 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.