From 9eb51a6a3609780b92a2779514afcbfec0df5e64 Mon Sep 17 00:00:00 2001 From: "Unknown W. Brackets" Date: Sun, 11 Feb 2018 11:17:34 -0800 Subject: [PATCH] GLES: Use mapped device memory when possible. --- ext/native/thin3d/GLQueueRunner.cpp | 3 + ext/native/thin3d/GLRenderManager.cpp | 87 +++++++++++++++++++++------ ext/native/thin3d/GLRenderManager.h | 31 +++++++++- 3 files changed, 99 insertions(+), 22 deletions(-) diff --git a/ext/native/thin3d/GLQueueRunner.cpp b/ext/native/thin3d/GLQueueRunner.cpp index 1053752af1..360fa54dd0 100644 --- a/ext/native/thin3d/GLQueueRunner.cpp +++ b/ext/native/thin3d/GLQueueRunner.cpp @@ -770,6 +770,7 @@ void GLQueueRunner::PerformRenderPass(const GLRStep &step) { // TODO: Add fast path for glBindVertexBuffer GLRInputLayout *layout = c.bindVertexBuffer.inputLayout; GLuint buf = c.bindVertexBuffer.buffer ? c.bindVertexBuffer.buffer->buffer : 0; + assert(!c.bindVertexBuffer.buffer->Mapped()); if (buf != curArrayBuffer) { glBindBuffer(GL_ARRAY_BUFFER, buf); curArrayBuffer = buf; @@ -797,12 +798,14 @@ void GLQueueRunner::PerformRenderPass(const GLRStep &step) { Crash(); } else if (c.bind_buffer.target == GL_ELEMENT_ARRAY_BUFFER) { GLuint buf = c.bind_buffer.buffer ? c.bind_buffer.buffer->buffer : 0; + assert(!c.bind_buffer.buffer->Mapped()); if (buf != curElemArrayBuffer) { glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buf); curElemArrayBuffer = buf; } } else { GLuint buf = c.bind_buffer.buffer ? c.bind_buffer.buffer->buffer : 0; + assert(!c.bind_buffer.buffer->Mapped()); glBindBuffer(c.bind_buffer.target, buf); } break; diff --git a/ext/native/thin3d/GLRenderManager.cpp b/ext/native/thin3d/GLRenderManager.cpp index d15448e898..d66deb5d4f 100644 --- a/ext/native/thin3d/GLRenderManager.cpp +++ b/ext/native/thin3d/GLRenderManager.cpp @@ -421,6 +421,11 @@ void GLRenderManager::Run(int frame) { BeginSubmitFrame(frame); FrameData &frameData = frameData_[frame]; + for (auto iter : frameData.activePushBuffers) { + iter->Flush(); + iter->UnmapDevice(); + } + auto &stepsOnThread = frameData_[frame].steps; auto &initStepsOnThread = frameData_[frame].initSteps; // queueRunner_.LogSteps(stepsOnThread); @@ -429,6 +434,10 @@ void GLRenderManager::Run(int frame) { stepsOnThread.clear(); initStepsOnThread.clear(); + for (auto iter : frameData.activePushBuffers) { + iter->MapDevice(); + } + switch (frameData.type) { case GLRRunType::END: EndSubmitFrame(frame); @@ -446,11 +455,6 @@ void GLRenderManager::Run(int frame) { } void GLRenderManager::FlushSync() { - // Need to flush any pushbuffers to VRAM before submitting draw calls. - for (auto iter : frameData_[curFrame_].activePushBuffers) { - iter->Flush(); - } - // TODO: Reset curRenderStep_? int curFrame = curFrame_; FrameData &frameData = frameData_[curFrame]; @@ -532,42 +536,51 @@ GLPushBuffer::~GLPushBuffer() { void GLPushBuffer::Map() { assert(!writePtr_); - // TODO: Even a good old glMapBuffer could actually work well here. - writePtr_ = buffers_[buf_].deviceMemory; + auto &info = buffers_[buf_]; + writePtr_ = info.deviceMemory ? info.deviceMemory : info.localMemory; + // Force alignment. This is needed for PushAligned() to work as expected. + while ((intptr_t)writePtr_ & 15) { + writePtr_++; + } assert(writePtr_); } void GLPushBuffer::Unmap() { assert(writePtr_); - // Here we simply upload the data to the last buffer. - // Might be worth trying with size_ instead of offset_, so the driver can replace - // the whole buffer. At least if it's close. - render_->BufferSubdata(buffers_[buf_].buffer, 0, offset_, buffers_[buf_].deviceMemory, false); + if (!buffers_[buf_].deviceMemory) { + // Here we simply upload the data to the last buffer. + // Might be worth trying with size_ instead of offset_, so the driver can replace + // the whole buffer. At least if it's close. + render_->BufferSubdata(buffers_[buf_].buffer, 0, offset_, buffers_[buf_].localMemory, false); + } writePtr_ = nullptr; } void GLPushBuffer::Flush() { - render_->BufferSubdata(buffers_[buf_].buffer, 0, offset_, buffers_[buf_].deviceMemory, false); - // Here we will submit all the draw calls, with the already known buffer and offsets. - // Might as well reset the write pointer here and start over the current buffer. - writePtr_ = buffers_[buf_].deviceMemory; - offset_ = 0; + // We don't need to do this for device memory. + if (!buffers_[buf_].deviceMemory && writePtr_) { + render_->BufferSubdata(buffers_[buf_].buffer, 0, offset_, buffers_[buf_].localMemory, false); + // Here we will submit all the draw calls, with the already known buffer and offsets. + // Might as well reset the write pointer here and start over the current buffer. + writePtr_ = buffers_[buf_].localMemory; + offset_ = 0; + } } bool GLPushBuffer::AddBuffer() { BufInfo info; - info.deviceMemory = new uint8_t[size_]; + info.localMemory = new uint8_t[size_]; info.buffer = render_->CreateBuffer(target_, size_, GL_DYNAMIC_DRAW); buf_ = buffers_.size(); - buffers_.resize(buf_ + 1); - buffers_[buf_] = info; + buffers_.push_back(info); return true; } void GLPushBuffer::Destroy() { for (BufInfo &info : buffers_) { + // This will automatically unmap device memory, if needed. render_->DeleteBuffer(info.buffer); - delete[] info.deviceMemory; + delete[] info.localMemory; } buffers_.clear(); } @@ -617,3 +630,37 @@ size_t GLPushBuffer::GetTotalSize() const { sum += offset_; return sum; } + +void GLPushBuffer::MapDevice() { + for (auto &info : buffers_) { + assert(!info.deviceMemory); + // TODO: Can we use GL_WRITE_ONLY? + info.deviceMemory = (uint8_t *)info.buffer->Map(GL_READ_WRITE); + + if (info.deviceMemory) { + delete[] info.localMemory; + info.localMemory = nullptr; + } else if (!info.localMemory) { + // Somehow it failed, let's dodge crashing. + info.localMemory = new uint8_t[info.buffer->size_]; + } + + assert(info.localMemory || info.deviceMemory); + } + + if (writePtr_) { + // This can happen during a sync. Remap. + writePtr_ = nullptr; + Map(); + } +} + +void GLPushBuffer::UnmapDevice() { + for (auto &info : buffers_) { + if (info.deviceMemory) { + // TODO: Technically this can return false? + info.buffer->Unmap(); + info.deviceMemory = nullptr; + } + } +} diff --git a/ext/native/thin3d/GLRenderManager.h b/ext/native/thin3d/GLRenderManager.h index c3b473e605..bbf46c65f1 100644 --- a/ext/native/thin3d/GLRenderManager.h +++ b/ext/native/thin3d/GLRenderManager.h @@ -138,10 +138,30 @@ public: glDeleteBuffers(1, &buffer); } } + + void *Map(GLenum access) { + glBindBuffer(target_, buffer); + void *p = glMapBuffer(target_, access); + mapped_ = p != nullptr; + return p; + } + + bool Unmap() { + glBindBuffer(target_, buffer); + mapped_ = false; + return glUnmapBuffer(target_) == GL_TRUE; + } + + bool Mapped() { + return mapped_; + } + GLuint buffer = 0; GLuint target_; int size_; + private: + bool mapped_ = false; }; enum class GLRRunType { @@ -747,8 +767,9 @@ private: class GLPushBuffer { public: struct BufInfo { - GLRBuffer *buffer; - uint8_t *deviceMemory; + GLRBuffer *buffer = nullptr; + uint8_t *localMemory = nullptr; + uint8_t *deviceMemory = nullptr; }; public: @@ -835,6 +856,10 @@ public: void Flush(); +protected: + void MapDevice(); + void UnmapDevice(); + private: bool AddBuffer(); void NextBuffer(size_t minSize); @@ -847,4 +872,6 @@ private: size_t size_ = 0; uint8_t *writePtr_ = nullptr; GLuint target_; + + friend class GLRenderManager; };