R"( #version 450 #define SSBO_LOCATION(x) (x + %loc) layout(local_size_x = %ws, local_size_y = 1, local_size_z = 1) in; layout(%set, binding=SSBO_LOCATION(0), std430) buffer ssbo0{ uint data_in[]; }; layout(%set, binding=SSBO_LOCATION(1), std430) buffer ssbo1{ uint data_out[]; }; layout(%push_block) uniform parameters { uint image_width; uint image_height; uint image_depth; uint image_logw; uint image_logh; uint image_logd; uint lod_count; }; struct invocation_properties { uint data_offset; uvec3 size; uvec3 size_log2; }; #define bswap_u16(bits) (bits & 0xFF) << 8 | (bits & 0xFF00) >> 8 | (bits & 0xFF0000) << 8 | (bits & 0xFF000000) >> 8 #define bswap_u32(bits) (bits & 0xFF) << 24 | (bits & 0xFF00) << 8 | (bits & 0xFF0000) >> 8 | (bits & 0xFF000000) >> 24 invocation_properties invocation; bool init_invocation_properties(const in uint offset) { invocation.data_offset = 0; invocation.size.x = image_width; invocation.size.y = image_height; invocation.size.z = image_depth; invocation.size_log2.x = image_logw; invocation.size_log2.y = image_logh; invocation.size_log2.z = image_logd; uint level_end = image_width * image_height * image_depth; uint level = 1; while (offset >= level_end && level < lod_count) { invocation.data_offset = level_end; invocation.size.xy /= 2; invocation.size.xy = max(invocation.size.xy, uvec2(1)); invocation.size_log2.xy = max(invocation.size_log2.xy, uvec2(1)); invocation.size_log2.xy --; level_end += (invocation.size.x * invocation.size.y * image_depth); level++; } return (offset < level_end); } uint get_z_index(const in uint x_, const in uint y_, const in uint z_) { uint offset = 0; uint shift = 0; uint x = x_; uint y = y_; uint z = z_; uint log2w = invocation.size_log2.x; uint log2h = invocation.size_log2.y; uint log2d = invocation.size_log2.z; do { if (log2w > 0) { offset |= (x & 1) << shift; shift++; x >>= 1; log2w--; } if (log2h > 0) { offset |= (y & 1) << shift; shift++; y >>= 1; log2h--; } if (log2d > 0) { offset |= (z & 1) << shift; shift++; z >>= 1; log2d--; } } while(x > 0 || y > 0 || z > 0); return offset; } void main() { uint invocations_x = (gl_NumWorkGroups.x * gl_WorkGroupSize.x); uint texel_id = (gl_GlobalInvocationID.y * invocations_x) + gl_GlobalInvocationID.x; uint word_count = %_wordcount; if (!init_invocation_properties(texel_id)) return; // Calculations done in texels, not bytes uint row_length = invocation.size.x; uint slice_length = (invocation.size.y * row_length); uint level_offset = (texel_id - invocation.data_offset); uint slice_offset = (level_offset % slice_length); uint z = (level_offset / slice_length); uint y = (slice_offset / row_length); uint x = (slice_offset % row_length); uint src_texel_id = get_z_index(x, y, z); uint dst_id = (texel_id * word_count); uint src_id = (src_texel_id + invocation.data_offset) * word_count; for (uint i = 0; i < word_count; ++i) { uint value = data_in[src_id++]; data_out[dst_id++] = %f(value); } } )"