blob: 268a3c7325020a1212f894ea69fd180498b85a0e [file]
/*
* Copyright 2026 Rive
*
* Renders one triangle immediately, via record and replay, and via the
* context's inline deferred flag. The goldens must be byte identical.
*/
#include "gm.hpp"
#include "gmutils.hpp"
#include "ore_gm_helper.hpp"
#if ORE_GM_HAS_BACKEND
#include "rive/renderer/render_canvas.hpp"
#include "rive/renderer/ore/ore_buffer.hpp"
#include "rive/renderer/ore/ore_pipeline.hpp"
#include "rive/renderer/ore/ore_render_pass.hpp"
#include "rive/renderer/ore/cmd/ore_command_buffer.hpp"
#include "rive/renderer/ore/cmd/ore_render_pass_recording.hpp"
#include "rive/renderer/ore/cmd/ore_replay.hpp"
#include "rive/renderer/ore/cmd/ore_deferred_render_pass.hpp"
#endif
using namespace rivegm;
using namespace rive;
using namespace rive::gpu;
#if ORE_GM_HAS_BACKEND
using namespace rive::ore;
// Disambiguates from rive::cmd.
#endif
// kInlineDeferred drives the context deferred flag through the same chooser
// the Lua beginRenderPass call site uses.
enum class ReplayMode
{
kImmediate,
kRecordReplay,
kInlineDeferred,
};
class OreDeferredReplayGM : public GM
{
public:
OreDeferredReplayGM(ReplayMode mode) : GM(256, 256), m_mode(mode) {}
ColorInt clearColor() const override { return 0xff000000; }
void onDraw(rive::Renderer* originalRenderer) override
{
auto renderContext = TestingWindow::Get()->renderContext();
if (!renderContext || !m_ore.ensureContext(renderContext))
return;
#if ORE_GM_HAS_BACKEND
auto& ctx = *renderContext->getOreContext();
auto canvas = renderContext->makeRenderCanvas(256, 256);
if (!canvas)
return;
auto colorTarget = ctx.wrapCanvasTexture(canvas.get());
if (!colorTarget)
return;
BufferDesc bd{};
bd.usage = BufferUsage::vertex;
bd.size = sizeof(ore_gm::kTriVertices);
bd.data = ore_gm::kTriVertices;
bd.label = "ore_deferred_replay_vb";
auto vb = ctx.makeBuffer(bd);
if (!vb)
return;
auto shader = ore_gm::loadShader(ctx, ore_gm::kTriangle);
if (!shader.vsModule)
return;
ore_gm::TrianglePipeline tri(shader,
colorTarget->texture()->format(),
"ore_deferred_replay_pipeline");
auto pipeline = ctx.makePipeline(tri.desc);
if (!pipeline)
{
fprintf(stderr,
"[ore_deferred_replay] pipeline failed: %s\n",
ctx.lastError().c_str());
return;
}
ColorAttachment ca{};
ca.view = colorTarget.get();
ca.loadOp = LoadOp::clear;
ca.storeOp = StoreOp::store;
ca.clearColor = {0.1f, 0.1f, 0.15f, 1.0f};
RenderPassDesc rpDesc{};
rpDesc.colorAttachments[0] = ca;
rpDesc.colorCount = 1;
rpDesc.label = "ore_deferred_replay_pass";
m_ore.beginFrame(renderContext);
if (m_mode == ReplayMode::kRecordReplay)
{
// Same calls as the immediate branch below.
ore::cmd::OreCommandBuffer cmdBuf;
{
ore::cmd::RenderPassRecording rec(&ctx, &cmdBuf, rpDesc);
rec.setPipeline(pipeline.get());
rec.setVertexBuffer(0, vb.get());
rec.setViewport(0, 0, 256, 256);
rec.draw(3);
rec.finish();
}
ore::cmd::replayCommandBuffer(ctx, cmdBuf);
}
else if (m_mode == ReplayMode::kInlineDeferred)
{
// finish records and then inline replays.
ctx.setDeferredRecording(true);
auto pass =
ore::cmd::beginRenderPassRecordingOrImmediate(ctx, rpDesc);
pass->setPipeline(pipeline.get());
pass->setVertexBuffer(0, vb.get());
pass->setViewport(0, 0, 256, 256);
pass->draw(3);
pass->finish();
ctx.setDeferredRecording(false);
}
else
{
auto pass = ctx.beginRenderPass(rpDesc);
pass->setPipeline(pipeline.get());
pass->setVertexBuffer(0, vb.get());
pass->setViewport(0, 0, 256, 256);
pass->draw(3);
pass->finish();
}
m_ore.endFrame(renderContext);
ore_gm::invalidateGLStateAfterOre(renderContext);
originalRenderer->save();
originalRenderer->drawImage(canvas->renderImage(),
{.filter = ImageFilter::nearest},
BlendMode::srcOver,
1);
originalRenderer->restore();
#endif
}
private:
ReplayMode m_mode;
ore_gm::OreGMContext m_ore;
};
GMREGISTER(ore_deferred_replay_immediate,
return new OreDeferredReplayGM(ReplayMode::kImmediate))
GMREGISTER(ore_deferred_replay,
return new OreDeferredReplayGM(ReplayMode::kRecordReplay))
GMREGISTER(ore_deferred_replay_inline,
return new OreDeferredReplayGM(ReplayMode::kInlineDeferred))