-
Notifications
You must be signed in to change notification settings - Fork 1.3k
Preserve depth buffer between 3D layers + optimize render order #9931
Changes from 5 commits
fb9c316
736359a
aa38666
d965338
7531448
044617d
0efb0b7
4af7bfa
e3c999a
84ec9e2
615328a
f0fa915
9e786c1
f0cabcd
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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 { | ||
|
@@ -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); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Instead of first creating the texture and FBO (inside 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( | ||
|
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -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); | ||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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). There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Right — I didn't dig into mapbox-gl-native/src/mbgl/renderer/renderer_impl.cpp Lines 426 to 434 in 7531448
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? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, it looks like the code in the (old)
Looking at the current code, this is already how it's set up. I assumed that There was a problem hiding this comment. Choose a reason for hiding this commentThe 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(); | ||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 |
||||||||||||||||||||||||||||||||||||||||
parameters.pass = RenderPass::Pass3D; | ||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||
const auto size = parameters.context.viewport.getCurrentValue().size; | ||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @kkaefer interesting — when I change this to mapbox-gl-native/src/mbgl/renderer/renderer_impl.cpp Lines 392 to 404 in 0efb0b7
on my iPhone, it appears you're right that this value is different, but that the dimensions of |
||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||
if (!parameters.staticData.depthRenderbuffer || | ||||||||||||||||||||||||||||||||||||||||
parameters.staticData.depthRenderbuffer->size != size) { | ||||||||||||||||||||||||||||||||||||||||
parameters.staticData.depthRenderbuffer = | ||||||||||||||||||||||||||||||||||||||||
parameters.context.createRenderbuffer<gl::RenderbufferType::DepthComponent>(size); | ||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. | ||||||||||||||||||||||||||||||||||||||||
|
@@ -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 {}; | ||||||||||||||||||||||||||||||||||||||||
|
There was a problem hiding this comment.
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).