mod profile_with_puffin;
use {
clap::Parser,
inline_spirv::inline_spirv,
log::debug,
rand::{Rng, rng, seq::IndexedRandom},
screen_13::prelude::*,
screen_13_window::{FrameContext, WindowBuilder, WindowError},
std::{mem::size_of, sync::Arc},
};
type Operation = fn(&mut FrameContext, &mut HashPool);
static OPERATIONS: &[Operation] = &[
record_compute_array_bind,
record_compute_bindless,
record_compute_no_op,
record_graphic_bindless,
record_graphic_load_store,
record_graphic_msaa_depth_stencil,
record_graphic_will_merge_subpass_input,
record_graphic_will_merge_common_color1,
record_graphic_will_merge_common_color2,
record_graphic_will_merge_common_depth1,
record_graphic_will_merge_common_depth2,
record_graphic_will_merge_common_depth3,
record_graphic_wont_merge,
record_accel_struct_builds,
record_transfer_graphic_multipass,
];
fn main() -> Result<(), WindowError> {
pretty_env_logger::init();
profile_with_puffin::init();
let mut rng = rng();
let screen_13 = WindowBuilder::default().debug(true).build()?;
let mut pool = HashPool::new(&screen_13.device);
let mut frame_count = 0;
let args = Args::parse();
screen_13.run(|mut frame| {
if frame_count == args.frame_count {
*frame.will_exit = true;
return;
}
frame_count += 1;
let clear_before: bool = rng.random();
if clear_before {
frame.render_graph.clear_color_image(frame.swapchain_image);
}
for _ in 0..args.ops_per_frame {
let op_fn = OPERATIONS.choose(&mut rng).unwrap();
op_fn(&mut frame, &mut pool);
}
if !clear_before {
frame.render_graph.clear_color_image(frame.swapchain_image);
}
})?;
debug!("OK");
Ok(())
}
fn record_accel_struct_builds(frame: &mut FrameContext, pool: &mut HashPool) {
const BLAS_COUNT: vk::DeviceSize = 32;
let vertex_buf = {
let mut buf = pool
.lease(BufferInfo::host_mem(
36,
vk::BufferUsageFlags::ACCELERATION_STRUCTURE_BUILD_INPUT_READ_ONLY_KHR
| vk::BufferUsageFlags::SHADER_DEVICE_ADDRESS
| vk::BufferUsageFlags::STORAGE_BUFFER,
))
.unwrap();
Buffer::copy_from_slice(&mut buf, 0, 0f32.to_ne_bytes());
Buffer::copy_from_slice(&mut buf, 4, 0f32.to_ne_bytes());
Buffer::copy_from_slice(&mut buf, 8, 0f32.to_ne_bytes());
Buffer::copy_from_slice(&mut buf, 12, 1f32.to_ne_bytes());
Buffer::copy_from_slice(&mut buf, 16, 1f32.to_ne_bytes());
Buffer::copy_from_slice(&mut buf, 20, 0f32.to_ne_bytes());
Buffer::copy_from_slice(&mut buf, 24, 2f32.to_ne_bytes());
Buffer::copy_from_slice(&mut buf, 28, 0f32.to_ne_bytes());
Buffer::copy_from_slice(&mut buf, 32, 0f32.to_ne_bytes());
buf
};
let index_buf = {
let mut buf = pool
.lease(BufferInfo::host_mem(
6,
vk::BufferUsageFlags::ACCELERATION_STRUCTURE_BUILD_INPUT_READ_ONLY_KHR
| vk::BufferUsageFlags::SHADER_DEVICE_ADDRESS
| vk::BufferUsageFlags::STORAGE_BUFFER,
))
.unwrap();
Buffer::copy_from_slice(&mut buf, 0, 0u16.to_ne_bytes());
Buffer::copy_from_slice(&mut buf, 2, 1u16.to_ne_bytes());
Buffer::copy_from_slice(&mut buf, 4, 2u16.to_ne_bytes());
buf
};
let blas_geometry_info = AccelerationStructureGeometryInfo::blas([(
AccelerationStructureGeometry {
max_primitive_count: 1,
flags: vk::GeometryFlagsKHR::OPAQUE,
geometry: AccelerationStructureGeometryData::Triangles {
index_addr: DeviceOrHostAddress::DeviceAddress(Buffer::device_address(&index_buf)),
index_type: vk::IndexType::UINT16,
max_vertex: 3,
transform_addr: None,
vertex_addr: DeviceOrHostAddress::DeviceAddress(Buffer::device_address(
&vertex_buf,
)),
vertex_format: vk::Format::R32G32B32_SFLOAT,
vertex_stride: 12,
},
},
vk::AccelerationStructureBuildRangeInfoKHR::default().primitive_count(1),
)]);
let blas_size = AccelerationStructure::size_of(frame.device, &blas_geometry_info);
let blas_info = AccelerationStructureInfo::blas(blas_size.create_size);
let instance_len = size_of::<vk::AccelerationStructureInstanceKHR>() as vk::DeviceSize;
let mut instance_buf = Buffer::create(
frame.device,
BufferInfo::host_mem(
instance_len * BLAS_COUNT,
vk::BufferUsageFlags::ACCELERATION_STRUCTURE_BUILD_INPUT_READ_ONLY_KHR
| vk::BufferUsageFlags::SHADER_DEVICE_ADDRESS,
),
)
.unwrap();
let accel_struct_scratch_offset_alignment = frame
.device
.physical_device
.accel_struct_properties
.as_ref()
.unwrap()
.min_accel_struct_scratch_offset_alignment
as vk::DeviceSize;
let mut blas_nodes = Vec::with_capacity(BLAS_COUNT as _);
for idx in 0..BLAS_COUNT {
let blas = pool.lease(blas_info).unwrap();
Buffer::copy_from_slice(
&mut instance_buf,
idx * instance_len,
AccelerationStructure::instance_slice(&[vk::AccelerationStructureInstanceKHR {
transform: vk::TransformMatrixKHR {
matrix: [
1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, ],
},
instance_custom_index_and_mask: vk::Packed24_8::new(0, 0xff),
instance_shader_binding_table_record_offset_and_flags: vk::Packed24_8::new(
0,
vk::GeometryInstanceFlagsKHR::TRIANGLE_FACING_CULL_DISABLE.as_raw() as _,
),
acceleration_structure_reference: vk::AccelerationStructureReferenceKHR {
device_handle: AccelerationStructure::device_address(&blas),
},
}]),
);
let blas_node = frame.render_graph.bind_node(blas);
let scratch_buf = frame.render_graph.bind_node(
pool.lease(
BufferInfo::device_mem(
blas_size.build_size,
vk::BufferUsageFlags::SHADER_DEVICE_ADDRESS
| vk::BufferUsageFlags::STORAGE_BUFFER,
)
.to_builder()
.alignment(accel_struct_scratch_offset_alignment),
)
.unwrap(),
);
blas_nodes.push((scratch_buf, blas_node));
}
let tlas_geometry_info = AccelerationStructureGeometryInfo::tlas([(
AccelerationStructureGeometry {
max_primitive_count: 1,
flags: vk::GeometryFlagsKHR::OPAQUE,
geometry: AccelerationStructureGeometryData::Instances {
array_of_pointers: false,
addr: DeviceOrHostAddress::DeviceAddress(Buffer::device_address(&instance_buf)),
},
},
vk::AccelerationStructureBuildRangeInfoKHR::default().primitive_count(1),
)]);
let instance_buf = frame.render_graph.bind_node(instance_buf);
let tlas_size = AccelerationStructure::size_of(frame.device, &tlas_geometry_info);
let tlas = pool
.lease(AccelerationStructureInfo::tlas(tlas_size.create_size))
.unwrap();
let tlas_node = frame.render_graph.bind_node(tlas);
let tlas_scratch_buf = frame.render_graph.bind_node(
pool.lease(
BufferInfo::device_mem(
tlas_size.build_size,
vk::BufferUsageFlags::SHADER_DEVICE_ADDRESS | vk::BufferUsageFlags::STORAGE_BUFFER,
)
.to_builder()
.alignment(accel_struct_scratch_offset_alignment),
)
.unwrap(),
);
let index_node = frame.render_graph.bind_node(index_buf);
let vertex_node = frame.render_graph.bind_node(vertex_buf);
let pass = frame
.render_graph
.begin_pass("build acceleration structures");
let mut pass = pass.read_node(index_node).read_node(vertex_node);
for (scratch_buf, blas_node) in &blas_nodes {
pass.access_node_mut(*scratch_buf, AccessType::AccelerationStructureBufferWrite);
pass.access_node_mut(*blas_node, AccessType::AccelerationStructureBuildWrite);
}
let blas_nodes_copy = blas_nodes
.iter()
.map(|(_, blas_node)| *blas_node)
.collect::<Vec<_>>();
let mut pass = pass.record_acceleration(move |accel, bindings| {
for (scratch_buf, blas_node) in blas_nodes {
let scratch_data = Buffer::device_address(&bindings[scratch_buf]);
accel.build_structure(&blas_geometry_info, blas_node, scratch_data);
}
});
for blas_node in blas_nodes_copy {
pass.access_node_mut(blas_node, AccessType::AccelerationStructureBuildRead);
}
pass.access_node_mut(instance_buf, AccessType::AccelerationStructureBuildRead);
pass.access_node_mut(
tlas_scratch_buf,
AccessType::AccelerationStructureBufferWrite,
);
pass.access_node_mut(tlas_node, AccessType::AccelerationStructureBuildWrite);
pass.record_acceleration(move |accel, bindings| {
let scratch_data = Buffer::device_address(&bindings[tlas_scratch_buf]);
accel.build_structure(&tlas_geometry_info, tlas_node, scratch_data);
});
}
fn record_compute_array_bind(frame: &mut FrameContext, pool: &mut HashPool) {
let pipeline = compute_pipeline(
"array_bind",
frame.device,
ComputePipelineInfo::default(),
Shader::new_compute(
inline_spirv!(
r#"
#version 460 core
layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
layout(constant_id = 0) const uint LAYER_COUNT = 1;
layout(push_constant) uniform PushConstants {
layout(offset = 0) float offset;
} push_const;
layout(set = 0, binding = 0) uniform sampler2D layer_images_sampler_llr[LAYER_COUNT];
void main() {
}
"#,
comp
)
.as_slice(),
)
.specialization_info(SpecializationInfo::new(
vec![vk::SpecializationMapEntry {
constant_id: 0,
offset: 0,
size: 4,
}],
5u32.to_ne_bytes(),
)),
);
let image_info = ImageInfo::image_2d(
64,
64,
vk::Format::R8G8B8A8_UNORM,
vk::ImageUsageFlags::SAMPLED | vk::ImageUsageFlags::TRANSFER_DST,
);
let images = [
frame
.render_graph
.bind_node(pool.lease(image_info).unwrap()),
frame
.render_graph
.bind_node(pool.lease(image_info).unwrap()),
frame
.render_graph
.bind_node(pool.lease(image_info).unwrap()),
frame
.render_graph
.bind_node(pool.lease(image_info).unwrap()),
frame
.render_graph
.bind_node(pool.lease(image_info).unwrap()),
];
frame
.render_graph
.clear_color_image(images[0])
.clear_color_image(images[1])
.clear_color_image(images[2])
.clear_color_image(images[3])
.clear_color_image(images[4])
.begin_pass("array-bind")
.bind_pipeline(&pipeline)
.read_descriptor((0, [0]), images[0])
.read_descriptor((0, [1]), images[1])
.read_descriptor((0, [2]), images[2])
.read_descriptor((0, [3]), images[3])
.read_descriptor((0, [4]), images[4])
.record_compute(|compute, _| {
compute
.push_constants(&0f32.to_ne_bytes())
.dispatch(64, 64, 1);
});
}
fn record_compute_bindless(frame: &mut FrameContext, pool: &mut HashPool) {
let pipeline = compute_pipeline(
"bindless",
frame.device,
ComputePipelineInfo::default(),
Shader::new_compute(
inline_spirv!(
r#"
#version 460 core
#extension GL_EXT_nonuniform_qualifier : require
layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
layout(push_constant) uniform PushConstants {
layout(offset = 0) uint count;
} push_const;
layout(set = 0, binding = 0, rgba8) writeonly uniform image2D dst[];
void main() {
for (uint idx = 0; idx < push_const.count; idx++) {
imageStore(
dst[idx],
ivec2(gl_GlobalInvocationID.x, gl_GlobalInvocationID.y),
vec4(0)
);
}
}
"#,
comp
)
.as_slice(),
),
);
let image_info = ImageInfo::image_2d(
64,
64,
vk::Format::R8G8B8A8_UNORM,
vk::ImageUsageFlags::SAMPLED | vk::ImageUsageFlags::STORAGE,
);
let images = [
frame
.render_graph
.bind_node(pool.lease(image_info).unwrap()),
frame
.render_graph
.bind_node(pool.lease(image_info).unwrap()),
frame
.render_graph
.bind_node(pool.lease(image_info).unwrap()),
frame
.render_graph
.bind_node(pool.lease(image_info).unwrap()),
frame
.render_graph
.bind_node(pool.lease(image_info).unwrap()),
];
frame
.render_graph
.begin_pass("compute-bindless")
.bind_pipeline(&pipeline)
.write_descriptor((0, [0]), images[0])
.write_descriptor((0, [1]), images[1])
.write_descriptor((0, [2]), images[2])
.write_descriptor((0, [3]), images[3])
.write_descriptor((0, [4]), images[4])
.record_compute(|compute, _| {
compute
.push_constants(&5u32.to_ne_bytes())
.dispatch(64, 64, 1);
});
}
fn record_compute_no_op(frame: &mut FrameContext, _: &mut HashPool) {
let pipeline = compute_pipeline(
"no_op",
frame.device,
ComputePipelineInfo::default(),
Shader::new_compute(
inline_spirv!(
r#"
#version 460 core
void main() {
}
"#,
comp
)
.as_slice(),
),
);
frame
.render_graph
.begin_pass("no-op")
.bind_pipeline(&pipeline)
.record_compute(|compute, _| {
compute.dispatch(1, 1, 1);
});
}
fn record_graphic_bindless(frame: &mut FrameContext, pool: &mut HashPool) {
let pipeline = graphic_vert_frag_pipeline(
frame.device,
GraphicPipelineInfo::default(),
inline_spirv!(
r#"
#version 460 core
void main() {
}
"#,
vert
)
.as_slice(),
inline_spirv!(
r#"
#version 460 core
#extension GL_EXT_nonuniform_qualifier : require
layout(push_constant) uniform PushConstants {
layout(offset = 0) uint count;
} push_const;
layout(set = 0, binding = 0) uniform sampler2D images_sampler_llr[];
layout(location = 0) out vec4 color_out;
void main() {
for (uint idx = 0; idx < push_const.count; idx++) {
color_out += texture(
images_sampler_llr[idx],
vec2(0)
);
}
}
"#,
frag
)
.as_slice(),
);
let image = frame.render_graph.bind_node(
pool.lease(ImageInfo::image_2d(
256,
256,
vk::Format::R8G8B8A8_UNORM,
vk::ImageUsageFlags::COLOR_ATTACHMENT | vk::ImageUsageFlags::INPUT_ATTACHMENT,
))
.unwrap(),
);
let image_info = ImageInfo::image_2d(
64,
64,
vk::Format::R8G8B8A8_UNORM,
vk::ImageUsageFlags::SAMPLED
| vk::ImageUsageFlags::STORAGE
| vk::ImageUsageFlags::TRANSFER_DST,
);
let images = [
frame
.render_graph
.bind_node(pool.lease(image_info).unwrap()),
frame
.render_graph
.bind_node(pool.lease(image_info).unwrap()),
frame
.render_graph
.bind_node(pool.lease(image_info).unwrap()),
frame
.render_graph
.bind_node(pool.lease(image_info).unwrap()),
frame
.render_graph
.bind_node(pool.lease(image_info).unwrap()),
];
frame
.render_graph
.clear_color_image(images[0])
.clear_color_image(images[1])
.clear_color_image(images[2])
.clear_color_image(images[3])
.clear_color_image(images[4])
.begin_pass("graphic-bindless")
.bind_pipeline(&pipeline)
.read_descriptor((0, [0]), images[0])
.read_descriptor((0, [1]), images[1])
.read_descriptor((0, [2]), images[2])
.read_descriptor((0, [3]), images[3])
.read_descriptor((0, [4]), images[4])
.clear_color(0, image)
.store_color(0, image)
.record_subpass(|subpass, _| {
subpass.push_constants(&5u32.to_ne_bytes()).draw(1, 1, 0, 0);
});
}
fn record_graphic_load_store(frame: &mut FrameContext, _: &mut HashPool) {
let pipeline = graphic_vert_frag_pipeline(
frame.device,
GraphicPipelineInfo::default(),
inline_spirv!(
r#"
#version 460 core
void main() {
}
"#,
vert
)
.as_slice(),
inline_spirv!(
r#"
#version 460 core
layout(location = 0) out vec4 color_out;
void main() {
color_out = vec4(0);
}
"#,
frag
)
.as_slice(),
);
frame
.render_graph
.begin_pass("load-store")
.bind_pipeline(&pipeline)
.load_color(0, frame.swapchain_image)
.store_color(0, frame.swapchain_image)
.record_subpass(|subpass, _| {
subpass.draw(1, 1, 0, 0);
});
}
fn record_graphic_msaa_depth_stencil(frame: &mut FrameContext, pool: &mut HashPool) {
let sample_count = {
let Vulkan10Limits {
framebuffer_color_sample_counts,
framebuffer_depth_sample_counts,
framebuffer_stencil_sample_counts,
..
} = frame.device.physical_device.properties_v1_0.limits;
match framebuffer_color_sample_counts
& framebuffer_depth_sample_counts
& framebuffer_stencil_sample_counts
{
s if s.contains(vk::SampleCountFlags::TYPE_64) => SampleCount::Type64,
s if s.contains(vk::SampleCountFlags::TYPE_32) => SampleCount::Type32,
s if s.contains(vk::SampleCountFlags::TYPE_16) => SampleCount::Type16,
s if s.contains(vk::SampleCountFlags::TYPE_8) => SampleCount::Type8,
s if s.contains(vk::SampleCountFlags::TYPE_4) => SampleCount::Type4,
s if s.contains(vk::SampleCountFlags::TYPE_2) => SampleCount::Type2,
s if s.contains(vk::SampleCountFlags::TYPE_1) => SampleCount::Type1,
_ => panic!("unsupported depth/stencil msaa"),
}
};
let depth_stencil_format = {
let mut best_format = None;
for format in [
vk::Format::D24_UNORM_S8_UINT,
vk::Format::D16_UNORM_S8_UINT,
vk::Format::D32_SFLOAT_S8_UINT,
] {
let format_props = Device::image_format_properties(
frame.device,
format,
vk::ImageType::TYPE_2D,
vk::ImageTiling::OPTIMAL,
vk::ImageUsageFlags::DEPTH_STENCIL_ATTACHMENT
| vk::ImageUsageFlags::TRANSIENT_ATTACHMENT,
vk::ImageCreateFlags::empty(),
);
if format_props.is_ok() {
best_format = Some(format);
break;
}
}
best_format.expect("Unsupported depth/stencil format")
};
let depth_resolve_mode = {
let mut best_mode = None;
for (resolve_flags, resolve_mode) in [
(vk::ResolveModeFlags::AVERAGE, ResolveMode::Average),
(vk::ResolveModeFlags::SAMPLE_ZERO, ResolveMode::SampleZero),
] {
if frame
.device
.physical_device
.depth_stencil_resolve_properties
.supported_depth_resolve_modes
.contains(resolve_flags)
{
best_mode = Some(resolve_mode);
break;
}
}
best_mode.expect("Unsupported depth resolve mode")
};
let pipeline = graphic_vert_frag_pipeline(
frame.device,
GraphicPipelineInfoBuilder::default().samples(sample_count),
inline_spirv!(
r#"
#version 460 core
const vec2 UV[3] = {
vec2(-1, -1),
vec2(-1, 1),
vec2(1, 1),
};
void main() {
gl_Position = vec4(UV[gl_VertexIndex], 0, 1);
}
"#,
vert
)
.as_slice(),
inline_spirv!(
r#"
#version 460 core
layout(location = 0) out vec4 color_out;
void main() {
color_out = vec4(1);
}
"#,
frag
)
.as_slice(),
);
let swapchain_format = frame.render_graph.node_info(frame.swapchain_image).fmt;
let msaa_color_image = frame.render_graph.bind_node(
pool.lease(
ImageInfo::image_2d(
frame.width,
frame.height,
swapchain_format,
vk::ImageUsageFlags::COLOR_ATTACHMENT | vk::ImageUsageFlags::TRANSIENT_ATTACHMENT,
)
.to_builder()
.sample_count(sample_count),
)
.unwrap(),
);
let msaa_depth_stencil_image = frame.render_graph.bind_node(
pool.lease(
ImageInfo::image_2d(
frame.width,
frame.height,
depth_stencil_format,
vk::ImageUsageFlags::DEPTH_STENCIL_ATTACHMENT
| vk::ImageUsageFlags::TRANSIENT_ATTACHMENT,
)
.to_builder()
.sample_count(sample_count),
)
.unwrap(),
);
let depth_stencil_image = frame.render_graph.bind_node(
pool.lease(ImageInfo::image_2d(
frame.width,
frame.height,
depth_stencil_format,
vk::ImageUsageFlags::DEPTH_STENCIL_ATTACHMENT,
))
.unwrap(),
);
let depth_stencil_mode = DepthStencilMode {
back: StencilMode::IGNORE,
bounds_test: true,
compare_op: vk::CompareOp::LESS_OR_EQUAL,
depth_test: true,
depth_write: true,
front: StencilMode {
fail_op: vk::StencilOp::ZERO,
pass_op: vk::StencilOp::REPLACE,
depth_fail_op: vk::StencilOp::ZERO,
compare_op: vk::CompareOp::LESS_OR_EQUAL,
compare_mask: 0xff,
write_mask: 0xff,
reference: 0x00,
},
min: 0.0.into(),
max: 1.0.into(),
stencil_test: true,
};
frame
.render_graph
.begin_pass("msaa-depth-stencil")
.bind_pipeline(&pipeline)
.set_depth_stencil(depth_stencil_mode)
.clear_color(0, msaa_color_image)
.clear_depth_stencil(msaa_depth_stencil_image)
.resolve_color(0, 1, frame.swapchain_image)
.resolve_depth_stencil(
2,
depth_stencil_image,
Some(depth_resolve_mode),
Some(ResolveMode::SampleZero),
)
.record_subpass(|subpass, _| {
subpass.draw(3, 1, 0, 0);
});
}
fn record_graphic_will_merge_common_color1(frame: &mut FrameContext, pool: &mut HashPool) {
let image = frame.render_graph.bind_node(
pool.lease(ImageInfo::image_2d(
256,
256,
vk::Format::R8G8B8A8_UNORM,
vk::ImageUsageFlags::COLOR_ATTACHMENT,
))
.unwrap(),
);
frame
.render_graph
.begin_pass("a")
.bind_pipeline(graphic_vert_frag_pipeline(
frame.device,
GraphicPipelineInfo::default(),
inline_spirv!(
r#"
#version 460 core
void main() { }
"#,
vert
)
.as_slice(),
inline_spirv!(
r#"
#version 460 core
layout(location = 0) out vec4 color0;
void main() {
color0 = vec4(0);
}
"#,
frag
)
.as_slice(),
))
.store_color(0, image)
.record_subpass(|subpass, _| {
subpass.draw(1, 1, 0, 0);
});
frame
.render_graph
.begin_pass("b")
.bind_pipeline(&graphic_vert_frag_pipeline(
frame.device,
GraphicPipelineInfo::default(),
inline_spirv!(
r#"
#version 460 core
void main() { }
"#,
vert
)
.as_slice(),
inline_spirv!(
r#"
#version 460 core
layout(location = 0) out vec4 color0;
void main() {
color0 = vec4(0);
}
"#,
frag
)
.as_slice(),
))
.load_color(0, image)
.store_color(0, image)
.record_subpass(|subpass, _| {
subpass.draw(1, 1, 0, 0);
});
}
fn record_graphic_will_merge_common_color2(frame: &mut FrameContext, pool: &mut HashPool) {
let image_0 = frame.render_graph.bind_node(
pool.lease(ImageInfo::image_2d(
256,
256,
vk::Format::R8G8B8A8_UNORM,
vk::ImageUsageFlags::COLOR_ATTACHMENT,
))
.unwrap(),
);
let image_1 = frame.render_graph.bind_node(
pool.lease(ImageInfo::image_2d(
256,
256,
vk::Format::R8G8B8A8_UNORM,
vk::ImageUsageFlags::COLOR_ATTACHMENT,
))
.unwrap(),
);
frame
.render_graph
.begin_pass("a")
.bind_pipeline(graphic_vert_frag_pipeline(
frame.device,
GraphicPipelineInfo::default(),
inline_spirv!(
r#"
#version 460 core
void main() { }
"#,
vert
)
.as_slice(),
inline_spirv!(
r#"
#version 460 core
layout(location = 0) out vec4 color0;
void main() {
color0 = vec4(0);
}
"#,
frag
)
.as_slice(),
))
.store_color(0, image_0)
.record_subpass(|subpass, _| {
subpass.draw(1, 1, 0, 0);
});
frame
.render_graph
.begin_pass("b")
.bind_pipeline(&graphic_vert_frag_pipeline(
frame.device,
GraphicPipelineInfo::default(),
inline_spirv!(
r#"
#version 460 core
void main() { }
"#,
vert
)
.as_slice(),
inline_spirv!(
r#"
#version 460 core
layout(location = 0) out vec4 color0;
layout(location = 1) out vec4 color1;
void main() {
color0 = vec4(0);
color1 = vec4(0);
}
"#,
frag
)
.as_slice(),
))
.load_color(0, image_0)
.store_color(0, image_0)
.store_color(1, image_1)
.record_subpass(|subpass, _| {
subpass.draw(1, 1, 0, 0);
});
frame
.render_graph
.begin_pass("c")
.bind_pipeline(&graphic_vert_frag_pipeline(
frame.device,
GraphicPipelineInfo::default(),
inline_spirv!(
r#"
#version 460 core
void main() { }
"#,
vert
)
.as_slice(),
inline_spirv!(
r#"
#version 460 core
layout(location = 0) out vec4 color0;
void main() {
color0 = vec4(0);
}
"#,
frag
)
.as_slice(),
))
.clear_color(0, image_0)
.store_color(0, image_0)
.record_subpass(|subpass, _| {
subpass.draw(1, 1, 0, 0);
});
}
fn record_graphic_will_merge_common_depth1(frame: &mut FrameContext, pool: &mut HashPool) {
let color_image = frame.render_graph.bind_node(
pool.lease(ImageInfo::image_2d(
256,
256,
vk::Format::R8G8B8A8_UNORM,
vk::ImageUsageFlags::COLOR_ATTACHMENT,
))
.unwrap(),
);
let depth_image = frame.render_graph.bind_node(
pool.lease(ImageInfo::image_2d(
256,
256,
vk::Format::D32_SFLOAT,
vk::ImageUsageFlags::DEPTH_STENCIL_ATTACHMENT,
))
.unwrap(),
);
frame
.render_graph
.begin_pass("a")
.bind_pipeline(graphic_vert_frag_pipeline(
frame.device,
GraphicPipelineInfo::default(),
inline_spirv!(
r#"
#version 460 core
void main() { }
"#,
vert
)
.as_slice(),
inline_spirv!(
r#"
#version 460 core
layout(location = 0) out vec4 color_out;
void main() {
color_out = vec4(0);
}
"#,
frag
)
.as_slice(),
))
.store_color(0, color_image)
.store_depth_stencil(depth_image)
.record_subpass(|subpass, _| {
subpass.draw(1, 1, 0, 0);
});
frame
.render_graph
.begin_pass("b")
.bind_pipeline(graphic_vert_frag_pipeline(
frame.device,
GraphicPipelineInfo::default(),
inline_spirv!(
r#"
#version 460 core
void main() { }
"#,
vert
)
.as_slice(),
inline_spirv!(
r#"
#version 460 core
void main() {
gl_FragDepth = 0.0;
}
"#,
frag
)
.as_slice(),
))
.load_depth_stencil(depth_image)
.store_depth_stencil(depth_image)
.record_subpass(|subpass, _| {
subpass.draw(1, 1, 0, 0);
});
}
fn record_graphic_will_merge_common_depth2(frame: &mut FrameContext, pool: &mut HashPool) {
let color_image = frame.render_graph.bind_node(
pool.lease(ImageInfo::image_2d(
256,
256,
vk::Format::R8G8B8A8_UNORM,
vk::ImageUsageFlags::COLOR_ATTACHMENT,
))
.unwrap(),
);
let depth_image = frame.render_graph.bind_node(
pool.lease(ImageInfo::image_2d(
256,
256,
vk::Format::D32_SFLOAT,
vk::ImageUsageFlags::DEPTH_STENCIL_ATTACHMENT,
))
.unwrap(),
);
frame
.render_graph
.begin_pass("a")
.bind_pipeline(graphic_vert_frag_pipeline(
frame.device,
GraphicPipelineInfo::default(),
inline_spirv!(
r#"
#version 460 core
void main() { }
"#,
vert
)
.as_slice(),
inline_spirv!(
r#"
#version 460 core
void main() {
gl_FragDepth = 0.0;
}
"#,
frag
)
.as_slice(),
))
.store_depth_stencil(depth_image)
.record_subpass(|subpass, _| {
subpass.draw(1, 1, 0, 0);
});
frame
.render_graph
.begin_pass("b")
.bind_pipeline(graphic_vert_frag_pipeline(
frame.device,
GraphicPipelineInfo::default(),
inline_spirv!(
r#"
#version 460 core
void main() { }
"#,
vert
)
.as_slice(),
inline_spirv!(
r#"
#version 460 core
layout(location = 0) out vec4 color_out;
void main() {
color_out = vec4(0);
}
"#,
frag
)
.as_slice(),
))
.store_color(0, color_image)
.load_depth_stencil(depth_image)
.store_depth_stencil(depth_image)
.record_subpass(|subpass, _| {
subpass.draw(1, 1, 0, 0);
});
}
fn record_graphic_will_merge_common_depth3(frame: &mut FrameContext, pool: &mut HashPool) {
let depth_image = frame.render_graph.bind_node(
pool.lease(ImageInfo::image_2d(
256,
256,
vk::Format::D32_SFLOAT,
vk::ImageUsageFlags::DEPTH_STENCIL_ATTACHMENT,
))
.unwrap(),
);
frame
.render_graph
.begin_pass("a")
.bind_pipeline(graphic_vert_frag_pipeline(
frame.device,
GraphicPipelineInfo::default(),
inline_spirv!(
r#"
#version 460 core
void main() { }
"#,
vert
)
.as_slice(),
inline_spirv!(
r#"
#version 460 core
void main() {
gl_FragDepth = 0.0;
}
"#,
frag
)
.as_slice(),
))
.store_depth_stencil(depth_image)
.record_subpass(|subpass, _| {
subpass.draw(1, 1, 0, 0);
});
frame
.render_graph
.begin_pass("b")
.bind_pipeline(graphic_vert_frag_pipeline(
frame.device,
GraphicPipelineInfo::default(),
inline_spirv!(
r#"
#version 460 core
void main() { }
"#,
vert
)
.as_slice(),
inline_spirv!(
r#"
#version 460 core
void main() {
gl_FragDepth = 0.0;
}
"#,
frag
)
.as_slice(),
))
.load_depth_stencil(depth_image)
.store_depth_stencil(depth_image)
.record_subpass(|subpass, _| {
subpass.draw(1, 1, 0, 0);
});
}
fn record_graphic_will_merge_subpass_input(frame: &mut FrameContext, pool: &mut HashPool) {
let vertex = inline_spirv!(
r#"
#version 460 core
void main() {
}
"#,
vert
)
.as_slice();
let pipeline_a = graphic_vert_frag_pipeline(
frame.device,
GraphicPipelineInfo::default(),
vertex,
inline_spirv!(
r#"
#version 460 core
layout(location = 0) out vec4 color_out;
void main() {
color_out = vec4(0);
}
"#,
frag
)
.as_slice(),
);
let pipeline_b = graphic_vert_frag_pipeline(
frame.device,
GraphicPipelineInfo::default(),
vertex,
inline_spirv!(
r#"
#version 460 core
layout(input_attachment_index = 0, binding = 0) uniform subpassInput color_in;
layout(location = 0) out vec4 color_out;
void main() {
color_out = subpassLoad(color_in);
}
"#,
frag
)
.as_slice(),
);
let image = frame.render_graph.bind_node(
pool.lease(ImageInfo::image_2d(
256,
256,
vk::Format::R8G8B8A8_UNORM,
vk::ImageUsageFlags::COLOR_ATTACHMENT
| vk::ImageUsageFlags::INPUT_ATTACHMENT
| vk::ImageUsageFlags::TRANSFER_DST,
))
.unwrap(),
);
frame
.render_graph
.begin_pass("a")
.bind_pipeline(&pipeline_a)
.clear_color(0, image)
.store_color(0, image)
.record_subpass(|subpass, _| {
subpass.draw(1, 1, 0, 0);
});
frame
.render_graph
.begin_pass("b")
.bind_pipeline(&pipeline_b)
.store_color(0, image)
.record_subpass(|subpass, _| {
subpass.draw(1, 1, 0, 0);
});
}
fn record_graphic_wont_merge(frame: &mut FrameContext, pool: &mut HashPool) {
let pipeline = graphic_vert_frag_pipeline(
frame.device,
GraphicPipelineInfo::default(),
inline_spirv!(
r#"
#version 460 core
void main() {
}
"#,
vert
)
.as_slice(),
inline_spirv!(
r#"
#version 460 core
layout(location = 0) out vec4 color;
void main() {
}
"#,
frag
)
.as_slice(),
);
let image = frame.render_graph.bind_node(
pool.lease(ImageInfo::image_2d(
256,
256,
vk::Format::R8G8B8A8_UNORM,
vk::ImageUsageFlags::COLOR_ATTACHMENT,
))
.unwrap(),
);
frame
.render_graph
.begin_pass("c")
.bind_pipeline(&pipeline)
.store_color(0, image)
.record_subpass(|subpass, _| {
subpass.draw(1, 1, 0, 0);
});
frame
.render_graph
.begin_pass("d")
.bind_pipeline(&pipeline)
.store_color(0, image)
.record_subpass(|subpass, _| {
subpass.draw(1, 1, 0, 0);
});
}
fn record_transfer_graphic_multipass(frame: &mut FrameContext, pool: &mut HashPool) {
let pipeline = graphic_vert_frag_pipeline(
frame.device,
GraphicPipelineInfo::default(),
inline_spirv!(
r#"
#version 460 core
void main() {
}
"#,
vert
)
.as_slice(),
inline_spirv!(
r#"
#version 460 core
layout(binding = 0) uniform sampler2D my_sampler_lle;
layout(location = 0) out vec4 color_out;
void main() {
color_out = texture(my_sampler_lle, vec2(0));
}
"#,
frag
)
.as_slice(),
);
let images = [
frame.render_graph.bind_node(
pool.lease(ImageInfo::image_2d(
256,
256,
vk::Format::R8G8B8A8_UNORM,
vk::ImageUsageFlags::SAMPLED | vk::ImageUsageFlags::TRANSFER_DST,
))
.unwrap(),
),
frame.render_graph.bind_node(
pool.lease(ImageInfo::image_2d(
256,
256,
vk::Format::R8G8B8A8_UNORM,
vk::ImageUsageFlags::SAMPLED | vk::ImageUsageFlags::TRANSFER_DST,
))
.unwrap(),
),
];
frame.render_graph.clear_color_image(images[0]);
frame.render_graph.clear_color_image(images[1]);
frame
.render_graph
.begin_pass("a")
.bind_pipeline(&pipeline)
.clear_color(0, frame.swapchain_image)
.store_color(0, frame.swapchain_image)
.read_descriptor(0, images[0])
.record_subpass(|subpass, _| {
subpass.draw(1, 1, 0, 0);
});
frame
.render_graph
.begin_pass("b")
.bind_pipeline(&pipeline)
.load_color(0, frame.swapchain_image)
.store_color(0, frame.swapchain_image)
.read_descriptor(0, images[1])
.record_subpass(|subpass, _| {
subpass.draw(1, 1, 0, 0);
});
}
fn compute_pipeline(
key: &'static str,
device: &Arc<Device>,
info: impl Into<ComputePipelineInfo>,
shader: impl Into<Shader>,
) -> Arc<ComputePipeline> {
use std::{cell::RefCell, collections::HashMap};
thread_local! {
static TLS: RefCell<HashMap<&'static str, Arc<ComputePipeline>>> = Default::default();
}
TLS.with(|tls| {
Arc::clone(
tls.borrow_mut().entry(key).or_insert_with(|| {
Arc::new(ComputePipeline::create(device, info, shader).unwrap())
}),
)
})
}
fn graphic_vert_frag_pipeline(
device: &Arc<Device>,
info: impl Into<GraphicPipelineInfo>,
vert_source: &'static [u32],
frag_source: &'static [u32],
) -> Arc<GraphicPipeline> {
use std::{cell::RefCell, collections::HashMap};
#[derive(Eq, Hash, PartialEq)]
struct Key {
info: GraphicPipelineInfo,
vert_source: &'static [u32],
frag_source: &'static [u32],
}
thread_local! {
static TLS: RefCell<HashMap<Key, Arc<GraphicPipeline>>> = Default::default();
}
let info = info.into();
TLS.with(|tls| {
Arc::clone(
tls.borrow_mut()
.entry(Key {
info,
vert_source,
frag_source,
})
.or_insert_with(move || {
Arc::new(
GraphicPipeline::create(
device,
info,
[
Shader::new_vertex(vert_source),
Shader::new_fragment(frag_source),
],
)
.unwrap(),
)
}),
)
})
}
#[derive(Parser)]
struct Args {
#[arg(long, default_value_t = 100)]
frame_count: usize,
#[arg(long, default_value_t = 16)]
ops_per_frame: usize,
}