blob: 4d1d553949db8ad20ddf0fa0d5d11fe6a5948725 [file]
/*
* Copyright 2026 Rive
*/
#if defined(RIVE_CANVAS) && defined(RIVE_ORE)
#include "rive/renderer/cmd/deferred_host.hpp"
#include "rive/renderer/render_canvas.hpp"
#include "rive/renderer/render_context.hpp"
#include "rive/renderer/render_context_impl.hpp"
#include <assert.h>
#include <stdio.h>
#if defined(__ANDROID__)
#include <android/log.h>
// stderr goes nowhere on Android, route diagnostics to logcat.
#define DEFERRED_WARN(...) \
__android_log_print(ANDROID_LOG_ERROR, "Rive", __VA_ARGS__)
#else
#define DEFERRED_WARN(...) fprintf(stderr, __VA_ARGS__)
#endif
namespace rive::cmd
{
rive::Factory* HostFrameSink::factory() { return renderContext(); }
rive::ore::Context* HostFrameSink::oreContext()
{
return m_replayOre ? renderContext()->ore() : nullptr;
}
rive::Renderer* HostFrameSink::beginScreenFrame(uint64_t target)
{
if (target != m_target && !m_acceptAnyScreenTarget)
{
// Another texture's segments reached this sink, so the session frame
// was taken while that texture was still recording. Drop them loudly
// rather than draw them here; the replayer counts them as dropped.
static int strays = 0;
if (strays < 16)
{
strays++;
DEFERRED_WARN("rive deferred: frame carried target %llu into the "
"sink for target %llu, dropping its draws\n",
(unsigned long long)target,
(unsigned long long)m_target);
}
// Native delivery is one host per session frame: hosts must record
// sequentially, so a foreign target here means overlapping opens and
// its draws would silently blank. Loud in debug; the warning above
// is all release gets.
assert(false && "deferred hosts must open and close their screen "
"target sequentially, a neighbor's frame reached "
"this sink");
return nullptr;
}
if (m_screen == nullptr)
{
m_screen = beginScreen(target, m_clear, m_color);
}
return m_screen;
}
void HostFrameSink::beginOreFrame()
{
if (auto* ore = oreContext())
{
ore->beginFrame({});
}
}
void HostFrameSink::endOreFrame()
{
if (auto* ore = oreContext())
{
ore->endFrame();
}
}
void HostFrameSink::afterOreFrame()
{
renderContext()->impl()->scrubStateAfterOre();
}
rive::Renderer* HostFrameSink::beginCanvasContent(
rive::gpu::RenderCanvas* canvas,
uint32_t clearColor)
{
m_activeCanvas = canvas;
auto* rc = renderContext();
// A deferred canvas has no texture until the replaying context gives it
// one, and this is the first thing that renders into it.
rc->impl()->ensureCanvasBacking(canvas);
rive::gpu::RenderContext::FrameDescriptor d{};
d.renderTargetWidth = canvas->width();
d.renderTargetHeight = canvas->height();
d.loadAction = rive::gpu::LoadAction::clear;
d.clearColor = clearColor;
d.disableRasterOrdering = m_disableRasterOrdering;
rc->beginFrame(d);
m_canvasRenderer = std::make_unique<rive::RiveRenderer>(rc);
return m_canvasRenderer.get();
}
void HostFrameSink::endCanvasContent()
{
if (m_activeCanvas == nullptr)
{
return;
}
auto* rc = renderContext();
rive::gpu::RenderContext::FlushResources fr{};
fr.renderTarget = m_activeCanvas->renderTarget();
if (m_useExternalCommandBuffer)
{
void* cb = rc->impl()->makeCommandBuffer();
fr.externalCommandBuffer = cb;
rc->flush(fr);
rc->impl()->commitCommandBuffer(cb);
}
else
{
rc->flush(fr);
}
m_canvasRenderer.reset();
m_activeCanvas = nullptr;
}
void DeferredInlineHost::beginRecord(bool clear, uint32_t color)
{
m_doClear = clear;
m_color = color;
m_session->recordOreReplayMarker();
}
bool DeferredInlineHost::replayOre() const
{
return m_session->oreContext().stream().commandBytes().size() != 0;
}
static void warnDroppedDraws(uint32_t droppedDraws)
{
if (droppedDraws == 0)
{
return;
}
static int drops = 0;
if (drops < 16)
{
drops++;
DEFERRED_WARN("rive deferred: %u draws DROPPED this frame (mixed "
"factories or replay bug)\n",
droppedDraws);
}
}
bool DeferredInlineHost::replayInline(HostFrameSink& sink,
const std::function<void()>& present)
{
// Nothing recorded, keep showing the last presented frame.
if (!m_session->recordedThisFrame())
{
return false;
}
m_replayer.replayFrame(*m_session, sink);
m_session->resetFrame();
if (sink.began())
{
present();
}
warnDroppedDraws(m_replayer.droppedDraws());
return true;
}
} // namespace rive::cmd
#endif // RIVE_CANVAS && RIVE_ORE