Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 8 additions & 8 deletions shaders/blur.comp
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,19 @@ layout(local_size_x = 16, local_size_y = 16) in;
layout(set = 0, binding = 0) uniform sampler2D inputTex;
layout(set = 0, binding = 1, rgba8) uniform writeonly image2D outputTex;

layout(push_constant) uniform PC { int horizontal; } pc;

void main() {
ivec2 gid = ivec2(gl_GlobalInvocationID.xy);
vec2 size = vec2(imageSize(outputTex));
vec2 texelSize = 1.0 / size;
vec4 color = vec4(0.0);
int count = 0;
for (int dx = -7; dx <= 7; dx++) {
for (int dy = -7; dy <= 7; dy++) {
vec2 offset = vec2(float(dx), float(dy)) * texelSize;
color += texture(inputTex, (vec2(gid) + 0.5) / size + offset);
count++;
}
for (int i = -7; i <= 7; i++) {
vec2 offset = pc.horizontal == 1
? vec2(float(i), 0.0) * texelSize
: vec2(0.0, float(i)) * texelSize;
color += texture(inputTex, (vec2(gid) + 0.5) / size + offset);
}
color /= float(count);
color /= 15.0;
imageStore(outputTex, gid, color);
}
115 changes: 115 additions & 0 deletions shaders/zoom.comp
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
#version 450

layout(local_size_x = 16, local_size_y = 16) in;

layout(set = 0, binding = 0) uniform sampler2D img_old;
layout(set = 0, binding = 1) uniform sampler2D img_new;
layout(set = 0, binding = 2, rgba8) uniform writeonly image2D output_img;

layout(push_constant) uniform PushConstants {
float t; // eased progress 0..1
float max_zoom; // e.g. 0.2 = 20% zoom
float old_w; // old wallpaper width
float old_h; // old wallpaper height
float new_w; // new wallpaper width
float new_h; // new wallpaper height
float dst_w; // screen width
float dst_h; // screen height
int mode; // 0=cover, 1=fit, 2=original
float anchor_x; // 0..1
float anchor_y; // 0..1
} pc;

void main() {
ivec2 coord = ivec2(gl_GlobalInvocationID.xy);
ivec2 size = imageSize(output_img);
if (coord.x >= size.x || coord.y >= size.y) return;

float img_w, img_h;
bool use_old = (pc.t <= 0.5);
if (use_old) {
img_w = pc.old_w;
img_h = pc.old_h;
} else {
img_w = pc.new_w;
img_h = pc.new_h;
}

float img_aspect = img_w / img_h;
float scr_aspect = pc.dst_w / pc.dst_h;

vec2 src;
bool visible = true;

if (pc.mode == 1) {
float scale = min(pc.dst_w / img_w, pc.dst_h / img_h);
vec2 sw = vec2(img_w, img_h) * scale;
vec2 off = (vec2(pc.dst_w, pc.dst_h) - sw) * vec2(pc.anchor_x, pc.anchor_y);
vec2 img_coord = vec2(coord) - off;
if (img_coord.x >= 0.0 && img_coord.x < sw.x && img_coord.y >= 0.0 && img_coord.y < sw.y) {
src = img_coord / scale;
} else {
visible = false;
}
} else if (pc.mode == 2) {
if (img_w <= pc.dst_w && img_h <= pc.dst_h) {
vec2 off = (vec2(pc.dst_w, pc.dst_h) - vec2(img_w, img_h))
* vec2(pc.anchor_x, pc.anchor_y);
vec2 img_coord = vec2(coord) - off;
if (img_coord.x >= 0.0 && img_coord.x < img_w && img_coord.y >= 0.0 && img_coord.y < img_h) {
src = img_coord;
} else {
visible = false;
}
} else {
if (img_aspect > scr_aspect) {
float u_range = scr_aspect / img_aspect;
float u_min = (1.0 - u_range) * pc.anchor_x;
src.x = (u_min + u_range * (coord.x / pc.dst_w)) * img_w;
src.y = coord.y / pc.dst_h * img_h;
} else {
float v_range = img_aspect / scr_aspect;
float v_min = (1.0 - v_range) * pc.anchor_y;
src.x = coord.x / pc.dst_w * img_w;
src.y = (v_min + v_range * (coord.y / pc.dst_h)) * img_h;
}
}
} else {
if (img_aspect > scr_aspect) {
float u_range = scr_aspect / img_aspect;
float u_min = (1.0 - u_range) * pc.anchor_x;
src.x = (u_min + u_range * (coord.x / pc.dst_w)) * img_w;
src.y = coord.y / pc.dst_h * img_h;
} else {
float v_range = img_aspect / scr_aspect;
float v_min = (1.0 - v_range) * pc.anchor_y;
src.x = coord.x / pc.dst_w * img_w;
src.y = (v_min + v_range * (coord.y / pc.dst_h)) * img_h;
}
}

vec4 color = vec4(0.0, 0.0, 0.0, 1.0);
if (visible) {
float zoom_scale;
if (use_old) {
float local_t = pc.t * 2.0;
zoom_scale = 1.0 + pc.max_zoom * local_t;
} else {
float local_t = (pc.t - 0.5) * 2.0;
zoom_scale = 1.0 + pc.max_zoom * (1.0 - local_t);
}

vec2 center = vec2(img_w, img_h) * 0.5;
vec2 sample_coord = center + (src - center) / zoom_scale;

vec2 uv = sample_coord / vec2(img_w, img_h);
if (use_old) {
color = texture(img_old, uv);
} else {
color = texture(img_new, uv);
}
}

imageStore(output_img, coord, color);
}

3 changes: 2 additions & 1 deletion src/colors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -615,6 +615,7 @@ fn deploy_palette(colors: &[ColorPalette]) {
let mut handles = Vec::new();
for (out, cmd, rendered) in deployments {
handles.push(std::thread::spawn(move || {
let t0 = std::time::Instant::now();

// resolve target file
let resolved_path = out.as_deref().and_then(|p| eval_shell(p));
Expand All @@ -633,7 +634,6 @@ fn deploy_palette(colors: &[ColorPalette]) {
eprintln!("[shell] failed to write {} {}", target, e);
return;
}
println!("[shell] deployed -> {}", target);

// execute post deployment command
if let Some(post_cmd) = &cmd {
Expand All @@ -643,6 +643,7 @@ fn deploy_palette(colors: &[ColorPalette]) {
eprintln!("[shell] failed to resolve {}", post_cmd);
}
}
println!("[shell] deployed in {:.2?} -> {}", t0.elapsed(), target);
}));
}

Expand Down
107 changes: 85 additions & 22 deletions src/filters.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,22 +52,24 @@ pub fn compute_pipeline(
device: &ash::Device,
spv: &[u32],
bindings: &[vk::DescriptorSetLayoutBinding],
push_constant_size: u32, // NEW
) -> Result<(vk::ShaderModule, vk::Pipeline, vk::DescriptorSetLayout), Box<dyn Error>> {

// shader module
let create_info = vk::ShaderModuleCreateInfo::default().code(spv);
let module = unsafe { device.create_shader_module(&create_info, None)? };

// descriptor set layout
let layout_info = vk::DescriptorSetLayoutCreateInfo::default().bindings(bindings);
let desc_layout = unsafe { device.create_descriptor_set_layout(&layout_info, None)? };

// pipeline layout
let set_layouts = [desc_layout];
let pipeline_layout_info = vk::PipelineLayoutCreateInfo::default().set_layouts(&set_layouts);
let push_range = vk::PushConstantRange {
stage_flags: vk::ShaderStageFlags::COMPUTE,
offset: 0,
size: push_constant_size,
};
let push_ranges = [push_range];
let mut pipeline_layout_info = vk::PipelineLayoutCreateInfo::default().set_layouts(&set_layouts);
if push_constant_size > 0 {
pipeline_layout_info = pipeline_layout_info.push_constant_ranges(&push_ranges);
}
let pipeline_layout = unsafe { device.create_pipeline_layout(&pipeline_layout_info, None)? };

// compute pipeline
let stage = vk::PipelineShaderStageCreateInfo::default()
.stage(vk::ShaderStageFlags::COMPUTE)
.module(module)
Expand All @@ -79,7 +81,6 @@ pub fn compute_pipeline(
device.create_compute_pipelines(vk::PipelineCache::null(), &[pipeline_info], None)
}.expect("Failed to create compute pipeline");
let pipeline = pipelines[0];

unsafe { device.destroy_pipeline_layout(pipeline_layout, None) };
Ok((module, pipeline, desc_layout))
}
Expand Down Expand Up @@ -111,7 +112,7 @@ pub fn filter_pipeline(
.descriptor_count(1)
.stage_flags(vk::ShaderStageFlags::COMPUTE),
];
compute_pipeline(device, &blur_words, &bindings)
compute_pipeline(device, &blur_words, &bindings, 4)
}
_ => Err(format!("unknown filter: {}", filter).into()),
}
Expand All @@ -122,12 +123,13 @@ pub fn filter_pipeline(

pub unsafe fn compute_filter(
vk_core: &VulkanCore,
_input_image: vk::Image,
output_image: vk::Image,
width: u32,
height: u32,
pipeline: vk::Pipeline,
descriptor_set_layout: vk::DescriptorSetLayout,
push_constants: &[u8],
dst_stage: vk::PipelineStageFlags,
configure: impl FnOnce(vk::DescriptorSet),
) -> Result<(), Box<dyn Error>> {
let pool_sizes = [
Expand All @@ -141,12 +143,20 @@ pub unsafe fn compute_filter(
let alloc_info = vk::DescriptorSetAllocateInfo::default()
.descriptor_pool(desc_pool)
.set_layouts(&set_layouts);
let desc_sets = unsafe { vk_core.device.allocate_descriptor_sets(&alloc_info)? };
let desc_set = desc_sets[0];
let desc_set = unsafe { vk_core.device.allocate_descriptor_sets(&alloc_info)? }[0];

configure(desc_set);

let pipeline_layout_info = vk::PipelineLayoutCreateInfo::default().set_layouts(&set_layouts);
let push_range = vk::PushConstantRange {
stage_flags: vk::ShaderStageFlags::COMPUTE,
offset: 0,
size: push_constants.len() as u32,
};
let push_ranges = [push_range];
let mut pipeline_layout_info = vk::PipelineLayoutCreateInfo::default().set_layouts(&set_layouts);
if !push_constants.is_empty() {
pipeline_layout_info = pipeline_layout_info.push_constant_ranges(&push_ranges);
}
let pipeline_layout = unsafe { vk_core.device.create_pipeline_layout(&pipeline_layout_info, None)? };

vk_core.record_commands(|command_buffer| {
Expand Down Expand Up @@ -179,6 +189,15 @@ pub unsafe fn compute_filter(
pipeline_layout,
0, &[desc_set], &[],
);
if !push_constants.is_empty() {
vk_core.device.cmd_push_constants(
command_buffer,
pipeline_layout,
vk::ShaderStageFlags::COMPUTE,
0,
push_constants,
);
}
}

let group_x = (width + 15) / 16;
Expand All @@ -200,7 +219,7 @@ pub unsafe fn compute_filter(
vk_core.device.cmd_pipeline_barrier(
command_buffer,
vk::PipelineStageFlags::COMPUTE_SHADER,
vk::PipelineStageFlags::FRAGMENT_SHADER,
dst_stage,
vk::DependencyFlags::empty(),
&[], &[], &[barrier2],
);
Expand All @@ -226,43 +245,84 @@ pub fn blur_texture(
blur_pipeline: vk::Pipeline,
blur_desc_layout: vk::DescriptorSetLayout,
) -> Result<VulkanTexture, Box<dyn Error>> {

let (output_image, output_memory) = vk_core.create_texture(
width, height,
vk::ImageUsageFlags::TRANSFER_SRC | vk::ImageUsageFlags::STORAGE,
vk::Format::R8G8B8A8_UNORM,
)?;

let (mid_image, mid_memory) = vk_core.create_texture(
width, height,
vk::ImageUsageFlags::SAMPLED | vk::ImageUsageFlags::STORAGE,
vk::Format::R8G8B8A8_UNORM,
)?;

let input_view = image_view(&vk_core.device, input_texture.image, vk::Format::R8G8B8A8_SRGB)?;
let mid_view = image_view(&vk_core.device, mid_image, vk::Format::R8G8B8A8_UNORM)?;
let output_view = image_view(&vk_core.device, output_image, vk::Format::R8G8B8A8_UNORM)?;
let sampler = linear_sampler(&vk_core.device)?;

unsafe {
compute_filter(
vk_core,
input_texture.image,
output_image,
mid_image,
width, height,
blur_pipeline,
blur_desc_layout,
&1i32.to_ne_bytes(),
vk::PipelineStageFlags::COMPUTE_SHADER,
|desc_set| {
let input_info = vk::DescriptorImageInfo::default()
.sampler(sampler)
.image_view(input_view)
.image_layout(vk::ImageLayout::SHADER_READ_ONLY_OPTIMAL);
let output_info = vk::DescriptorImageInfo::default()
.image_view(mid_view)
.image_layout(vk::ImageLayout::GENERAL);
let input_infos = [input_info];
let output_infos = [output_info];
let writes = [
vk::WriteDescriptorSet::default()
.dst_set(desc_set).dst_binding(0)
.descriptor_type(vk::DescriptorType::COMBINED_IMAGE_SAMPLER)
.image_info(&input_infos),
vk::WriteDescriptorSet::default()
.dst_set(desc_set).dst_binding(1)
.descriptor_type(vk::DescriptorType::STORAGE_IMAGE)
.image_info(&output_infos),
];
vk_core.device.update_descriptor_sets(&writes, &[]);
},
)?;
}

unsafe {
compute_filter(
vk_core,
output_image,
width, height,
blur_pipeline,
blur_desc_layout,
&0i32.to_ne_bytes(),
vk::PipelineStageFlags::FRAGMENT_SHADER,
|desc_set| {
let input_info = vk::DescriptorImageInfo::default()
.sampler(sampler)
.image_view(mid_view)
.image_layout(vk::ImageLayout::SHADER_READ_ONLY_OPTIMAL);
let output_info = vk::DescriptorImageInfo::default()
.image_view(output_view)
.image_layout(vk::ImageLayout::GENERAL);
let input_infos = [input_info];
let output_infos = [output_info];
let writes = [
vk::WriteDescriptorSet::default()
.dst_set(desc_set)
.dst_binding(0)
.dst_set(desc_set).dst_binding(0)
.descriptor_type(vk::DescriptorType::COMBINED_IMAGE_SAMPLER)
.image_info(&input_infos),
vk::WriteDescriptorSet::default()
.dst_set(desc_set)
.dst_binding(1)
.dst_set(desc_set).dst_binding(1)
.descriptor_type(vk::DescriptorType::STORAGE_IMAGE)
.image_info(&output_infos),
];
Expand All @@ -273,8 +333,11 @@ pub fn blur_texture(

unsafe {
vk_core.device.destroy_image_view(input_view, None);
vk_core.device.destroy_image_view(mid_view, None);
vk_core.device.destroy_image_view(output_view, None);
vk_core.device.destroy_sampler(sampler, None);
vk_core.device.destroy_image(mid_image, None);
vk_core.device.free_memory(mid_memory, None);
}

Ok(VulkanTexture { image: output_image, _memory: output_memory, width, height })
Expand Down
Loading
Loading