use {inline_spirv::inline_spirv, rand::random, screen_13::prelude::*, std::sync::Arc};
fn main() -> Result<(), DisplayError> {
pretty_env_logger::init();
let screen_13 = EventLoop::new().debug(true).build()?;
let mut cache = HashPool::new(&screen_13.device);
let mut frame_count = 0;
screen_13.run(|mut frame| {
frame_count += 1;
if frame_count == 10 {
*frame.will_exit = true;
}
let operations_per_frame = 16;
let operation: u8 = random();
for _ in 0..operations_per_frame {
match operation % 7 {
0 => record_compute_array_bind(&mut frame, &mut cache),
1 => record_compute_bindless(&mut frame, &mut cache),
2 => record_compute_no_op(&mut frame),
3 => record_graphic_bindless(&mut frame, &mut cache),
4 => record_graphic_load_store(&mut frame),
5 => record_graphic_will_merge_subpass_input(&mut frame, &mut cache),
6 => record_graphic_wont_merge(&mut frame, &mut cache),
_ => unreachable!(),
}
}
frame.render_graph.clear_color_image(frame.swapchain_image);
})?;
debug!("OK");
Ok(())
}
fn record_compute_array_bind(frame: &mut FrameContext, cache: &mut HashPool) {
let pipeline = compute_pipeline(
"array_bind",
frame.device,
ComputePipelineInfo::new(
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::new_2d(
vk::Format::R8G8B8A8_UNORM,
64,
64,
vk::ImageUsageFlags::SAMPLED,
)
.build();
let images = [
frame
.render_graph
.bind_node(cache.lease(image_info).unwrap()),
frame
.render_graph
.bind_node(cache.lease(image_info).unwrap()),
frame
.render_graph
.bind_node(cache.lease(image_info).unwrap()),
frame
.render_graph
.bind_node(cache.lease(image_info).unwrap()),
frame
.render_graph
.bind_node(cache.lease(image_info).unwrap()),
];
frame
.render_graph
.begin_pass("no-op")
.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, cache: &mut HashPool) {
let pipeline = compute_pipeline(
"bindless",
frame.device,
ComputePipelineInfo::new(
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::new_2d(
vk::Format::R8G8B8A8_UNORM,
64,
64,
vk::ImageUsageFlags::SAMPLED | vk::ImageUsageFlags::STORAGE,
)
.build();
let images = [
frame
.render_graph
.bind_node(cache.lease(image_info).unwrap()),
frame
.render_graph
.bind_node(cache.lease(image_info).unwrap()),
frame
.render_graph
.bind_node(cache.lease(image_info).unwrap()),
frame
.render_graph
.bind_node(cache.lease(image_info).unwrap()),
frame
.render_graph
.bind_node(cache.lease(image_info).unwrap()),
];
frame
.render_graph
.begin_pass("no-op")
.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) {
let pipeline = compute_pipeline(
"no_op",
frame.device,
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, cache: &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(
cache
.lease(ImageInfo::new_2d(
vk::Format::R8G8B8A8_UNORM,
256,
256,
vk::ImageUsageFlags::COLOR_ATTACHMENT | vk::ImageUsageFlags::INPUT_ATTACHMENT,
))
.unwrap(),
);
let image_info = ImageInfo::new_2d(
vk::Format::R8G8B8A8_UNORM,
64,
64,
vk::ImageUsageFlags::SAMPLED | vk::ImageUsageFlags::STORAGE,
)
.build();
let images = [
frame
.render_graph
.bind_node(cache.lease(image_info).unwrap()),
frame
.render_graph
.bind_node(cache.lease(image_info).unwrap()),
frame
.render_graph
.bind_node(cache.lease(image_info).unwrap()),
frame
.render_graph
.bind_node(cache.lease(image_info).unwrap()),
frame
.render_graph
.bind_node(cache.lease(image_info).unwrap()),
];
frame
.render_graph
.begin_pass("a")
.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)
.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) {
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_will_merge_subpass_input(frame: &mut FrameContext, cache: &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(
cache
.lease(ImageInfo::new_2d(
vk::Format::R8G8B8A8_UNORM,
256,
256,
vk::ImageUsageFlags::COLOR_ATTACHMENT | vk::ImageUsageFlags::INPUT_ATTACHMENT,
))
.unwrap(),
);
frame
.render_graph
.begin_pass("a")
.bind_pipeline(&pipeline_a)
.clear_color(0)
.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, cache: &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(
cache
.lease(ImageInfo::new_2d(
vk::Format::R8G8B8A8_UNORM,
256,
256,
vk::ImageUsageFlags::COLOR_ATTACHMENT,
))
.unwrap(),
);
frame
.render_graph
.begin_pass("c")
.bind_pipeline(&pipeline)
.store_color(0, image)
.record_subpass(|subpass| {
subpass.draw(0, 0, 0, 0);
});
frame
.render_graph
.begin_pass("d")
.bind_pipeline(&pipeline)
.store_color(0, image)
.record_subpass(|subpass| {
subpass.draw(0, 0, 0, 0);
});
}
fn compute_pipeline(
key: &'static str,
device: &Arc<Device>,
info: impl Into<ComputePipelineInfo>,
) -> 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).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: info.clone(),
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(),
)
}),
)
})
}