| #version 450 | |
| #extension GL_EXT_buffer_reference : require | |
| layout(local_size_x = 64) in; | |
| layout(constant_id = 0) const uint c_field_count = 1; | |
| layout(std430, buffer_reference, buffer_reference_align = 8) | |
| buffer writeonly dst_queries_t { | |
| uvec2 queries[]; | |
| }; | |
| layout(std430, buffer_reference, buffer_reference_align = 8) | |
| readonly buffer src_queries_t { | |
| uvec2 queries[]; | |
| }; | |
| struct query_map_entry_t { | |
| uint dst_index; | |
| uint src_index; | |
| uint next; | |
| }; | |
| layout(std430, buffer_reference, buffer_reference_align = 4) | |
| readonly buffer query_map_t { | |
| query_map_entry_t entries[]; | |
| }; | |
| layout(push_constant) | |
| uniform u_info_t { | |
| dst_queries_t dst_buffer; | |
| src_queries_t src_buffer; | |
| query_map_t query_map; | |
| uint query_count; | |
| }; | |
| uvec2 uadd64(uvec2 a, uvec2 b) { | |
| uint carry; | |
| uint lo = uaddCarry(a.x, b.x, carry); | |
| uint hi = a.y + b.y + carry; | |
| return uvec2(lo, hi); | |
| } | |
| void main() { | |
| uint thread_id = gl_GlobalInvocationID.x; | |
| if (thread_id >= query_count) | |
| return; | |
| // The query map is an array of linked lists, with the | |
| // first query_count entries guaranteed to be list heads | |
| query_map_entry_t entry = query_map.entries[thread_id]; | |
| uvec2 dst_data[c_field_count]; | |
| // By copying the first query we get the reset for free | |
| for (uint i = 0; i < c_field_count; i++) | |
| dst_data[i] = src_buffer.queries[c_field_count * entry.src_index + i]; | |
| // Accumulate data from additional queries | |
| while (entry.next != ~0u) { | |
| entry = query_map.entries[entry.next]; | |
| for (uint i = 0; i < c_field_count; i++) | |
| dst_data[i] = uadd64(dst_data[i], src_buffer.queries[c_field_count * entry.src_index + i]); | |
| } | |
| // dst_index has the same value for all entries in the list | |
| for (uint i = 0; i < c_field_count; i++) | |
| dst_buffer.queries[c_field_count * entry.dst_index + i] = dst_data[i]; | |
| } |