diff --git a/include/mbgl/mtl/context.hpp b/include/mbgl/mtl/context.hpp index 2b6129339..3468d4826 100644 --- a/include/mbgl/mtl/context.hpp +++ b/include/mbgl/mtl/context.hpp @@ -14,6 +14,7 @@ #include #include +#include #include namespace mbgl { @@ -108,10 +109,19 @@ public: virtual bool emplaceOrUpdateUniformBuffer(gfx::UniformBufferPtr&, const void* data, std::size_t size); + /// Get a reusable buffer containing the standard fixed tile vertices (+/- `util::EXTENT`) + const BufferResource& getTileVertexBuffer(); + + /// Get a reusable buffer containing the standard fixed tile indexes + const BufferResource& getTileIndexBuffer(); + private: RendererBackend& backend; bool cleanupOnDestruction = true; + std::optional tileVertexBuffer; + std::optional tileIndexBuffer; + gfx::RenderingStats stats; }; diff --git a/include/mbgl/shaders/mtl/shader_group.hpp b/include/mbgl/shaders/mtl/shader_group.hpp index 40aa8a977..214240957 100644 --- a/include/mbgl/shaders/mtl/shader_group.hpp +++ b/include/mbgl/shaders/mtl/shader_group.hpp @@ -30,11 +30,11 @@ public: constexpr auto& fragMain = shaders::ShaderSource::fragmentMainFunction; const std::string shaderName = std::string(name); - const auto shaderSource = std::string(shaders::prelude) + source; auto shader = get(shaderName); if (!shader) { auto& context = static_cast(gfxContext); + const auto shaderSource = std::string(shaders::prelude) + source; shader = context.createProgram(shaderName, shaderSource, vertMain, fragMain, programParameters, {}); assert(shader); if (!shader || !registerShader(shader, shaderName)) { diff --git a/platform/ios/benchmark/MBXBenchViewController.mm b/platform/ios/benchmark/MBXBenchViewController.mm index a3b3727d6..43309d325 100644 --- a/platform/ios/benchmark/MBXBenchViewController.mm +++ b/platform/ios/benchmark/MBXBenchViewController.mm @@ -46,6 +46,7 @@ self.mapView = [[MLNMapView alloc] initWithFrame:self.view.bounds styleURL:url]; self.mapView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; self.mapView.delegate = self; + self.mapView.opaque = YES; self.mapView.zoomEnabled = NO; self.mapView.scrollEnabled = NO; self.mapView.rotateEnabled = NO; diff --git a/src/mbgl/geometry/feature_index.cpp b/src/mbgl/geometry/feature_index.cpp index 4bd811002..c4b731d65 100644 --- a/src/mbgl/geometry/feature_index.cpp +++ b/src/mbgl/geometry/feature_index.cpp @@ -34,8 +34,7 @@ mbgl::Point project(const mbgl::LatLng& coordinate, const mbgl::Transfor namespace mbgl { FeatureIndex::FeatureIndex(std::unique_ptr tileData_) - : grid(util::EXTENT, util::EXTENT, util::EXTENT / 16) // 16x16 grid -> 32px cell - , + : grid(util::EXTENT, util::EXTENT, util::EXTENT / 16), // 16x16 grid -> 32px cell tileData(std::move(tileData_)) {} void FeatureIndex::insert(const GeometryCollection& geometries, diff --git a/src/mbgl/geometry/feature_index.hpp b/src/mbgl/geometry/feature_index.hpp index 8577304ac..01aa6d505 100644 --- a/src/mbgl/geometry/feature_index.hpp +++ b/src/mbgl/geometry/feature_index.hpp @@ -75,6 +75,9 @@ public: const GeometryTileData* getData() { return tileData.get(); } + /// Set the expected number of elements per cell to avoid small re-allocations for populated cells + void reserve(std::size_t value) { grid.reserve(value); } + void insert(const GeometryCollection&, std::size_t index, const std::string& sourceLayerName, diff --git a/src/mbgl/mtl/context.cpp b/src/mbgl/mtl/context.cpp index 186079043..e41e7d2ab 100644 --- a/src/mbgl/mtl/context.cpp +++ b/src/mbgl/mtl/context.cpp @@ -14,6 +14,7 @@ #include #include #include +#include #include #include #include @@ -184,6 +185,23 @@ bool Context::emplaceOrUpdateUniformBuffer(gfx::UniformBufferPtr& buffer, const } } +const BufferResource& Context::getTileVertexBuffer() { + if (!tileVertexBuffer) { + const auto vertices = RenderStaticData::tileVertices(); + constexpr auto vertexSize = sizeof(decltype(vertices)::Vertex::a1); + tileVertexBuffer.emplace(createBuffer(vertices.data(), vertices.bytes(), gfx::BufferUsageType::StaticDraw)); + } + return *tileVertexBuffer; +} + +const BufferResource& Context::getTileIndexBuffer() { + if (!tileIndexBuffer) { + const auto indexes = RenderStaticData::quadTriangleIndices(); + tileIndexBuffer.emplace(createBuffer(indexes.data(), indexes.bytes(), gfx::BufferUsageType::StaticDraw)); + } + return *tileIndexBuffer; +} + void Context::setDirtyState() {} std::unique_ptr Context::createOffscreenTexture(Size size, diff --git a/src/mbgl/renderer/paint_parameters.cpp b/src/mbgl/renderer/paint_parameters.cpp index 65d404368..dbde6452e 100644 --- a/src/mbgl/renderer/paint_parameters.cpp +++ b/src/mbgl/renderer/paint_parameters.cpp @@ -208,34 +208,49 @@ void PaintParameters::renderTileClippingMasks(TIter beg, TIter end, GetTileIDFun return; } - const auto& mtlContext = static_cast(context); + auto& mtlContext = static_cast(context); const auto& mtlShader = static_cast(*shader); const auto& mtlRenderPass = static_cast(*renderPass); const auto& encoder = mtlRenderPass.getMetalEncoder(); const auto colorMode = gfx::ColorMode::disabled(); - constexpr std::size_t indexCount = 6; - std::optional indexRes; + const mtl::BufferResource* indexRes = nullptr; + constexpr NS::UInteger indexCount = 6; - // TODO: refactor to use `Context::makeDepthStencilState` const auto init = [&]() { - // TODO: a lot of this can be cached // Create a vertex buffer from the fixed tile coordinates - const auto vertices = RenderStaticData::tileVertices(); - constexpr auto vertexSize = sizeof(decltype(vertices)::Vertex::a1); - auto vertexRes = mtlContext.createBuffer(vertices.data(), vertices.bytes(), gfx::BufferUsageType::StaticDraw); + constexpr auto vertexSize = sizeof(gfx::Vertex::a1); + const auto& vertexRes = mtlContext.getTileVertexBuffer(); if (!vertexRes) { - return; + assert(vertexSize == vertexRes.getSizeInBytes()); + return false; + } + + // Create a buffer from the fixed tile indexes + if (!indexRes) { + indexRes = &mtlContext.getTileIndexBuffer(); + if (!indexRes) { + return false; + } + assert(indexCount * sizeof(std::uint16_t) == indexRes->getSizeInBytes()); + } + + if (auto depthStencilState = mtlContext.makeDepthStencilState( + clipMaskDepthMode, clipMaskStencilMode, mtlRenderPass)) { + encoder->setDepthStencilState(depthStencilState.get()); + } else { + assert(!"Failed to create depth-stencil state"); + return false; } - encoder->setVertexBuffer(vertexRes.getMetalBuffer().get(), /*offset=*/0, ShaderClass::attributes[0].index); // A vertex descriptor tells Metal what's in the vertex buffer auto vertDesc = NS::RetainPtr(MTL::VertexDescriptor::vertexDescriptor()); auto attribDesc = NS::TransferPtr(MTL::VertexAttributeDescriptor::alloc()->init()); auto layoutDesc = NS::TransferPtr(MTL::VertexBufferLayoutDescriptor::alloc()->init()); if (!vertDesc || !attribDesc || !layoutDesc) { - return; + return false; } + attribDesc->setBufferIndex(ShaderClass::attributes[0].index); attribDesc->setOffset(0); attribDesc->setFormat(MTL::VertexFormatShort2); @@ -245,35 +260,30 @@ void PaintParameters::renderTileClippingMasks(TIter beg, TIter end, GetTileIDFun vertDesc->attributes()->setObject(attribDesc.get(), 0); vertDesc->layouts()->setObject(layoutDesc.get(), 0); - // Create a buffer from the fixed tile indexes - const auto indexes = RenderStaticData::quadTriangleIndices(); - assert(indexes.elements() == indexCount); - indexRes.emplace(mtlContext.createBuffer(indexes.data(), indexes.bytes(), gfx::BufferUsageType::StaticDraw)); - if (!indexRes || !*indexRes) { - return; - } - - // gfx::CullFaceMode::disabled(); - encoder->setCullMode(MTL::CullModeNone); - - if (auto depthStencilState = mtlContext.makeDepthStencilState( - clipMaskDepthMode, clipMaskStencilMode, mtlRenderPass)) { - encoder->setDepthStencilState(depthStencilState.get()); - } else { - assert(!"Failed to create depth-stencil state"); - return; - } - // Create a render pipeline state, telling Metal how to render the primitives const auto& renderPassDescriptor = mtlRenderPass.getDescriptor(); if (auto state = mtlShader.getRenderPipelineState(renderPassDescriptor, vertDesc, colorMode)) { encoder->setRenderPipelineState(state.get()); } else { - return; + return false; } + + encoder->setCullMode(MTL::CullModeNone); + encoder->setVertexBuffer(vertexRes.getMetalBuffer().get(), /*offset=*/0, ShaderClass::attributes[0].index); + + return true; }; - // For each tile in the set... + std::vector maskIDs; + std::vector tileIndexes; + std::vector tileUBOs; + + const auto tileCount = std::distance(beg, end); + maskIDs.reserve(tileCount); + tileIndexes.reserve(tileCount); + tileUBOs.reserve(tileCount); + + // For each tile in the set, work out the stencil ID for (auto i = beg; i != end; ++i) { const auto& tileID = f(*i); @@ -289,22 +299,31 @@ void PaintParameters::renderTileClippingMasks(TIter beg, TIter end, GetTileIDFun } } - if (!indexRes) { - init(); - } + maskIDs.push_back(stencilID); + tileUBOs.emplace_back(shaders::ClipUBO{/* .matrix = */ util::cast(matrixForTile(tileID))}); + } - encoder->setStencilReferenceValue(stencilID); + // Set up the encoder if we have anything to draw + if (tileUBOs.empty() || !init()) { + return; + } - const auto ubo = shaders::ClipUBO{/* .matrix = */ util::cast(matrixForTile(tileID))}; - // Create a buffer for the per-tile UBO data - if (auto uboRes = mtlContext.createBuffer(&ubo, sizeof(ubo), gfx::BufferUsageType::StaticDraw)) { - encoder->setVertexBuffer(uboRes.getMetalBuffer().get(), /*offset=*/0, ShaderClass::uniforms[0].index); - } else { - break; - } + // Create a buffer for the UBO data. + // This could potentially be reused, but `PaintParameters` is recreated for each frame. + constexpr auto uboSize = sizeof(shaders::ClipUBO); + const auto uboRes = mtlContext.createBuffer( + tileUBOs.data(), tileUBOs.size() * uboSize, gfx::BufferUsageType::StaticDraw); + const auto uboPtr = uboRes.getMetalBuffer().get(); + if (!uboPtr) { + return; + } + // For each tile in the set... + for (std::size_t ii = 0; ii < maskIDs.size(); ++ii) { + encoder->setStencilReferenceValue(maskIDs[ii]); + encoder->setVertexBuffer(uboPtr, /*offset=*/ii * uboSize, ShaderClass::uniforms[0].index); encoder->drawIndexedPrimitives(MTL::PrimitiveType::PrimitiveTypeTriangle, - static_cast(indexCount), + indexCount, MTL::IndexType::IndexTypeUInt16, indexRes->getMetalBuffer().get(), /*indexOffset=*/0, diff --git a/src/mbgl/tile/geometry_tile_worker.cpp b/src/mbgl/tile/geometry_tile_worker.cpp index 65f906875..57285f099 100644 --- a/src/mbgl/tile/geometry_tile_worker.cpp +++ b/src/mbgl/tile/geometry_tile_worker.cpp @@ -351,6 +351,11 @@ void GeometryTileWorker::parse() { featureIndex = std::make_unique(*data ? (*data)->clone() : nullptr); + // Avoid small reallocations for populated cells. + // If we had a total feature count, this could be based on that and the cell count. + constexpr auto estimatedElementsPerCell = 8; + featureIndex->reserve(estimatedElementsPerCell); + GlyphDependencies glyphDependencies; ImageDependencies imageDependencies; diff --git a/src/mbgl/util/grid_index.hpp b/src/mbgl/util/grid_index.hpp index 3cbd9ae9f..6e987d7b7 100644 --- a/src/mbgl/util/grid_index.hpp +++ b/src/mbgl/util/grid_index.hpp @@ -61,6 +61,9 @@ public: using BBox = mapbox::geometry::box; using BCircle = geometry::circle; + /// Set the expected number of elements per cell to avoid small re-allocations for populated cells + void reserve(std::size_t value) { estimatedElementsPerCell = value; } + void insert(T&& t, const BBox&); void insert(T&& t, const BCircle&); @@ -90,6 +93,8 @@ private: const float width; const float height; + std::size_t estimatedElementsPerCell = 0; + const std::size_t xCellCount; const std::size_t yCellCount; const double xScale; @@ -118,20 +123,20 @@ GridIndex::GridIndex(const float width_, const float height_, const uint32_t template void GridIndex::insert(T&& t, const BBox& bbox) { - size_t uid = boxElements.size(); + const size_t uid = boxElements.size(); - auto cx1 = convertToXCellCoord(bbox.min.x); - auto cy1 = convertToYCellCoord(bbox.min.y); - auto cx2 = convertToXCellCoord(bbox.max.x); - auto cy2 = convertToYCellCoord(bbox.max.y); + const auto cx1 = convertToXCellCoord(bbox.min.x); + const auto cy1 = convertToYCellCoord(bbox.min.y); + const auto cx2 = convertToXCellCoord(bbox.max.x); + const auto cy2 = convertToYCellCoord(bbox.max.y); - std::size_t x; - std::size_t y; - std::size_t cellIndex; - for (x = cx1; x <= cx2; ++x) { - for (y = cy1; y <= cy2; ++y) { - cellIndex = xCellCount * y + x; - boxCells[cellIndex].push_back(uid); + for (std::size_t x = cx1; x <= cx2; ++x) { + for (std::size_t y = cy1; y <= cy2; ++y) { + auto& cell = boxCells[xCellCount * y + x]; + if (estimatedElementsPerCell && cell.empty()) { + cell.reserve(estimatedElementsPerCell); + } + cell.push_back(uid); } } @@ -140,20 +145,20 @@ void GridIndex::insert(T&& t, const BBox& bbox) { template void GridIndex::insert(T&& t, const BCircle& bcircle) { - size_t uid = circleElements.size(); + const size_t uid = circleElements.size(); - auto cx1 = convertToXCellCoord(bcircle.center.x - bcircle.radius); - auto cy1 = convertToYCellCoord(bcircle.center.y - bcircle.radius); - auto cx2 = convertToXCellCoord(bcircle.center.x + bcircle.radius); - auto cy2 = convertToYCellCoord(bcircle.center.y + bcircle.radius); + const auto cx1 = convertToXCellCoord(bcircle.center.x - bcircle.radius); + const auto cy1 = convertToYCellCoord(bcircle.center.y - bcircle.radius); + const auto cx2 = convertToXCellCoord(bcircle.center.x + bcircle.radius); + const auto cy2 = convertToYCellCoord(bcircle.center.y + bcircle.radius); - std::size_t x; - std::size_t y; - std::size_t cellIndex; - for (x = cx1; x <= cx2; ++x) { - for (y = cy1; y <= cy2; ++y) { - cellIndex = xCellCount * y + x; - circleCells[cellIndex].push_back(uid); + for (std::size_t x = cx1; x <= cx2; ++x) { + for (std::size_t y = cy1; y <= cy2; ++y) { + auto& cell = circleCells[xCellCount * y + x]; + if (estimatedElementsPerCell && cell.empty()) { + cell.reserve(estimatedElementsPerCell); + } + cell.push_back(uid); } }