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.cpp
Views: 1401
#include "Common/MemoryUtil.h"1#include "Common/GPU/OpenGL/GLMemory.h"2#include "Common/GPU/OpenGL/GLRenderManager.h"3#include "Common/GPU/OpenGL/GLFeatures.h"4#include "Common/Data/Text/Parsers.h"56extern std::thread::id renderThreadId;7#if MAX_LOGLEVEL >= DEBUG_LEVEL8static bool OnRenderThread() {9return std::this_thread::get_id() == renderThreadId;10}11#endif1213void *GLRBuffer::Map(GLBufferStrategy strategy) {14_assert_(buffer_ != 0);1516GLbitfield access = GL_MAP_WRITE_BIT;17if ((strategy & GLBufferStrategy::MASK_FLUSH) != 0) {18access |= GL_MAP_FLUSH_EXPLICIT_BIT;19}20if ((strategy & GLBufferStrategy::MASK_INVALIDATE) != 0) {21access |= GL_MAP_INVALIDATE_BUFFER_BIT;22}2324void *p = nullptr;25bool allowNativeBuffer = strategy != GLBufferStrategy::SUBDATA;26if (allowNativeBuffer) {27glBindBuffer(target_, buffer_);2829if (gl_extensions.ARB_buffer_storage || gl_extensions.EXT_buffer_storage) {30#if !PPSSPP_PLATFORM(IOS)31if (!hasStorage_) {32GLbitfield storageFlags = access & ~(GL_MAP_INVALIDATE_BUFFER_BIT | GL_MAP_FLUSH_EXPLICIT_BIT);33#ifdef USING_GLES234#ifdef GL_EXT_buffer_storage35glBufferStorageEXT(target_, size_, nullptr, storageFlags);36#endif37#else38glBufferStorage(target_, size_, nullptr, storageFlags);39#endif40hasStorage_ = true;41}42#endif43p = glMapBufferRange(target_, 0, size_, access);44} else if (gl_extensions.VersionGEThan(3, 0, 0)) {45// GLES3 or desktop 3.46p = glMapBufferRange(target_, 0, size_, access);47} else if (!gl_extensions.IsGLES) {48#ifndef USING_GLES249p = glMapBuffer(target_, GL_READ_WRITE);50#endif51}52}5354mapped_ = p != nullptr;55return p;56}5758bool GLRBuffer::Unmap() {59glBindBuffer(target_, buffer_);60mapped_ = false;61return glUnmapBuffer(target_) == GL_TRUE;62}6364GLPushBuffer::GLPushBuffer(GLRenderManager *render, GLuint target, size_t size, const char *tag) : render_(render), size_(size), target_(target), tag_(tag) {65AddBuffer();66RegisterGPUMemoryManager(this);67}6869GLPushBuffer::~GLPushBuffer() {70UnregisterGPUMemoryManager(this);71Destroy(true);72}7374void GLPushBuffer::Map() {75_assert_(!writePtr_);76auto &info = buffers_[buf_];77writePtr_ = info.deviceMemory ? info.deviceMemory : info.localMemory;78info.flushOffset = 0;79// Force alignment. This is needed for PushAligned() to work as expected.80while ((intptr_t)writePtr_ & 15) {81writePtr_++;82offset_++;83info.flushOffset++;84}85_assert_(writePtr_);86}8788void GLPushBuffer::Unmap() {89_assert_(writePtr_);90if (!buffers_[buf_].deviceMemory) {91// Here we simply upload the data to the last buffer.92// Might be worth trying with size_ instead of offset_, so the driver can replace93// the whole buffer. At least if it's close.94render_->BufferSubdata(buffers_[buf_].buffer, 0, offset_, buffers_[buf_].localMemory, false);95} else {96buffers_[buf_].flushOffset = offset_;97}98writePtr_ = nullptr;99}100101void GLPushBuffer::Flush() {102// Must be called from the render thread.103_dbg_assert_(OnRenderThread());104if (buf_ >= buffers_.size()) {105_dbg_assert_msg_(false, "buf_ somehow got out of sync: %d vs %d", (int)buf_, (int)buffers_.size());106return;107}108109buffers_[buf_].flushOffset = offset_;110if (!buffers_[buf_].deviceMemory && writePtr_) {111auto &info = buffers_[buf_];112if (info.flushOffset != 0) {113_assert_(info.buffer->buffer_);114glBindBuffer(target_, info.buffer->buffer_);115glBufferSubData(target_, 0, info.flushOffset, info.localMemory);116}117118// Here we will submit all the draw calls, with the already known buffer and offsets.119// Might as well reset the write pointer here and start over the current buffer.120writePtr_ = info.localMemory;121offset_ = 0;122info.flushOffset = 0;123}124125// For device memory, we flush all buffers here.126if ((strategy_ & GLBufferStrategy::MASK_FLUSH) != 0) {127for (auto &info : buffers_) {128if (info.flushOffset == 0 || !info.deviceMemory)129continue;130131glBindBuffer(target_, info.buffer->buffer_);132glFlushMappedBufferRange(target_, 0, info.flushOffset);133info.flushOffset = 0;134}135}136}137138void GLPushBuffer::AddBuffer() {139// INFO_LOG(Log::G3D, "GLPushBuffer(%s): Allocating %d bytes", tag_, size_);140BufInfo info;141info.localMemory = (uint8_t *)AllocateAlignedMemory(size_, 16);142_assert_msg_(info.localMemory != 0, "GLPushBuffer alloc fail: %d (%s)", (int)size_, tag_);143info.buffer = render_->CreateBuffer(target_, size_, GL_DYNAMIC_DRAW);144info.size = size_;145buf_ = buffers_.size();146buffers_.push_back(info);147}148149void GLPushBuffer::Destroy(bool onRenderThread) {150if (buf_ == -1)151return; // Already destroyed152for (BufInfo &info : buffers_) {153// This will automatically unmap device memory, if needed.154// NOTE: We immediately delete the buffer, don't go through the deleter, if we're on the render thread.155if (onRenderThread) {156delete info.buffer;157} else {158render_->DeleteBuffer(info.buffer);159}160FreeAlignedMemory(info.localMemory);161}162buffers_.clear();163buf_ = -1;164}165166void GLPushBuffer::NextBuffer(size_t minSize) {167// First, unmap the current memory.168Unmap();169170buf_++;171if (buf_ >= buffers_.size() || minSize > size_) {172// Before creating the buffer, adjust to the new size_ if necessary.173while (size_ < minSize) {174size_ <<= 1;175}176AddBuffer();177}178179// Now, move to the next buffer and map it.180offset_ = 0;181Map();182}183184void GLPushBuffer::Defragment() {185_dbg_assert_msg_(!OnRenderThread(), "Defragment must not run on the render thread");186187if (buffers_.size() <= 1) {188// Let's take this opportunity to jettison any localMemory we don't need.189for (auto &info : buffers_) {190if (info.deviceMemory) {191FreeAlignedMemory(info.localMemory);192info.localMemory = nullptr;193}194}195return;196}197198// Okay, we have more than one. Destroy them all and start over with a larger one.199200// When calling AddBuffer, we sometimes increase size_. So if we allocated multiple buffers in a frame,201// they won't all have the same size. Sum things up properly.202size_t newSize = 0;203for (int i = 0; i < (int)buffers_.size(); i++) {204newSize += buffers_[i].size;205}206207Destroy(false);208209// Set some sane but very free limits. If there's another spike, we'll just allocate more anyway.210size_ = std::min(std::max(newSize, (size_t)65536), (size_t)(512 * 1024 * 1024));211AddBuffer();212}213214size_t GLPushBuffer::GetTotalSize() const {215size_t sum = 0;216// When calling AddBuffer, we sometimes increase size_. So if we allocated multiple buffers in a frame,217// they won't all have the same size. Sum things up properly.218if (buffers_.size() > 1) {219for (int i = 0; i < (int)buffers_.size() - 1; i++) {220sum += buffers_[i].size;221}222}223sum += offset_;224return sum;225}226227void GLPushBuffer::MapDevice(GLBufferStrategy strategy) {228_dbg_assert_msg_(OnRenderThread(), "MapDevice must run on render thread");229230strategy_ = strategy;231if (strategy_ == GLBufferStrategy::SUBDATA) {232return;233}234235bool mapChanged = false;236for (auto &info : buffers_) {237if (!info.buffer->buffer_ || info.deviceMemory) {238// Can't map - no device buffer associated yet or already mapped.239continue;240}241242info.deviceMemory = (uint8_t *)info.buffer->Map(strategy_);243mapChanged = mapChanged || info.deviceMemory != nullptr;244245if (!info.deviceMemory && !info.localMemory) {246// Somehow it failed, let's dodge crashing.247info.localMemory = (uint8_t *)AllocateAlignedMemory(info.buffer->size_, 16);248mapChanged = true;249}250251_dbg_assert_msg_(info.localMemory || info.deviceMemory, "Local or device memory must succeed");252}253254if (writePtr_ && mapChanged) {255// This can happen during a sync. Remap.256writePtr_ = nullptr;257Map();258}259}260261void GLPushBuffer::UnmapDevice() {262_dbg_assert_msg_(OnRenderThread(), "UnmapDevice must run on render thread");263264for (auto &info : buffers_) {265if (info.deviceMemory) {266// TODO: Technically this can return false?267info.buffer->Unmap();268info.deviceMemory = nullptr;269}270}271}272273void GLPushBuffer::GetDebugString(char *buffer, size_t bufSize) const {274snprintf(buffer, bufSize, "%s: %s/%s (%d)", tag_, NiceSizeFormat(this->offset_).c_str(), NiceSizeFormat(this->size_).c_str(), (int)buffers_.size());275}276277278