blob: 073191a2cd8a44cf28f19ae82e6d5c32bdfb3f70 [file]
/*
* Copyright 2026 Rive
*/
#pragma once
#ifdef RIVE_CANVAS
#include "common/testing_window.hpp"
#include "rive/renderer/render_canvas.hpp"
#include "rive/renderer/render_context.hpp"
#include "rive/renderer/rive_renderer.hpp"
#include "rive/renderer/cmd/deferred_replayer.hpp"
#include <cassert>
#include <memory>
namespace rive_tests
{
// A DeferredFrameSink that replays a recorded frame against a TestingWindow's
// real render context, offscreen canvas frames included. This is what a test
// needs in order to see the *pixels* a deferred recording produces: the
// GPU-free sinks (deferred_test::TestSink, SerializingFactory) drop canvas
// content instead of rasterizing it.
//
// The window's frame is expected to be already open when the first sink action
// runs -- that frame is flushed and later ones resume with preserve, so the
// caller still ends the frame through the window and reads pixels back the
// usual way.
class TestingWindowFrameSink : public rive::cmd::DeferredFrameSink
{
public:
TestingWindowFrameSink(
TestingWindow* window,
rive::gpu::RenderContext* rc,
const rive::gpu::RenderContext::FrameDescriptor& mainDesc) :
m_window(window), m_rc(rc), m_mainDesc(mainDesc)
{}
rive::Factory* factory() override { return m_window->factory(); }
rive::gpu::RenderContext* renderContext() override { return m_rc; }
// The window owns one main render target.
rive::Renderer* beginScreenFrame(uint64_t target) override
{
assert(target == 0);
flushOpenFrame();
auto d = m_mainDesc;
d.loadAction = rive::gpu::LoadAction::preserveRenderTarget;
m_rc->beginFrame(std::move(d));
m_frameOpen = true;
m_screen = std::make_unique<rive::RiveRenderer>(m_rc);
return m_screen.get();
}
rive::Renderer* beginCanvasContent(rive::gpu::RenderCanvas* canvas,
uint32_t clearColor) override
{
flushOpenFrame();
m_activeCanvas = canvas;
m_canvasFrames++;
auto d = m_mainDesc;
d.renderTargetWidth = canvas->width();
d.renderTargetHeight = canvas->height();
d.loadAction = rive::gpu::LoadAction::clear;
d.clearColor = clearColor;
m_rc->beginFrame(std::move(d));
m_frameOpen = true;
m_canvasRenderer = std::make_unique<rive::RiveRenderer>(m_rc);
return m_canvasRenderer.get();
}
void endCanvasContent() override
{
if (m_activeCanvas == nullptr)
{
return;
}
m_window->flushPLSContext(m_activeCanvas->renderTarget());
m_frameOpen = false;
m_canvasRenderer = nullptr;
m_activeCanvas = nullptr;
}
// Offscreen frames opened while replaying, i.e. how many times the content
// was actually rasterized into a canvas.
size_t canvasFrames() const { return m_canvasFrames; }
private:
// The window (or the previous replay) leaves the main frame open.
void flushOpenFrame()
{
if (!m_flushedWindowFrame || m_frameOpen)
{
m_window->flushPLSContext();
m_flushedWindowFrame = true;
m_frameOpen = false;
}
}
TestingWindow* m_window;
rive::gpu::RenderContext* m_rc;
rive::gpu::RenderContext::FrameDescriptor m_mainDesc;
bool m_flushedWindowFrame = false;
bool m_frameOpen = false;
size_t m_canvasFrames = 0;
std::unique_ptr<rive::RiveRenderer> m_screen;
std::unique_ptr<rive::RiveRenderer> m_canvasRenderer;
rive::gpu::RenderCanvas* m_activeCanvas = nullptr;
};
} // namespace rive_tests
#endif // RIVE_CANVAS