Loading libs/hwui/FrameBuilder.cpp +35 −8 Original line number Diff line number Diff line Loading @@ -34,7 +34,7 @@ namespace uirenderer { FrameBuilder::FrameBuilder(const LayerUpdateQueue& layers, const SkRect& clip, uint32_t viewportWidth, uint32_t viewportHeight, const std::vector< sp<RenderNode> >& nodes, const LightGeometry& lightGeometry, const Rect &contentDrawBounds, Caches* caches) const LightGeometry& lightGeometry, const Rect &contentDrawBounds, Caches& caches) : mCanvasState(*this) , mCaches(caches) , mLightRadius(lightGeometry.radius) { Loading Loading @@ -364,15 +364,13 @@ void FrameBuilder::deferShadow(const RenderNodeOp& casterNodeOp) { casterPath = frameAllocatedPath; } if (CC_LIKELY(!mCanvasState.getRenderTargetClipBounds().isEmpty())) { Matrix4 shadowMatrixXY(casterNodeOp.localMatrix); Matrix4 shadowMatrixZ(casterNodeOp.localMatrix); node.applyViewPropertyTransforms(shadowMatrixXY, false); node.applyViewPropertyTransforms(shadowMatrixZ, true); LOG_ALWAYS_FATAL_IF(!mCaches, "Caches needed for shadows"); sp<TessellationCache::ShadowTask> task = mCaches->tessellationCache.getShadowTask( sp<TessellationCache::ShadowTask> task = mCaches.tessellationCache.getShadowTask( mCanvasState.currentTransform(), mCanvasState.getLocalClipBounds(), casterAlpha >= 1.0f, Loading Loading @@ -483,13 +481,14 @@ void FrameBuilder::deferRenderNodeOp(const RenderNodeOp& op) { * Defers an unmergeable, strokeable op, accounting correctly * for paint's style on the bounds being computed. */ void FrameBuilder::deferStrokeableOp(const RecordedOp& op, batchid_t batchId, const BakedOpState* FrameBuilder::deferStrokeableOp(const RecordedOp& op, batchid_t batchId, BakedOpState::StrokeBehavior strokeBehavior) { // Note: here we account for stroke when baking the op BakedOpState* bakedState = BakedOpState::tryStrokeableOpConstruct( mAllocator, *mCanvasState.writableSnapshot(), op, strokeBehavior); if (!bakedState) return; // quick rejected if (!bakedState) return nullptr; // quick rejected currentLayer().deferUnmergeableOp(mAllocator, bakedState, batchId); return bakedState; } /** Loading Loading @@ -607,7 +606,10 @@ void FrameBuilder::deferPatchOp(const PatchOp& op) { } void FrameBuilder::deferPathOp(const PathOp& op) { deferStrokeableOp(op, OpBatchType::Bitmap); auto state = deferStrokeableOp(op, OpBatchType::AlphaMaskTexture); if (CC_LIKELY(state)) { mCaches.pathCache.precache(op.path, op.paint); } } void FrameBuilder::deferPointsOp(const PointsOp& op) { Loading @@ -620,7 +622,12 @@ void FrameBuilder::deferRectOp(const RectOp& op) { } void FrameBuilder::deferRoundRectOp(const RoundRectOp& op) { deferStrokeableOp(op, tessBatchId(op)); auto state = deferStrokeableOp(op, tessBatchId(op)); if (CC_LIKELY(state && !op.paint->getPathEffect())) { // TODO: consider storing tessellation task in BakedOpState mCaches.tessellationCache.precacheRoundRect(state->computedState.transform, *(op.paint), op.unmappedBounds.getWidth(), op.unmappedBounds.getHeight(), op.rx, op.ry); } } void FrameBuilder::deferRoundRectPropsOp(const RoundRectPropsOp& op) { Loading Loading @@ -660,12 +667,28 @@ void FrameBuilder::deferTextOp(const TextOp& op) { } else { currentLayer().deferUnmergeableOp(mAllocator, bakedState, batchId); } FontRenderer& fontRenderer = mCaches.fontRenderer.getFontRenderer(); auto& totalTransform = bakedState->computedState.transform; if (totalTransform.isPureTranslate() || totalTransform.isPerspective()) { fontRenderer.precache(op.paint, op.glyphs, op.glyphCount, SkMatrix::I()); } else { // Partial transform case, see BakedOpDispatcher::renderTextOp float sx, sy; totalTransform.decomposeScale(sx, sy); fontRenderer.precache(op.paint, op.glyphs, op.glyphCount, SkMatrix::MakeScale( roundf(std::max(1.0f, sx)), roundf(std::max(1.0f, sy)))); } } void FrameBuilder::deferTextOnPathOp(const TextOnPathOp& op) { BakedOpState* bakedState = tryBakeUnboundedOpState(op); if (!bakedState) return; // quick rejected currentLayer().deferUnmergeableOp(mAllocator, bakedState, textBatchId(*(op.paint))); mCaches.fontRenderer.getFontRenderer().precache( op.paint, op.glyphs, op.glyphCount, SkMatrix::I()); } void FrameBuilder::deferTextureLayerOp(const TextureLayerOp& op) { Loading Loading @@ -827,5 +850,9 @@ void FrameBuilder::deferEndUnclippedLayerOp(const EndUnclippedLayerOp& /* ignore } } void FrameBuilder::finishDefer() { mCaches.fontRenderer.endPrecaching(); } } // namespace uirenderer } // namespace android libs/hwui/FrameBuilder.h +9 −6 Original line number Diff line number Diff line Loading @@ -65,14 +65,16 @@ public: uint32_t viewportWidth, uint32_t viewportHeight, const std::vector< sp<RenderNode> >& nodes, const LightGeometry& lightGeometry, Caches* caches) : FrameBuilder(layers, clip, viewportWidth, viewportHeight, nodes, lightGeometry, Rect(), caches) {} Caches& caches) : FrameBuilder(layers, clip, viewportWidth, viewportHeight, nodes, lightGeometry, Rect(), caches) {} FrameBuilder(const LayerUpdateQueue& layers, const SkRect& clip, uint32_t viewportWidth, uint32_t viewportHeight, const std::vector< sp<RenderNode> >& nodes, const LightGeometry& lightGeometry, const Rect &contentDrawBounds, Caches* caches); const Rect &contentDrawBounds, Caches& caches); virtual ~FrameBuilder() {} Loading @@ -81,10 +83,10 @@ public: * * It constructs a lookup array of lambdas, which allows a recorded BakeOpState to use * state->op->opId to lookup a receiver that will be called when the op is replayed. * */ template <typename StaticDispatcher, typename Renderer> void replayBakedOps(Renderer& renderer) { finishDefer(); /** * Defines a LUT of lambdas which allow a recorded BakedOpState to use state->op->opId to * dispatch the op via a method on a static dispatcher when the op is replayed. Loading Loading @@ -157,6 +159,7 @@ public: virtual GLuint getTargetFbo() const override { return 0; } private: void finishDefer(); enum class ChildrenSelectMode { Negative, Positive Loading Loading @@ -198,7 +201,7 @@ private: return mAllocator.create<SkPath>(); } void deferStrokeableOp(const RecordedOp& op, batchid_t batchId, const BakedOpState* deferStrokeableOp(const RecordedOp& op, batchid_t batchId, BakedOpState::StrokeBehavior strokeBehavior = BakedOpState::StrokeBehavior::StyleDefined); /** Loading Loading @@ -230,7 +233,7 @@ private: CanvasState mCanvasState; Caches* mCaches = nullptr; Caches& mCaches; float mLightRadius; Loading libs/hwui/renderthread/CanvasContext.cpp +2 −2 Original line number Diff line number Diff line Loading @@ -345,10 +345,10 @@ void CanvasContext::draw() { mEglManager.damageFrame(frame, dirty); #if HWUI_NEW_OPS auto& caches = Caches::getInstance(); FrameBuilder frameBuilder(mLayerUpdateQueue, dirty, frame.width(), frame.height(), mRenderNodes, mLightGeometry, mContentDrawBounds, &Caches::getInstance()); mRenderNodes, mLightGeometry, mContentDrawBounds, caches); mLayerUpdateQueue.clear(); auto&& caches = Caches::getInstance(); BakedOpRenderer renderer(caches, mRenderThread.renderState(), mOpaque, mLightInfo); frameBuilder.replayBakedOps<BakedOpDispatcher>(renderer); Loading libs/hwui/tests/common/scenes/GlyphStressAnimation.cpp 0 → 100644 +64 −0 Original line number Diff line number Diff line /* * Copyright (C) 2015 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "TestSceneBase.h" #include "utils/Color.h" #include <minikin/Layout.h> #include <hwui/Paint.h> #include <cstdio> class GlyphStressAnimation; static TestScene::Registrar _GlyphStress(TestScene::Info{ "glyphstress", "A stress test for both the glyph cache, and glyph rendering.", TestScene::simpleCreateScene<GlyphStressAnimation> }); class GlyphStressAnimation : public TestScene { public: sp<RenderNode> container; void createContent(int width, int height, TestCanvas& canvas) override { container = TestUtils::createNode(0, 0, width, height, nullptr); doFrame(0); // update container canvas.drawColor(Color::White, SkXfermode::kSrcOver_Mode); canvas.drawRenderNode(container.get()); } void doFrame(int frameNr) override { std::unique_ptr<uint16_t[]> text = TestUtils::utf8ToUtf16( "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"); ssize_t textLength = 26 * 2; TestCanvas canvas( container->stagingProperties().getWidth(), container->stagingProperties().getHeight()); Paint paint; paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); paint.setAntiAlias(true); paint.setColor(Color::Black); for (int i = 0; i < 5; i++) { paint.setTextSize(10 + (frameNr % 20) + i * 20); canvas.drawText(text.get(), 0, textLength, textLength, 0, 100 * (i + 2), kBidi_Force_LTR, paint, nullptr); } container->setStagingDisplayList(canvas.finishRecording()); } }; libs/hwui/tests/microbench/FrameBuilderBench.cpp +4 −4 Original line number Diff line number Diff line Loading @@ -65,7 +65,7 @@ void BM_FrameBuilder_defer(benchmark::State& state) { auto nodes = createTestNodeList(); while (state.KeepRunning()) { FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 200), 100, 200, nodes, sLightGeometry, nullptr); nodes, sLightGeometry, Caches::getInstance()); benchmark::DoNotOptimize(&frameBuilder); } } Loading @@ -80,7 +80,7 @@ void BM_FrameBuilder_deferAndRender(benchmark::State& state) { while (state.KeepRunning()) { FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 200), 100, 200, nodes, sLightGeometry, nullptr); nodes, sLightGeometry, Caches::getInstance()); BakedOpRenderer renderer(caches, renderState, true, sLightInfo); frameBuilder.replayBakedOps<BakedOpDispatcher>(renderer); Loading Loading @@ -119,7 +119,7 @@ void BM_FrameBuilder_defer_scene(benchmark::State& state) { while (state.KeepRunning()) { FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(gDisplay.w, gDisplay.h), gDisplay.w, gDisplay.h, nodes, sLightGeometry, nullptr); nodes, sLightGeometry, Caches::getInstance()); benchmark::DoNotOptimize(&frameBuilder); } } Loading @@ -137,7 +137,7 @@ void BM_FrameBuilder_deferAndRender_scene(benchmark::State& state) { while (state.KeepRunning()) { FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(gDisplay.w, gDisplay.h), gDisplay.w, gDisplay.h, nodes, sLightGeometry, nullptr); nodes, sLightGeometry, Caches::getInstance()); BakedOpRenderer renderer(caches, renderState, true, sLightInfo); frameBuilder.replayBakedOps<BakedOpDispatcher>(renderer); Loading Loading
libs/hwui/FrameBuilder.cpp +35 −8 Original line number Diff line number Diff line Loading @@ -34,7 +34,7 @@ namespace uirenderer { FrameBuilder::FrameBuilder(const LayerUpdateQueue& layers, const SkRect& clip, uint32_t viewportWidth, uint32_t viewportHeight, const std::vector< sp<RenderNode> >& nodes, const LightGeometry& lightGeometry, const Rect &contentDrawBounds, Caches* caches) const LightGeometry& lightGeometry, const Rect &contentDrawBounds, Caches& caches) : mCanvasState(*this) , mCaches(caches) , mLightRadius(lightGeometry.radius) { Loading Loading @@ -364,15 +364,13 @@ void FrameBuilder::deferShadow(const RenderNodeOp& casterNodeOp) { casterPath = frameAllocatedPath; } if (CC_LIKELY(!mCanvasState.getRenderTargetClipBounds().isEmpty())) { Matrix4 shadowMatrixXY(casterNodeOp.localMatrix); Matrix4 shadowMatrixZ(casterNodeOp.localMatrix); node.applyViewPropertyTransforms(shadowMatrixXY, false); node.applyViewPropertyTransforms(shadowMatrixZ, true); LOG_ALWAYS_FATAL_IF(!mCaches, "Caches needed for shadows"); sp<TessellationCache::ShadowTask> task = mCaches->tessellationCache.getShadowTask( sp<TessellationCache::ShadowTask> task = mCaches.tessellationCache.getShadowTask( mCanvasState.currentTransform(), mCanvasState.getLocalClipBounds(), casterAlpha >= 1.0f, Loading Loading @@ -483,13 +481,14 @@ void FrameBuilder::deferRenderNodeOp(const RenderNodeOp& op) { * Defers an unmergeable, strokeable op, accounting correctly * for paint's style on the bounds being computed. */ void FrameBuilder::deferStrokeableOp(const RecordedOp& op, batchid_t batchId, const BakedOpState* FrameBuilder::deferStrokeableOp(const RecordedOp& op, batchid_t batchId, BakedOpState::StrokeBehavior strokeBehavior) { // Note: here we account for stroke when baking the op BakedOpState* bakedState = BakedOpState::tryStrokeableOpConstruct( mAllocator, *mCanvasState.writableSnapshot(), op, strokeBehavior); if (!bakedState) return; // quick rejected if (!bakedState) return nullptr; // quick rejected currentLayer().deferUnmergeableOp(mAllocator, bakedState, batchId); return bakedState; } /** Loading Loading @@ -607,7 +606,10 @@ void FrameBuilder::deferPatchOp(const PatchOp& op) { } void FrameBuilder::deferPathOp(const PathOp& op) { deferStrokeableOp(op, OpBatchType::Bitmap); auto state = deferStrokeableOp(op, OpBatchType::AlphaMaskTexture); if (CC_LIKELY(state)) { mCaches.pathCache.precache(op.path, op.paint); } } void FrameBuilder::deferPointsOp(const PointsOp& op) { Loading @@ -620,7 +622,12 @@ void FrameBuilder::deferRectOp(const RectOp& op) { } void FrameBuilder::deferRoundRectOp(const RoundRectOp& op) { deferStrokeableOp(op, tessBatchId(op)); auto state = deferStrokeableOp(op, tessBatchId(op)); if (CC_LIKELY(state && !op.paint->getPathEffect())) { // TODO: consider storing tessellation task in BakedOpState mCaches.tessellationCache.precacheRoundRect(state->computedState.transform, *(op.paint), op.unmappedBounds.getWidth(), op.unmappedBounds.getHeight(), op.rx, op.ry); } } void FrameBuilder::deferRoundRectPropsOp(const RoundRectPropsOp& op) { Loading Loading @@ -660,12 +667,28 @@ void FrameBuilder::deferTextOp(const TextOp& op) { } else { currentLayer().deferUnmergeableOp(mAllocator, bakedState, batchId); } FontRenderer& fontRenderer = mCaches.fontRenderer.getFontRenderer(); auto& totalTransform = bakedState->computedState.transform; if (totalTransform.isPureTranslate() || totalTransform.isPerspective()) { fontRenderer.precache(op.paint, op.glyphs, op.glyphCount, SkMatrix::I()); } else { // Partial transform case, see BakedOpDispatcher::renderTextOp float sx, sy; totalTransform.decomposeScale(sx, sy); fontRenderer.precache(op.paint, op.glyphs, op.glyphCount, SkMatrix::MakeScale( roundf(std::max(1.0f, sx)), roundf(std::max(1.0f, sy)))); } } void FrameBuilder::deferTextOnPathOp(const TextOnPathOp& op) { BakedOpState* bakedState = tryBakeUnboundedOpState(op); if (!bakedState) return; // quick rejected currentLayer().deferUnmergeableOp(mAllocator, bakedState, textBatchId(*(op.paint))); mCaches.fontRenderer.getFontRenderer().precache( op.paint, op.glyphs, op.glyphCount, SkMatrix::I()); } void FrameBuilder::deferTextureLayerOp(const TextureLayerOp& op) { Loading Loading @@ -827,5 +850,9 @@ void FrameBuilder::deferEndUnclippedLayerOp(const EndUnclippedLayerOp& /* ignore } } void FrameBuilder::finishDefer() { mCaches.fontRenderer.endPrecaching(); } } // namespace uirenderer } // namespace android
libs/hwui/FrameBuilder.h +9 −6 Original line number Diff line number Diff line Loading @@ -65,14 +65,16 @@ public: uint32_t viewportWidth, uint32_t viewportHeight, const std::vector< sp<RenderNode> >& nodes, const LightGeometry& lightGeometry, Caches* caches) : FrameBuilder(layers, clip, viewportWidth, viewportHeight, nodes, lightGeometry, Rect(), caches) {} Caches& caches) : FrameBuilder(layers, clip, viewportWidth, viewportHeight, nodes, lightGeometry, Rect(), caches) {} FrameBuilder(const LayerUpdateQueue& layers, const SkRect& clip, uint32_t viewportWidth, uint32_t viewportHeight, const std::vector< sp<RenderNode> >& nodes, const LightGeometry& lightGeometry, const Rect &contentDrawBounds, Caches* caches); const Rect &contentDrawBounds, Caches& caches); virtual ~FrameBuilder() {} Loading @@ -81,10 +83,10 @@ public: * * It constructs a lookup array of lambdas, which allows a recorded BakeOpState to use * state->op->opId to lookup a receiver that will be called when the op is replayed. * */ template <typename StaticDispatcher, typename Renderer> void replayBakedOps(Renderer& renderer) { finishDefer(); /** * Defines a LUT of lambdas which allow a recorded BakedOpState to use state->op->opId to * dispatch the op via a method on a static dispatcher when the op is replayed. Loading Loading @@ -157,6 +159,7 @@ public: virtual GLuint getTargetFbo() const override { return 0; } private: void finishDefer(); enum class ChildrenSelectMode { Negative, Positive Loading Loading @@ -198,7 +201,7 @@ private: return mAllocator.create<SkPath>(); } void deferStrokeableOp(const RecordedOp& op, batchid_t batchId, const BakedOpState* deferStrokeableOp(const RecordedOp& op, batchid_t batchId, BakedOpState::StrokeBehavior strokeBehavior = BakedOpState::StrokeBehavior::StyleDefined); /** Loading Loading @@ -230,7 +233,7 @@ private: CanvasState mCanvasState; Caches* mCaches = nullptr; Caches& mCaches; float mLightRadius; Loading
libs/hwui/renderthread/CanvasContext.cpp +2 −2 Original line number Diff line number Diff line Loading @@ -345,10 +345,10 @@ void CanvasContext::draw() { mEglManager.damageFrame(frame, dirty); #if HWUI_NEW_OPS auto& caches = Caches::getInstance(); FrameBuilder frameBuilder(mLayerUpdateQueue, dirty, frame.width(), frame.height(), mRenderNodes, mLightGeometry, mContentDrawBounds, &Caches::getInstance()); mRenderNodes, mLightGeometry, mContentDrawBounds, caches); mLayerUpdateQueue.clear(); auto&& caches = Caches::getInstance(); BakedOpRenderer renderer(caches, mRenderThread.renderState(), mOpaque, mLightInfo); frameBuilder.replayBakedOps<BakedOpDispatcher>(renderer); Loading
libs/hwui/tests/common/scenes/GlyphStressAnimation.cpp 0 → 100644 +64 −0 Original line number Diff line number Diff line /* * Copyright (C) 2015 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "TestSceneBase.h" #include "utils/Color.h" #include <minikin/Layout.h> #include <hwui/Paint.h> #include <cstdio> class GlyphStressAnimation; static TestScene::Registrar _GlyphStress(TestScene::Info{ "glyphstress", "A stress test for both the glyph cache, and glyph rendering.", TestScene::simpleCreateScene<GlyphStressAnimation> }); class GlyphStressAnimation : public TestScene { public: sp<RenderNode> container; void createContent(int width, int height, TestCanvas& canvas) override { container = TestUtils::createNode(0, 0, width, height, nullptr); doFrame(0); // update container canvas.drawColor(Color::White, SkXfermode::kSrcOver_Mode); canvas.drawRenderNode(container.get()); } void doFrame(int frameNr) override { std::unique_ptr<uint16_t[]> text = TestUtils::utf8ToUtf16( "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"); ssize_t textLength = 26 * 2; TestCanvas canvas( container->stagingProperties().getWidth(), container->stagingProperties().getHeight()); Paint paint; paint.setTextEncoding(SkPaint::kGlyphID_TextEncoding); paint.setAntiAlias(true); paint.setColor(Color::Black); for (int i = 0; i < 5; i++) { paint.setTextSize(10 + (frameNr % 20) + i * 20); canvas.drawText(text.get(), 0, textLength, textLength, 0, 100 * (i + 2), kBidi_Force_LTR, paint, nullptr); } container->setStagingDisplayList(canvas.finishRecording()); } };
libs/hwui/tests/microbench/FrameBuilderBench.cpp +4 −4 Original line number Diff line number Diff line Loading @@ -65,7 +65,7 @@ void BM_FrameBuilder_defer(benchmark::State& state) { auto nodes = createTestNodeList(); while (state.KeepRunning()) { FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 200), 100, 200, nodes, sLightGeometry, nullptr); nodes, sLightGeometry, Caches::getInstance()); benchmark::DoNotOptimize(&frameBuilder); } } Loading @@ -80,7 +80,7 @@ void BM_FrameBuilder_deferAndRender(benchmark::State& state) { while (state.KeepRunning()) { FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(100, 200), 100, 200, nodes, sLightGeometry, nullptr); nodes, sLightGeometry, Caches::getInstance()); BakedOpRenderer renderer(caches, renderState, true, sLightInfo); frameBuilder.replayBakedOps<BakedOpDispatcher>(renderer); Loading Loading @@ -119,7 +119,7 @@ void BM_FrameBuilder_defer_scene(benchmark::State& state) { while (state.KeepRunning()) { FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(gDisplay.w, gDisplay.h), gDisplay.w, gDisplay.h, nodes, sLightGeometry, nullptr); nodes, sLightGeometry, Caches::getInstance()); benchmark::DoNotOptimize(&frameBuilder); } } Loading @@ -137,7 +137,7 @@ void BM_FrameBuilder_deferAndRender_scene(benchmark::State& state) { while (state.KeepRunning()) { FrameBuilder frameBuilder(sEmptyLayerUpdateQueue, SkRect::MakeWH(gDisplay.w, gDisplay.h), gDisplay.w, gDisplay.h, nodes, sLightGeometry, nullptr); nodes, sLightGeometry, Caches::getInstance()); BakedOpRenderer renderer(caches, renderState, true, sLightInfo); frameBuilder.replayBakedOps<BakedOpDispatcher>(renderer); Loading