From 79f441499b42703d2a666edb188052468ceed5c9 Mon Sep 17 00:00:00 2001 From: ExMix Date: Sat, 19 Dec 2015 21:32:29 +0300 Subject: [PATCH] [drape] merge batches on iOS and desktop --- drape/drape_tests/glfunctions.cpp | 2 +- drape/glconstants.cpp | 50 ++++++++ drape/glconstants.hpp | 9 ++ drape/glextensions_list.cpp | 3 + drape/glextensions_list.hpp | 1 + drape/glfunctions.cpp | 24 +++- drape/glfunctions.hpp | 5 +- drape/glstate.cpp | 2 + drape/render_bucket.hpp | 6 + drape/vertex_array_buffer.hpp | 6 + drape_frontend/batch_merge_helper.cpp | 178 ++++++++++++++++++++++++++ drape_frontend/batch_merge_helper.hpp | 23 ++++ drape_frontend/drape_frontend.pro | 2 + drape_frontend/frontend_renderer.cpp | 45 +++++++ drape_frontend/frontend_renderer.hpp | 2 + drape_frontend/render_group.hpp | 1 + 16 files changed, 355 insertions(+), 4 deletions(-) create mode 100644 drape_frontend/batch_merge_helper.cpp create mode 100644 drape_frontend/batch_merge_helper.hpp diff --git a/drape/drape_tests/glfunctions.cpp b/drape/drape_tests/glfunctions.cpp index 86d48a20ed..88710a6086 100644 --- a/drape/drape_tests/glfunctions.cpp +++ b/drape/drape_tests/glfunctions.cpp @@ -258,7 +258,7 @@ void GLFunctions::glDisable(glConst mode) {} void GLFunctions::glUniformValueiv(int8_t location, int32_t * v, uint32_t size) {} -void * GLFunctions::glMapBuffer(glConst target) { return 0; } +void * GLFunctions::glMapBuffer(glConst, glConst) { return 0; } void GLFunctions::glUnmapBuffer(glConst target) {} diff --git a/drape/glconstants.cpp b/drape/glconstants.cpp index c83610bf51..7252777a76 100644 --- a/drape/glconstants.cpp +++ b/drape/glconstants.cpp @@ -33,6 +33,48 @@ #define WRITE_ONLY_DEF 0x88B9 #endif +#if defined(GL_READ_ONLY) + #define READ_ONLY_DEF GL_READ_ONLY +#else + #define READ_ONLY_DEF 0x88B8 +#endif + +#if defined(GL_MAP_READ_BIT_EXT) + #define READ_BIT_DEF GL_MAP_READ_BIT_EXT +#else + #define READ_BIT_DEF 0x0001 +#endif + +#if defined(GL_MAP_WRITE_BIT_EXT) + #define WRITE_BIT_DEF GL_MAP_WRITE_BIT_EXT +#else + #define WRITE_BIT_DEF 0x0002 +#endif + +#if defined(GL_MAP_INVALIDATE_RANGE_BIT_EXT) + #define INVALIDATE_RANGE_BIT_DEF GL_MAP_INVALIDATE_RANGE_BIT_EXT +#else + #define INVALIDATE_RANGE_BIT_DEF 0x0004 +#endif + +#if defined(GL_MAP_INVALIDATE_BUFFER_BIT_EXT) + #define INVALIDATE_BUFFER_BIT_DEF GL_MAP_INVALIDATE_BUFFER_BIT_EXT +#else + #define INVALIDATE_BUFFER_BIT_DEF 0x0008 +#endif + +#if defined(GL_MAP_FLUSH_EXPLICIT_BIT_EXT) + #define FLUSH_EXPLICIT_BIT_DEF GL_MAP_FLUSH_EXPLICIT_BIT_EXT +#else + #define FLUSH_EXPLICIT_BIT_DEF 0x0010 +#endif + +#if defined(GL_MAP_UNSYNCHRONIZED_BIT_EXT) + #define UNSYNCHRONIZED_BIT_DEF GL_MAP_UNSYNCHRONIZED_BIT_EXT +#else + #define UNSYNCHRONIZED_BIT_DEF 0x0020 +#endif + namespace gl_const { @@ -53,6 +95,14 @@ const glConst GLBufferSize = GL_BUFFER_SIZE; const glConst GLBufferUsage = GL_BUFFER_USAGE; const glConst GLWriteOnly = WRITE_ONLY_DEF; +const glConst GLReadOnly = READ_ONLY_DEF; + +const glConst GLReadBufferBit = READ_BIT_DEF; +const glConst GLWriteBufferBit = WRITE_BIT_DEF; +const glConst GLInvalidateRange = INVALIDATE_RANGE_BIT_DEF; +const glConst GLInvalidateBuffer = INVALIDATE_BUFFER_BIT_DEF; +const glConst GLFlushExplicit = FLUSH_EXPLICIT_BIT_DEF; +const glConst GLUnsynchronized = UNSYNCHRONIZED_BIT_DEF; const glConst GLStaticDraw = GL_STATIC_DRAW; const glConst GLStreamDraw = GL_STREAM_DRAW; diff --git a/drape/glconstants.hpp b/drape/glconstants.hpp index 5946778ae3..f0cdb684a6 100644 --- a/drape/glconstants.hpp +++ b/drape/glconstants.hpp @@ -27,6 +27,15 @@ extern const glConst GLBufferUsage; /// VBO Access extern const glConst GLWriteOnly; +extern const glConst GLReadOnly; + +/// MapBufferRange +extern const glConst GLWriteBufferBit; +extern const glConst GLReadBufferBit; +extern const glConst GLInvalidateRange; +extern const glConst GLInvalidateBuffer; +extern const glConst GLFlushExplicit; +extern const glConst GLUnsynchronized; /// BufferUsage extern const glConst GLStaticDraw; diff --git a/drape/glextensions_list.cpp b/drape/glextensions_list.cpp index f34b629d62..b9647ebefd 100644 --- a/drape/glextensions_list.cpp +++ b/drape/glextensions_list.cpp @@ -86,17 +86,20 @@ GLExtensionsList::GLExtensionsList() m_impl->CheckExtension(TextureNPOT, "GL_OES_texture_npot"); m_impl->CheckExtension(MapBuffer, "GL_OES_mapbuffer"); m_impl->CheckExtension(UintIndices, "GL_OES_element_index_uint"); + m_impl->CheckExtension(MapBufferRange, "GL_EXT_map_buffer_range"); #elif defined(OMIM_OS_WINDOWS) m_impl->CheckExtension(TextureNPOT, "GL_ARB_texture_non_power_of_two"); m_impl->SetExtension(VertexArrayObject, false); m_impl->SetExtension(RequiredInternalFormat, false); m_impl->SetExtension(MapBuffer, true); + m_impl->SetExtension(MapBufferRange, false); m_impl->SetExtension(UintIndices, true); #else m_impl->CheckExtension(VertexArrayObject, "GL_APPLE_vertex_array_object"); m_impl->CheckExtension(TextureNPOT, "GL_ARB_texture_non_power_of_two"); m_impl->SetExtension(RequiredInternalFormat, false); m_impl->SetExtension(MapBuffer, true); + m_impl->SetExtension(MapBufferRange, false); m_impl->SetExtension(UintIndices, true); #endif } diff --git a/drape/glextensions_list.hpp b/drape/glextensions_list.hpp index 623e8e55dc..d5ffc143ed 100644 --- a/drape/glextensions_list.hpp +++ b/drape/glextensions_list.hpp @@ -15,6 +15,7 @@ public: RequiredInternalFormat, MapBuffer, UintIndices, + MapBufferRange }; static GLExtensionsList & Instance(); diff --git a/drape/glfunctions.cpp b/drape/glfunctions.cpp index ab85aff281..2cf6ab83a1 100644 --- a/drape/glfunctions.cpp +++ b/drape/glfunctions.cpp @@ -56,6 +56,8 @@ typedef void (DP_APIENTRY *TglBufferDataFn)(GLenum target, GLsizeiptr size, GLvo typedef void (DP_APIENTRY *TglBufferSubDataFn)(GLenum target, GLintptr offset, GLsizeiptr size, GLvoid const * data); typedef void * (DP_APIENTRY *TglMapBufferFn)(GLenum target, GLenum access); typedef GLboolean(DP_APIENTRY *TglUnmapBufferFn)(GLenum target); +typedef void * (DP_APIENTRY *TglMapBufferRangeFn)(GLenum target, GLintptr offset, GLsizeiptr length, GLbitfield access); +typedef void (DP_APIENTRY *TglFlushMappedBufferRangeFn)(GLenum target, GLintptr offset, GLsizeiptr length); typedef GLuint(DP_APIENTRY *TglCreateShaderFn)(GLenum type); typedef void (DP_APIENTRY *TglShaderSourceFn)(GLuint shaderID, GLsizei count, GLchar const ** string, GLint const * length); @@ -122,6 +124,8 @@ TglBufferDataFn glBufferDataFn = nullptr; TglBufferSubDataFn glBufferSubDataFn = nullptr; TglMapBufferFn glMapBufferFn = nullptr; TglUnmapBufferFn glUnmapBufferFn = nullptr; +TglMapBufferRangeFn glMapBufferRangeFn = nullptr; +TglFlushMappedBufferRangeFn glFlushMappedBufferRangeFn = nullptr; /// Shaders TglCreateShaderFn glCreateShaderFn = nullptr; @@ -383,6 +387,8 @@ void GLFunctions::Init() glDeleteVertexArrayFn = &glDeleteVertexArraysOES; glMapBufferFn = &::glMapBufferOES; glUnmapBufferFn = &::glUnmapBufferOES; + glMapBufferRangeFn = &::glMapBufferRangeEXT; + glFlushMappedBufferRangeFn = &::glFlushMappedBufferRangeEXT; #elif defined(OMIM_OS_WINDOWS) if (dp::GLExtensionsList::Instance().IsSupported(dp::GLExtensionsList::VertexArrayObject)) { @@ -667,10 +673,10 @@ void GLFunctions::glBufferSubData(glConst target, uint32_t size, void const * da GLCHECK(glBufferSubDataFn(target, offset, size, data)); } -void * GLFunctions::glMapBuffer(glConst target) +void * GLFunctions::glMapBuffer(glConst target, glConst access) { ASSERT(glMapBufferFn != nullptr, ()); - void * result = glMapBufferFn(target, gl_const::GLWriteOnly); + void * result = glMapBufferFn(target, access); GLCHECKCALL(); return result; } @@ -682,6 +688,20 @@ void GLFunctions::glUnmapBuffer(glConst target) GLCHECKCALL(); } +void * GLFunctions::glMapBufferRange(glConst target, uint32_t offset, uint32_t length, glConst access) +{ + ASSERT(glMapBufferRangeFn != nullptr, ()); + void * result = glMapBufferRangeFn(target, offset, length, access); + GLCHECKCALL(); + return result; +} + +void GLFunctions::glFlushMappedBufferRange(glConst target, uint32_t offset, uint32_t length) +{ + ASSERT(glFlushMappedBufferRangeFn != nullptr, ()); + GLCHECK(glFlushMappedBufferRangeFn(target, offset, length)); +} + uint32_t GLFunctions::glCreateShader(glConst type) { ASSERT(glCreateShaderFn != nullptr, ()); diff --git a/drape/glfunctions.hpp b/drape/glfunctions.hpp index 95c2232d81..9af88e67e9 100644 --- a/drape/glfunctions.hpp +++ b/drape/glfunctions.hpp @@ -61,9 +61,12 @@ public: static void glBufferData(glConst target, uint32_t size, void const * data, glConst usage); static void glBufferSubData(glConst target, uint32_t size, void const * data, uint32_t offset); - static void * glMapBuffer(glConst target); + static void * glMapBuffer(glConst target, glConst access = gl_const::GLWriteOnly); static void glUnmapBuffer(glConst target); + static void * glMapBufferRange(glConst target, uint32_t offset, uint32_t length, glConst access); + static void glFlushMappedBufferRange(glConst target, uint32_t offset, uint32_t length); + /// Shaders support static uint32_t glCreateShader(glConst type); static void glShaderSource(uint32_t shaderID, string const & src, string const & defines); diff --git a/drape/glstate.cpp b/drape/glstate.cpp index 9da2f32773..a5b1417941 100644 --- a/drape/glstate.cpp +++ b/drape/glstate.cpp @@ -85,6 +85,8 @@ bool GLState::operator<(GLState const & other) const return m_gpuProgramIndex < other.m_gpuProgramIndex; if (m_gpuProgram3dIndex != other.m_gpuProgram3dIndex) return m_gpuProgram3dIndex < other.m_gpuProgram3dIndex; + if (m_depthFunction != other.m_depthFunction) + return m_depthFunction < other.m_depthFunction; if (m_colorTexture != other.m_colorTexture) return m_colorTexture < other.m_colorTexture; diff --git a/drape/render_bucket.hpp b/drape/render_bucket.hpp index ec65e14cd5..003a7f1743 100644 --- a/drape/render_bucket.hpp +++ b/drape/render_bucket.hpp @@ -4,6 +4,11 @@ class ScreenBase; +namespace df +{ +class BatchMergeHelper; +} + namespace dp { @@ -13,6 +18,7 @@ class VertexArrayBuffer; class RenderBucket { + friend class df::BatchMergeHelper; public: RenderBucket(drape_ptr && buffer); ~RenderBucket(); diff --git a/drape/vertex_array_buffer.hpp b/drape/vertex_array_buffer.hpp index ad75f74d06..24696c20e2 100644 --- a/drape/vertex_array_buffer.hpp +++ b/drape/vertex_array_buffer.hpp @@ -10,6 +10,11 @@ #include "std/map.hpp" +namespace df +{ +class BatchMergeHelper; +} + namespace dp { @@ -32,6 +37,7 @@ struct IndicesRange class VertexArrayBuffer { typedef map > TBuffersMap; + friend class df::BatchMergeHelper; public: VertexArrayBuffer(uint32_t indexBufferSize, uint32_t dataBufferSize); ~VertexArrayBuffer(); diff --git a/drape_frontend/batch_merge_helper.cpp b/drape_frontend/batch_merge_helper.cpp new file mode 100644 index 0000000000..80c36054ad --- /dev/null +++ b/drape_frontend/batch_merge_helper.cpp @@ -0,0 +1,178 @@ +#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) +{ + 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_uniforms = oldGroup->m_uniforms; + newGroup->m_generalUniforms = oldGroup->m_generalUniforms; + newGroup->AddBucket(move(bucket)); + + buffer->Preflush(); + buffer->Build(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::TBuffersMap::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); + } + + uint32_t indexOffset = newBuffer->GetStartIndexValue(); + + for (dp::VertexArrayBuffer::TBuffersMap::value_type const & vboNode : buffer->m_staticBuffers) + { + copyVertecesFn(vboNode, rawDataBuffer, newBuffer); + } + + for (dp::VertexArrayBuffer::TBuffersMap::value_type 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); + } +} + +} diff --git a/drape_frontend/batch_merge_helper.hpp b/drape_frontend/batch_merge_helper.hpp new file mode 100644 index 0000000000..012a9d999d --- /dev/null +++ b/drape_frontend/batch_merge_helper.hpp @@ -0,0 +1,23 @@ +#pragma once + +#include "drape/pointers.hpp" +#include "drape_frontend/render_group.hpp" + +namespace dp +{ +class VertexArrayBuffer; +} + +namespace df +{ + +class BatchMergeHelper +{ +public: + static bool IsMergeSupported(); + + static void MergeBatches(vector> & batches, + vector> & mergedBatches); +}; + +} diff --git a/drape_frontend/drape_frontend.pro b/drape_frontend/drape_frontend.pro index e947587be6..0951b21c5d 100755 --- a/drape_frontend/drape_frontend.pro +++ b/drape_frontend/drape_frontend.pro @@ -91,6 +91,7 @@ SOURCES += \ watch/geometry_processors.cpp \ watch/feature_processor.cpp \ watch/default_font.cpp \ + batch_merge_helper.cpp \ HEADERS += \ animation/base_interpolator.hpp \ @@ -187,3 +188,4 @@ HEADERS += \ watch/brush_info.hpp \ watch/geometry_processors.hpp \ watch/feature_processor.hpp \ + batch_merge_helper.hpp \ diff --git a/drape_frontend/frontend_renderer.cpp b/drape_frontend/frontend_renderer.cpp index 07da4cb99e..944e7dd80b 100755 --- a/drape_frontend/frontend_renderer.cpp +++ b/drape_frontend/frontend_renderer.cpp @@ -6,6 +6,7 @@ #include "drape_frontend/transparent_layer.hpp" #include "drape_frontend/visual_params.hpp" #include "drape_frontend/user_mark_shapes.hpp" +#include "drape_frontend/batch_merge_helper.hpp" #include "drape/debug_rect_renderer.hpp" #include "drape/shader_def.hpp" @@ -898,6 +899,50 @@ void FrontendRenderer::RenderScene(ScreenBase const & modelView) #ifdef DRAW_INFO AfterDrawFrame(); #endif + + MergeBuckets(); +} + +void FrontendRenderer::MergeBuckets() +{ + if (BatchMergeHelper::IsMergeSupported() == false) + return; + + ++m_mergeBucketsCounter; + if (m_mergeBucketsCounter < 60) + return; + + if (m_renderGroups.empty()) + return; + + using TGroup = pair; + using TGroupMap = map>>; + TGroupMap forMerge; + + vector> newGroups; + newGroups.reserve(m_renderGroups.size()); + + m_mergeBucketsCounter = 0; + size_t groupsCount = m_renderGroups.size(); + for (size_t i = 0; i < groupsCount; ++i) + { + ref_ptr group = make_ref(m_renderGroups[i]); + dp::GLState state = group->GetState(); + if (state.GetDepthLayer() == dp::GLState::GeometryLayer && + group->IsPendingOnDelete() == false) + { + TGroup key = make_pair(state, group->GetTileKey()); + forMerge[key].push_back(move(m_renderGroups[i])); + } + else + newGroups.push_back(move(m_renderGroups[i])); + } + + for (TGroupMap::value_type & node : forMerge) + BatchMergeHelper::MergeBatches(node.second, newGroups); + + LOG(LINFO, ("Batches have been merged : ", m_renderGroups.size() - newGroups.size())); + m_renderGroups = move(newGroups); } bool FrontendRenderer::IsPerspective() const diff --git a/drape_frontend/frontend_renderer.hpp b/drape_frontend/frontend_renderer.hpp index 352c2744dd..182f9ab046 100755 --- a/drape_frontend/frontend_renderer.hpp +++ b/drape_frontend/frontend_renderer.hpp @@ -137,6 +137,7 @@ protected: private: void OnResize(ScreenBase const & screen); void RenderScene(ScreenBase const & modelView); + void MergeBuckets(); void RenderSingleGroup(ScreenBase const & modelView, ref_ptr group); void RefreshProjection(ScreenBase const & screen); void RefreshModelView(ScreenBase const & screen); @@ -251,6 +252,7 @@ private: ref_ptr m_requestedTiles; uint64_t m_maxGeneration; + int m_mergeBucketsCounter = 0; #ifdef DEBUG bool m_isTeardowned; diff --git a/drape_frontend/render_group.hpp b/drape_frontend/render_group.hpp index 978b35321f..0cda779dfa 100755 --- a/drape_frontend/render_group.hpp +++ b/drape_frontend/render_group.hpp @@ -50,6 +50,7 @@ private: class RenderGroup : public BaseRenderGroup { typedef BaseRenderGroup TBase; + friend class BatchMergeHelper; public: RenderGroup(dp::GLState const & state, TileKey const & tileKey); ~RenderGroup();