| /* | |
| * 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) |