Repository navigation
release: v0.34.11 - #388
Merged
Merged
Conversation
… completes Release is Rust's drop, not WebGPU's destroy (ADR-056). A query set released after timestamp writes or a resolve were recorded was destroyed at the previous submission index, so the HAL query pool went away under the submitted work: VUID-vkDestroyQueryPool-queryPool-00793 on Vulkan and zero timestamps on DX12. CreateQuerySet gives the set a core.ResourceRef and Release drops it. The command encoder clones the reference when a render or compute pass records timestamp writes or ResolveQuerySet records a resolve (Rust wgpu-core's command buffer trackers hold Arc<QuerySet>), and Submit rejects only a set whose HAL query set is gone, as query_set.try_raw does (device/queue.rs:2167-2168). Test first: hal/allbackends/query_set_release_test.go failed on Vulkan (VVL) and DX12 before this change. P1-CORE-021.
RenderPipeline and ComputePipeline kept no reference to the PipelineLayout they were created from. WebGPU has no destroy() for a layout, so the only way an application lets one go is Release, which is Rust's drop: it deferred vkDestroyPipelineLayout, and every later SetBindGroup of the pipeline passed the destroyed handle to vkCmdBindDescriptorSets (VUID-vkCmdBindDescriptorSets- layout-parameter; on the test machine the Vulkan driver crashed with an access violation). Rust wgpu-core holds an Arc<PipelineLayout> in both pipelines (pipeline.rs:496, :1155). PipelineLayout now has a core.ResourceRef whose onZero defers the HAL destroy through the destroy queue at the last submission index, as a sampler's does, and both pipeline kinds clone it. The pipeline drops it in the same onZero that destroys its own HAL pipeline: pipeline first, then layout, so a released layout stays valid for the pipelines created with it. A pipeline created from an already released layout is rejected with ErrReleased instead of naming a dying HAL handle and reviving its reference count. Bind group layouts keep no reference: VkPipelineLayoutCreateInfo says the implementation must not access the pSetLayouts objects outside the duration of vkCreatePipelineLayout, so a VkDescriptorSetLayout may be destroyed after a dependent pipeline layout exists. Rust and Dawn hold their own bind group layout objects for validation, not to keep that Vulkan handle alive. Test-first in hal/allbackends: both pipeline kinds are created from an explicit layout, the layout is released, WaitIdle runs the deferred destruction, and only then does a new pass bind and draw or dispatch. Unfixed the Vulkan driver crashes in vkCmdBindDescriptorSets; fixed, every backend reads back the expected values with the validation layer quiet. P2-CORE-022
Four comparisons against TrackerIndex bounds were written as int constants: int(^TrackerIndex(0)-1) is 4294967294 and does not fit a 32-bit int, so core/track (buffer, texture, encoder tracker) and internal/raytracing (math.MaxUint32 in safeIntToUint32) failed to compile for GOARCH=386. Compare in uint64 instead: the tracker guard is now uint64(i) >= uint64(InvalidTrackerIndex), which rejects the same values on 64-bit and is vacuous on 32-bit, where an int cannot reach the invalid index; the raytracing clamp converts v to uint64 after its negative check. Semantics on amd64, arm64 and 386 are unchanged; 386 was simply never buildable. Reported by gogpu (BUG-ELSEWHERE, v0.34.10).
…ion scope errorFilterToRust mapped any value outside the three WebGPU filters to Validation, so PushErrorScope(ErrorFilter(42)) silently pushed a scope that could never capture what the caller asked for. Never swallow an error. PushErrorScope now returns an error in every build: the native path validates in core (ValidateErrorFilter, the spec's pushErrorScope TypeError), the rust path rejects the value before it reaches wgpu-native, which panics on an unknown filter and the panic aborts the process across the FFI boundary. The browser stub keeps its panic and only gains the return type. Callers that call PushErrorScope as a statement are unaffected; none in the ecosystem used the return value before because there was none. Tests: a table over every filter constant plus unmapped values in core and under -tags rust. P3-API-025.
…eline layout Release is Rust's drop (ADR-056), so a layout the application released may have its HAL destruction queued. CreateBindGroup passed desc.Layout.hal to vkAllocateDescriptorSets anyway: on the unfixed code the test crashed with an access violation inside the Vulkan driver (the layout was already destroyed), VUID-vkAllocateDescriptorSets-pAllocateInfo's class. CreatePipelineLayout read the same handles when building the pipeline layout. Both now refuse a released layout with ErrReleased, as CreateBindGroup already refuses a released sampler. No reference count is added: the HAL bind group does not keep the layout — Rust wgpu-hal's Vulkan BindGroup holds only the descriptor set — and the layout is read at creation only, so a group created while the layout was alive is unaffected by a later Release. P2-CORE-023. Test first: hal/allbackends/released_layout_test.go crashed on Vulkan before the fix and passes on vulkan, dx12 (FXC and DXIL), gles and software after it.
SetViewport recorded whatever it was given: a negative or non-finite size, an origin outside the spec's viewport range, or a depth range outside [0, 1] went straight to the HAL (invalid VkViewport values on Vulkan, undefined behaviour on DX12) and, since the software backend honours MinDepth/MaxDepth, silently changed the rendered depth. Validate the viewport as GPURenderPassEncoder.setViewport requires (spec/index.bs:13695-13708) and as Rust wgpu-core does (command/render.rs:2986): the first failed rule puts the encoder into its deferred error state, which End and Finish report, and nothing is recorded into the HAL pass. Rust lets a NaN origin or size through because its f32 API has no IDL boundary in front of it; the spec rejects it, so the check is explicit here. Vertex attribute formats get the same treatment at pipeline creation: an undefined or unknown gputypes.VertexFormat has size 0, which every existing vertex check treats as a no-op, and the backends then map it to a wrong format (GLES and Vulkan Float32x4, Metal MTLVertexFormatInvalid, DX12 DXGI_FORMAT_UNKNOWN, software zeros). Rust's VertexFormat enum cannot express such a value and Dawn validates it (RenderPipeline.cpp:118); core reports it as an invalid vertex format instead. P2-CORE-019.
CI linted one linux target, so code under darwin and windows build tags (hal/metal, hal/dx12, hal/gles/wgl) was never linted in CI: a PR could carry darwin or windows findings into main between releases (gogpu#519 hit this). - lint-targets: golangci-lint for windows/amd64 and darwin/arm64 on a linux runner, GOOS/GOARCH in the step env, fail-fast false. The module has no arch-specific files and no GOOS+GOARCH build tags, so one GOARCH per GOOS covers every file; js/wasm stays a build check in cross-compile. - golangci-lint 2.15.0 comes from gogpu/.github/actions/setup-golangci-lint (sha256-checked, cached, pinned by SHA) and the action runs with install-mode none: no more 'latest' drift, no per-run release download. - every action is pinned by commit SHA with a version comment; dependabot tracks github-actions and the /tools module, the root module stays on the ecosystem cascade schedule. - go-mod-outdated is pinned in tools/go.mod, replacing @latest and the retry loop. - test: the plain run is CGO_ENABLED=0 (goffi fakecgo, the path users ship); the race run stays CGO_ENABLED=1 with the toolchain detector. - lint-targets joins the Test gate. Job names stay Lint and Test: the ruleset matches that exact name and the switch is a separate decision.
32-bit int is a build class CI did not cover: four constants that fit a 64-bit int broke the 386 build silently until gogpu reported it (P1-INFRA-008). The leg builds only — no GPU and no tests there. windows and not linux: goffi's internal/syscall has no linux/386 files, so the linux/386 combination cannot build in the ecosystem today; the 32-bit int checks apply either way. needs the P1-INFRA-008 fix (1183df5) to pass.
Two test files used values that do not fit 32-bit: a t.Errorf argument of 4294967296 (4 << 30, defaults to int) in memory/types_test.go, and a 64-bit ANativeWindow handle built with a constant shift in android_surface_policy_test.go. GOARCH=386 go vet cannot compile either, so the 386 leg added for P1-INFRA-008 would fail on the tests rather than on the code. The heap size is printed as uint64; the handle's shift count is a variable, so the constant no longer overflows when the package is type-checked for a 32-bit target, where the test skips as it already did for the arm64 ABI case.
P1-GLES-025. Three defects found by running the backend on an OpenGL ES 3.2 context (Mesa llvmpipe, the wgpu_gles_force_es seam): - A multisampled render target was created with glTexImage2DMultisample, which OpenGL ES does not have, so every MSAA texture failed with GL_INVALID_OPERATION 0x502 and a pass that resolved MSAA could not be created at all. It is a renderbuffer now, attached to the framebuffer with glFramebufferRenderbuffer, as Rust wgpu-hal creates render targets (gles/device.rs create_texture). - Device.MapBuffer mapped with glMapBuffer, which OpenGL ES does not have either (Mesa exposes no glMapBufferOES on ES 3.x): the mapping failed silently and the CPU copy stayed zero-filled. Every buffer readback of GPU-written data on ES therefore read zeros, which is the whole class of "compute and storage readbacks are all zeros" failures. Mapping goes through glMapBufferRange, as in Rust wgpu-hal (gles/device.rs map_buffer). - Textures were allocated with mutable TexImage storage. OpenGL ES rejects an image binding (glBindImageTexture) of a texture with mutable storage with GL_INVALID_OPERATION, so imageStore and imageLoad wrote nowhere. 2D, cube and layered textures use immutable storage (glTexStorage2D/3D) when the context has it, which is Rust wgpu-hal's TEXTURE_STORAGE path (gles/adapter.rs, device.rs create_texture). Tests, all forced-ES (integration && linux && wgpu_gles_force_es): an MSAA clear-and-resolve readback, three compute readbacks over a constant, a storage buffer and a uniform block, and a storage-texture write. Each fails on the old code (GL errors and zero readbacks) and passes now; the public API tests TestMSAAStoreWithResolveIsLoadable, TestSampledRenderTarget, TestBindGroupRebindCompute, TestWriteBufferThenDispatchReadsIt, TestStorageTextureWriteThenSample and TestStorageWriteArrayLayer go green on that context as well.
P2-GLES-024. FeatureTextureCompressionBC was offered only when the EXT extension names were listed. Intel's Windows GL 4.6 driver (Iris Xe) offers RGTC as core GL and lists GL_ARB_texture_compression_rgtc, so no BC feature was advertised there even though every BC format decodes. RGTC (BC4-5) is core in desktop GL 3.0 and BPTC (BC6-7) in GL 4.2, and the ARB names count as well; S3TC (BC1-3) stays an extension on both APIs, with the sRGB S3TC extension required on ES. This is better than Rust wgpu-hal, which reads the extension strings only and misses the same driver; the deviation is documented at the detection. Test: a nine-case table of the detection (desktop 4.6 with ARB/EXT names, core 4.2, 3.3 and 4.1 without BPTC, a missing S3TC, and ES with and without the sRGB and BPTC extensions). On Windows the cross-backend TestCompressedTextureDecode/gles-vs-software now compares 24 of 24 formats on the Iris Xe GL adapter, BC included.
The stencil tests covered increment-clamp and decrement-wrap, but nothing pinned the wrap operations' result under a write mask, which gg's EvenOdd stroke construct builds on: with stencilWriteMask 0x01 an increment of 1 lands in bit 1 and is dropped, and a decrement of 0 wraps to 255, whose bit 0 survives. TestStencilWrap draws a front- or back-facing triangle into a Depth24PlusStencil8 attachment cleared to a known value, with compare "always", and reads the stencil aspect back through CopyTextureToBuffer. Each case checks every covered texel against the operation and every uncovered one against the clear value, so a backend that runs no stencil operation, or that paints the whole target, fails. Expectations follow the WebGPU stencil operation definitions, VkStencilOp (INCREMENT_AND_WRAP, DECREMENT_AND_WRAP) with the write-mask formula sa = (sa & ~sw) | (sg & sw) of the Vulkan stencil test, and D3D12's D3D12_STENCIL_OP_INCR and DECR.
naga v0.21.6 fixes the fragment position builtin's w on HLSL/DXIL (P1-HLSL-037): it passed SV_Position through, whose w is the clip-space w, while WGSL defines the fragment position's w as 1/w. wgpu excluded both dx12 legs from that check since the readback was written; the exclusion is gone and the assertion holds on dx12-fxc and dx12-dxil, verified against the pinned candidate 0b74bc3 and again against the released tag. Also in v0.21.6: the streaming WGSL lexer (behaviour-neutral, byte-identical IR over a 308-file differential), DXIL built-in index into a local array, DXIL refuses workgroup storage above the shader model's limit, HLSL storage stores nested past 20, and an opt-in SPIR-V demote option (off by default; wgpu does not set it yet). IR unchanged, one additive public field. Downstream gate: wgpu GO on the pinned SHA, gg GO on the same candidate.
The case naga added for P2-DXIL-107/092, taken verbatim: a short-circuit || chain over runtime values decides an if/else, and the chain's bool local meets at two if merges. As phi i1 WARP ran neither arm's stores; as alloca i1 D3D12's GPU-based validation dropped them. Four invocations, sentinel fill, so a wrong arm shows as a value and a missing store shows as the sentinel. It runs on every backend in the readback corpus. Without the D3D12 debug layer the case passes on naga v0.21.6 as well (that shape is correct there); the discriminating run is WARP + GPU-based validation, kept in the gate harness (P2-INFRA-009) rather than here.
v0.21.7 is v0.21.6 plus the DXIL mem2reg fix (naga P2-DXIL-107/092): bool locals merge as i32 instead of phi i1 or alloca i1, shapes WARP ran no store of and D3D12's GPU-based validation dropped. Verified against the tag the same way as the candidate: the gated SHA 8149de3 and the released module differ only in CHANGELOG.md and ROADMAP.md. The flow/short-circuit-or-rt readback joined the corpus in the previous commit; this one takes the release in. No IR or API change.
ROADMAP: current state and the released v0.34.11 list, with what moves to v0.34.12 (P2-CORE-026/027, P2-INFRA-009, GLES-023, Metal, ASTC). AGENTS.md version line: v0.34.11, naga v0.21.7. ARCHITECTURE: the GLES patterns the release introduced — multisampled renderbuffers, immutable storage, and glMapBufferRange, BC from core versions and ARB names. CONTRIBUTING: the CI jobs the lint-targets and 386 legs added.
Codecov Report❌ Patch coverage is 📢 Thoughts on this report? Let us know! |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
A lifetime and validation patch: resources released after recording stay alive through the work that uses them, pass input is validated as the spec requires, GLES works on real ES contexts, and the module builds on 32-bit. naga v0.21.7 brings the DX12
position.wfix and the DXIL WARP pair.Commits
[0.34.11] - 2026-11-08
A lifetime and validation patch: resources released after recording stay alive through the work that uses them,
pass input is validated as the spec requires, and the module builds on 32-bit.
Fixed
QuerySet.Releasedestroyed the HAL query pool at the previous submission index, so a set released after timestamp writes or aResolveQuerySetwere recorded went away under the submitted command buffer:VUID-vkDestroyQueryPool-queryPool-00793on Vulkan, zero timestamps on DX12. Query sets are reference counted as samplers are; the command encoder holds the set's reference until the submission completes, andSubmitrefuses only a set whose HAL resource is gone (ErrSubmitQuerySetDestroyed).PipelineLayoutreleased after the pipeline was created was destroyed while laterSetBindGroupcalls still named it — an invalidVkPipelineLayoutatvkCmdBindDescriptorSets. Pipelines now hold their layout's reference and drop it when their own resource is destroyed.CreateBindGroupandCreatePipelineLayoutnow returnErrReleased, asCreateBindGroupalready did for a released sampler. Before, the released layout's HAL handle reachedvkAllocateDescriptorSetsand crashed inside the driver.SetViewportis validated as the spec requires (P2-CORE-019). Non-finite or negative sizes, an origin outside the viewport range, or a depth range outside[0, 1]is aRenderPassValidationErroron the encoder instead of invalid values reaching the driver. Where the spec and Rust wgpu differ, the spec decides.CreateRenderPipelineErrorInvalidVertexFormat). Each backend used to substitute a format of its own (float32x4 on Vulkan and GLES, an invalid Metal format, an unknown DXGI format).TrackerIndexandmath.MaxUint32were written asintconstants and overflowed a 32-bitint, so every 386 build of wgpu failed to compile. Comparison is done inuint64now; behaviour on 64-bit targets is unchanged. Build only — there is no GPU on 386, and ecosystem CI needs goffi's own 386 support before the other repositories can run the leg. Two test files that did not type-check for 32-bit are fixed with it.glTexImage2DMultisample, which ES does not have, so every MSAA texture failed withGL_INVALID_OPERATION; they are renderbuffers now (glRenderbufferStorageMultisample), as in Rust wgpu-hal.MapBufferusedglMapBuffer, absent from ES, so every buffer readback of GPU-written data returned zeros while rendering was correct; it maps withglMapBufferRange. AndglBindImageTexturerejects a texture with mutable storage on ES, so compute storage textures wrote nothing; textures are allocated with immutable storage (glTexStorage2D/glTexStorage3D) where the context offers it.EXT_texture_compression_*names, so a driver that listsGL_ARB_texture_compression_rgtc(Intel on Windows) advertised no BC formats at all. RGTC from desktop GL 3.0, BPTC from 4.2 and theARBnames are accepted now; the compressed-format readback compares all 24 formats against the software backend on that adapter.Changed
@builtin(position).wis now WGSL's1/w(naga P1-HLSL-037): the perspective readback asserts it on both DX12 legs instead of excluding them. DXILmem2regpromotes bool locals asi32(naga P2-DXIL-107/092): a short-circuit||chain's local used to be aphi i1or analloca i1, and WARP, the D3D12 software adapter, ran neither arm's stores on the first shape while D3D12's GPU-based validation dropped them on the second. The new readback caseflow/short-circuit-or-rtpins the values on every backend; the discriminating WARP + GPU-based-validation run is kept in the gate harness. Also in the release: the streaming WGSL lexer (behaviour-neutral), a DXIL built-in used as a runtime array index, a DXIL refusal of workgroup storage above the model's limit instead of a bad module, HLSL storage stores nested past 20, and an opt-in SPIR-V demote-to-helper (spirv.Options.DemoteToHelperInvocation, off by default). No IR change.Device.PushErrorScopereturns an error for a filter outside the three WebGPU filters (P3-API-025). It used to map an unknown value toValidationsilently, pushing a scope that could never capture what the caller asked for. The native build validates in core,-tags rustrejects the value before it reaches wgpu-native, which panics on an unknown filter and whose panic would abort the process across the FFI boundary. Calls written as statements are unaffected.Infrastructure
Testgate; switching it toRequired checksis a separate, coordinated step.Known issues