Repository navigation
fix(core): scope texture/buffer usage conflicts per pass, not per encoder - #367
Merged
Merged
Conversation
…oder A render pass followed by CopyTextureToBuffer of its target in the same encoder failed Finish with 'texture usage conflict: existing 32 incompatible with 4' (triangle-headless readback). Since v0.31.6 copy usages were merged into one encoder-wide usage scope with conflict checks, but WebGPU only defines conflicts inside one usage scope (a render pass, a dispatch, a single copy command). Follow the Rust wgpu-core tracker model (track/mod.rs): - Each render pass and each copy command is its own TextureUsageScope / BufferUsageScope; conflicts are validated only inside it. - New EncoderTextureTracker / EncoderBufferTracker keep the first (start) and last (end) usage of every resource in the command buffer and return transitions between usage scopes (set_single / set_from_usage_scope). Copies record them right before the copy; render pass attachments before the HAL pass begins. - Submit-time merge emits device.end -> cb.start barriers and adopts cb.end (MergeEncoder), instead of one OR-ed state for both. - CommandEncoder.TransitionTextures updates the tracker, replacing the explicitTextureTransitions special case. Regression tests: render-then-readback, upload-then-render, upload-then-sample, copy-then-vertex-buffer, render-copy-render in one encoder. Tests that asserted cross-command conflicts now assert transitions; self-copies remain rejected atomically.
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.
Problem
A render pass followed by a copy of its target in the same encoder fails at
Finish:This is the readback pattern of
examples/triangle-headless, which fails like this on current main on Vulkan, DX12 and GLES. Uploading into a texture or vertex buffer and then rendering with it in the same encoder fails the same way.It is a regression from v0.31.6 (#312): copy usages were merged into one encoder-wide usage scope, and conflicts were checked across the whole encoder. WebGPU defines usage conflicts only inside one usage scope: a render pass, a dispatch, or a single copy command. Between scopes, a resource may change usage freely; the implementation inserts the barrier.
Fix
Usage tracking now follows the Rust wgpu-core tracker model (
wgpu-core/src/track/mod.rs):TextureUsageScope/BufferUsageScope. Conflicts are validated only inside it: the same texture as color and depth/stencil attachment, a texture or buffer copied onto itself.EncoderTextureTracker/EncoderBufferTracker(core/track/encoder_tracker.go) keep the first (start) and last (end) usage of every resource in the command buffer and return the transitions between scopes. This matchesTextureTracker::set_single/set_from_usage_scopeintrack/texture.rsandBufferTracker::set_singleintrack/buffer.rs. Copy commands record their transitions right before the copy, ascommand/transfer.rsdoes (set_single(src, COPY_SRC),set_single(dst, COPY_DST)). Render pass attachments record theirs before the HAL pass begins. Example:ColorTarget → CopySrcbefore the readback copy.CommandEncoder.TransitionTexturesupdates the tracker, so the next command does not repeat an explicit barrier. This replaces theexplicitTextureTransitionsspecial case.Tests
encoder_usage_scope_test.go: render then readback, upload then render, upload then sample, copy then vertex buffer, render → copy → render, all in one encoder. A behaviour-only copy of this file was run against main: 4 of 5 fail with the false conflicts. The fifth (sampled in a pass) passes on main only because bind-group textures are not tracked yet (see below).core/track/encoder_tracker_test.gofor start/end/transition bookkeeping.examples/triangle-headless: SUCCESS on Vulkan (Khronos validation layer, log file empty aterror,warn), DX12 and GLES (Intel Iris Xe). Main fails on all three with the error above.go build,go test ./...,go mod tidy, golangci-lint on windows/linux/darwin.Not in this PR (pre-existing gaps, not regressions)
validate_copy_within_same_textureincommand/transfer.rs).The diff is about 800 non-test lines across
core/command.go,core/track/andencoder_native.go. It is one change (the tracker model), so it is not split.