-
-
Notifications
You must be signed in to change notification settings - Fork 4.7k
Move MeshUniform allocation from the CPU to the GPU.
#23662
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from 8 commits
5cee165
6822e6d
fb9b207
fa25563
d0b8f2b
bf4302f
b332040
527a8c6
6ab6ee1
43bfe79
f6cf62e
f557ea9
5bd16b9
8ab0a3a
f0bb715
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,225 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // A compute shader that allocates `MeshUniform`s. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // This shader runs before mesh preprocessing in order to determine the | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // positions of `MeshUniform`s. Unlike `MeshInputUniform`s, which are scattered | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // throughout the buffer, `MeshUniform`s are indexed by instance ID, and so we | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // must place instances of the same mesh together in the buffer. One dispatch | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // call corresponds to one batch set (i.e. one multidraw operation), and one | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // thread corresponds to one bin (a.k.a. draw, a.k.a. batch). | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Essentially, the goal of this shader is to perform a prefix sum, using the | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // "scan-then-fan" approach. It has three phases: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // 1. *Local scan*: Perform a [Hillis-Steele scan] on each chunk of draws, where | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // the size of each chunk (i.e. the number of draws) is equal to the workgroup | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // size (256). Write the total size for this chunk to the fan buffer. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // 2. *Global scan*: Do a Hillis-Steele scan on the fan buffer. Now we know the | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // running total for each chunk. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // 3. *Fan*: Copy the running total for each chunk to every element of that | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // chunk. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Note that, for batch sets (i.e. multidraw indirect calls) that have fewer | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // than 256 batches in them, we only need step (1). This is the common case. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // [Hillis-Steele scan]: https://en.wikipedia.org/wiki/Prefix_sum#Algorithm_1:_Shorter_span,_more_parallel | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| #import bevy_pbr::mesh_preprocess_types::{BinMetadata, IndirectParametersMetadata} | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Information needed to allocate `MeshUniform`s. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| struct UniformAllocationMetadata { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // The index of this batch set in the `IndirectBatchSet` array. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // We write this into the `indirect_parameters_metadata`. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| batch_set_index: u32, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // The number of bins (a.k.a. draws, a.k.a. batches) in this batch set. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| bin_count: u32, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // The index of the first set of indirect parameters for this batch set. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // This is also the index of the first `IndirectParametersMetadata`, as | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // that's a parallel array with the indirect parameters. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| first_indirect_parameters_index: u32, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // The index of the first `MeshUniform` slot for this batch set. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| first_output_mesh_uniform_index: u32, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Padding. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| pad: array<vec4<f32>, 15u>, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // The number of threads in a workgroup. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const WORKGROUP_SIZE: u32 = 256u; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Information needed to allocate `MeshUniform`s. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @group(0) @binding(0) var<uniform> allocate_uniforms_metadata: UniformAllocationMetadata; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Information for each bin, including the indirect parameters offset and the | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // instance count. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @group(0) @binding(1) var<storage> bin_metadata: array<BinMetadata>; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // The array of indirect parameters metadata that we fill out, one for each | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // batch. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @group(0) @binding(2) var<storage, read_write> indirect_parameters_metadata: | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| array<IndirectParametersMetadata>; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // A temporary buffer that stores the mesh uniform index of the last instance | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // plus one for each workgroup (i.e. for each 256-bin chunk). | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // This is accumulated in the second stage and written out in the third. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @group(0) @binding(3) var<storage, read_write> fan_buffer: array<u32>; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Scratch memory that stores the prefix sum for every element in our chunk. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| var<workgroup> output_offsets: array<u32, 256>; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // The first step of the prefix sum. This computes the prefix sum for each | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // 256-element chunk. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Note that this will be the *only* step in the operation if the total number | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // of bins in this batch set is 256 or fewer. Thus we must fill in the indirect | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // parameters metadata for each batch here, as we can't guarantee that the | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // following two steps will be run at all. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @compute @workgroup_size(256, 1, 1) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| fn allocate_local_scan( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @builtin(local_invocation_id) local_id: vec3<u32>, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @builtin(workgroup_id) group_id: vec3<u32>, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @builtin(global_invocation_id) global_id: vec3<u32> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let bin_count = allocate_uniforms_metadata.bin_count; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let block_start = group_id.x * WORKGROUP_SIZE; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let block_end = min(block_start + WORKGROUP_SIZE, bin_count); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // If this is the first workgroup, take the first output index from the | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // metadata into account. But if this is the second chunk or beyond, don't | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // do that, as the second and third phases will add it in and we don't want | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // to double-count it. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (group_id.x == 0u) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| output_offsets[local_id.x] = allocate_uniforms_metadata.first_output_mesh_uniform_index; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } else { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| output_offsets[local_id.x] = 0u; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| workgroupBarrier(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // We're doing an inclusive sum, so put the instance count in the *next* bin. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (global_id.x < block_end && local_id.x < WORKGROUP_SIZE - 1u) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| output_offsets[local_id.x + 1] = bin_metadata[global_id.x].instance_count; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| workgroupBarrier(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Prefix sum within our workgroup. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| hillis_steele_scan(local_id.x); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Now write the indirect parameters metadata for this batch. We fill in the | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // `base_output_index` with the value of the prefix sum (which might be | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // incomplete if this isn't the first chunk). We also populate a few | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // bookkeeping fields for later rendering passes to use. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (global_id.x < block_end) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let indirect_parameters_offset = | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| allocate_uniforms_metadata.first_indirect_parameters_index + | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| bin_metadata[global_id.x].indirect_parameters_offset; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| indirect_parameters_metadata[indirect_parameters_offset].base_output_index = | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| output_offsets[local_id.x]; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| indirect_parameters_metadata[indirect_parameters_offset].batch_set_index = | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| allocate_uniforms_metadata.batch_set_index; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // These parameters get filled in later. Initialize them to zero for now. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // This is required in the case of the early/late instance counts | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // because the mesh preprocessing shader will atomically increment them. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| indirect_parameters_metadata[indirect_parameters_offset].mesh_index = 0u; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| indirect_parameters_metadata[indirect_parameters_offset].early_instance_count = 0u; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| indirect_parameters_metadata[indirect_parameters_offset].late_instance_count = 0u; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // If this is the last element in the workgroup, put the total number of | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // instances (plus the first output mesh uniform index if we're the first | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // workgroup) in the fan buffer in preparation for the next phase. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (local_id.x == WORKGROUP_SIZE - 1u) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| fan_buffer[group_id.x] = output_offsets[WORKGROUP_SIZE - 1u] + | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| bin_metadata[global_id.x].instance_count; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // The second step of the prefix sum. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // This step takes the intermediate fan values computed in the previous step | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // (i.e. the sum going out of each chunk) and performs one or more Hillis-Steele | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // scans in order to compute the fan value going into each chunk. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // This step is omitted if there are 256 or fewer total draws. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @compute @workgroup_size(256, 1, 1) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| fn allocate_global_scan(@builtin(local_invocation_id) local_id: vec3<u32>) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| var sum = 0u; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let chunk_count = div_ceil(allocate_uniforms_metadata.bin_count, WORKGROUP_SIZE); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Do a sequential loop over each block of 256 chunks. Because each | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // iteration of this loop covers 64K meshes, the fact that it's sequential | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // isn't going to be a problem in practice. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| for (var block_start = 0u; block_start < chunk_count; block_start += WORKGROUP_SIZE) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Set up the Hillis-Steele scan. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let block_end = min(block_start + WORKGROUP_SIZE, chunk_count); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let global_id = block_start + local_id.x; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (global_id < block_end) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| output_offsets[local_id.x] = sum + fan_buffer[global_id]; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| workgroupBarrier(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Perform the scan. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| hillis_steele_scan(local_id.x); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Write the value back. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Note that we don't need a workgroup barrier here because | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // `hillis_steele_scan` already did one. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (global_id < block_end) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| fan_buffer[global_id] = output_offsets[local_id.x]; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Save the sum coming out of this block for the next one. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| sum = output_offsets[WORKGROUP_SIZE - 1u]; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is wrong for more than 64k meshes, because it adds the sum before the scan. Before 64k the sum is always 0, which is why it works for "small" values. I ran it with a small example locally which produced visible artifacts, and with these changes it seems identical with main. I also think we technically need a barrier between the loop iterations?
Suggested change
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The lack of barrier is fine, we have workgroupBarriers before and hillis_steele_scan should always end with one as well.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ah yes, you’re right on both counts. The workgroup barrier is necessary to avoid the write-after-read hazard and the sum has to be added in after the Hillis-Steele scan, not before, to avoid propagating the sum value to all the entries.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You overlooked a tiny part of the diff. Since
Suggested change
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Oh, sorry, yes, you’re right. Fixed. |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // The third step of the prefix sum. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // We take the summed fan value computed in the previous step and add it in to | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // each value of each chunk beyond the first. We dispatch one fewer workgroup | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // here than in step (1), because there's nothing to do for the first chunk. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // This step is omitted if there are 256 or fewer total draws. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @compute @workgroup_size(256, 1, 1) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| fn allocate_fan( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @builtin(workgroup_id) group_id: vec3<u32>, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @builtin(global_invocation_id) global_id: vec3<u32> | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let id = global_id.x + WORKGROUP_SIZE; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let bin_count = allocate_uniforms_metadata.bin_count; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (id >= bin_count) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let fan_value = fan_buffer[group_id.x]; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let indirect_parameters_offset = | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| allocate_uniforms_metadata.first_indirect_parameters_index + | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| bin_metadata[id].indirect_parameters_offset; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| indirect_parameters_metadata[indirect_parameters_offset].base_output_index += fan_value; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Calculates a running exclusive sum. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // https://en.wikipedia.org/wiki/Prefix_sum#Algorithm_1:_Shorter_span,_more_parallel | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| fn hillis_steele_scan(local_id: u32) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| for (var offset = 1u; offset < WORKGROUP_SIZE; offset *= 2u) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| var term = 0u; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (local_id >= offset) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| term = output_offsets[local_id - offset]; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| workgroupBarrier(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| output_offsets[local_id] += term; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| workgroupBarrier(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Divides unsigned integer a by b, rounding up. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| fn div_ceil(a: u32, b: u32) -> u32 { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return (a + b - 1u) / b; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
does this need a barrier or is the comment above referring to not really needing it?
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
hillis_steele_scan()does a workgroup barrier after modifyingoutput_offsets, so we don’t need another one here.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I had the same question when I was going through this last night, might be good to add a comment to make it more obvious
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK, I added a comment.