#version 450 layout(local_size_x = 8, local_size_y = 8) in; layout(binding = 0) uniform sampler2D input_texture; layout(binding = 1, rgba8) uniform writeonly image2D output_image; layout(push_constant) uniform PushConstants { vec2 scale_factor; vec2 input_size; } constants; vec4 cubic(float v) { vec4 n = vec4(1.0, 2.0, 3.0, 4.0) - v; vec4 s = n * n * n; float x = s.x; float y = s.y - 4.0 * s.x; float z = s.z - 4.0 * s.y + 6.0 * s.x; float w = s.w - 4.0 * s.z + 6.0 * s.y - 4.0 * s.x; return vec4(x, y, z, w) * (1.0/6.0); } vec4 bicubic_sample(sampler2D tex, vec2 tex_coord) { vec2 tex_size = constants.input_size; vec2 inv_tex_size = 1.0 / tex_size; tex_coord = tex_coord * tex_size - 0.5; vec2 fxy = fract(tex_coord); tex_coord -= fxy; vec4 xcubic = cubic(fxy.x); vec4 ycubic = cubic(fxy.y); vec4 c = tex_coord.xxyy + vec2(-0.5, +1.5).xyxy; vec4 s = vec4(xcubic.xz + xcubic.yw, ycubic.xz + ycubic.yw); vec4 offset = c + vec4(xcubic.yw, ycubic.yw) / s; offset *= inv_tex_size.xxyy; vec4 sample0 = texture(tex, offset.xz); vec4 sample1 = texture(tex, offset.yz); vec4 sample2 = texture(tex, offset.xw); vec4 sample3 = texture(tex, offset.yw); float sx = s.x / (s.x + s.y); float sy = s.z / (s.z + s.w); return mix( mix(sample3, sample2, sx), mix(sample1, sample0, sx), sy ); } void main() { ivec2 pos = ivec2(gl_GlobalInvocationID.xy); ivec2 size = imageSize(output_image); if (pos.x >= size.x || pos.y >= size.y) { return; } vec2 tex_coord = vec2(pos) / vec2(size); vec4 color = bicubic_sample(input_texture, tex_coord); imageStore(output_image, pos, color); }