CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!
CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!
Path: blob/master/Common/GPU/OpenGL/GLMemory.h
Views: 1401
#pragma once12#include <vector>3#include <cstdint>4#include <cstring>56#include "Common/GPU/GPUBackendCommon.h"7#include "Common/GPU/OpenGL/GLCommon.h"8#include "Common/Log.h"910enum class GLBufferStrategy {11SUBDATA = 0,1213MASK_FLUSH = 0x10,14MASK_INVALIDATE = 0x20,1516// Map/unmap the buffer each frame.17FRAME_UNMAP = 1,18// Map/unmap and also invalidate the buffer on map.19INVALIDATE_UNMAP = MASK_INVALIDATE,20// Map/unmap and explicitly flushed changed ranges.21FLUSH_UNMAP = MASK_FLUSH,22// Map/unmap, invalidate on map, and explicit flush.23FLUSH_INVALIDATE_UNMAP = MASK_FLUSH | MASK_INVALIDATE,24};2526static inline int operator &(const GLBufferStrategy &lhs, const GLBufferStrategy &rhs) {27return (int)lhs & (int)rhs;28}2930class GLRBuffer {31public:32GLRBuffer(GLuint target, size_t size) : target_(target), size_((int)size) {}33~GLRBuffer() {34if (buffer_) {35glDeleteBuffers(1, &buffer_);36}37}3839void *Map(GLBufferStrategy strategy);40bool Unmap();4142bool Mapped() const {43return mapped_;44}4546GLuint buffer_ = 0;47GLuint target_;48int size_;4950private:51bool mapped_ = false;52bool hasStorage_ = false;53};5455class GLRenderManager;5657// Similar to VulkanPushBuffer but is currently less efficient - it collects all the data in58// RAM then does a big memcpy/buffer upload at the end of the frame. This is at least a lot59// faster than the hundreds of buffer uploads or memory array buffers we used before.60// On modern GL we could avoid the copy using glBufferStorage but not sure it's worth the61// trouble.62// We need to manage the lifetime of this together with the other resources so its destructor63// runs on the render thread.64class GLPushBuffer : public GPUMemoryManager {65public:66friend class GLRenderManager;6768struct BufInfo {69GLRBuffer *buffer = nullptr;70uint8_t *localMemory = nullptr;71uint8_t *deviceMemory = nullptr;72size_t flushOffset = 0;73size_t size;74};7576GLPushBuffer(GLRenderManager *render, GLuint target, size_t size, const char *tag);77~GLPushBuffer();7879void Reset() { offset_ = 0; }8081void GetDebugString(char *buffer, size_t bufSize) const override;8283const char *Name() const override { return tag_; }; // for sorting8485// Utility for users of this class, not used internally.86enum { INVALID_OFFSET = 0xFFFFFFFF };8788// Needs context in case of defragment.89void Begin() {90buf_ = 0;91offset_ = 0;92// Note: we must defrag because some buffers may be smaller than size_.93Defragment();94Map();95_dbg_assert_(writePtr_);96}9798void BeginNoReset() {99Map();100}101102void End() {103Unmap();104}105106void Map();107void Unmap();108109bool IsReady() const {110return writePtr_ != nullptr;111}112113// Recommended - lets you write directly into the buffer through the returned pointer.114// If you didn't end up using all the memory you grabbed here, then before calling Allocate or Push115// again, call Rewind (see below).116uint8_t *Allocate(uint32_t numBytes, uint32_t alignment, GLRBuffer **buf, uint32_t *bindOffset) {117uint32_t offset = ((uint32_t)offset_ + alignment - 1) & ~(alignment - 1);118if (offset + numBytes <= size_) {119// Common path.120offset_ = offset + numBytes;121*buf = buffers_[buf_].buffer;122*bindOffset = offset;123return writePtr_ + offset;124}125126NextBuffer(numBytes);127*bindOffset = 0;128*buf = buffers_[buf_].buffer;129// Need to mark the allocated range used in the new buffer. How did things work before this?130offset_ = numBytes;131return writePtr_;132}133134// For convenience if all you'll do is to copy.135uint32_t Push(const void *data, uint32_t numBytes, int alignment, GLRBuffer **buf) {136uint32_t bindOffset;137uint8_t *ptr = Allocate(numBytes, alignment, buf, &bindOffset);138memcpy(ptr, data, numBytes);139return bindOffset;140}141142uint8_t *GetPtr(uint32_t offset) {143return writePtr_ + offset;144}145146// If you didn't use all of the previous allocation you just made (obviously can't be another one),147// you can return memory to the buffer by specifying the offset up until which you wrote data.148// Pass in the buffer you got last time. If that buffer has been filled already, no rewind can be safely done.149// (well technically would be possible but not worth the trouble).150void Rewind(GLRBuffer *buffer, uint32_t offset) {151if (buffer == buffers_[buf_].buffer) {152_dbg_assert_(offset != INVALID_OFFSET);153_dbg_assert_(offset <= offset_);154offset_ = offset;155}156}157158size_t GetOffset() const { return offset_; }159size_t GetTotalSize() const;160161void Destroy(bool onRenderThread);162void Flush();163164protected:165void MapDevice(GLBufferStrategy strategy);166void UnmapDevice();167168private:169void AddBuffer(); // asserts on failure170void NextBuffer(size_t minSize);171void Defragment();172173GLRenderManager *render_;174std::vector<BufInfo> buffers_;175size_t buf_ = 0;176size_t offset_ = 0;177size_t size_ = 0;178uint8_t *writePtr_ = nullptr;179GLuint target_;180GLBufferStrategy strategy_ = GLBufferStrategy::SUBDATA;181const char *tag_;182};183184185