#include "batch_merge_helper.hpp" #include "drape/glextensions_list.hpp" #include "drape/render_bucket.hpp" #include "drape/vertex_array_buffer.hpp" namespace df { void ReadBufferData(void * dst, glConst target, uint32_t size) { #ifdef OMIM_OS_DESKTOP void * bufferPointer = GLFunctions::glMapBuffer(target, gl_const::GLReadOnly); #else void * bufferPointer = GLFunctions::glMapBufferRange(target, 0, size, gl_const::GLReadBufferBit); #endif memcpy(dst, bufferPointer, size); } struct NotSupported32BitIndex{}; struct Supported32BitIndex{}; template void TransformIndeces(void * pointer, uint32_t count, uint32_t offset) { ASSERT(false, ()); } template<> void TransformIndeces(void * pointer, uint32_t count, uint32_t offset) { uint32_t * indexPtr = reinterpret_cast(pointer); for (uint32_t i = 0; i < count; ++i) { *indexPtr += offset; ++indexPtr; } } template<> void TransformIndeces(void * pointer, uint32_t count, uint32_t offset) { uint16_t * indexPtr = reinterpret_cast(pointer); uint16_t indexOffset = static_cast(offset); for (uint32_t i = 0; i < count; ++i) { *indexPtr += indexOffset; ++indexPtr; } } bool BatchMergeHelper::IsMergeSupported() { #if defined(OMIM_OS_DESKTOP) return true; #else static bool isSupported = dp::GLExtensionsList::Instance().IsSupported(dp::GLExtensionsList::MapBufferRange); return isSupported; #endif } void BatchMergeHelper::MergeBatches(vector> & batches, vector> & mergedBatches, bool isPerspective) { ASSERT(!batches.empty(), ()); if (batches.size() < 2) { mergedBatches.emplace_back(move(batches.front())); return; } uint32_t const kIndexBufferSize = 30000; uint32_t const kVertexBufferSize = 20000; using TBuffer = dp::VertexArrayBuffer; using TBucket = dp::RenderBucket; auto flushFn = [&](drape_ptr && bucket, ref_ptr buffer) { if (buffer->GetIndexCount() == 0) return; ref_ptr oldGroup = make_ref(batches.front()); drape_ptr newGroup = make_unique_dp(oldGroup->GetState(), oldGroup->GetTileKey()); newGroup->m_shader = oldGroup->m_shader; newGroup->m_shader3d = oldGroup->m_shader3d; newGroup->m_uniforms = oldGroup->m_uniforms; newGroup->m_generalUniforms = oldGroup->m_generalUniforms; newGroup->AddBucket(move(bucket)); buffer->Preflush(); if (isPerspective) newGroup->m_shader3d->Bind(); else newGroup->m_shader->Bind(); buffer->Build(isPerspective ? newGroup->m_shader3d : newGroup->m_shader); mergedBatches.push_back(move(newGroup)); }; auto allocateFn = [=](drape_ptr & bucket, ref_ptr & buffer) { bucket = make_unique_dp(make_unique_dp(kIndexBufferSize, kVertexBufferSize)); buffer = bucket->GetBuffer(); }; auto copyVertecesFn = [](TBuffer::BuffersMap::value_type const & vboNode, vector & rawDataBuffer, ref_ptr newBuffer) { dp::BindingInfo const & binding = vboNode.first; ref_ptr vbo = vboNode.second->GetBuffer(); uint32_t vertexCount = vbo->GetCurrentSize(); uint32_t bufferLength = vertexCount * vbo->GetElementSize(); if (rawDataBuffer.size() < bufferLength) rawDataBuffer.resize(bufferLength); vbo->Bind(); ReadBufferData(rawDataBuffer.data(), gl_const::GLArrayBuffer, bufferLength); GLFunctions::glUnmapBuffer(gl_const::GLArrayBuffer); newBuffer->UploadData(binding, rawDataBuffer.data(), vertexCount); }; drape_ptr bucket; ref_ptr newBuffer; allocateFn(bucket, newBuffer); vector rawDataBuffer; for (drape_ptr const & group : batches) { for (drape_ptr const & b : group->m_renderBuckets) { ASSERT(b->m_overlay.empty(), ()); ref_ptr buffer = b->GetBuffer(); uint32_t vertexCount = buffer->GetStartIndexValue(); uint32_t indexCount = buffer->GetIndexCount(); if (newBuffer->GetAvailableIndexCount() < vertexCount || newBuffer->GetAvailableVertexCount() < indexCount) { flushFn(move(bucket), newBuffer); allocateFn(bucket, newBuffer); } bucket->SetFeatureMinZoom(b->GetMinZoom()); uint32_t indexOffset = newBuffer->GetStartIndexValue(); for (auto const & vboNode : buffer->m_staticBuffers) { copyVertecesFn(vboNode, rawDataBuffer, newBuffer); } for (auto const & vboNode : buffer->m_dynamicBuffers) { copyVertecesFn(vboNode, rawDataBuffer, newBuffer); } uint32_t indexByteCount = indexCount * dp::IndexStorage::SizeOfIndex(); if (rawDataBuffer.size() < indexByteCount) rawDataBuffer.resize(indexByteCount); buffer->m_indexBuffer->GetBuffer()->Bind(); ReadBufferData(rawDataBuffer.data(), gl_const::GLElementArrayBuffer, indexByteCount); GLFunctions::glUnmapBuffer(gl_const::GLElementArrayBuffer); if (dp::IndexStorage::IsSupported32bit()) TransformIndeces(rawDataBuffer.data(), indexCount, indexOffset); else TransformIndeces(rawDataBuffer.data(), indexCount, indexOffset); newBuffer->UploadIndexes(rawDataBuffer.data(), indexCount); } } if (newBuffer->GetIndexCount() > 0) { flushFn(move(bucket), newBuffer); allocateFn(bucket, newBuffer); } } }