Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 63 additions & 0 deletions docs/evidence/windows-dx12-indirect-v1.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
# Windows DX12 draw identity and visibility oracle

Executing the Windows shared tests exposed two failures hidden by the previous
CI shell setup. Both reproduce on AMD Radeon 760M DX12 and Microsoft WARP,
with either FXC or DXC. The original full pixel checks pass under Vulkan.

## Counted indirect drawing

The GPU-generated commands contain the correct first-instance values, but the
DX12 counted path renders only the first identity. Submitting the same commands
through ordinary multi-draw or individual indirect calls passes the original
four-pixel identity oracle. Inspection of wgpu-hal 29.0.1 shows that counted
submission uses the common command signature, while ordinary indirect submission
uses the pipeline signature carrying the special first-vertex/instance constants.
The 29.0.4 implementation retains that count path.

Bloom now uses its existing bounded ordinary multi-draw fallback for DX12.
One device/backend policy governs static GPU-driven submission, virtual draw
emission, and virtual visibility rasterization. Reported counted-submission
support reflects that policy. Vulkan retains counted submission when enabled;
Metal retains its existing fallback. This is a correctness workaround; no DX12
performance improvement or upstream HAL fix is claimed.

## Visibility reconstruction oracle

A reduced shader isolates pipeline error `0x80070057` to the combination of
fragment `position` and `primitive_index`. Each input independently creates a
valid pipeline. This fails with either compiler on both DX12 adapters. The
production visibility ID raster does not consume fragment position; its
reconstruction runs separately.

The raster oracle carries linearly interpolated NDC and recovers the exact
pixel center before calling the unchanged production barycentric function.
Recovering the center is necessary because interpolation adds subpixel error;
a direct interpolated-coordinate control fails the original limits even under
Vulkan. Draw/primitive identity, opposite face orientation, clear sentinel,
nonuniform clip W, and the original `2e-5` barycentric limit remain checked.
No pipeline-only diagnostic, alternate shader injection, or unconditional test
success is retained in the source change.

## Validation and limits

Both focused original GPU oracles pass in five configurations: Radeon Vulkan,
Radeon DX12/FXC, Radeon DX12/DXC, WARP/FXC, and WARP/DXC (10 executions).
Adapter identities and successful execution are checked in the retained logs.
The affected test helpers now honor `WGPU_BACKEND`, shader compiler options,
and the test-only `BLOOM_TEST_FORCE_FALLBACK_ADAPTER` selector.

The initial full library runs pass on DX12 and Vulkan: 488 passed, zero failed,
one ignored. For the DX12 run, a process-local Vulkan loader override prevents
older test helpers using `Backends::all` from selecting Vulkan. All 32 emitted
adapter identities are DX12. No persistent graphics-driver setting changes.
The final patch also exercises the production virtual-visibility path with its
default submission choice as well as the explicitly forced binned path.
Full shared-suite and hosted validation of that final patch are in progress. The earlier hosted
access violation remains unresolved; subsequent non-crashing executions do not
establish a fix. Unsupported adapter features and missing optional asset fixtures
remain explicit limitations, and no unavailable hardware acceptance is claimed.

Diagnostic controls, original failures, candidate patches, commands, and results
are retained in `tools/quality/out/windows-engine-plan/hosted-windows-gpu/`.
The [native example build evidence](windows-example-gate-v1.md) is published
separately and does not qualify executable startup or clean installation.
22 changes: 13 additions & 9 deletions docs/evidence/windows-example-ci-v1.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,9 @@ The remaining `perry-embed` failure is independent. Perry 0.5.1182 does not
support the example's `bloomViewGetNativeHandle` call. Official 0.5.1219 supports
that API but its prebuilt standard library fails to link missing HTTP extension
symbols. Official 0.5.1220 had already exposed the same standard-library issue
in the original #153 work. The embedded-view example remains unqualified;
compiler source/build investigation continues rather than removing the example
from the required inventory.
in the original #153 work. The matching source-built runtime in the
[native example gate](windows-example-gate-v1.md) now links all 20 examples,
including the embedded view. Actual embedded-view startup remains unqualified.

## Hosted Windows execution gap

Expand All @@ -33,21 +33,25 @@ The Tests workflow now selects Bash explicitly and requires the emitted shared
test summary on every host and the native-build summary on Windows. Artifact
upload fails when a summary is absent, making another silent non-execution a
failure. The CI command contract checks both the shell and required evidence.
Hosted execution on this correction still needs verification from actual Cargo
output and summary contents.
Hosted execution is now verified from Cargo output and retained summaries.

The first hosted run at `88bc633` executes Cargo and uploads both failure
summaries. It exposes a second setup defect: Git Bash prepends its own
`/usr/bin/link.exe` ahead of the MSVC linker. Cargo then invokes the hard-link
utility with Microsoft linker arguments. The shared CI entry point now restores
the active Visual Studio developer toolchain ahead of Git's tools and fails
with an actionable message if that configured linker is missing. The next
hosted run must verify the build and shared suite with that correction.
with an actionable message if that configured linker is missing. At `636b69a`, the next hosted native build passes. The shared suite
exposes two DX12 GPU failures and an access violation. At `59244b9`, a complete
shared run reports 486 passed, two failed, and one ignored; the earlier crash
is not thereby fixed. The [DX12 follow-up](windows-dx12-indirect-v1.md) retains
the reproductions and corrective work.

Audit commands, original failure logs, compiler-release metadata, source patch,
and the six corrected executable hashes are retained under
`tools/quality/out/windows-engine-plan/all-examples/`. The original audit is also
included in the [#159 evidence release](https://github.com/Bloom-Engine/engine/releases/tag/quality-evidence-ssgi-surface-20260911).

This is progress on #140/#142/#74. All-example PR compilation, real starter and
embedded-view startup, and clean package installation are still required.
The [published build evidence](https://github.com/Bloom-Engine/engine/releases/tag/quality-evidence-windows-examples-20260911)
retains these results and the all-20 local/hosted native links. This advances
#140/#142/#74. Real starter and embedded-view startup, clean package installation,
and the complete Windows shared suite remain required.
12 changes: 11 additions & 1 deletion docs/evidence/windows-example-gate-v1.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,12 @@ old output or object-only compilation cannot count as success. Failures and
timeouts do not hide later example results.

Three orchestration regressions, the full quality-contract component, and
repository contracts pass locally. Hosted example execution is pending.
repository contracts pass locally. At exact source `59244b9`, the local full
CI component passes all 20 links in 87.703 seconds; quick links Pong in 4.703
seconds. Hosted [Tests run 34544031931](https://github.com/Bloom-Engine/engine/actions/runs/34544031931)
passes the native engine build and all 20 links in 194.778 seconds. Its merge
checkout `962f8b4` has the same Git tree as the tested PR head. These build times
are observations. Source receipts and per-example hashes are retained.

The preceding [Windows CI correction](windows-example-ci-v1.md) now proves an
actual hosted native build at `636b69a`. Its shared suite exposes two DX12 GPU
Expand All @@ -51,3 +56,8 @@ No broader CI, startup, packaging, or hardware issue is closed by this report.
Commands, failed profiles, successful executable hashes, setup receipts, and
logs are retained under
`tools/quality/out/windows-engine-plan/all-examples/`.

The [published report and ZIP](https://github.com/Bloom-Engine/engine/releases/tag/quality-evidence-windows-examples-20260911)
contain 287 verified payloads, including failures and successful local/hosted
receipts. The archive is 9,940,053 bytes with SHA-256
`2ebcb258a72e473723331dcd9fbaad1781b36d0ea70b0e95a3bcfd90fcf0a9b9`.
6 changes: 3 additions & 3 deletions docs/windows-engine-plan.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ first nine-scene Radeon evidence are in draft PR #154. Follow-up work starts at
| #127 Vulkan PT correctness | Three deterministic progressive and motion runs, both negative controls, finite intermediates, reset/lighting/rigid-motion checks, retained report | Canonical hardware gate, all four focused temporal tests, and CPU reference sanity check pass on Radeon/Vulkan; [report](evidence/issue-127-windows-vulkan-v1.md) and [raw evidence](https://github.com/Bloom-Engine/engine/releases/tag/quality-evidence-155-windows-vulkan-20260910) published |
| #128 Windows image discrepancies | Identify the first incorrect stage or document a reviewed backend-specific baseline decision; rerun the full strict corpus and reproducibility checks | Cutout and surface corrections pass all nine Radeon images. At #159 source `d610d6a`, full runs 2 and 3 pass every configured check and reproduce 257 artifacts byte-identically with matching metadata and timing differences inside existing noise bounds. Earlier invalid runs retain their failures; named hardware acceptance remains separate |
| #135 / #149 temporal reconstruction | Enforced motion/producer/quality-preset corpus, representative scenes, fractional/native and frozen A/B timing, memory/resize checks, platform evidence | Device/resource, stationary SSGI, and profiler fixes are retained. The surface correction passes original HD startup limits and 154,720 analytic receiver checks on Vulkan, DX12, and hosted Metal; 93 local goldens pass, including lighting recovery. The full Radeon corpus passes twice. Wider representative scenes, frozen A/B performance, memory/resize, and platform acceptance remain open |
| #140 integration gates | Same required local/hosted lanes pass on exact source; release package startup and all-example evidence | #159 has 24 successful CheckRun statuses, but Windows logs reveal no Cargo execution because Bash was invoked through PowerShell without an explicit shell. Local Windows execution is verified. This follow-up selects Bash and requires execution-summary artifacts; actual hosted validation is pending. All-example PR compilation and release-install acceptance remain separate requirements |
| #140 integration gates | Same required local/hosted lanes pass on exact source; release package startup and all-example evidence | #160 fixes silent Windows CI non-execution and MSVC PATH ordering. #161 passes the actual native engine build and all 20 native links locally and in hosted CI; [evidence](https://github.com/Bloom-Engine/engine/releases/tag/quality-evidence-windows-examples-20260911) is published. Two real DX12 shared-test failures and an earlier access violation remain under investigation. Release startup/install acceptance remains open |
| #138 capability fallback | Actual constrained-adapter startup and relevant forced-tier corpus, truthful capability outputs | Existing implementation/evidence preserved; physical constrained-limit acceptance still needs proof |
| PR integration | Reviewable changes, passing required checks, full issue evidence, merge-ready rendering branch | #147 and the stacked fixes #154–#159 remain drafts; no merge performed |

Expand Down Expand Up @@ -64,8 +64,8 @@ audit are saved in `tools/quality/out/windows-engine-plan/plan-requirements.json
Windows installation. The [native example gate](evidence/windows-example-gate-v1.md)
passes all 20 links locally using Perry 0.5.1220 and one matching source-built
runtime profile. It adds required full-lane Windows PR compilation and
rejects missing or stale executable outputs. Hosted example validation,
actual startup, and clean package installation remain required.
rejects missing or stale executable outputs. Hosted example validation also passes at `59244b9`; actual startup and clean
package installation remain required.
3. Complete the wider temporal/geometry, performance, memory, resize, and
capability corpus. The
[HD surface correction](evidence/windows-ssgi-surface-v1.md) and two valid
Expand Down
16 changes: 13 additions & 3 deletions native/shared/src/renderer/gpu_driven.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,18 @@ pub fn request_features_if_supported(supported: wgpu::Features, required: &mut w
super::visibility_buffer::request_feature_if_supported(supported, required);
}

/// Whether counted indirect submission preserves Bloom's draw identities.
/// wgpu 29's DX12 count path uses the common command signature and loses the
/// per-command first vertex/instance constants. Ordinary multi-draw uses the
/// pipeline signature correctly. Keep DX12 on the existing bounded fallback
/// until the counted path passes the GPU identity oracle with an updated HAL.
pub(crate) fn supports_indirect_count(device: &wgpu::Device) -> bool {
device.adapter_info().backend != wgpu::Backend::Dx12
&& device
.features()
.contains(wgpu::Features::MULTI_DRAW_INDIRECT_COUNT)
}

#[derive(Copy, Clone, Debug, Default, Eq, PartialEq)]
pub struct GeometrySlice {
pub vertex_offset: u64,
Expand Down Expand Up @@ -394,9 +406,7 @@ impl GpuDrivenRenderer {
let tier_ready = global_material_layout.is_some();
let enabled =
cfg!(not(target_arch = "wasm32")) && feature_ready && tier_ready && !forced_off;
let count_supported = device
.features()
.contains(wgpu::Features::MULTI_DRAW_INDIRECT_COUNT);
let count_supported = supports_indirect_count(device);
let routed_visibility = super::visibility_buffer::requested_mode().shades() && enabled;
let draw_capacity = 64;
let draw_buffer = create_draw_buffer(device, draw_capacity);
Expand Down
32 changes: 24 additions & 8 deletions native/shared/src/renderer/visibility_buffer_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -157,12 +157,16 @@ fn large_vertex_arenas_split_at_aligned_storage_binding_boundaries() {
#[cfg(not(target_arch = "wasm32"))]
fn try_device(required_features: wgpu::Features) -> Option<(wgpu::Device, wgpu::Queue)> {
let instance = wgpu::Instance::new(wgpu::InstanceDescriptor {
backends: wgpu::Backends::all(),
backends: wgpu::Backends::from_env().unwrap_or(wgpu::Backends::all()),
backend_options: wgpu::BackendOptions::from_env_or_default(),
..wgpu::InstanceDescriptor::new_without_display_handle()
});
let adapter =
pollster::block_on(instance.request_adapter(&wgpu::RequestAdapterOptions::default()))
.ok()?;
let adapter = pollster::block_on(instance.request_adapter(&wgpu::RequestAdapterOptions {
force_fallback_adapter: std::env::var_os("BLOOM_TEST_FORCE_FALLBACK_ADAPTER").is_some(),
..Default::default()
}))
.ok()?;
eprintln!("GPU oracle adapter: {:?}", adapter.get_info());
if !adapter.features().contains(required_features) {
eprintln!("adapter lacks required visibility-oracle features");
return None;
Expand Down Expand Up @@ -218,10 +222,17 @@ fn gpu_raster_ids_faces_and_reconstruction_match_the_cpu_oracle() {
[2.0, 3.2, 2.0, 4.0],
[1.8, -1.6, 1.0, 2.0],
];
// wgpu 29's DX12 HLSL interface cannot link fragment Position together
// with PrimitiveIndex. Carry linear NDC instead, then recover the exact
// pixel center: raster interpolation has subpixel precision and must not
// add coordinate error to the production reconstruction being checked.
let shader_source = format!(
"enable primitive_index;\n\
{RECONSTRUCTION_WGSL}\n\
struct VertexOut {{ @builtin(position) position: vec4<f32>, }};\n\
struct VertexOut {{\n\
@builtin(position) position: vec4<f32>,\n\
@location(0) @interpolate(linear) ndc: vec2<f32>,\n\
}};\n\
struct FragmentOut {{\n\
@location(0) visibility: vec2<u32>,\n\
@location(1) barycentrics: vec4<f32>,\n\
Expand All @@ -240,17 +251,22 @@ fn gpu_raster_ids_faces_and_reconstruction_match_the_cpu_oracle() {
@vertex fn vs_main(@builtin(vertex_index) index: u32) -> VertexOut {{\n\
var out: VertexOut;\n\
out.position = clip_position(index);\n\
out.ndc = out.position.xy / out.position.w;\n\
return out;\n\
}}\n\
@fragment fn fs_main(\n\
in: VertexOut,\n\
@location(0) @interpolate(linear) ndc: vec2<f32>,\n\
@builtin(primitive_index) primitive_id: u32,\n\
@builtin(front_facing) front_facing: bool,\n\
) -> FragmentOut {{\n\
let first = primitive_id * 3u;\n\
let pixel_center = floor(vec2<f32>(\n\
(ndc.x + 1.0) * 0.5 * {WIDTH}.0,\n\
(1.0 - ndc.y) * 0.5 * {HEIGHT}.0,\n\
)) + vec2<f32>(0.5);\n\
let point_ndc = vec2<f32>(\n\
in.position.x / {WIDTH}.0 * 2.0 - 1.0,\n\
1.0 - in.position.y / {HEIGHT}.0 * 2.0,\n\
pixel_center.x / {WIDTH}.0 * 2.0 - 1.0,\n\
1.0 - pixel_center.y / {HEIGHT}.0 * 2.0,\n\
);\n\
let barycentrics = bloom_perspective_barycentrics(\n\
point_ndc,\n\
Expand Down
6 changes: 2 additions & 4 deletions native/shared/src/virtual_geometry/draw_emission.rs
Original file line number Diff line number Diff line change
Expand Up @@ -217,10 +217,8 @@ impl GpuVirtualDrawEmitter {
compilation_options: Default::default(),
cache: None,
});
let counted = device
.features()
.contains(wgpu::Features::MULTI_DRAW_INDIRECT_COUNT)
&& !force_binned_fallback;
let counted =
crate::renderer::gpu_driven::supports_indirect_count(device) && !force_binned_fallback;
let binned_fallback = (!counted)
.then(|| create_binned_fallback(device, selector, &state_buffer, draw_capacity));
Ok(Self {
Expand Down
Loading
Loading