Metal Optimizations (#1748)
This commit is contained in:
@@ -14,6 +14,7 @@
|
||||
#include <mbgl/mtl/mtl_fwd.hpp>
|
||||
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <unordered_map>
|
||||
|
||||
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<BufferResource> tileVertexBuffer;
|
||||
std::optional<BufferResource> tileIndexBuffer;
|
||||
|
||||
gfx::RenderingStats stats;
|
||||
};
|
||||
|
||||
|
||||
@@ -30,11 +30,11 @@ public:
|
||||
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)) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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>
|
||||
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
@@ -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);
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -61,6 +61,9 @@ public:
|
||||
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&);
|
||||
|
||||
@@ -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<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();
|
||||
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<T>::insert(T&& t, const BBox& bbox) {
|
||||
|
||||
template <class T>
|
||||
void GridIndex<T>::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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user