blob: 0f7eac708f2cc5e1213acb8c24719c6354daa64b [file]
/*
* Copyright 2026 Rive
*
* ── Non-comparison depth-sample witness ──
*
* Clears a `depth32float` target to 1.0 in a depth-only pass, then samples
* it through a REGULAR sampler with both `textureSampleLevel` (explicit
* lod) and `textureSample` (implicit lod) — the depth-prepass read shape
* (a G-buffer depth reconstruction, a fog raymarch start distance).
*
* This is distinct from `ore_binding_shadow_sampler*`, which use
* `textureSampleCompare` through a `sampler_comparison`. The
* non-comparison read used to break the HLSL SM5.0 bake: SPIRV-Cross
* emitted `SampleCmp` / `SampleCmpLevelZero` for ANY sample of a depth
* image, and FXC rejects those against a `SamplerState` (error X3013), so
* `makeShaderModule` failed on D3D11/D3D12 while every naga-fed backend
* rendered fine. Fixed by `spirv_cross_patches/0001` (only a Dref operand
* selects the Cmp forms).
*
* ── Expected output ──
*
* Solid green — tint(0,1,0,1) × depth(1.0) × depth(1.0).
*/
#include "gm.hpp"
#include "gmutils.hpp"
#include "ore_gm_helper.hpp"
#if defined(ORE_BACKEND_METAL) || defined(ORE_BACKEND_GL) || \
defined(ORE_BACKEND_VK) || defined(ORE_BACKEND_WGPU) || \
defined(ORE_BACKEND_D3D11) || defined(ORE_BACKEND_D3D12) || \
defined(ORE_BACKEND_RHI)
#include "rive/renderer/render_canvas.hpp"
#include "rive/renderer/ore/ore_buffer.hpp"
#include "rive/renderer/ore/ore_bind_group.hpp"
#include "rive/renderer/ore/ore_shader_module.hpp"
#include "rive/renderer/ore/ore_pipeline.hpp"
#include "rive/renderer/ore/ore_render_pass.hpp"
#include "rive/renderer/ore/ore_sampler.hpp"
#include "rive/renderer/ore/ore_texture.hpp"
#endif
using namespace rivegm;
using namespace rive;
using namespace rive::gpu;
#if defined(ORE_BACKEND_METAL) || defined(ORE_BACKEND_GL) || \
defined(ORE_BACKEND_VK) || defined(ORE_BACKEND_WGPU) || \
defined(ORE_BACKEND_D3D11) || defined(ORE_BACKEND_D3D12) || \
defined(ORE_BACKEND_RHI)
using namespace rive::ore;
#define ORE_DEPTH_SAMPLE_ACTIVE
#endif
class OreDepthSampleGM : public GM
{
public:
OreDepthSampleGM() : GM(128, 128) {}
ColorInt clearColor() const override { return 0xff000000; }
void onDraw(rive::Renderer* originalRenderer) override
{
auto renderContext = TestingWindow::Get()->renderContext();
if (!renderContext || !m_ore.ensureContext(renderContext))
return;
#ifdef ORE_DEPTH_SAMPLE_ACTIVE
auto& ctx = *renderContext->getOreContext();
struct Uniforms
{
float tint[4];
};
static const Uniforms kU = {{0.0f, 1.0f, 0.0f, 1.0f}};
BufferDesc uboDesc{};
uboDesc.usage = BufferUsage::uniform;
uboDesc.size = sizeof(Uniforms);
uboDesc.data = &kU;
uboDesc.label = "depth_sample_ubo";
auto uboBuf = ctx.makeBuffer(uboDesc);
TextureDesc depthDesc{};
depthDesc.width = 1;
depthDesc.height = 1;
depthDesc.format = TextureFormat::depth32float;
depthDesc.type = TextureType::texture2D;
depthDesc.renderTarget = true;
depthDesc.numMipmaps = 1;
depthDesc.sampleCount = 1;
depthDesc.label = "depth_sample_depth";
auto depthTex = ctx.makeTexture(depthDesc);
if (!depthTex)
{
fprintf(stderr,
"[ore_depth_sample] depthTex makeTexture failed: %s\n",
ctx.lastError().c_str());
return;
}
TextureViewDesc depthViewDesc{};
depthViewDesc.texture = depthTex.get();
depthViewDesc.dimension = TextureViewDimension::texture2D;
depthViewDesc.baseMipLevel = 0;
depthViewDesc.mipCount = 1;
depthViewDesc.baseLayer = 0;
depthViewDesc.layerCount = 1;
auto depthView = ctx.makeTextureView(depthViewDesc);
if (!depthView)
{
fprintf(stderr,
"[ore_depth_sample] depthView makeTextureView failed: %s\n",
ctx.lastError().c_str());
return;
}
SamplerDesc sampDesc{};
sampDesc.minFilter = Filter::nearest;
sampDesc.magFilter = Filter::nearest;
sampDesc.label = "depth_sample_point";
auto pointSampler = ctx.makeSampler(sampDesc);
auto shader = ore_gm::loadShader(ctx, ore_gm::kDepthSampleWitness);
if (!shader.vsModule || !shader.psModule)
{
fprintf(stderr,
"[ore_depth_sample] loadShader failed: %s\n",
ctx.lastError().c_str());
return;
}
auto layout0 =
ore_gm::makeLayoutFromShader(ctx, shader.psModule.get(), 0);
auto layout1 =
ore_gm::makeLayoutFromShader(ctx, shader.psModule.get(), 1);
BindGroupLayout* layouts[] = {layout0.get(), layout1.get()};
PipelineDesc pipeDesc{};
pipeDesc.vertexModule = shader.vsModule.get();
pipeDesc.fragmentModule = shader.psModule.get();
pipeDesc.vertexEntryPoint = shader.vsEntryPoint;
pipeDesc.fragmentEntryPoint = shader.fsEntryPoint;
pipeDesc.vertexBufferCount = 0;
pipeDesc.topology = PrimitiveTopology::triangleList;
pipeDesc.colorTargets[0].format = TextureFormat::rgba8unorm;
pipeDesc.colorCount = 1;
pipeDesc.bindGroupLayouts = layouts;
pipeDesc.bindGroupLayoutCount = 2;
pipeDesc.label = "ore_depth_sample_pipeline";
std::string pipelineError;
auto pipeline = ctx.makePipeline(pipeDesc, &pipelineError);
if (!pipeline)
{
fprintf(stderr,
"[ore_depth_sample] pipeline creation failed: %s\n",
pipelineError.c_str());
return;
}
BindGroupDesc::UBOEntry ubo0[1]{};
ubo0[0].slot = 0;
ubo0[0].buffer = uboBuf.get();
ubo0[0].offset = 0;
ubo0[0].size = sizeof(Uniforms);
BindGroupDesc bg0Desc{};
bg0Desc.layout = layout0.get();
bg0Desc.ubos = ubo0;
bg0Desc.uboCount = 1;
bg0Desc.label = "depth_sample_bg_uniforms";
auto bg0 = ctx.makeBindGroup(bg0Desc);
if (!bg0)
{
fprintf(stderr,
"[ore_depth_sample] bg0 makeBindGroup failed: %s\n",
ctx.lastError().c_str());
return;
}
BindGroupDesc::TexEntry tex1[1]{};
tex1[0].slot = 1;
tex1[0].view = depthView.get();
BindGroupDesc::SampEntry samp1[1]{};
samp1[0].slot = 0;
samp1[0].sampler = pointSampler.get();
BindGroupDesc bg1Desc{};
bg1Desc.layout = layout1.get();
bg1Desc.textures = tex1;
bg1Desc.textureCount = 1;
bg1Desc.samplers = samp1;
bg1Desc.samplerCount = 1;
bg1Desc.label = "depth_sample_bg_textures";
auto bg1 = ctx.makeBindGroup(bg1Desc);
if (!bg1)
{
fprintf(stderr,
"[ore_depth_sample] bg1 makeBindGroup failed: %s\n",
ctx.lastError().c_str());
return;
}
auto canvas = renderContext->makeRenderCanvas(128, 128);
if (!canvas)
{
fprintf(stderr, "[ore_depth_sample] makeRenderCanvas failed\n");
return;
}
auto canvasTarget = ctx.wrapCanvasTexture(canvas.get());
if (!canvasTarget)
{
fprintf(stderr,
"[ore_depth_sample] wrapCanvasTexture failed: %s\n",
ctx.lastError().c_str());
return;
}
m_ore.beginFrame(renderContext);
// Depth-only clear pass writes the known value the color pass reads.
RenderPassDesc clearDesc{};
clearDesc.colorCount = 0;
clearDesc.depthStencil.view = depthView.get();
clearDesc.depthStencil.depthLoadOp = LoadOp::clear;
clearDesc.depthStencil.depthStoreOp = StoreOp::store;
clearDesc.depthStencil.depthClearValue = 1.0f;
clearDesc.label = "ore_depth_sample_clear_pass";
auto clearPass = ctx.beginRenderPass(clearDesc);
clearPass->finish();
RenderPassDesc rpDesc{};
rpDesc.colorAttachments[0].view = canvasTarget.get();
rpDesc.colorAttachments[0].loadOp = LoadOp::clear;
rpDesc.colorAttachments[0].storeOp = StoreOp::store;
rpDesc.colorAttachments[0].clearColor = {1, 0, 0, 1};
rpDesc.colorCount = 1;
rpDesc.label = "ore_depth_sample_pass";
auto pass = ctx.beginRenderPass(rpDesc);
pass->setPipeline(pipeline.get());
pass->setBindGroup(0, bg0.get());
pass->setBindGroup(1, bg1.get());
pass->setViewport(0, 0, 128, 128);
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 // ORE_DEPTH_SAMPLE_ACTIVE
}
private:
ore_gm::OreGMContext m_ore;
};
GMREGISTER(ore_depth_sample, return new OreDepthSampleGM)