Skip to content

Commit

Permalink
Add mip map example
Browse files Browse the repository at this point in the history
  • Loading branch information
attackgoat committed Aug 30, 2024
1 parent bdc4a81 commit fa16928
Showing 1 changed file with 250 additions and 0 deletions.
250 changes: 250 additions & 0 deletions examples/mip_map.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,250 @@
mod profile_with_puffin;

use {
bytemuck::{bytes_of, Pod, Zeroable},
core::f32,
glam::{vec3, Vec4},
inline_spirv::inline_spirv,
screen_13::prelude::*,
screen_13_window::{Window, WindowError},
std::sync::Arc,
};

// TODO: Add texelFetch option

fn main() -> Result<(), WindowError> {
pretty_env_logger::init();
profile_with_puffin::init();

let window = Window::new()?;

let size = 237u32;
let mip_level_count = size.ilog2();

assert_ne!(mip_level_count, 0, "size must be greater than one");

let image_info = ImageInfo::image_2d(
size,
size,
vk::Format::R8G8B8A8_UNORM,
vk::ImageUsageFlags::COLOR_ATTACHMENT | vk::ImageUsageFlags::SAMPLED,
)
.to_builder()
.mip_level_count(mip_level_count)
.build();
let image = Arc::new(Image::create(&window.device, image_info)?);

fill_mip_levels(&window.device, &image)?;

let splat = splat(&window.device)?;

window.run(|frame| {
// It is 100% certain that the swapchain supports color attachment usage, so this is shown
// for completeness only
// https://vulkan.gpuinfo.org/listsurfaceusageflags.php
assert!(frame
.render_graph
.node_info(frame.swapchain_image)
.usage
.contains(vk::ImageUsageFlags::COLOR_ATTACHMENT));

let image = frame.render_graph.bind_node(&image);
let swapchain_info = frame.render_graph.node_info(frame.swapchain_image);
let stripe_width = swapchain_info.width / mip_level_count;

let mut pass = frame
.render_graph
.begin_pass("splat mips")
.bind_pipeline(&splat);

for mip_level in 0..mip_level_count {
let stripe_x = mip_level * stripe_width;
pass = pass
.read_descriptor_as(
0,
image,
image_info
.default_view_info()
.to_builder()
.base_mip_level(mip_level)
.mip_level_count(Some(1)),
)
.store_color(0, frame.swapchain_image)
.set_render_area(stripe_x as _, 0, stripe_width, swapchain_info.height)
.record_subpass(|subpass, _| {
subpass.draw(6, 1, 0, 0);
});
}
})
}

fn fill_mip_levels(device: &Arc<Device>, image: &Arc<Image>) -> Result<(), DriverError> {
#[derive(Clone, Copy, Pod, Zeroable)]
#[repr(C)]
struct PushConstants {
a: Vec4,
b: Vec4,
}

let vertical_gradient = Arc::new(GraphicPipeline::create(
device,
GraphicPipelineInfo::default(),
[
Shader::new_vertex(
inline_spirv!(
r#"
#version 460 core
const vec2 POSITION[] = {
vec2(-1, -1),
vec2(-1, 1),
vec2( 1, 1),
vec2(-1, -1),
vec2( 1, 1),
vec2( 1, -1),
};
layout(location = 0) out float ab;
void main() {
vec2 position = POSITION[gl_VertexIndex];
ab = max(position.y, 0);
gl_Position = vec4(position, 0, 1);
}
"#,
vert
)
.as_slice(),
),
Shader::new_fragment(
inline_spirv!(
r#"
#version 460 core
layout(push_constant) uniform PushConstants {
layout(offset = 0) vec3 a;
layout(offset = 16) vec3 b;
};
layout(location = 0) in float ab;
layout(location = 0) out vec4 color;
void main() {
color = vec4(mix(a, b, ab), 1);
}
"#,
frag
)
.as_slice(),
),
],
)?);

let mut render_graph = RenderGraph::new();
let mut pass = render_graph
.begin_pass("fill mip levels")
.bind_pipeline(&vertical_gradient);
let image_info = image.info;
let image = pass.bind_node(image);

for mip_level in 0..image_info.mip_level_count {
pass = pass
.store_color_as(
0,
image,
image_info
.default_view_info()
.to_builder()
.base_mip_level(mip_level)
.mip_level_count(Some(1)),
)
.record_subpass(|subpass, _| {
subpass
.push_constants(bytes_of(&PushConstants {
a: vec3(0.0, 1.0, 0.0).extend(f32::NAN),
b: vec3(1.0, 0.0, 1.0).extend(f32::NAN),
}))
.draw(6, 1, 0, 0);
});
}

// This is the overly-complicated way of picking queue family 0
let queue_family_index = device
.physical_device
.queue_families
.iter()
.enumerate()
.find_map(|(idx, family)| {
family
.queue_flags
.contains(vk::QueueFlags::GRAPHICS)
.then_some(idx)
})
.ok_or(DriverError::Unsupported)?;

// Submits to the GPU but does not wait for anything to be finished
render_graph
.resolve()
.submit(&mut LazyPool::new(device), queue_family_index, 0)
.map(|_| ())
}

fn splat(device: &Arc<Device>) -> Result<Arc<GraphicPipeline>, DriverError> {
Ok(Arc::new(GraphicPipeline::create(
device,
GraphicPipelineInfo::default(),
[
Shader::new_vertex(
inline_spirv!(
r#"
#version 460 core
const vec2 POSITION[] = {
vec2(-1, -1),
vec2(-1, 1),
vec2( 1, 1),
vec2(-1, -1),
vec2( 1, 1),
vec2( 1, -1),
};
const vec2 TEXCOORD[] = {
vec2(0, 0),
vec2(0, 1),
vec2(1, 1),
vec2(0, 0),
vec2(1, 1),
vec2(1, 0),
};
layout(location = 0) out vec2 texcoord;
void main() {
texcoord = TEXCOORD[gl_VertexIndex];
gl_Position = vec4(POSITION[gl_VertexIndex], 0, 1);
}
"#,
vert
)
.as_slice(),
),
Shader::new_fragment(
inline_spirv!(
r#"
#version 460 core
layout(binding = 0) uniform sampler2D image;
layout(location = 0) in vec2 texcoord;
layout(location = 0) out vec4 color;
void main() {
color = texture(image, texcoord);
}
"#,
frag
)
.as_slice(),
),
],
)?))
}

0 comments on commit fa16928

Please sign in to comment.