| /* | |
| * Copyright 2026 Rive | |
| */ | |
| #pragma once | |
| #ifdef RIVE_CANVAS | |
| #include "common/testing_window.hpp" | |
| #include "rive/renderer/render_context.hpp" | |
| #include "rive/renderer/render_context_impl.hpp" | |
| #include "rive/renderer/render_canvas.hpp" | |
| #include "rive/renderer/rive_renderer.hpp" | |
| #include "rive/renderer/cmd/deferred_replayer.hpp" | |
| #include <assert.h> | |
| #include <memory> | |
| // DeferredFrameSink over TestingWindow. Leaves the screen frame open for the | |
| // caller to present via endFrame. | |
| class TestingWindowFrameSink : public rive::cmd::DeferredFrameSink | |
| { | |
| public: | |
| explicit TestingWindowFrameSink( | |
| const TestingWindow::FrameOptions& options) : | |
| m_rc(TestingWindow::Get()->renderContext()), m_options(options) | |
| {} | |
| rive::Factory* factory() override | |
| { | |
| return TestingWindow::Get()->factory(); | |
| } | |
| rive::gpu::RenderContext* renderContext() override { return m_rc; } | |
| // The tools render into the one TestingWindow, so there is a single | |
| // target. | |
| rive::Renderer* beginScreenFrame(uint64_t target) override | |
| { | |
| assert(target == 0); | |
| m_screen = TestingWindow::Get()->beginFrame(m_options); | |
| return m_screen.get(); | |
| } | |
| void beginOreFrame() override { TestingWindow::Get()->beginOreFrame(); } | |
| void endOreFrame() override { TestingWindow::Get()->endOreFrame(); } | |
| rive::Renderer* beginCanvasContent(rive::gpu::RenderCanvas* canvas, | |
| uint32_t clearColor) override | |
| { | |
| m_activeCanvas = canvas; | |
| rive::gpu::RenderContext::FrameDescriptor d{}; | |
| d.renderTargetWidth = canvas->width(); | |
| d.renderTargetHeight = canvas->height(); | |
| d.loadAction = rive::gpu::LoadAction::clear; | |
| d.clearColor = clearColor; | |
| // Canvas content is the same frame as the screen content around it, so | |
| // it has to be drawn under the same rules. | |
| d.triangulationThresholds = m_options.triangulationThresholds; | |
| m_rc->beginFrame(d); | |
| m_canvasRenderer = std::make_unique<rive::RiveRenderer>(m_rc); | |
| return m_canvasRenderer.get(); | |
| } | |
| void endCanvasContent() override | |
| { | |
| if (m_activeCanvas == nullptr) | |
| return; | |
| void* cb = m_rc->impl()->makeCommandBuffer(); | |
| rive::gpu::RenderContext::FlushResources fr{}; | |
| fr.renderTarget = m_activeCanvas->renderTarget(); | |
| fr.externalCommandBuffer = cb; | |
| m_rc->flush(fr); | |
| m_rc->impl()->commitCommandBuffer(cb); | |
| m_canvasRenderer.reset(); | |
| m_activeCanvas = nullptr; | |
| } | |
| private: | |
| rive::gpu::RenderContext* m_rc; | |
| TestingWindow::FrameOptions m_options; | |
| std::unique_ptr<rive::Renderer> m_screen; | |
| std::unique_ptr<rive::RiveRenderer> m_canvasRenderer; | |
| rive::gpu::RenderCanvas* m_activeCanvas = nullptr; | |
| }; | |
| #endif // RIVE_CANVAS |