diff options
Diffstat (limited to 'Client/Source/Graphics/GPUDataBuffer.cpp')
-rw-r--r-- | Client/Source/Graphics/GPUDataBuffer.cpp | 207 |
1 files changed, 207 insertions, 0 deletions
diff --git a/Client/Source/Graphics/GPUDataBuffer.cpp b/Client/Source/Graphics/GPUDataBuffer.cpp new file mode 100644 index 0000000..f5149d9 --- /dev/null +++ b/Client/Source/Graphics/GPUDataBuffer.cpp @@ -0,0 +1,207 @@ +#include <math.h> + +#include "GPUDataBuffer.h" + +namespace GPU +{ + + // 修改buffer数据要绑定到这个目标上,其他情况下用GetHandle()绑定到其他目标 + // GL_COPY_READ_BUFFER + static const GLenum kBufferTarget = GL_COPY_WRITE_BUFFER; + + DataBuffer::DataBuffer() + { + glGenBuffers(1, &m_Handle); + m_Size = 0; + } + + DataBuffer::~DataBuffer() + { + glDeleteBuffers(1, &m_Handle); + } + + void DataBuffer::Restore(int size, GLenum usage) + { + RestoreWithData(size, usage, 0); + } + + void DataBuffer::RestoreWithData(int size, GLenum usage, const void* data) + { + glBindBuffer(kBufferTarget, m_Handle); + glBufferData(kBufferTarget, size, data, usage); + glBindBuffer(kBufferTarget, 0); + m_Size = size; + m_Usage = usage; + } + + // glBufferSubData + // glMapBuffer + // glMapBufferRange (best one) + + void DataBuffer::Upload(int offset, int size, const void* data) + { + glBindBuffer(kBufferTarget, m_Handle); + glBufferSubData(kBufferTarget, offset, size, data); + glBindBuffer(kBufferTarget, 0); + } + + void* DataBuffer::Map(uint32 access) + { + glBindBuffer(kBufferTarget, m_Handle); + void* ptr = glMapBuffer(kBufferTarget, access); + glBindBuffer(kBufferTarget, 0); + return ptr; + } + + void* DataBuffer::MapRange(int offset, int size, uint32 access) + { + glBindBuffer(kBufferTarget, m_Handle); + void* ptr = glMapBufferRange(kBufferTarget, offset, size, access); + glBindBuffer(kBufferTarget, 0); + return ptr; + } + + void DataBuffer::FlushMapedRange(int offset, int size) + { + glBindBuffer(kBufferTarget, m_Handle); + glFlushMappedBufferRange(kBufferTarget, offset, size); + glBindBuffer(kBufferTarget, 0); + } + + void DataBuffer::UnMap() + { + glBindBuffer(kBufferTarget, m_Handle); + glUnmapBuffer(kBufferTarget); + glBindBuffer(kBufferTarget, 0); + } + + void DataBuffer::Orphan() + { + glBindBuffer(kBufferTarget, m_Handle); + glBufferData(kBufferTarget, 0, 0, GL_STREAM_DRAW); + glBindBuffer(kBufferTarget, 0); + } + +//--------------------------------------------------------------------------------------- + + static bool IsBufferPoolCreated = false; + + BufferPool::BufferPool() + :m_LiveBuffers() + { + Assert(!IsBufferPoolCreated); + IsBufferPoolCreated = true; + } + + BufferPool::~BufferPool() + { + } + + 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 int ComputeBufferWeight(DataBuffer* buffer, int desiredSize, GLenum desiredUsage) + { + const int bufferSize = buffer->GetSize(); + const GLenum bufferUsage = buffer->GetUsage(); + + if (bufferSize == 0) + return kBufferAllocateWeight; + + const int sizeDiff = std::abs(bufferSize - desiredSize); + + return float(sizeDiff)*kBufferSizeWeightFactor + ((bufferUsage != desiredUsage) ? kBufferUsageDiffWeight : 0.f); + } + + DataBuffer* BufferPool::ClaimBuffer(int size, GLenum usage) + { + const float maxWeight = kBufferAllocateWeight; + const int maxCandidates = 5; // Number of potential candidates to consider actually. + + const int sizeClass = GetSizeClass(size); + int numCandidates = 0; // Number of potential candidates considered + int bestBufferNdx = -1; + float bestWeight = std::numeric_limits<float>::infinity(); + + for (int idx = 0; idx < m_LiveBuffers[sizeClass].size(); ++idx) + { + DataBuffer* buffer = m_LiveBuffers[sizeClass][idx]; + const float weight = ComputeBufferWeight(buffer, size, usage); + + if (weight < maxWeight && weight < bestWeight) + { + bestWeight = weight; + bestBufferNdx = idx; + ++numCandidates; + } + + if (numCandidates >= maxCandidates) + break; // Do not try other buffers, sorry. + } + + if (bestBufferNdx >= 0) + { + DataBuffer* selectedBuffer = m_LiveBuffers[sizeClass][bestBufferNdx]; + + if (bestBufferNdx + 1 != m_LiveBuffers[sizeClass].size()) + std::swap(m_LiveBuffers[sizeClass][bestBufferNdx], m_LiveBuffers[sizeClass].back()); + m_LiveBuffers[sizeClass].pop_back(); + + return selectedBuffer; + } + else + return new DataBuffer(); + + } + + void BufferPool::ReleaseBuffer(DataBuffer* buffer) + { + InsertToLive(buffer); + } + + void BufferPool::OnEndFrame() + { + UpdatePendingBuffersArray(); + } + + void BufferPool::UpdatePendingBuffersArray() + { + } + + void BufferPool::InsertToLive(DataBuffer* buffer) + { + const int bufferSize = buffer->GetSize(); + const int sizeClass = GetSizeClass(bufferSize); + + m_LiveBuffers[sizeClass].push_back(buffer); + } + + uint BufferPool::GetSizeClass(uint bufferSize) + { + for (int idx = 0; idx < kSizeClassCount; ++idx) + { + if (bufferSize < GetSizeClassLimit(idx)) + return idx; + } + Assert(false); + return 0; + } + + DataBuffer* ClaimBuffer(int size, GLenum usage) + { + return BufferPool::Instance()->ClaimBuffer(size, usage); + } + + void ReleaseBuffer(DataBuffer* buffer) + { + BufferPool::Instance()->ReleaseBuffer(buffer); + } + + int BufferPool::GetSizeClassLimit(int classNdx) + { + // (0, 2^10] 2^11 2^12 2^13 2^14 2^15 INT_MAX + return classNdx + 1 < kSizeClassCount ? (1 << (classNdx*kSizeClassStepLog2 + kSizeClassBaseLog2)) : INT_MAX; + } + +} |