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

Port of https://github.com/mapbox/mapbox-gl-js/pull/5101: adds a new render pass `Pass3D` before any other rendering wherein we render layers with 3D passes (fill-extrusion layers) to offscreen framebuffers, sharing a depth renderbuffer between those layers in order to render 3D space correctly. Those framebuffers are saved on the RenderLayers and copied back to the map during the translucent pass. Rendering to offscreen framebuffers before we do any clear + draw means we can avoid expensive framebuffer restores.
This commit is contained in:
Lauren Budorick
2017-09-21 14:26:48 -07:00
committed by GitHub
parent b91d9676a6
commit a9ddf5b7fd
23 changed files with 223 additions and 154 deletions

View File

@@ -35,6 +35,8 @@ public:
// set to the current state.
virtual void bind() = 0;
virtual Size getFramebufferSize() const = 0;
protected:
// Called with the name of an OpenGL extension that should be loaded. RendererBackend implementations
// must call the API-specific version that obtains the function pointer for this function,

View File

@@ -115,6 +115,10 @@ void NativeMapView::bind() {
setViewport(0, 0, getFramebufferSize());
}
mbgl::Size NativeMapView::getFramebufferSize() const {
return { static_cast<uint32_t>(fbWidth), static_cast<uint32_t>(fbHeight) };
}
/**
* From mbgl::RendererBackend.
*/
@@ -1428,10 +1432,6 @@ void NativeMapView::_destroySurface() {
}
}
mbgl::Size NativeMapView::getFramebufferSize() const {
return { static_cast<uint32_t>(fbWidth), static_cast<uint32_t>(fbHeight) };
}
void NativeMapView::updateAssumedState() {
assumeFramebufferBinding(0);
assumeViewport(0, 0, getFramebufferSize());

View File

@@ -58,6 +58,9 @@ public:
// mbgl::RendererBackend //
void bind() override;
mbgl::Size getFramebufferSize() const override;
void updateAssumedState() override;
// Deprecated //
@@ -282,8 +285,6 @@ private:
EGLConfig chooseConfig(const EGLConfig configs[], EGLint numConfigs);
mbgl::Size getFramebufferSize() const;
void updateFps();
private:

View File

@@ -64,6 +64,10 @@ void HeadlessBackend::bind() {
context_.viewport = { 0, 0, size };
}
Size HeadlessBackend::getFramebufferSize() const {
return size;
}
void HeadlessBackend::updateAssumedState() {
// no-op
}

View File

@@ -15,6 +15,7 @@ public:
~HeadlessBackend() override;
void bind() override;
Size getFramebufferSize() const override;
void updateAssumedState() override;
void setSize(Size);

View File

@@ -37,7 +37,7 @@ public:
void invalidate();
mbgl::Size getSize() const;
mbgl::Size getFramebufferSize() const;
mbgl::Size getFramebufferSize() const override;
// mbgl::RendererBackend implementation
void bind() override;

View File

@@ -5464,6 +5464,10 @@ public:
}
}
mbgl::Size getFramebufferSize() const override {
return nativeView.framebufferSize;
}
void onCameraWillChange(mbgl::MapObserver::CameraChangeMode mode) override {
bool animated = mode == mbgl::MapObserver::CameraChangeMode::Animated;
[nativeView cameraWillChangeAnimated:animated];

View File

@@ -2883,6 +2883,10 @@ public:
setViewport(0, 0, nativeView.framebufferSize);
}
mbgl::Size getFramebufferSize() const override {
return nativeView.framebufferSize;
}
mbgl::PremultipliedImage readStillImage() {
return readFramebuffer(nativeView.framebufferSize);
}

View File

@@ -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",

View File

@@ -1505,7 +1505,7 @@ QMapboxGLPrivate::QMapboxGLPrivate(QMapboxGL *q, const QMapboxGLSettings &settin
static_cast<mbgl::GLContextMode>(settings.contextMode())),
*this);
connect(frontend.get(), SIGNAL(updated()), this, SLOT(invalidate()));
mapObj = std::make_unique<mbgl::Map>(
*frontend,
*this, sanitizedSize(size),
@@ -1528,18 +1528,18 @@ QMapboxGLPrivate::~QMapboxGLPrivate()
{
}
mbgl::Size QMapboxGLPrivate::framebufferSize() const {
mbgl::Size QMapboxGLPrivate::getFramebufferSize() const {
return sanitizedSize(fbSize);
}
void QMapboxGLPrivate::updateAssumedState() {
assumeFramebufferBinding(fbObject);
assumeViewport(0, 0, framebufferSize());
assumeViewport(0, 0, getFramebufferSize());
}
void QMapboxGLPrivate::bind() {
setFramebufferBinding(fbObject);
setViewport(0, 0, framebufferSize());
setViewport(0, 0, getFramebufferSize());
}
void QMapboxGLPrivate::invalidate()

View File

@@ -20,10 +20,10 @@ public:
explicit QMapboxGLPrivate(QMapboxGL *, const QMapboxGLSettings &, const QSize &size, qreal pixelRatio);
virtual ~QMapboxGLPrivate();
mbgl::Size framebufferSize() const;
// mbgl::RendererBackend implementation.
void bind() final;
mbgl::Size getFramebufferSize() const final;
void updateAssumedState() final;
void activate() final {}
void deactivate() final {}

View File

@@ -457,6 +457,9 @@ Framebuffer Context::createFramebuffer(const Texture& color) {
Framebuffer
Context::createFramebuffer(const Texture& color,
const Renderbuffer<RenderbufferType::DepthComponent>& depthTarget) {
if (color.size != depthTarget.size) {
throw std::runtime_error("Renderbuffer size mismatch");
}
auto fbo = createFramebuffer();
bindFramebuffer = fbo;
MBGL_CHECK_ERROR(glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, color.texture, 0));

View File

@@ -9,9 +9,23 @@ 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;
void shouldClear(bool clear) {
dirty = clear;
}
bool needsClearing() {
return dirty;
}
private:
bool dirty;
};
} // namespace gl

View File

@@ -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,100 @@ void RenderFillExtrusionLayer::render(PaintParameters& parameters, RenderSource*
return;
}
const auto size = parameters.context.viewport.getCurrentValue().size;
if (parameters.pass == RenderPass::Pass3D) {
const auto& size = parameters.staticData.backendSize;
if (!parameters.staticData.extrusionTexture || parameters.staticData.extrusionTexture->getSize() != size) {
parameters.staticData.extrusionTexture = OffscreenTexture(parameters.context, size, OffscreenTextureAttachment::Depth);
if (!renderTexture || renderTexture->getSize() != size) {
renderTexture = OffscreenTexture(parameters.context, size, *parameters.staticData.depthRenderbuffer);
}
renderTexture->bind();
optional<float> depthClearValue = {};
if (parameters.staticData.depthRenderbuffer->needsClearing()) depthClearValue = 1.0;
// Flag the depth buffer as no longer needing to be cleared for the remainder of this pass.
parameters.staticData.depthRenderbuffer->shouldClear(false);
parameters.context.setStencilMode(gl::StencilMode::disabled());
parameters.context.clear(Color{ 0.0f, 0.0f, 0.0f, 0.0f }, depthClearValue, {});
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());
}
}
} else if (parameters.pass == RenderPass::Translucent) {
parameters.context.bindTexture(renderTexture->getTexture());
const auto& size = parameters.staticData.backendSize;
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());
}
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());
}
} 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.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());
}
}
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());
}
bool RenderFillExtrusionLayer::queryIntersectsFeature(

View File

@@ -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 {
@@ -30,6 +32,8 @@ public:
style::FillExtrusionPaintProperties::PossiblyEvaluated evaluated;
const style::FillExtrusionLayer::Impl& impl() const;
optional<OffscreenTexture> renderTexture;
};
template <>

View File

@@ -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()) },

View File

@@ -60,6 +60,7 @@ public:
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;

View File

@@ -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) {

View File

@@ -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>
@@ -26,7 +25,9 @@ public:
SegmentVector<RasterAttributes> rasterSegments;
SegmentVector<ExtrusionTextureAttributes> extrusionTextureSegments;
optional<OffscreenTexture> extrusionTexture;
optional<gl::Renderbuffer<gl::RenderbufferType::DepthComponent>> depthRenderbuffer;
bool has3D = false;
Size backendSize;
Programs programs;

View File

@@ -13,6 +13,7 @@
#include <mbgl/renderer/render_tile.hpp>
#include <mbgl/renderer/layers/render_background_layer.hpp>
#include <mbgl/renderer/layers/render_custom_layer.hpp>
#include <mbgl/renderer/layers/render_fill_extrusion_layer.hpp>
#include <mbgl/renderer/style_diff.hpp>
#include <mbgl/renderer/query.hpp>
#include <mbgl/renderer/backend_scope.hpp>
@@ -276,6 +277,10 @@ void Renderer::Impl::render(const UpdateParameters& updateParameters) {
RenderLayer* layer = getRenderLayer(layerImpl->id);
assert(layer);
if (!parameters.staticData.has3D && layer->is<RenderFillExtrusionLayer>()) {
parameters.staticData.has3D = true;
}
if (!layer->needsRendering(zoomHistory.lastZoom)) {
continue;
}
@@ -372,6 +377,39 @@ void Renderer::Impl::render(const UpdateParameters& updateParameters) {
parameters.imageManager.upload(parameters.context, 0);
parameters.lineAtlas.upload(parameters.context, 0);
parameters.frameHistory.upload(parameters.context, 0);
// Update all clipping IDs + upload buckets.
for (const auto& entry : renderSources) {
if (entry.second->isEnabled()) {
entry.second->startRender(parameters);
}
}
}
// - 3D PASS -------------------------------------------------------------------------------------
// Renders any 3D layers bottom-to-top to unique FBOs with texture attachments, but share the same
// depth rbo between them.
if (parameters.staticData.has3D) {
parameters.staticData.backendSize = parameters.backend.getFramebufferSize();
MBGL_DEBUG_GROUP(parameters.context, "3d");
parameters.pass = RenderPass::Pass3D;
if (!parameters.staticData.depthRenderbuffer ||
parameters.staticData.depthRenderbuffer->size != parameters.staticData.backendSize) {
parameters.staticData.depthRenderbuffer =
parameters.context.createRenderbuffer<gl::RenderbufferType::DepthComponent>(parameters.staticData.backendSize);
}
parameters.staticData.depthRenderbuffer->shouldClear(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);
}
}
}
// - CLEAR -------------------------------------------------------------------------------------
@@ -390,15 +428,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 {};

View File

@@ -11,20 +11,22 @@ OffscreenTexture& OffscreenTexture::operator=(OffscreenTexture&&) = default;
class OffscreenTexture::Impl {
public:
Impl(gl::Context& context_, const Size size_, OffscreenTextureAttachment type_)
: context(context_), size(std::move(size_)), type(type_) {
Impl(gl::Context& context_, const Size size_)
: context(context_), size(std::move(size_)) {
assert(!size.isEmpty());
}
Impl(gl::Context& context_,
const Size size_,
gl::Renderbuffer<gl::RenderbufferType::DepthComponent>& depth_)
: context(context_), size(std::move(size_)), depth(&depth_) {
assert(!size.isEmpty());
}
void bind() {
if (!framebuffer) {
texture = context.createTexture(size, gl::TextureFormat::RGBA);
if (type == OffscreenTextureAttachment::Depth) {
gl::Renderbuffer<gl::RenderbufferType::DepthComponent> depth =
context.createRenderbuffer<gl::RenderbufferType::DepthComponent>(size);
framebuffer = context.createFramebuffer(*texture, depth);
if (depth) {
framebuffer = context.createFramebuffer(*texture, *depth);
} else {
framebuffer = context.createFramebuffer(*texture);
}
@@ -53,15 +55,21 @@ public:
private:
gl::Context& context;
const Size size;
OffscreenTextureAttachment type;
optional<gl::Framebuffer> framebuffer;
optional<gl::Texture> texture;
gl::Renderbuffer<gl::RenderbufferType::DepthComponent>* depth = nullptr;
};
OffscreenTexture::OffscreenTexture(gl::Context& context,
const Size size)
: impl(std::make_unique<Impl>(context, std::move(size))) {
assert(!size.isEmpty());
}
OffscreenTexture::OffscreenTexture(gl::Context& context,
const Size size,
OffscreenTextureAttachment type)
: impl(std::make_unique<Impl>(context, std::move(size), type)) {
gl::Renderbuffer<gl::RenderbufferType::DepthComponent>& renderbuffer)
: impl(std::make_unique<Impl>(context, std::move(size), renderbuffer)) {
assert(!size.isEmpty());
}

View File

@@ -9,16 +9,13 @@ class Context;
class Texture;
} // namespace gl
enum class OffscreenTextureAttachment {
None,
Depth,
};
class OffscreenTexture {
public:
OffscreenTexture(gl::Context&,
Size size = { 256, 256 },
OffscreenTextureAttachment type = OffscreenTextureAttachment::None);
Size size = { 256, 256 });
OffscreenTexture(gl::Context&,
Size size,
gl::Renderbuffer<gl::RenderbufferType::DepthComponent>&);
~OffscreenTexture();
OffscreenTexture(OffscreenTexture&&);
OffscreenTexture& operator=(OffscreenTexture&&);

View File

@@ -12,6 +12,10 @@ public:
void bind() override {
}
mbgl::Size getFramebufferSize() const override {
return mbgl::Size{};
}
void activate() override {
if (activateFunction) activateFunction();
}
@@ -87,15 +91,15 @@ TEST(BackendScope, NestedScopes) {
TEST(BackendScope, ChainedScopes) {
bool activatedA = false;
bool activatedB = false;
StubRendererBackend backendA;
backendA.activateFunction = [&] { activatedA = true; };
backendA.deactivateFunction = [&] { activatedA = false; };
StubRendererBackend backendB;
backendB.activateFunction = [&] { activatedB = true; };
backendB.deactivateFunction = [&] { activatedB = false; };
{
BackendScope scopeA { backendA };
ASSERT_TRUE(activatedA);
@@ -107,7 +111,7 @@ TEST(BackendScope, ChainedScopes) {
ASSERT_FALSE(activatedB);
ASSERT_TRUE(activatedA);
}
ASSERT_FALSE(activatedA);
ASSERT_FALSE(activatedB);
}