summaryrefslogtreecommitdiff
path: root/Runtime/GfxDevice/opengles30/DataBuffersGLES30.cpp
diff options
context:
space:
mode:
authorchai <chaifix@163.com>2019-08-14 22:50:43 +0800
committerchai <chaifix@163.com>2019-08-14 22:50:43 +0800
commit15740faf9fe9fe4be08965098bbf2947e096aeeb (patch)
treea730ec236656cc8cab5b13f088adfaed6bb218fb /Runtime/GfxDevice/opengles30/DataBuffersGLES30.cpp
+Unity Runtime codeHEADmaster
Diffstat (limited to 'Runtime/GfxDevice/opengles30/DataBuffersGLES30.cpp')
-rw-r--r--Runtime/GfxDevice/opengles30/DataBuffersGLES30.cpp429
1 files changed, 429 insertions, 0 deletions
diff --git a/Runtime/GfxDevice/opengles30/DataBuffersGLES30.cpp b/Runtime/GfxDevice/opengles30/DataBuffersGLES30.cpp
new file mode 100644
index 0000000..24484d7
--- /dev/null
+++ b/Runtime/GfxDevice/opengles30/DataBuffersGLES30.cpp
@@ -0,0 +1,429 @@
+#include "UnityPrefix.h"
+#include "DataBuffersGLES30.h"
+
+#if GFX_SUPPORTS_OPENGLES30
+
+#include "AssertGLES30.h"
+#include "IncludesGLES30.h"
+#include "Runtime/GfxDevice/GLDataBufferCommon.h"
+
+#include <algorithm>
+
+#if 1
+ #define DBG_LOG_BUF_GLES30(...) {}
+#else
+ #define DBG_LOG_BUF_GLES30(...) {printf_console(__VA_ARGS__);printf_console("\n");}
+#endif
+
+static const float kBufferAllocateWeight = 10.0f; //! Default weight for buffer allocation from scratch.
+static const float kBufferSizeWeightFactor = 1.f/8096.f; //!< Weight factor for size difference.
+static const float kBufferUsageDiffWeight = 8.f; //!< Weight factor for usage difference.
+
+static const float kBufferDeleteSizeWeightFactor = 1.f/16384.f; //!< Deleting scale factor.
+static const float kBufferDeleteMaxWeightFactor = 300.0f; //!< ...
+static const float kBufferDeleteMinWeight = 30.0f; //!< Required weight for deleting buffer.
+
+static const UInt32 kBufferPruneFrequency = 10; //!< Unneeded buffers are pruned once per kBufferPruneFrequency frames.
+
+bool BufferUpdateCausesStallGLES30 (const DataBufferGLES30* buffer)
+{
+ // \note If needed, the min age should me made ES3 specific graphics capability.
+ return buffer->GetRenderAge() < kBufferUpdateMinAgeGLES30;
+}
+
+//! Computes weight for sorting buffers
+static float ComputeBufferWeight (const DataBufferGLES30* buffer, int desiredSize, UInt32 desiredUsage)
+{
+ const int bufferSize = buffer->GetSize();
+ const UInt32 bufferUsage = buffer->GetUsage();
+
+ if (buffer->GetSize() == 0)
+ return kBufferAllocateWeight;
+
+ const int sizeDiff = std::abs(bufferSize-desiredSize);
+
+ return float(sizeDiff)*kBufferSizeWeightFactor + ((bufferUsage != desiredUsage) ? kBufferUsageDiffWeight : 0.f);
+}
+
+//! Compute weight for eliminating old buffers.
+static float ComputeBufferDeleteWeight (const DataBufferGLES30* buffer)
+{
+ // \todo [2013-05-13 pyry] Take into account current memory pressure and buffer size.
+ const UInt32 renderAge = buffer->GetRenderAge();
+ return float(renderAge) - std::min(kBufferDeleteMaxWeightFactor, float(buffer->GetSize())*kBufferDeleteSizeWeightFactor);
+}
+
+struct WeightedBufferGLES3
+{
+ int sizeClass;
+ int bufferNdx;
+ float weight;
+
+ WeightedBufferGLES3 (int sizeClass_, int bufferNdx_, float weight_)
+ : sizeClass (sizeClass_)
+ , bufferNdx (bufferNdx_)
+ , weight (weight_)
+ {
+ }
+};
+
+struct CompareWeightsLess
+{
+ inline bool operator() (const WeightedBufferGLES3& a, const WeightedBufferGLES3& b) const
+ {
+ return a.weight < b.weight;
+ }
+};
+
+struct CompareWeightsGreater
+{
+ inline bool operator() (const WeightedBufferGLES3& a, const WeightedBufferGLES3& b) const
+ {
+ return a.weight > b.weight;
+ }
+};
+
+// DataBufferGLES30
+
+static const GLenum kBufferTarget = GL_COPY_WRITE_BUFFER; //!< Target used for buffer operations.
+
+DataBufferGLES30::DataBufferGLES30 (BufferManagerGLES30& bufferManager)
+ : m_manager (bufferManager)
+ , m_buffer (0)
+ , m_size (0)
+ , m_usage (0)
+ , m_lastRecreated (0)
+ , m_lastUpdated (0)
+ , m_lastRendered (0)
+{
+ GLES_CHK(glGenBuffers(1, (GLuint*)&m_buffer));
+}
+
+DataBufferGLES30::~DataBufferGLES30 (void)
+{
+ if (m_buffer)
+ glDeleteBuffers(1, (GLuint*)&m_buffer);
+}
+
+void DataBufferGLES30::Disown (void)
+{
+ m_buffer = 0;
+}
+
+void DataBufferGLES30::Release (void)
+{
+ m_manager.ReleaseBuffer(this);
+}
+
+void DataBufferGLES30::RecreateStorage (int size, UInt32 usage)
+{
+ RecreateWithData(size, usage, 0);
+}
+
+void DataBufferGLES30::RecreateWithData (int size, UInt32 usage, const void* data)
+{
+ glBindBuffer(kBufferTarget, m_buffer);
+ glBufferData(kBufferTarget, size, data, usage);
+ glBindBuffer(kBufferTarget, 0);
+ GLESAssert();
+
+ RecordRecreate(size, usage);
+}
+
+void DataBufferGLES30::Upload (int offset, int size, const void* data)
+{
+ glBindBuffer(kBufferTarget, m_buffer);
+ glBufferSubData(kBufferTarget, offset, size, data);
+ glBindBuffer(kBufferTarget, 0);
+ GLESAssert();
+
+ RecordUpdate();
+}
+
+void* DataBufferGLES30::Map (int offset, int size, UInt32 mapBits)
+{
+ glBindBuffer(kBufferTarget, m_buffer);
+ void* ptr = glMapBufferRange(kBufferTarget, offset, size, mapBits);
+ glBindBuffer(kBufferTarget, 0);
+ GLESAssert();
+
+ return ptr;
+}
+
+void DataBufferGLES30::FlushMappedRange (int offset, int size)
+{
+ glBindBuffer(kBufferTarget, m_buffer);
+ glFlushMappedBufferRange(kBufferTarget, offset, size);
+ glBindBuffer(kBufferTarget, 0);
+ GLESAssert();
+}
+
+void DataBufferGLES30::Unmap (void)
+{
+ glBindBuffer(kBufferTarget, m_buffer);
+ glUnmapBuffer(kBufferTarget);
+ glBindBuffer(kBufferTarget, 0);
+ GLESAssert();
+}
+
+void DataBufferGLES30::RecordRecreate (int size, UInt32 usage)
+{
+ if (BufferUpdateCausesStallGLES30(this))
+ DBG_LOG_BUF_GLES30("DataBufferGLES30: Warning: buffer with render age %u was recreated!", GetRenderAge());
+
+ m_size = size;
+ m_usage = usage;
+ m_lastRecreated = m_manager.GetFrameIndex();
+
+ // Update GFX mem allocation stats
+ REGISTER_EXTERNAL_GFX_DEALLOCATION(MAKE_DATA_BUFFER_ID(m_buffer));
+ REGISTER_EXTERNAL_GFX_ALLOCATION_REF(MAKE_DATA_BUFFER_ID(m_buffer), size, this);
+}
+
+void DataBufferGLES30::RecordUpdate (void)
+{
+ if (BufferUpdateCausesStallGLES30(this))
+ DBG_LOG_BUF_GLES30("DataBufferGLES30: Warning: buffer with render age %u was updated!", GetRenderAge());
+
+ m_lastUpdated = m_manager.GetFrameIndex();
+}
+
+void DataBufferGLES30::RecordRender (void)
+{
+ m_lastRendered = m_manager.GetFrameIndex();
+}
+
+// \note Overflow is perfectly ok here.
+
+UInt32 DataBufferGLES30::GetRecreateAge (void) const
+{
+ return m_manager.GetFrameIndex() - m_lastRecreated;
+}
+
+UInt32 DataBufferGLES30::GetUpdateAge (void) const
+{
+ return m_manager.GetFrameIndex() - m_lastUpdated;
+}
+
+UInt32 DataBufferGLES30::GetRenderAge (void) const
+{
+ return m_manager.GetFrameIndex() - m_lastRendered;
+}
+
+// BufferManagerGLES30
+
+BufferManagerGLES30::BufferManagerGLES30 (void)
+ : m_frameIndex(0)
+{
+}
+
+BufferManagerGLES30::~BufferManagerGLES30 (void)
+{
+ Clear();
+}
+
+void BufferManagerGLES30::Clear (void)
+{
+ for (std::vector<DataBufferGLES30*>::iterator i = m_pendingBuffers.begin(); i != m_pendingBuffers.end(); i++)
+ delete *i;
+ m_pendingBuffers.clear();
+
+ for (int ndx = 0; ndx < kSizeClassCount; ndx++)
+ {
+ for (std::vector<DataBufferGLES30*>::iterator i = m_liveBuffers[ndx].begin(); i != m_liveBuffers[ndx].end(); i++)
+ delete *i;
+ m_liveBuffers[ndx].clear();
+ }
+}
+
+inline int BufferManagerGLES30::GetSizeClass (int size)
+{
+ for (int ndx = 0; ndx < kSizeClassCount; ndx++)
+ {
+ if (size < GetSizeClassLimit(ndx))
+ return ndx;
+ }
+ Assert(false);
+ return 0;
+}
+
+DataBufferGLES30* BufferManagerGLES30::AcquireBuffer (int size, UInt32 usage)
+{
+ const float maxWeight = kBufferAllocateWeight;
+ const int maxCandidates = 5; // Number of potential candidates to consider actually.
+
+ const int sizeClass = GetSizeClass(size); // Try only that size class
+ int numCandidates = 0; // Number of potential candidates considered
+ int bestBufferNdx = -1;
+ float bestWeight = std::numeric_limits<float>::infinity();
+
+ for (int bufferNdx = 0; bufferNdx < (int)m_liveBuffers[sizeClass].size(); bufferNdx++)
+ {
+ DataBufferGLES30* buffer = m_liveBuffers[sizeClass][bufferNdx];
+ const float weight = ComputeBufferWeight(buffer, size, usage);
+
+ if (weight <= maxWeight && weight < bestWeight)
+ {
+ bestBufferNdx = bufferNdx;
+ bestWeight = weight;
+
+ numCandidates += 1;
+ }
+
+ if (numCandidates >= maxCandidates)
+ break; // Do not try other buffers, sorry.
+ }
+
+ DBG_LOG_BUF_GLES30("BufferManagerGLES30::AcquireBuffer(%d, 0x%04x): tried %d candidates", size, usage, numCandidates);
+
+ if (bestBufferNdx >= 0)
+ {
+ const int bufferNdx = bestBufferNdx;
+ DataBufferGLES30* selectedBuffer = m_liveBuffers[sizeClass][bufferNdx];
+
+ if (bufferNdx+1 != m_liveBuffers[sizeClass].size())
+ std::swap(m_liveBuffers[sizeClass][bufferNdx], m_liveBuffers[sizeClass].back());
+ m_liveBuffers[sizeClass].pop_back();
+
+ DBG_LOG_BUF_GLES30(" => selected buffer [%d]%d, weight = %f", sizeClass, bufferNdx, ComputeBufferWeight(selectedBuffer, size, usage));
+ return selectedBuffer;
+ }
+ else
+ {
+ DBG_LOG_BUF_GLES30(" => creating new buffer");
+ return new DataBufferGLES30(*this);
+ }
+}
+
+void BufferManagerGLES30::ReleaseBuffer (DataBufferGLES30* buffer)
+{
+ if (!BufferUpdateCausesStallGLES30(buffer))
+ InsertIntoLive(buffer);
+ else
+ m_pendingBuffers.push_back(buffer);
+}
+
+void BufferManagerGLES30::AdvanceFrame (void)
+{
+ m_frameIndex += 1; // \note Overflow is ok.
+
+ UpdateLiveSetFromPending();
+
+ // \todo [2013-05-13 pyry] Do we want to do pruning somewhere else as well?
+ if ((m_frameIndex % kBufferPruneFrequency) == 0)
+ PruneFreeBuffers();
+}
+
+void BufferManagerGLES30::UpdateLiveSetFromPending (void)
+{
+ int bufNdx = 0;
+ while (bufNdx < (int)m_pendingBuffers.size())
+ {
+ if (!BufferUpdateCausesStallGLES30(m_pendingBuffers[bufNdx]))
+ {
+ DataBufferGLES30* newLiveBuffer = m_pendingBuffers[bufNdx];
+
+ if (bufNdx+1 != m_pendingBuffers.size())
+ std::swap(m_pendingBuffers[bufNdx], m_pendingBuffers.back());
+ m_pendingBuffers.pop_back();
+
+ InsertIntoLive(newLiveBuffer);
+ // \note bufNdx now contains a new buffer and it must be processed as well. Thus bufNdx is not incremented.
+ }
+ else
+ bufNdx += 1;
+ }
+}
+
+void BufferManagerGLES30::InsertIntoLive (DataBufferGLES30* buffer)
+{
+ const int bufSize = buffer->GetSize();
+ const int sizeClass = GetSizeClass(bufSize);
+
+ m_liveBuffers[sizeClass].push_back(buffer);
+}
+
+UInt32 BufferManagerGLES30::GetTotalFreeSize (void)
+{
+ UInt32 totalBytes = 0;
+
+ for (std::vector<DataBufferGLES30*>::const_iterator bufIter = m_pendingBuffers.begin(); bufIter != m_pendingBuffers.end(); ++bufIter)
+ totalBytes += (*bufIter)->GetSize();
+
+ for (int ndx = 0; ndx < kSizeClassCount; ndx++)
+ {
+ for (std::vector<DataBufferGLES30*>::const_iterator bufIter = m_liveBuffers[ndx].begin(); bufIter != m_liveBuffers[ndx].end(); ++bufIter)
+ totalBytes += (*bufIter)->GetSize();
+ }
+
+ return totalBytes;
+}
+
+void BufferManagerGLES30::PruneFreeBuffers (void)
+{
+ const UInt32 numBytesInFreeList = GetTotalFreeSize();
+ DBG_LOG_BUF_GLES30("BufferManagerGLES30: %u B / %.2f MiB in free buffers", numBytesInFreeList, float(numBytesInFreeList) / float(1<<20));
+
+ // \todo [2013-05-13 pyry] Do this properly - take into account allocated memory size.
+
+ UInt32 numBytesFreed = 0;
+ int numBuffersDeleted = 0;
+
+ // \note pending buffers are ignored. They will end up in live soon anyway.
+ for (int sizeClass = 0; sizeClass < kSizeClassCount; sizeClass++)
+ {
+ int bufNdx = 0;
+ while (bufNdx < m_liveBuffers[sizeClass].size())
+ {
+ DataBufferGLES30* buffer = m_liveBuffers[sizeClass][bufNdx];
+ const float weight = ComputeBufferDeleteWeight(buffer);
+
+ if (weight >= kBufferDeleteMinWeight)
+ {
+ if (bufNdx+1 != m_liveBuffers[sizeClass].size())
+ std::swap(m_liveBuffers[sizeClass][bufNdx], m_liveBuffers[sizeClass].back());
+ m_liveBuffers[sizeClass].pop_back();
+
+ numBytesFreed += buffer->GetSize();
+ numBuffersDeleted += 1;
+
+ delete buffer;
+ }
+ else
+ bufNdx += 1;
+ }
+ }
+
+ DBG_LOG_BUF_GLES30(" => freed %d buffers, %u B / %.2f MiB", numBuffersDeleted, numBytesFreed, float(numBytesFreed) / float(1<<20));
+}
+
+void BufferManagerGLES30::InvalidateAll (void)
+{
+ for (std::vector<DataBufferGLES30*>::iterator iter = m_pendingBuffers.begin(); iter != m_pendingBuffers.end(); ++iter)
+ {
+ (*iter)->Disown();
+ delete *iter;
+ }
+ m_pendingBuffers.clear();
+
+ for (int classNdx = 0; classNdx < kSizeClassCount; classNdx++)
+ {
+ for (std::vector<DataBufferGLES30*>::iterator iter = m_liveBuffers[classNdx].begin(); iter != m_liveBuffers[classNdx].end(); ++iter)
+ {
+ (*iter)->Disown();
+ delete *iter;
+ }
+ m_liveBuffers[classNdx].clear();
+ }
+}
+
+BufferManagerGLES30* g_bufferManager = 0;
+
+BufferManagerGLES30* GetBufferManagerGLES30 (void)
+{
+ if (!g_bufferManager)
+ g_bufferManager = new BufferManagerGLES30();
+ return g_bufferManager;
+}
+
+#endif // GFX_SUPPORTS_OPENGLES30