@ludicon/spark.js
Version:
Real-Time GPU Texture Codecs for the Web
5 lines (4 loc) • 4.17 kB
JavaScript
const utils = "struct Params {\n to_srgb: u32,\n};\n\n@group(0) @binding(0) var src : texture_2d<f32>;\n@group(0) @binding(1) var dst : texture_storage_2d<rgba8unorm, write>;\n@group(0) @binding(2) var smp: sampler;\n@group(0) @binding(3) var<uniform> params: Params;\n\nfn linear_to_srgb_vec3(c: vec3<f32>) -> vec3<f32> {\n return select(\n 1.055 * pow(c, vec3<f32>(1.0 / 2.4)) - 0.055,\n c * 12.92,\n c <= vec3<f32>(0.0031308)\n );\n}\n\nfn linear_to_srgb_vec4(c: vec4<f32>) -> vec4<f32> {\n return vec4<f32>(linear_to_srgb_vec3(c.xyz), 1.0);\n}\n\n@compute @workgroup_size(8, 8)\nfn mipmap(@builtin(global_invocation_id) id : vec3<u32>) {\n let dstSize = textureDimensions(dst).xy;\n if (id.x >= dstSize.x || id.y >= dstSize.y) {\n return;\n }\n\n let size_rcp = vec2f(1.0) / vec2f(dstSize);\n let uv0 = vec2f(id.xy) * size_rcp;\n let uv1 = uv0 + size_rcp;\n\n var color = vec4f(0.0);\n color += textureSampleLevel(src, smp, vec2f(uv0.x, uv0.y), 0);\n color += textureSampleLevel(src, smp, vec2f(uv1.x, uv0.y), 0);\n color += textureSampleLevel(src, smp, vec2f(uv0.x, uv1.y), 0);\n color += textureSampleLevel(src, smp, vec2f(uv1.x, uv1.y), 0);\n color *= 0.25; \n\n if (params.to_srgb != 0) {\n color = linear_to_srgb_vec4(color);\n }\n\n textureStore(dst, id.xy, color);\n}\n\n@compute @workgroup_size(8, 8)\nfn resize(@builtin(global_invocation_id) id : vec3<u32>) {\n let dstSize = textureDimensions(dst).xy;\n if (id.x >= dstSize.x || id.y >= dstSize.y) {\n return;\n }\n\n let uv = (vec2f(id.xy) + vec2f(0.5)) / vec2f(dstSize);\n var color = textureSampleLevel(src, smp, uv, 0);\n\n if (params.to_srgb != 0) {\n color = linear_to_srgb_vec4(color);\n }\n\n textureStore(dst, id.xy, color);\n}\n\n@compute @workgroup_size(8, 8)\nfn flipy(@builtin(global_invocation_id) id : vec3<u32>) {\n let dstSize = textureDimensions(dst).xy;\n if (id.x >= dstSize.x || id.y >= dstSize.y) {\n return;\n }\n\n let uv = vec2f(f32(id.x), f32(dstSize.y - 1u - id.y)) / vec2f(dstSize);\n var color = textureSampleLevel(src, smp, uv, 0);\n\n if (params.to_srgb != 0) {\n color = linear_to_srgb_vec4(color);\n }\n\n textureStore(dst, id.xy, color);\n}\n\n\n@group(0) @binding(1) var<storage, read_write> global_counters: array<atomic<u32>, 3>;\n\nvar<workgroup> local_opaque: atomic<u32>;\nvar<workgroup> local_grayscale: atomic<u32>;\nvar<workgroup> local_invalid_normals: atomic<u32>;\n\n@compute @workgroup_size(8, 8)\nfn detect_channel_count(@builtin(global_invocation_id) global_id: vec3<u32>,\n @builtin(local_invocation_index) local_id: u32) {\n \n if (local_id == 0u) {\n atomicStore(&local_opaque, 1u);\n atomicStore(&local_grayscale, 1u);\n atomicStore(&local_invalid_normals, 0u);\n }\n workgroupBarrier();\n\n let tex_size = textureDimensions(src);\n if (global_id.x < tex_size.x && global_id.y < tex_size.y) {\n\n let color = textureLoad(src, vec2<i32>(global_id.xy), 0);\n\n // Alpha check\n if (color.a < 1.0) {\n atomicStore(&local_opaque, 0u);\n }\n\n // Grayscale check\n if (color.r != color.g || color.g != color.b) {\n atomicStore(&local_grayscale, 0u);\n }\n\n // Normal check\n let n = color.rgb * 2.0 - vec3(1.0);\n let len = length(n);\n\n if (abs(len - 1.0) > 0.2 || n.z < -0.1) {\n atomicAdd(&local_invalid_normals, 1u);\n }\n }\n\n workgroupBarrier();\n\n if (local_id == 0u) {\n // If not opaque, write not-opaque flag.\n if (atomicLoad(&local_opaque) == 0u) {\n atomicStore(&global_counters[0], 1u);\n }\n\n // If not greyscale, write not greyscale flag.\n if (atomicLoad(&local_grayscale) == 0u) {\n atomicStore(&global_counters[1], 1u);\n }\n\n // Add number of texels that are not normal.\n atomicAdd(&global_counters[2], atomicLoad(&local_invalid_normals));\n }\n}\n\n\n// @@ Compute RMSE?\n\n\n";
export {
utils as default
};