Skip to content

Commit

Permalink
Metal Optimizations (#1748)
Browse files Browse the repository at this point in the history
  • Loading branch information
TimSylvester authored Oct 11, 2023
1 parent d63f94f commit ce0096a
Show file tree
Hide file tree
Showing 9 changed files with 135 additions and 75 deletions.
10 changes: 10 additions & 0 deletions include/mbgl/mtl/context.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
#include <mbgl/mtl/mtl_fwd.hpp>

#include <memory>
#include <optional>
#include <unordered_map>

namespace mbgl {
Expand Down Expand Up @@ -108,10 +109,19 @@ class Context final : public gfx::Context {

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<BufferResource> tileVertexBuffer;
std::optional<BufferResource> tileIndexBuffer;

gfx::RenderingStats stats;
};

Expand Down
2 changes: 1 addition & 1 deletion include/mbgl/shaders/mtl/shader_group.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,11 @@ class ShaderGroup final : public gfx::ShaderGroup {
constexpr auto& fragMain = shaders::ShaderSource<ShaderID, gfx::Backend::Type::Metal>::fragmentMainFunction;

const std::string shaderName = std::string(name);
const auto shaderSource = std::string(shaders::prelude) + source;

auto shader = get<mtl::ShaderProgram>(shaderName);
if (!shader) {
auto& context = static_cast<Context&>(gfxContext);
const auto shaderSource = std::string(shaders::prelude) + source;
shader = context.createProgram(shaderName, shaderSource, vertMain, fragMain, programParameters, {});
assert(shader);
if (!shader || !registerShader(shader, shaderName)) {
Expand Down
1 change: 1 addition & 0 deletions platform/ios/benchmark/MBXBenchViewController.mm
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ - (void)viewDidLoad
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;
Expand Down
3 changes: 1 addition & 2 deletions src/mbgl/geometry/feature_index.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,7 @@ mbgl::Point<double> project(const mbgl::LatLng& coordinate, const mbgl::Transfor
namespace mbgl {

FeatureIndex::FeatureIndex(std::unique_ptr<const GeometryTileData> 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,
Expand Down
3 changes: 3 additions & 0 deletions src/mbgl/geometry/feature_index.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,9 @@ class FeatureIndex {

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,
Expand Down
18 changes: 18 additions & 0 deletions src/mbgl/mtl/context.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
#include <mbgl/mtl/upload_pass.hpp>
#include <mbgl/programs/program_parameters.hpp>
#include <mbgl/renderer/paint_parameters.hpp>
#include <mbgl/renderer/render_static_data.hpp>
#include <mbgl/renderer/render_target.hpp>
#include <mbgl/shaders/mtl/shader_program.hpp>
#include <mbgl/util/traits.hpp>
Expand Down Expand Up @@ -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<gfx::OffscreenTexture> Context::createOffscreenTexture(Size size,
Expand Down
107 changes: 63 additions & 44 deletions src/mbgl/renderer/paint_parameters.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -208,34 +208,49 @@ void PaintParameters::renderTileClippingMasks(TIter beg, TIter end, GetTileIDFun
return;
}

const auto& mtlContext = static_cast<mtl::Context&>(context);
auto& mtlContext = static_cast<mtl::Context&>(context);
const auto& mtlShader = static_cast<const mtl::ShaderProgram&>(*shader);
const auto& mtlRenderPass = static_cast<mtl::RenderPass&>(*renderPass);
const auto& encoder = mtlRenderPass.getMetalEncoder();
const auto colorMode = gfx::ColorMode::disabled();

constexpr std::size_t indexCount = 6;
std::optional<mtl::BufferResource> 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<PositionOnlyLayoutAttributes>::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);
Expand All @@ -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<std::uint32_t> maskIDs;
std::vector<std::uint32_t> tileIndexes;
std::vector<shaders::ClipUBO> 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);

Expand All @@ -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<float>(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<float>(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<NS::UInteger>(indexCount),
indexCount,
MTL::IndexType::IndexTypeUInt16,
indexRes->getMetalBuffer().get(),
/*indexOffset=*/0,
Expand Down
5 changes: 5 additions & 0 deletions src/mbgl/tile/geometry_tile_worker.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -351,6 +351,11 @@ void GeometryTileWorker::parse() {

featureIndex = std::make_unique<FeatureIndex>(*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;

Expand Down
61 changes: 33 additions & 28 deletions src/mbgl/util/grid_index.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,9 @@ class GridIndex {
using BBox = mapbox::geometry::box<float>;
using BCircle = geometry::circle<float>;

/// 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&);

Expand Down Expand Up @@ -90,6 +93,8 @@ class GridIndex {
const float width;
const float height;

std::size_t estimatedElementsPerCell = 0;

const std::size_t xCellCount;
const std::size_t yCellCount;
const double xScale;
Expand Down Expand Up @@ -118,20 +123,20 @@ GridIndex<T>::GridIndex(const float width_, const float height_, const uint32_t

template <class T>
void GridIndex<T>::insert(T&& t, const BBox& bbox) {
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);

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);
const size_t uid = boxElements.size();

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);

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);
}
}

Expand All @@ -140,20 +145,20 @@ void GridIndex<T>::insert(T&& t, const BBox& bbox) {

template <class T>
void GridIndex<T>::insert(T&& t, const BCircle& bcircle) {
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);

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);
const size_t uid = circleElements.size();

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);

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);
}
}

Expand Down

0 comments on commit ce0096a

Please sign in to comment.