Skip to content
This repository has been archived by the owner on Aug 8, 2023. It is now read-only.

Preserve depth buffer between 3D layers + optimize render order #9931

Merged
merged 14 commits into from
Sep 21, 2017
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion platform/node/test/ignores.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
"render-tests/debug/tile": "https://github.com/mapbox/mapbox-gl-native/issues/3841",
"render-tests/extent/1024-circle": "needs investigation",
"render-tests/extent/1024-symbol": "needs investigation",
"render-tests/fill-extrusion-multiple/multiple": "https://github.com/mapbox/mapbox-gl-native/issues/9894",
"render-tests/fill-extrusion-pattern/@2x": "https://github.com/mapbox/mapbox-gl-js/issues/3327",
"render-tests/fill-extrusion-pattern/function-2": "https://github.com/mapbox/mapbox-gl-js/issues/3327",
"render-tests/fill-extrusion-pattern/function": "https://github.com/mapbox/mapbox-gl-js/issues/3327",
Expand Down
9 changes: 2 additions & 7 deletions src/mbgl/gl/context.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -454,15 +454,10 @@ Framebuffer Context::createFramebuffer(const Texture& color) {
return { color.size, std::move(fbo) };
}

Framebuffer
Context::createFramebuffer(const Texture& color,
const Renderbuffer<RenderbufferType::DepthComponent>& depthTarget) {
auto fbo = createFramebuffer();
bindFramebuffer = fbo;
MBGL_CHECK_ERROR(glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, color.texture, 0));
void
Context::attachRenderbuffer(const Renderbuffer<RenderbufferType::DepthComponent>& depthTarget) {
MBGL_CHECK_ERROR(glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depthTarget.renderbuffer));
checkFramebuffer();
return { depthTarget.size, std::move(fbo) };
}

UniqueTexture
Expand Down
4 changes: 2 additions & 2 deletions src/mbgl/gl/context.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -96,8 +96,8 @@ class Context : private util::noncopyable {
Framebuffer createFramebuffer(const Texture&,
const Renderbuffer<RenderbufferType::DepthStencil>&);
Framebuffer createFramebuffer(const Texture&);
Framebuffer createFramebuffer(const Texture&,
const Renderbuffer<RenderbufferType::DepthComponent>&);

void attachRenderbuffer(const Renderbuffer<RenderbufferType::DepthComponent>&);

template <typename Image,
TextureFormat format = Image::channels == 4 ? TextureFormat::RGBA
Expand Down
7 changes: 6 additions & 1 deletion src/mbgl/gl/renderbuffer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,14 @@ namespace gl {
template <RenderbufferType renderbufferType>
class Renderbuffer {
public:
Renderbuffer(Size size_, UniqueRenderbuffer renderbuffer_, bool dirty_ = false)
: size(std::move(size_)), renderbuffer(std::move(renderbuffer_)), dirty(dirty_) {
}

using type = std::integral_constant<RenderbufferType, renderbufferType>;
Size size;
gl::UniqueRenderbuffer renderbuffer;
UniqueRenderbuffer renderbuffer;
bool dirty;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you please document what "dirty" means in this context? (or alternatively write convenience/wrapper functions around clearing the renderbuffer).

};

} // namespace gl
Expand Down
193 changes: 90 additions & 103 deletions src/mbgl/renderer/layers/render_fill_extrusion_layer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,9 @@ void RenderFillExtrusionLayer::transition(const TransitionParameters& parameters
void RenderFillExtrusionLayer::evaluate(const PropertyEvaluationParameters& parameters) {
evaluated = unevaluated.evaluate(parameters);

passes = (evaluated.get<style::FillExtrusionOpacity>() > 0) ? RenderPass::Translucent
: RenderPass::None;
passes = (evaluated.get<style::FillExtrusionOpacity>() > 0)
? (RenderPass::Translucent | RenderPass::Pass3D)
: RenderPass::None;
}

bool RenderFillExtrusionLayer::hasTransition() const {
Expand All @@ -50,113 +51,99 @@ void RenderFillExtrusionLayer::render(PaintParameters& parameters, RenderSource*
return;
}

const auto size = parameters.context.viewport.getCurrentValue().size;
if (parameters.pass == RenderPass::Pass3D) {
const auto size = parameters.context.viewport.getCurrentValue().size;

if (!parameters.staticData.extrusionTexture || parameters.staticData.extrusionTexture->getSize() != size) {
parameters.staticData.extrusionTexture = OffscreenTexture(parameters.context, size, OffscreenTextureAttachment::Depth);
}

parameters.staticData.extrusionTexture->bind();

parameters.context.setStencilMode(gl::StencilMode::disabled());
parameters.context.setDepthMode(parameters.depthModeForSublayer(0, gl::DepthMode::ReadWrite));
parameters.context.clear(Color{ 0.0f, 0.0f, 0.0f, 0.0f }, 1.0f, {});

if (evaluated.get<FillExtrusionPattern>().from.empty()) {
for (const RenderTile& tile : renderTiles) {
assert(dynamic_cast<FillExtrusionBucket*>(tile.tile.getBucket(*baseImpl)));
FillExtrusionBucket& bucket = *reinterpret_cast<FillExtrusionBucket*>(tile.tile.getBucket(*baseImpl));

parameters.programs.fillExtrusion.get(evaluated).draw(
parameters.context,
gl::Triangles(),
parameters.depthModeForSublayer(0, gl::DepthMode::ReadWrite),
gl::StencilMode::disabled(),
parameters.colorModeForRenderPass(),
FillExtrusionUniforms::values(
tile.translatedClipMatrix(evaluated.get<FillExtrusionTranslate>(),
evaluated.get<FillExtrusionTranslateAnchor>(),
parameters.state),
parameters.state,
parameters.evaluatedLight
),
*bucket.vertexBuffer,
*bucket.indexBuffer,
bucket.triangleSegments,
bucket.paintPropertyBinders.at(getID()),
evaluated,
parameters.state.getZoom(),
getID());
if (!renderTexture || renderTexture->getSize() != size) {
renderTexture = OffscreenTexture(parameters.context, size);
}
} else {
optional<ImagePosition> imagePosA = parameters.imageManager.getPattern(evaluated.get<FillExtrusionPattern>().from);
optional<ImagePosition> imagePosB = parameters.imageManager.getPattern(evaluated.get<FillExtrusionPattern>().to);

if (!imagePosA || !imagePosB) {
return;
renderTexture->bind();
renderTexture->attachRenderbuffer(*parameters.staticData.depthRenderbuffer);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of first creating the texture and FBO (inside OffscreenTexture), validating + binding, then attaching the framebuffer (which requires revalidation), we should pass the depthRenderbuffer as an (optional) argument to OffscreenTexture construction so that we can internally call context.createFramebuffer(const Texture&, const Renderbuffer<RenderbufferType::DepthStencil>&) instead of exposing an additional bindRenderbuffer.

Additionally, it looks like the depth renderbuffer is attached on every frame (which requires revalidation on every frame). Moving it to the constructor would solve that problem as well.


parameters.context.setStencilMode(gl::StencilMode::disabled());
optional<float> depthClearValue = {};
if (parameters.staticData.depthRenderbuffer->dirty) depthClearValue = 1.0;
parameters.context.clear(Color{ 0.0f, 0.0f, 0.0f, 0.0f }, depthClearValue, {});
parameters.staticData.depthRenderbuffer->dirty = false;

if (evaluated.get<FillExtrusionPattern>().from.empty()) {
for (const RenderTile& tile : renderTiles) {
assert(dynamic_cast<FillExtrusionBucket*>(tile.tile.getBucket(*baseImpl)));
FillExtrusionBucket& bucket =
*reinterpret_cast<FillExtrusionBucket*>(tile.tile.getBucket(*baseImpl));

parameters.programs.fillExtrusion.get(evaluated).draw(
parameters.context, gl::Triangles(),
parameters.depthModeFor3D(gl::DepthMode::ReadWrite),
gl::StencilMode::disabled(), parameters.colorModeForRenderPass(),
FillExtrusionUniforms::values(
tile.translatedClipMatrix(evaluated.get<FillExtrusionTranslate>(),
evaluated.get<FillExtrusionTranslateAnchor>(),
parameters.state),
parameters.state, parameters.evaluatedLight),
*bucket.vertexBuffer, *bucket.indexBuffer, bucket.triangleSegments,
bucket.paintPropertyBinders.at(getID()), evaluated, parameters.state.getZoom(),
getID());
}
} else {
optional<ImagePosition> imagePosA =
parameters.imageManager.getPattern(evaluated.get<FillExtrusionPattern>().from);
optional<ImagePosition> imagePosB =
parameters.imageManager.getPattern(evaluated.get<FillExtrusionPattern>().to);

if (!imagePosA || !imagePosB) {
return;
}

parameters.imageManager.bind(parameters.context, 0);

for (const RenderTile& tile : renderTiles) {
assert(dynamic_cast<FillExtrusionBucket*>(tile.tile.getBucket(*baseImpl)));
FillExtrusionBucket& bucket =
*reinterpret_cast<FillExtrusionBucket*>(tile.tile.getBucket(*baseImpl));

parameters.programs.fillExtrusionPattern.get(evaluated).draw(
parameters.context, gl::Triangles(),
parameters.depthModeFor3D(gl::DepthMode::ReadWrite),
gl::StencilMode::disabled(), parameters.colorModeForRenderPass(),
FillExtrusionPatternUniforms::values(
tile.translatedClipMatrix(evaluated.get<FillExtrusionTranslate>(),
evaluated.get<FillExtrusionTranslateAnchor>(),
parameters.state),
parameters.imageManager.getPixelSize(), *imagePosA, *imagePosB,
evaluated.get<FillExtrusionPattern>(), tile.id, parameters.state,
-std::pow(2, tile.id.canonical.z) / util::tileSize / 8.0f,
parameters.evaluatedLight),
*bucket.vertexBuffer, *bucket.indexBuffer, bucket.triangleSegments,
bucket.paintPropertyBinders.at(getID()), evaluated, parameters.state.getZoom(),
getID());
}
}

parameters.imageManager.bind(parameters.context, 0);

for (const RenderTile& tile : renderTiles) {
assert(dynamic_cast<FillExtrusionBucket*>(tile.tile.getBucket(*baseImpl)));
FillExtrusionBucket& bucket = *reinterpret_cast<FillExtrusionBucket*>(tile.tile.getBucket(*baseImpl));

parameters.programs.fillExtrusionPattern.get(evaluated).draw(
parameters.context,
gl::Triangles(),
parameters.depthModeForSublayer(0, gl::DepthMode::ReadWrite),
gl::StencilMode::disabled(),
parameters.colorModeForRenderPass(),
FillExtrusionPatternUniforms::values(
tile.translatedClipMatrix(evaluated.get<FillExtrusionTranslate>(),
evaluated.get<FillExtrusionTranslateAnchor>(),
parameters.state),
parameters.imageManager.getPixelSize(),
*imagePosA,
*imagePosB,
evaluated.get<FillExtrusionPattern>(),
tile.id,
parameters.state,
-std::pow(2, tile.id.canonical.z) / util::tileSize / 8.0f,
parameters.evaluatedLight
),
*bucket.vertexBuffer,
*bucket.indexBuffer,
bucket.triangleSegments,
bucket.paintPropertyBinders.at(getID()),
evaluated,
parameters.state.getZoom(),
getID());
}
}
} else if (parameters.pass == RenderPass::Translucent) {
parameters.context.bindTexture(renderTexture->getTexture());

parameters.backend.bind();
parameters.context.bindTexture(parameters.staticData.extrusionTexture->getTexture());

mat4 viewportMat;
matrix::ortho(viewportMat, 0, size.width, size.height, 0, 0, 1);

const Properties<>::PossiblyEvaluated properties;

parameters.programs.extrusionTexture.draw(
parameters.context,
gl::Triangles(),
gl::DepthMode::disabled(),
gl::StencilMode::disabled(),
parameters.colorModeForRenderPass(),
ExtrusionTextureProgram::UniformValues{
uniforms::u_matrix::Value{ viewportMat }, uniforms::u_world::Value{ size },
uniforms::u_image::Value{ 0 },
uniforms::u_opacity::Value{ evaluated.get<FillExtrusionOpacity>() }
},
parameters.staticData.extrusionTextureVertexBuffer,
parameters.staticData.quadTriangleIndexBuffer,
parameters.staticData.extrusionTextureSegments,
ExtrusionTextureProgram::PaintPropertyBinders{ properties, 0 },
properties,
parameters.state.getZoom(),
getID());
const auto size = parameters.context.viewport.getCurrentValue().size;

mat4 viewportMat;
matrix::ortho(viewportMat, 0, size.width, size.height, 0, 0, 1);

const Properties<>::PossiblyEvaluated properties;

parameters.programs.extrusionTexture.draw(
parameters.context, gl::Triangles(), gl::DepthMode::disabled(),
gl::StencilMode::disabled(), parameters.colorModeForRenderPass(),
ExtrusionTextureProgram::UniformValues{
uniforms::u_matrix::Value{ viewportMat }, uniforms::u_world::Value{ size },
uniforms::u_image::Value{ 0 },
uniforms::u_opacity::Value{ evaluated.get<FillExtrusionOpacity>() } },
parameters.staticData.extrusionTextureVertexBuffer,
parameters.staticData.quadTriangleIndexBuffer,
parameters.staticData.extrusionTextureSegments,
ExtrusionTextureProgram::PaintPropertyBinders{ properties, 0 }, properties,
parameters.state.getZoom(), getID());
}
}

bool RenderFillExtrusionLayer::queryIntersectsFeature(
Expand Down
4 changes: 4 additions & 0 deletions src/mbgl/renderer/layers/render_fill_extrusion_layer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
#include <mbgl/renderer/render_layer.hpp>
#include <mbgl/style/layers/fill_extrusion_layer_impl.hpp>
#include <mbgl/style/layers/fill_extrusion_layer_properties.hpp>
#include <mbgl/util/optional.hpp>
#include <mbgl/util/offscreen_texture.hpp>

namespace mbgl {

Expand Down Expand Up @@ -30,6 +32,8 @@ class RenderFillExtrusionLayer: public RenderLayer {
style::FillExtrusionPaintProperties::PossiblyEvaluated evaluated;

const style::FillExtrusionLayer::Impl& impl() const;

optional<OffscreenTexture> renderTexture;
};

template <>
Expand Down
4 changes: 4 additions & 0 deletions src/mbgl/renderer/paint_parameters.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,10 @@ gl::DepthMode PaintParameters::depthModeForSublayer(uint8_t n, gl::DepthMode::Ma
return gl::DepthMode { gl::DepthMode::LessEqual, mask, { nearDepth, farDepth } };
}

gl::DepthMode PaintParameters::depthModeFor3D(gl::DepthMode::Mask mask) const {
return gl::DepthMode { gl::DepthMode::LessEqual, mask, { 0.0, 1.0 } };
}

gl::StencilMode PaintParameters::stencilModeForClipping(const ClipID& id) const {
return gl::StencilMode {
gl::StencilMode::Equal { static_cast<uint32_t>(id.mask.to_ulong()) },
Expand Down
1 change: 1 addition & 0 deletions src/mbgl/renderer/paint_parameters.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ class PaintParameters {
Programs& programs;

gl::DepthMode depthModeForSublayer(uint8_t n, gl::DepthMode::Mask) const;
gl::DepthMode depthModeFor3D(gl::DepthMode::Mask) const;
gl::StencilMode stencilModeForClipping(const ClipID&) const;
gl::ColorMode colorModeForRenderPass() const;

Expand Down
1 change: 1 addition & 0 deletions src/mbgl/renderer/render_pass.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ enum class RenderPass : uint8_t {
None = 0,
Opaque = 1 << 0,
Translucent = 1 << 1,
Pass3D = 1 << 2,
};

MBGL_CONSTEXPR RenderPass operator|(RenderPass a, RenderPass b) {
Expand Down
3 changes: 1 addition & 2 deletions src/mbgl/renderer/render_static_data.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@
#include <mbgl/gl/index_buffer.hpp>
#include <mbgl/programs/programs.hpp>
#include <mbgl/util/optional.hpp>
#include <mbgl/util/offscreen_texture.hpp>

#include <string>

Expand All @@ -26,7 +25,7 @@ class RenderStaticData {
SegmentVector<RasterAttributes> rasterSegments;
SegmentVector<ExtrusionTextureAttributes> extrusionTextureSegments;

optional<OffscreenTexture> extrusionTexture;
optional<gl::Renderbuffer<gl::RenderbufferType::DepthComponent>> depthRenderbuffer;

Programs programs;

Expand Down
50 changes: 41 additions & 9 deletions src/mbgl/renderer/renderer_impl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -369,6 +369,47 @@ void Renderer::Impl::render(const UpdateParameters& updateParameters) {
parameters.frameHistory.upload(parameters.context, 0);
}

// - PREPARE + CLIP ------------------------------------------------------------------------------
{
MBGL_DEBUG_GROUP(parameters.context, "clip");

// Update all clipping IDs.
for (const auto& entry : renderSources) {
if (entry.second->isEnabled()) {
entry.second->startRender(parameters);
}
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Drawing the stencil mask before the 3D pass doesn't seem necessary since we're not using it, right? Drawing the clipping mask into the stencil buffer before means that we're drawing to the FBO before the clear step, which means that the OpenGL implementation has to restore the previous frame's stencil buffer (and it might potentially produce inaccurate clipping masks).

Copy link
Contributor Author

@lbud lbud Sep 8, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right — I didn't dig into startRender fully, but it looks like it both generates clipping IDs (I don't know how this works) and uploads buffers. (Without moving this step before drawing, the fill-extrusion primitive drawing runs into EXC_BAD_ACCESS here: )
image
And then like I said I know nothing about how the stencil drawing works but it at least reads like the actual rendering of stencil masks is still done in a separate step:

// - CLIPPING MASKS ----------------------------------------------------------------------------
// Draws the clipping masks to the stencil buffer.
{
MBGL_DEBUG_GROUP(parameters.context, "clipping masks");
static const style::FillPaintProperties::PossiblyEvaluated properties {};
static const FillProgram::PaintPropertyBinders paintAttibuteData(properties, 0);
for (const auto& clipID : parameters.clipIDGenerator.getClipIDs()) {

If that's the case, maybe copying the descriptions of this (comments + debug group) exactly is misleading and should be updated to sound more like "upload buffers + generate clipping IDs." Or does uploading and generating clipping IDs need to be separated?

Copy link
Contributor

@kkaefer kkaefer Sep 11, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, it looks like the code in the (old) CLIPPING MASKS section does a few things: startRender collects the clipping IDs (CPU code only, no OpenGL involved here), and uploads buffers (OpenGL is involved here), then proceeds to actually render the clipping mask. I think we can split this up so we have the following code flow:

  • Bind
  • Call upload/startRender
  • 3D pass
  • Clear

Looking at the current code, this is already how it's set up. I assumed that startRender would actually be drawing, but that turned out to be false.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I consolidated this group with the "upload" group above it in 9e786c1.


// - 3D PASS -------------------------------------------------------------------------------------
// Renders any 3D layers bottom-to-top to unique FBOs with texture attachments, but share the same
// depth rbo between them.
{
MBGL_DEBUG_GROUP(parameters.context, "3d");
parameters.backend.bind();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe this call isn't necessary. Binding the backend means that the default framebuffer is going to be bound and that its viewport will be set accordingly. However, in the first call to RenderFillExtrusionLayer::render(), we're calling renderTexture->bind();, which binds the OffscreenTexture's framebuffer.

parameters.pass = RenderPass::Pass3D;

const auto size = parameters.context.viewport.getCurrentValue().size;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

parameters.context contains the value of the current OpenGL state (unless it is dirty). Instead of using this value, we should use the canonical value from parameters.state.getSize(), since the OpenGL state's value could be dirty or set to some other value if we don't bind the backend first (see above).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@kkaefer interesting — when I change this to parameters.state.getSize() here (renderbuffer creation) and in framebuffer/texture creation

if (parameters.staticData.has3D) {
MBGL_DEBUG_GROUP(parameters.context, "3d");
parameters.backend.bind();
parameters.pass = RenderPass::Pass3D;
const auto size = parameters.context.viewport.getCurrentValue().size;
if (!parameters.staticData.depthRenderbuffer ||
parameters.staticData.depthRenderbuffer->size != size) {
parameters.staticData.depthRenderbuffer =
parameters.context.createRenderbuffer<gl::RenderbufferType::DepthComponent>(size);
}
parameters.staticData.depthRenderbuffer->shouldClear(true);

if (parameters.pass == RenderPass::Pass3D) {
const auto size = parameters.context.viewport.getCurrentValue().size;
if (!renderTexture || renderTexture->getSize() != size) {
renderTexture = OffscreenTexture(parameters.context, size, *parameters.staticData.depthRenderbuffer);
}

on my iPhone, it appears you're right that this value is different, but that the dimensions of parameters.state.getSize() are half those of the others (non-retina). If I change them, fill-extrusion layers degrade to non-retina quality…


if (!parameters.staticData.depthRenderbuffer ||
parameters.staticData.depthRenderbuffer->size != size) {
parameters.staticData.depthRenderbuffer =
parameters.context.createRenderbuffer<gl::RenderbufferType::DepthComponent>(size);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Before creating any objects here, we should first check if there are even extrusion layers in the stylesheet. Otherwise, all of those resources + backend binding won't be necessary.

}
parameters.staticData.depthRenderbuffer->dirty = true;

uint32_t i = static_cast<uint32_t>(order.size()) - 1;
for (auto it = order.begin(); it != order.end(); ++it, --i) {
parameters.currentLayer = i;
if (it->layer.hasRenderPass(parameters.pass)) {
MBGL_DEBUG_GROUP(parameters.context, it->layer.getID());
it->layer.render(parameters, it->source);
}
}

// The main backend/framebuffer will be rebound in the clear step.
}

// - CLEAR -------------------------------------------------------------------------------------
// Renders the backdrop of the OpenGL view. This also paints in areas where we don't have any
// tiles whatsoever.
Expand All @@ -385,15 +426,6 @@ void Renderer::Impl::render(const UpdateParameters& updateParameters) {
// - CLIPPING MASKS ----------------------------------------------------------------------------
// Draws the clipping masks to the stencil buffer.
{
MBGL_DEBUG_GROUP(parameters.context, "clip");

// Update all clipping IDs.
for (const auto& entry : renderSources) {
if (entry.second->isEnabled()) {
entry.second->startRender(parameters);
}
}

MBGL_DEBUG_GROUP(parameters.context, "clipping masks");

static const style::FillPaintProperties::PossiblyEvaluated properties {};
Expand Down
Loading