mirror of
https://github.com/vulkano-rs/vulkano.git
synced 2024-11-21 22:34:43 +00:00
Task graph [5/10]: the new command buffer (#2567)
This commit is contained in:
parent
d89e5cf608
commit
bad11eef71
10
Cargo.lock
generated
10
Cargo.lock
generated
@ -208,6 +208,16 @@ dependencies = [
|
||||
"objc2 0.5.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "bloom"
|
||||
version = "0.0.0"
|
||||
dependencies = [
|
||||
"vulkano",
|
||||
"vulkano-shaders",
|
||||
"vulkano-taskgraph",
|
||||
"winit 0.29.15",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "buffer-allocator"
|
||||
version = "0.0.0"
|
||||
|
@ -41,11 +41,8 @@ use std::{
|
||||
time::{SystemTime, UNIX_EPOCH},
|
||||
};
|
||||
use vulkano::{
|
||||
buffer::{Buffer, BufferContents, BufferCreateInfo, BufferUsage, Subbuffer},
|
||||
command_buffer::{
|
||||
sys::RawRecordingCommandBuffer, BufferImageCopy, ClearColorImageInfo,
|
||||
CopyBufferToImageInfo, RenderPassBeginInfo,
|
||||
},
|
||||
buffer::{Buffer, BufferContents, BufferCreateInfo, BufferUsage},
|
||||
command_buffer::RenderPassBeginInfo,
|
||||
descriptor_set::{
|
||||
allocator::StandardDescriptorSetAllocator, DescriptorSet, WriteDescriptorSet,
|
||||
},
|
||||
@ -57,7 +54,7 @@ use vulkano::{
|
||||
image::{
|
||||
sampler::{Sampler, SamplerCreateInfo},
|
||||
view::ImageView,
|
||||
Image, ImageCreateInfo, ImageType, ImageUsage,
|
||||
Image, ImageAspects, ImageCreateInfo, ImageSubresourceLayers, ImageType, ImageUsage,
|
||||
},
|
||||
instance::{Instance, InstanceCreateFlags, InstanceCreateInfo},
|
||||
memory::allocator::{AllocationCreateInfo, DeviceLayout, MemoryTypeFilter},
|
||||
@ -81,6 +78,9 @@ use vulkano::{
|
||||
DeviceSize, Validated, VulkanError, VulkanLibrary,
|
||||
};
|
||||
use vulkano_taskgraph::{
|
||||
command_buffer::{
|
||||
BufferImageCopy, ClearColorImageInfo, CopyBufferToImageInfo, RecordingCommandBuffer,
|
||||
},
|
||||
graph::{CompileInfo, ExecuteError, TaskGraph},
|
||||
resource::{AccessType, Flight, HostAccessType, ImageLayoutType, Resources},
|
||||
resource_map, Id, QueueFamilyType, Task, TaskContext, TaskResult,
|
||||
@ -224,7 +224,7 @@ fn main() -> Result<(), impl Error> {
|
||||
{transfer_family_index} for transfers",
|
||||
);
|
||||
|
||||
let resources = Resources::new(device.clone(), Default::default());
|
||||
let resources = Resources::new(&device, &Default::default());
|
||||
|
||||
let graphics_flight_id = resources.create_flight(MAX_FRAMES_IN_FLIGHT).unwrap();
|
||||
let transfer_flight_id = resources.create_flight(1).unwrap();
|
||||
@ -343,16 +343,18 @@ fn main() -> Result<(), impl Error> {
|
||||
// Initialize the resources.
|
||||
unsafe {
|
||||
vulkano_taskgraph::execute(
|
||||
graphics_queue.clone(),
|
||||
resources.clone(),
|
||||
&graphics_queue,
|
||||
&resources,
|
||||
graphics_flight_id,
|
||||
|cbf, tcx| {
|
||||
tcx.write_buffer::<[MyVertex]>(vertex_buffer_id, ..)?
|
||||
.copy_from_slice(&vertices);
|
||||
|
||||
for &texture_id in &texture_ids {
|
||||
let texture = tcx.image(texture_id)?.image();
|
||||
cbf.clear_color_image(&ClearColorImageInfo::image(texture.clone()))?;
|
||||
cbf.clear_color_image(&ClearColorImageInfo {
|
||||
image: texture_id,
|
||||
..Default::default()
|
||||
})?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
@ -504,7 +506,7 @@ fn main() -> Result<(), impl Error> {
|
||||
framebuffers,
|
||||
};
|
||||
|
||||
let mut task_graph = TaskGraph::new(resources.clone(), 1, 4);
|
||||
let mut task_graph = TaskGraph::new(&resources, 1, 4);
|
||||
|
||||
let virtual_swapchain_id = task_graph.add_swapchain(&SwapchainCreateInfo::default());
|
||||
let virtual_texture_id = task_graph.add_image(&texture_create_info);
|
||||
@ -543,9 +545,9 @@ fn main() -> Result<(), impl Error> {
|
||||
);
|
||||
|
||||
let task_graph = unsafe {
|
||||
task_graph.compile(CompileInfo {
|
||||
queues: vec![graphics_queue.clone()],
|
||||
present_queue: Some(graphics_queue.clone()),
|
||||
task_graph.compile(&CompileInfo {
|
||||
queues: &[&graphics_queue],
|
||||
present_queue: Some(&graphics_queue),
|
||||
flight_id: graphics_flight_id,
|
||||
..Default::default()
|
||||
})
|
||||
@ -652,7 +654,6 @@ fn main() -> Result<(), impl Error> {
|
||||
}
|
||||
Event::LoopExiting => {
|
||||
let flight = resources.flight(graphics_flight_id).unwrap();
|
||||
flight.destroy_object(pipeline.clone());
|
||||
flight.destroy_objects(rcx.framebuffers.drain(..));
|
||||
flight.destroy_objects(uniform_buffer_sets.clone());
|
||||
flight.destroy_objects(sampler_sets.clone());
|
||||
@ -729,14 +730,13 @@ impl Task for RenderTask {
|
||||
|
||||
unsafe fn execute(
|
||||
&self,
|
||||
cbf: &mut RawRecordingCommandBuffer,
|
||||
cbf: &mut RecordingCommandBuffer<'_>,
|
||||
tcx: &mut TaskContext<'_>,
|
||||
rcx: &Self::World,
|
||||
) -> TaskResult {
|
||||
let frame_index = tcx.current_frame_index();
|
||||
let swapchain_state = tcx.swapchain(self.swapchain_id)?;
|
||||
let image_index = swapchain_state.current_image_index().unwrap();
|
||||
let vertex_buffer = Subbuffer::from(tcx.buffer(self.vertex_buffer_id)?.buffer().clone());
|
||||
|
||||
// Write to the uniform buffer designated for this frame.
|
||||
*tcx.write_buffer(self.uniform_buffer_id, ..)? = vs::Data {
|
||||
@ -755,16 +755,16 @@ impl Task for RenderTask {
|
||||
},
|
||||
};
|
||||
|
||||
cbf.begin_render_pass(
|
||||
cbf.as_raw().begin_render_pass(
|
||||
&RenderPassBeginInfo {
|
||||
clear_values: vec![Some([0.0, 0.0, 0.0, 1.0].into())],
|
||||
..RenderPassBeginInfo::framebuffer(rcx.framebuffers[image_index as usize].clone())
|
||||
},
|
||||
&Default::default(),
|
||||
)?
|
||||
.set_viewport(0, slice::from_ref(&rcx.viewport))?
|
||||
.bind_pipeline_graphics(&self.pipeline)?
|
||||
.bind_descriptor_sets(
|
||||
)?;
|
||||
cbf.set_viewport(0, slice::from_ref(&rcx.viewport))?;
|
||||
cbf.bind_pipeline_graphics(&self.pipeline)?;
|
||||
cbf.as_raw().bind_descriptor_sets(
|
||||
PipelineBindPoint::Graphics,
|
||||
self.pipeline.layout(),
|
||||
0,
|
||||
@ -778,13 +778,12 @@ impl Task for RenderTask {
|
||||
.clone()
|
||||
.into(),
|
||||
],
|
||||
)?
|
||||
.bind_vertex_buffers(0, slice::from_ref(&vertex_buffer))?;
|
||||
)?;
|
||||
cbf.bind_vertex_buffers(0, &[self.vertex_buffer_id], &[0], &[], &[])?;
|
||||
|
||||
let vertex_count = vertex_buffer.reinterpret_ref::<[MyVertex]>().len();
|
||||
unsafe { cbf.draw(vertex_count as u32, 1, 0, 0) }?;
|
||||
unsafe { cbf.draw(4, 1, 0, 0) }?;
|
||||
|
||||
cbf.end_render_pass(&Default::default())?;
|
||||
cbf.as_raw().end_render_pass(&Default::default())?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@ -831,7 +830,7 @@ fn run_worker(
|
||||
.unwrap()
|
||||
});
|
||||
|
||||
let mut task_graph = TaskGraph::new(resources.clone(), 1, 3);
|
||||
let mut task_graph = TaskGraph::new(&resources, 1, 3);
|
||||
|
||||
let virtual_front_staging_buffer_id = task_graph.add_buffer(&BufferCreateInfo::default());
|
||||
let virtual_back_staging_buffer_id = task_graph.add_buffer(&BufferCreateInfo::default());
|
||||
@ -861,8 +860,8 @@ fn run_worker(
|
||||
);
|
||||
|
||||
let task_graph = unsafe {
|
||||
task_graph.compile(CompileInfo {
|
||||
queues: vec![transfer_queue],
|
||||
task_graph.compile(&CompileInfo {
|
||||
queues: &[&transfer_queue],
|
||||
flight_id: transfer_flight_id,
|
||||
..Default::default()
|
||||
})
|
||||
@ -942,7 +941,7 @@ impl Task for UploadTask {
|
||||
|
||||
unsafe fn execute(
|
||||
&self,
|
||||
cbf: &mut RawRecordingCommandBuffer,
|
||||
cbf: &mut RecordingCommandBuffer<'_>,
|
||||
tcx: &mut TaskContext<'_>,
|
||||
¤t_corner: &Self::World,
|
||||
) -> TaskResult {
|
||||
@ -967,41 +966,37 @@ impl Task for UploadTask {
|
||||
tcx.write_buffer::<[_]>(self.front_staging_buffer_id, ..)?
|
||||
.fill(color);
|
||||
|
||||
let texture = tcx.image(self.texture_id)?.image();
|
||||
|
||||
cbf.copy_buffer_to_image(&CopyBufferToImageInfo {
|
||||
regions: [BufferImageCopy {
|
||||
image_subresource: texture.subresource_layers(),
|
||||
src_buffer: self.front_staging_buffer_id,
|
||||
dst_image: self.texture_id,
|
||||
regions: &[BufferImageCopy {
|
||||
image_subresource: ImageSubresourceLayers {
|
||||
aspects: ImageAspects::COLOR,
|
||||
mip_level: 0,
|
||||
array_layers: 0..1,
|
||||
},
|
||||
image_offset: CORNER_OFFSETS[current_corner % 4],
|
||||
image_extent: [TRANSFER_GRANULARITY, TRANSFER_GRANULARITY, 1],
|
||||
..Default::default()
|
||||
}]
|
||||
.into(),
|
||||
..CopyBufferToImageInfo::buffer_image(
|
||||
tcx.buffer(self.front_staging_buffer_id)?
|
||||
.buffer()
|
||||
.clone()
|
||||
.into(),
|
||||
texture.clone(),
|
||||
)
|
||||
}],
|
||||
..Default::default()
|
||||
})?;
|
||||
|
||||
if current_corner > 0 {
|
||||
cbf.copy_buffer_to_image(&CopyBufferToImageInfo {
|
||||
regions: [BufferImageCopy {
|
||||
image_subresource: texture.subresource_layers(),
|
||||
src_buffer: self.back_staging_buffer_id,
|
||||
dst_image: self.texture_id,
|
||||
regions: &[BufferImageCopy {
|
||||
image_subresource: ImageSubresourceLayers {
|
||||
aspects: ImageAspects::COLOR,
|
||||
mip_level: 0,
|
||||
array_layers: 0..1,
|
||||
},
|
||||
image_offset: CORNER_OFFSETS[(current_corner - 1) % 4],
|
||||
image_extent: [TRANSFER_GRANULARITY, TRANSFER_GRANULARITY, 1],
|
||||
..Default::default()
|
||||
}]
|
||||
.into(),
|
||||
..CopyBufferToImageInfo::buffer_image(
|
||||
tcx.buffer(self.back_staging_buffer_id)?
|
||||
.buffer()
|
||||
.clone()
|
||||
.into(),
|
||||
texture.clone(),
|
||||
)
|
||||
}],
|
||||
..Default::default()
|
||||
})?;
|
||||
}
|
||||
|
||||
|
18
examples/bloom/Cargo.toml
Normal file
18
examples/bloom/Cargo.toml
Normal file
@ -0,0 +1,18 @@
|
||||
[package]
|
||||
name = "bloom"
|
||||
version = "0.0.0"
|
||||
edition = "2021"
|
||||
publish = false
|
||||
|
||||
[[bin]]
|
||||
name = "bloom"
|
||||
path = "main.rs"
|
||||
test = false
|
||||
bench = false
|
||||
doc = false
|
||||
|
||||
[dependencies]
|
||||
vulkano = { workspace = true, default-features = true }
|
||||
vulkano-shaders = { workspace = true }
|
||||
vulkano-taskgraph = { workspace = true }
|
||||
winit = { workspace = true, default-features = true }
|
157
examples/bloom/bloom.rs
Normal file
157
examples/bloom/bloom.rs
Normal file
@ -0,0 +1,157 @@
|
||||
use crate::RenderContext;
|
||||
use std::{slice, sync::Arc};
|
||||
use vulkano::{
|
||||
image::{mip_level_extent, Image},
|
||||
pipeline::{
|
||||
compute::ComputePipelineCreateInfo, ComputePipeline, PipelineBindPoint,
|
||||
PipelineShaderStageCreateInfo,
|
||||
},
|
||||
sync::{AccessFlags, PipelineStages},
|
||||
};
|
||||
use vulkano_taskgraph::{
|
||||
command_buffer::{DependencyInfo, MemoryBarrier, RecordingCommandBuffer},
|
||||
Id, Task, TaskContext, TaskResult,
|
||||
};
|
||||
|
||||
const THRESHOLD: f32 = 1.5;
|
||||
const KNEE: f32 = 0.1;
|
||||
const INTENSITY: f32 = 1.0;
|
||||
|
||||
pub struct BloomTask {
|
||||
downsample_pipeline: Arc<ComputePipeline>,
|
||||
upsample_pipeline: Arc<ComputePipeline>,
|
||||
bloom_image_id: Id<Image>,
|
||||
}
|
||||
|
||||
impl BloomTask {
|
||||
pub fn new(rcx: &RenderContext, bloom_image_id: Id<Image>) -> Self {
|
||||
let downsample_pipeline = {
|
||||
let cs = downsample::load(rcx.device.clone())
|
||||
.unwrap()
|
||||
.entry_point("main")
|
||||
.unwrap();
|
||||
let stage = PipelineShaderStageCreateInfo::new(cs);
|
||||
|
||||
ComputePipeline::new(
|
||||
rcx.device.clone(),
|
||||
None,
|
||||
ComputePipelineCreateInfo::stage_layout(stage, rcx.pipeline_layout.clone()),
|
||||
)
|
||||
.unwrap()
|
||||
};
|
||||
|
||||
let upsample_pipeline = {
|
||||
let cs = upsample::load(rcx.device.clone())
|
||||
.unwrap()
|
||||
.entry_point("main")
|
||||
.unwrap();
|
||||
let stage = PipelineShaderStageCreateInfo::new(cs);
|
||||
|
||||
ComputePipeline::new(
|
||||
rcx.device.clone(),
|
||||
None,
|
||||
ComputePipelineCreateInfo::stage_layout(stage, rcx.pipeline_layout.clone()),
|
||||
)
|
||||
.unwrap()
|
||||
};
|
||||
|
||||
BloomTask {
|
||||
downsample_pipeline,
|
||||
upsample_pipeline,
|
||||
bloom_image_id,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Task for BloomTask {
|
||||
type World = RenderContext;
|
||||
|
||||
unsafe fn execute(
|
||||
&self,
|
||||
cbf: &mut RecordingCommandBuffer<'_>,
|
||||
tcx: &mut TaskContext<'_>,
|
||||
rcx: &Self::World,
|
||||
) -> TaskResult {
|
||||
cbf.as_raw().bind_descriptor_sets(
|
||||
PipelineBindPoint::Compute,
|
||||
&rcx.pipeline_layout,
|
||||
0,
|
||||
slice::from_ref(&rcx.descriptor_set),
|
||||
)?;
|
||||
|
||||
let bloom_image = tcx.image(self.bloom_image_id)?.image();
|
||||
|
||||
let dependency_info = DependencyInfo {
|
||||
memory_barriers: &[MemoryBarrier {
|
||||
src_stages: PipelineStages::COMPUTE_SHADER,
|
||||
src_access: AccessFlags::SHADER_WRITE,
|
||||
dst_stages: PipelineStages::COMPUTE_SHADER,
|
||||
dst_access: AccessFlags::SHADER_READ,
|
||||
..Default::default()
|
||||
}],
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
cbf.bind_pipeline_compute(&self.downsample_pipeline)?;
|
||||
|
||||
for src_mip_level in 0..bloom_image.mip_levels() - 1 {
|
||||
let dst_mip_level = src_mip_level + 1;
|
||||
let dst_extent = mip_level_extent(bloom_image.extent(), dst_mip_level).unwrap();
|
||||
let group_counts = dst_extent.map(|c| (c + 7) / 8);
|
||||
|
||||
cbf.push_constants(
|
||||
&rcx.pipeline_layout,
|
||||
0,
|
||||
&downsample::PushConstants {
|
||||
dst_mip_level,
|
||||
threshold: THRESHOLD,
|
||||
knee: KNEE,
|
||||
},
|
||||
)?;
|
||||
|
||||
unsafe { cbf.dispatch(group_counts) }?;
|
||||
|
||||
cbf.pipeline_barrier(&dependency_info)?;
|
||||
}
|
||||
|
||||
cbf.bind_pipeline_compute(&self.upsample_pipeline)?;
|
||||
|
||||
for dst_mip_level in (0..bloom_image.mip_levels() - 1).rev() {
|
||||
let dst_extent = mip_level_extent(bloom_image.extent(), dst_mip_level).unwrap();
|
||||
let group_counts = dst_extent.map(|c| (c + 7) / 8);
|
||||
|
||||
cbf.push_constants(
|
||||
&rcx.pipeline_layout,
|
||||
0,
|
||||
&upsample::PushConstants {
|
||||
dst_mip_level,
|
||||
intensity: INTENSITY,
|
||||
},
|
||||
)?;
|
||||
|
||||
unsafe { cbf.dispatch(group_counts) }?;
|
||||
|
||||
if dst_mip_level != 0 {
|
||||
cbf.pipeline_barrier(&dependency_info)?;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
mod downsample {
|
||||
vulkano_shaders::shader! {
|
||||
ty: "compute",
|
||||
path: "downsample.glsl",
|
||||
include: ["."],
|
||||
}
|
||||
}
|
||||
|
||||
mod upsample {
|
||||
vulkano_shaders::shader! {
|
||||
ty: "compute",
|
||||
path: "upsample.glsl",
|
||||
include: ["."],
|
||||
}
|
||||
}
|
97
examples/bloom/downsample.glsl
Normal file
97
examples/bloom/downsample.glsl
Normal file
@ -0,0 +1,97 @@
|
||||
#version 450
|
||||
#include <shared_exponent.glsl>
|
||||
|
||||
const uint MAX_BLOOM_MIP_LEVELS = 6;
|
||||
const float EPSILON = 1.19209290e-07;
|
||||
|
||||
layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in;
|
||||
|
||||
layout(set = 0, binding = 0) uniform sampler bloom_sampler;
|
||||
layout(set = 0, binding = 1) uniform texture2D bloom_texture;
|
||||
layout(set = 0, binding = 2, r32ui) uniform uimage2D bloom_mip_chain[MAX_BLOOM_MIP_LEVELS];
|
||||
|
||||
layout(push_constant) uniform PushConstants {
|
||||
uint dst_mip_level;
|
||||
float threshold;
|
||||
float knee;
|
||||
};
|
||||
|
||||
uint src_mip_level = dst_mip_level - 1;
|
||||
|
||||
vec3 quadraticThreshold(vec3 color) {
|
||||
float brightness = max(color.r, max(color.g, color.b));
|
||||
float quadratic_response = clamp(brightness - threshold + knee, 0.0, 2.0 * knee);
|
||||
quadratic_response = (quadratic_response * quadratic_response) / (0.25 / knee);
|
||||
|
||||
color *= max(quadratic_response, brightness - threshold) / max(brightness, EPSILON);
|
||||
|
||||
return color;
|
||||
}
|
||||
|
||||
vec3 prefilter(vec3 color) {
|
||||
return quadraticThreshold(color);
|
||||
}
|
||||
|
||||
vec3 sample1(vec2 uv) {
|
||||
return textureLod(sampler2D(bloom_texture, bloom_sampler), uv, src_mip_level).rgb;
|
||||
}
|
||||
|
||||
// 13-tap box filter.
|
||||
// ┌───┬───┬───┐
|
||||
// │ A │ B │ C │
|
||||
// ├──╴D╶─╴E╶──┤
|
||||
// │ F │ G │ H │
|
||||
// ├──╴I╶─╴J╶──┤
|
||||
// │ K │ L │ M │
|
||||
// └───┴───┴───┘
|
||||
vec3 downsampleBox13(vec2 uv, vec2 src_texel_size) {
|
||||
vec3 a = sample1(uv + vec2(-2.0, -2.0) * src_texel_size);
|
||||
vec3 b = sample1(uv + vec2( 0.0, -2.0) * src_texel_size);
|
||||
vec3 c = sample1(uv + vec2( 2.0, -2.0) * src_texel_size);
|
||||
vec3 d = sample1(uv + vec2(-1.0, -1.0) * src_texel_size);
|
||||
vec3 e = sample1(uv + vec2( 1.0, -1.0) * src_texel_size);
|
||||
vec3 f = sample1(uv + vec2(-2.0, 0.0) * src_texel_size);
|
||||
vec3 g = sample1(uv + vec2( 0.0, 0.0) * src_texel_size);
|
||||
vec3 h = sample1(uv + vec2( 2.0, 0.0) * src_texel_size);
|
||||
vec3 i = sample1(uv + vec2(-1.0, 1.0) * src_texel_size);
|
||||
vec3 j = sample1(uv + vec2( 1.0, 1.0) * src_texel_size);
|
||||
vec3 k = sample1(uv + vec2(-2.0, 2.0) * src_texel_size);
|
||||
vec3 l = sample1(uv + vec2( 0.0, 2.0) * src_texel_size);
|
||||
vec3 m = sample1(uv + vec2( 2.0, 2.0) * src_texel_size);
|
||||
|
||||
vec3 color;
|
||||
color = (d + e + i + j) * 0.25 * 0.5;
|
||||
color += (a + b + f + g) * 0.25 * 0.125;
|
||||
color += (b + c + g + h) * 0.25 * 0.125;
|
||||
color += (f + g + k + l) * 0.25 * 0.125;
|
||||
color += (g + h + l + m) * 0.25 * 0.125;
|
||||
|
||||
return color;
|
||||
}
|
||||
|
||||
void store(ivec2 dst_coord, vec3 color) {
|
||||
uint packed = convertToSharedExponent(color);
|
||||
imageStore(bloom_mip_chain[dst_mip_level], dst_coord, uvec4(packed, 0, 0, 0));
|
||||
}
|
||||
|
||||
void main() {
|
||||
ivec2 dst_coord = ivec2(gl_GlobalInvocationID.xy);
|
||||
ivec2 dst_size = imageSize(bloom_mip_chain[dst_mip_level]);
|
||||
|
||||
if (dst_coord.x > dst_size.x || dst_coord.y > dst_size.y) {
|
||||
return;
|
||||
}
|
||||
|
||||
ivec2 src_size = textureSize(sampler2D(bloom_texture, bloom_sampler), int(src_mip_level));
|
||||
vec2 src_texel_size = 1.0 / vec2(src_size);
|
||||
vec2 uv = (vec2(dst_coord) + 0.5) / vec2(dst_size);
|
||||
vec3 color = downsampleBox13(uv, src_texel_size);
|
||||
|
||||
if (src_mip_level == 0) {
|
||||
color = prefilter(color);
|
||||
}
|
||||
|
||||
color = max(color, 0.0001);
|
||||
|
||||
store(dst_coord, color);
|
||||
}
|
598
examples/bloom/main.rs
Normal file
598
examples/bloom/main.rs
Normal file
@ -0,0 +1,598 @@
|
||||
// TODO: document
|
||||
|
||||
use bloom::BloomTask;
|
||||
use scene::SceneTask;
|
||||
use std::{cmp, error::Error, sync::Arc};
|
||||
use tonemap::TonemapTask;
|
||||
use vulkano::{
|
||||
descriptor_set::{
|
||||
allocator::StandardDescriptorSetAllocator,
|
||||
layout::{
|
||||
DescriptorSetLayout, DescriptorSetLayoutBinding, DescriptorSetLayoutCreateInfo,
|
||||
DescriptorType,
|
||||
},
|
||||
DescriptorImageViewInfo, DescriptorSet, DescriptorSetWithOffsets, WriteDescriptorSet,
|
||||
},
|
||||
device::{
|
||||
physical::PhysicalDeviceType, Device, DeviceCreateInfo, DeviceExtensions, DeviceOwned,
|
||||
Queue, QueueCreateInfo, QueueFlags,
|
||||
},
|
||||
format::{Format, NumericFormat},
|
||||
image::{
|
||||
max_mip_levels,
|
||||
sampler::{Filter, Sampler, SamplerCreateInfo, SamplerMipmapMode, LOD_CLAMP_NONE},
|
||||
view::{ImageView, ImageViewCreateInfo},
|
||||
Image, ImageAspects, ImageCreateFlags, ImageCreateInfo, ImageLayout, ImageSubresourceRange,
|
||||
ImageType, ImageUsage,
|
||||
},
|
||||
instance::{Instance, InstanceCreateFlags, InstanceCreateInfo},
|
||||
memory::allocator::AllocationCreateInfo,
|
||||
pipeline::{
|
||||
graphics::viewport::Viewport,
|
||||
layout::{PipelineLayoutCreateInfo, PushConstantRange},
|
||||
PipelineLayout,
|
||||
},
|
||||
shader::ShaderStages,
|
||||
swapchain::{ColorSpace, Surface, Swapchain, SwapchainCreateInfo},
|
||||
Validated, Version, VulkanError, VulkanLibrary,
|
||||
};
|
||||
use vulkano_taskgraph::{
|
||||
graph::{CompileInfo, ExecuteError, TaskGraph},
|
||||
resource::{AccessType, Flight, ImageLayoutType, Resources},
|
||||
resource_map, Id, QueueFamilyType,
|
||||
};
|
||||
use winit::{
|
||||
event::{Event, WindowEvent},
|
||||
event_loop::{ControlFlow, EventLoop},
|
||||
window::{Window, WindowBuilder},
|
||||
};
|
||||
|
||||
mod bloom;
|
||||
mod scene;
|
||||
mod tonemap;
|
||||
|
||||
const MAX_FRAMES_IN_FLIGHT: u32 = 2;
|
||||
const MAX_BLOOM_MIP_LEVELS: u32 = 6;
|
||||
|
||||
fn main() -> Result<(), impl Error> {
|
||||
let event_loop = EventLoop::new().unwrap();
|
||||
|
||||
let library = VulkanLibrary::new().unwrap();
|
||||
let required_extensions = Surface::required_extensions(&event_loop).unwrap();
|
||||
let instance = Instance::new(
|
||||
library,
|
||||
InstanceCreateInfo {
|
||||
flags: InstanceCreateFlags::ENUMERATE_PORTABILITY,
|
||||
enabled_extensions: required_extensions,
|
||||
..Default::default()
|
||||
},
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let mut rcx = RenderContext::new(&event_loop, &instance);
|
||||
|
||||
let mut task_graph = TaskGraph::new(&rcx.resources, 3, 2);
|
||||
|
||||
let virtual_swapchain_id = task_graph.add_swapchain(&SwapchainCreateInfo::default());
|
||||
let virtual_bloom_image_id = task_graph.add_image(&ImageCreateInfo::default());
|
||||
|
||||
let scene_node_id = task_graph
|
||||
.create_task_node("Scene", QueueFamilyType::Graphics, SceneTask::new(&rcx))
|
||||
.image_access(
|
||||
virtual_bloom_image_id,
|
||||
AccessType::ColorAttachmentWrite,
|
||||
ImageLayoutType::Optimal,
|
||||
)
|
||||
.build();
|
||||
let bloom_node_id = task_graph
|
||||
.create_task_node(
|
||||
"Bloom",
|
||||
QueueFamilyType::Compute,
|
||||
BloomTask::new(&rcx, virtual_bloom_image_id),
|
||||
)
|
||||
.image_access(
|
||||
virtual_bloom_image_id,
|
||||
AccessType::ComputeShaderSampledRead,
|
||||
ImageLayoutType::General,
|
||||
)
|
||||
.image_access(
|
||||
virtual_bloom_image_id,
|
||||
AccessType::ComputeShaderStorageWrite,
|
||||
ImageLayoutType::General,
|
||||
)
|
||||
.build();
|
||||
let tonemap_node_id = task_graph
|
||||
.create_task_node(
|
||||
"Tonemap",
|
||||
QueueFamilyType::Graphics,
|
||||
TonemapTask::new(&rcx, virtual_swapchain_id),
|
||||
)
|
||||
.image_access(
|
||||
virtual_swapchain_id.current_image_id(),
|
||||
AccessType::ColorAttachmentWrite,
|
||||
ImageLayoutType::Optimal,
|
||||
)
|
||||
.image_access(
|
||||
virtual_bloom_image_id,
|
||||
AccessType::FragmentShaderSampledRead,
|
||||
ImageLayoutType::General,
|
||||
)
|
||||
.build();
|
||||
|
||||
task_graph.add_edge(scene_node_id, bloom_node_id).unwrap();
|
||||
task_graph.add_edge(bloom_node_id, tonemap_node_id).unwrap();
|
||||
|
||||
let mut task_graph = unsafe {
|
||||
task_graph.compile(&CompileInfo {
|
||||
queues: &[&rcx.queue],
|
||||
present_queue: Some(&rcx.queue),
|
||||
flight_id: rcx.flight_id,
|
||||
..Default::default()
|
||||
})
|
||||
}
|
||||
.unwrap();
|
||||
|
||||
let mut recreate_swapchain = false;
|
||||
|
||||
event_loop.run(move |event, elwt| {
|
||||
elwt.set_control_flow(ControlFlow::Poll);
|
||||
|
||||
match event {
|
||||
Event::WindowEvent {
|
||||
event: WindowEvent::CloseRequested,
|
||||
..
|
||||
} => {
|
||||
elwt.exit();
|
||||
}
|
||||
Event::WindowEvent {
|
||||
event: WindowEvent::Resized(_),
|
||||
..
|
||||
} => {
|
||||
recreate_swapchain = true;
|
||||
}
|
||||
Event::WindowEvent {
|
||||
event: WindowEvent::RedrawRequested,
|
||||
..
|
||||
} => {
|
||||
let image_extent: [u32; 2] = rcx.window.inner_size().into();
|
||||
|
||||
if image_extent.contains(&0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if recreate_swapchain {
|
||||
rcx.handle_resize();
|
||||
|
||||
task_graph
|
||||
.task_node_mut(scene_node_id)
|
||||
.unwrap()
|
||||
.task_mut()
|
||||
.downcast_mut::<SceneTask>()
|
||||
.unwrap()
|
||||
.handle_resize(&rcx);
|
||||
task_graph
|
||||
.task_node_mut(tonemap_node_id)
|
||||
.unwrap()
|
||||
.task_mut()
|
||||
.downcast_mut::<TonemapTask>()
|
||||
.unwrap()
|
||||
.handle_resize(&rcx);
|
||||
|
||||
recreate_swapchain = false;
|
||||
}
|
||||
|
||||
let flight = rcx.resources.flight(rcx.flight_id).unwrap();
|
||||
|
||||
flight.wait(None).unwrap();
|
||||
|
||||
let resource_map = resource_map!(
|
||||
&task_graph,
|
||||
virtual_swapchain_id => rcx.swapchain_id,
|
||||
virtual_bloom_image_id => rcx.bloom_image_id,
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
match unsafe {
|
||||
task_graph.execute(resource_map, &rcx, || rcx.window.pre_present_notify())
|
||||
} {
|
||||
Ok(()) => {}
|
||||
Err(ExecuteError::Swapchain {
|
||||
error: Validated::Error(VulkanError::OutOfDate),
|
||||
..
|
||||
}) => {
|
||||
recreate_swapchain = true;
|
||||
}
|
||||
Err(e) => {
|
||||
panic!("failed to execute next frame: {e:?}");
|
||||
}
|
||||
}
|
||||
}
|
||||
Event::AboutToWait => {
|
||||
rcx.window.request_redraw();
|
||||
}
|
||||
Event::LoopExiting => {
|
||||
rcx.cleanup();
|
||||
|
||||
task_graph
|
||||
.task_node_mut(scene_node_id)
|
||||
.unwrap()
|
||||
.task_mut()
|
||||
.downcast_mut::<SceneTask>()
|
||||
.unwrap()
|
||||
.cleanup(&rcx);
|
||||
task_graph
|
||||
.task_node_mut(tonemap_node_id)
|
||||
.unwrap()
|
||||
.task_mut()
|
||||
.downcast_mut::<TonemapTask>()
|
||||
.unwrap()
|
||||
.cleanup(&rcx);
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
pub struct RenderContext {
|
||||
device: Arc<Device>,
|
||||
queue: Arc<Queue>,
|
||||
resources: Arc<Resources>,
|
||||
flight_id: Id<Flight>,
|
||||
window: Arc<Window>,
|
||||
swapchain_id: Id<Swapchain>,
|
||||
swapchain_format: Format,
|
||||
bloom_image_id: Id<Image>,
|
||||
viewport: Viewport,
|
||||
pipeline_layout: Arc<PipelineLayout>,
|
||||
descriptor_set_allocator: Arc<StandardDescriptorSetAllocator>,
|
||||
sampler: Arc<Sampler>,
|
||||
descriptor_set: DescriptorSetWithOffsets,
|
||||
}
|
||||
|
||||
impl RenderContext {
|
||||
fn new(event_loop: &EventLoop<()>, instance: &Arc<Instance>) -> Self {
|
||||
let mut device_extensions = DeviceExtensions {
|
||||
khr_swapchain: true,
|
||||
..DeviceExtensions::empty()
|
||||
};
|
||||
let (physical_device, queue_family_index) = instance
|
||||
.enumerate_physical_devices()
|
||||
.unwrap()
|
||||
.filter(|p| {
|
||||
p.api_version() >= Version::V1_1 || p.supported_extensions().khr_maintenance2
|
||||
})
|
||||
.filter(|p| p.supported_extensions().contains(&device_extensions))
|
||||
.filter_map(|p| {
|
||||
p.queue_family_properties()
|
||||
.iter()
|
||||
.enumerate()
|
||||
.position(|(i, q)| {
|
||||
q.queue_flags
|
||||
.contains(QueueFlags::GRAPHICS | QueueFlags::COMPUTE)
|
||||
&& p.presentation_support(i as u32, event_loop).unwrap()
|
||||
})
|
||||
.map(|i| (p, i as u32))
|
||||
})
|
||||
.min_by_key(|(p, _)| match p.properties().device_type {
|
||||
PhysicalDeviceType::DiscreteGpu => 0,
|
||||
PhysicalDeviceType::IntegratedGpu => 1,
|
||||
PhysicalDeviceType::VirtualGpu => 2,
|
||||
PhysicalDeviceType::Cpu => 3,
|
||||
PhysicalDeviceType::Other => 4,
|
||||
_ => 5,
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
println!(
|
||||
"Using device: {} (type: {:?})",
|
||||
physical_device.properties().device_name,
|
||||
physical_device.properties().device_type,
|
||||
);
|
||||
|
||||
if physical_device.api_version() < Version::V1_1 {
|
||||
device_extensions.khr_maintenance2 = true;
|
||||
}
|
||||
|
||||
if physical_device.api_version() < Version::V1_2
|
||||
&& physical_device.supported_extensions().khr_image_format_list
|
||||
{
|
||||
device_extensions.khr_image_format_list = true;
|
||||
}
|
||||
|
||||
let (device, mut queues) = Device::new(
|
||||
physical_device,
|
||||
DeviceCreateInfo {
|
||||
enabled_extensions: device_extensions,
|
||||
queue_create_infos: vec![QueueCreateInfo {
|
||||
queue_family_index,
|
||||
..Default::default()
|
||||
}],
|
||||
..Default::default()
|
||||
},
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let queue = queues.next().unwrap();
|
||||
|
||||
let resources = Resources::new(&device, &Default::default());
|
||||
|
||||
let flight_id = resources.create_flight(MAX_FRAMES_IN_FLIGHT).unwrap();
|
||||
|
||||
let window = Arc::new(WindowBuilder::new().build(event_loop).unwrap());
|
||||
let surface = Surface::from_window(instance.clone(), window.clone()).unwrap();
|
||||
|
||||
let swapchain_format;
|
||||
let swapchain_id = {
|
||||
let surface_capabilities = device
|
||||
.physical_device()
|
||||
.surface_capabilities(&surface, Default::default())
|
||||
.unwrap();
|
||||
(swapchain_format, _) = device
|
||||
.physical_device()
|
||||
.surface_formats(&surface, Default::default())
|
||||
.unwrap()
|
||||
.into_iter()
|
||||
.find(|&(format, color_space)| {
|
||||
format.numeric_format_color() == Some(NumericFormat::SRGB)
|
||||
&& color_space == ColorSpace::SrgbNonLinear
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
resources
|
||||
.create_swapchain(
|
||||
flight_id,
|
||||
surface,
|
||||
SwapchainCreateInfo {
|
||||
min_image_count: surface_capabilities.min_image_count.max(3),
|
||||
image_format: swapchain_format,
|
||||
image_extent: window.inner_size().into(),
|
||||
image_usage: ImageUsage::COLOR_ATTACHMENT,
|
||||
composite_alpha: surface_capabilities
|
||||
.supported_composite_alpha
|
||||
.into_iter()
|
||||
.next()
|
||||
.unwrap(),
|
||||
..Default::default()
|
||||
},
|
||||
)
|
||||
.unwrap()
|
||||
};
|
||||
|
||||
let viewport = Viewport {
|
||||
offset: [0.0, 0.0],
|
||||
extent: window.inner_size().into(),
|
||||
depth_range: 0.0..=1.0,
|
||||
};
|
||||
|
||||
let pipeline_layout = PipelineLayout::new(
|
||||
device.clone(),
|
||||
PipelineLayoutCreateInfo {
|
||||
set_layouts: vec![DescriptorSetLayout::new(
|
||||
device.clone(),
|
||||
DescriptorSetLayoutCreateInfo {
|
||||
bindings: [
|
||||
(
|
||||
0,
|
||||
DescriptorSetLayoutBinding {
|
||||
stages: ShaderStages::FRAGMENT | ShaderStages::COMPUTE,
|
||||
..DescriptorSetLayoutBinding::descriptor_type(
|
||||
DescriptorType::Sampler,
|
||||
)
|
||||
},
|
||||
),
|
||||
(
|
||||
1,
|
||||
DescriptorSetLayoutBinding {
|
||||
stages: ShaderStages::FRAGMENT | ShaderStages::COMPUTE,
|
||||
..DescriptorSetLayoutBinding::descriptor_type(
|
||||
DescriptorType::SampledImage,
|
||||
)
|
||||
},
|
||||
),
|
||||
(
|
||||
2,
|
||||
DescriptorSetLayoutBinding {
|
||||
stages: ShaderStages::COMPUTE,
|
||||
descriptor_count: MAX_BLOOM_MIP_LEVELS,
|
||||
..DescriptorSetLayoutBinding::descriptor_type(
|
||||
DescriptorType::StorageImage,
|
||||
)
|
||||
},
|
||||
),
|
||||
]
|
||||
.into_iter()
|
||||
.collect(),
|
||||
..Default::default()
|
||||
},
|
||||
)
|
||||
.unwrap()],
|
||||
push_constant_ranges: vec![PushConstantRange {
|
||||
stages: ShaderStages::FRAGMENT | ShaderStages::COMPUTE,
|
||||
offset: 0,
|
||||
size: 12,
|
||||
}],
|
||||
..Default::default()
|
||||
},
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let descriptor_set_allocator = Arc::new(StandardDescriptorSetAllocator::new(
|
||||
device.clone(),
|
||||
Default::default(),
|
||||
));
|
||||
|
||||
let sampler = Sampler::new(
|
||||
device.clone(),
|
||||
SamplerCreateInfo {
|
||||
mag_filter: Filter::Linear,
|
||||
min_filter: Filter::Linear,
|
||||
mipmap_mode: SamplerMipmapMode::Nearest,
|
||||
lod: 0.0..=LOD_CLAMP_NONE,
|
||||
..Default::default()
|
||||
},
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let (bloom_image_id, descriptor_set) = window_size_dependent_setup(
|
||||
&resources,
|
||||
swapchain_id,
|
||||
&pipeline_layout,
|
||||
&sampler,
|
||||
&descriptor_set_allocator,
|
||||
);
|
||||
|
||||
RenderContext {
|
||||
device,
|
||||
queue,
|
||||
window,
|
||||
resources,
|
||||
flight_id,
|
||||
swapchain_id,
|
||||
swapchain_format,
|
||||
bloom_image_id,
|
||||
viewport,
|
||||
pipeline_layout,
|
||||
sampler,
|
||||
descriptor_set_allocator,
|
||||
descriptor_set,
|
||||
}
|
||||
}
|
||||
|
||||
fn handle_resize(&mut self) {
|
||||
let window_size = self.window.inner_size();
|
||||
|
||||
self.swapchain_id = self
|
||||
.resources
|
||||
.recreate_swapchain(self.swapchain_id, |create_info| SwapchainCreateInfo {
|
||||
image_extent: window_size.into(),
|
||||
..create_info
|
||||
})
|
||||
.expect("failed to recreate swapchain");
|
||||
|
||||
let flight = self.resources.flight(self.flight_id).unwrap();
|
||||
let bloom_image_state =
|
||||
unsafe { self.resources.remove_image(self.bloom_image_id) }.unwrap();
|
||||
flight.destroy_object(bloom_image_state.image().clone());
|
||||
flight.destroy_object(self.descriptor_set.as_ref().0.clone());
|
||||
|
||||
(self.bloom_image_id, self.descriptor_set) = window_size_dependent_setup(
|
||||
&self.resources,
|
||||
self.swapchain_id,
|
||||
&self.pipeline_layout,
|
||||
&self.sampler,
|
||||
&self.descriptor_set_allocator,
|
||||
);
|
||||
|
||||
self.viewport.extent = window_size.into();
|
||||
}
|
||||
|
||||
fn cleanup(&mut self) {
|
||||
let flight = self.resources.flight(self.flight_id).unwrap();
|
||||
flight.destroy_object(self.descriptor_set.as_ref().0.clone());
|
||||
}
|
||||
}
|
||||
|
||||
/// This function is called once during initialization, then again whenever the window is resized.
|
||||
fn window_size_dependent_setup(
|
||||
resources: &Resources,
|
||||
swapchain_id: Id<Swapchain>,
|
||||
pipeline_layout: &Arc<PipelineLayout>,
|
||||
sampler: &Arc<Sampler>,
|
||||
descriptor_set_allocator: &Arc<StandardDescriptorSetAllocator>,
|
||||
) -> (Id<Image>, DescriptorSetWithOffsets) {
|
||||
let device = resources.device();
|
||||
let swapchain_state = resources.swapchain(swapchain_id).unwrap();
|
||||
let images = swapchain_state.images();
|
||||
let extent = images[0].extent();
|
||||
|
||||
let bloom_image_mip_levels = cmp::min(MAX_BLOOM_MIP_LEVELS, max_mip_levels(extent));
|
||||
|
||||
let bloom_image_id = {
|
||||
let view_formats = if device.api_version() >= Version::V1_2
|
||||
|| device.enabled_extensions().khr_image_format_list
|
||||
{
|
||||
vec![Format::R32_UINT, Format::E5B9G9R9_UFLOAT_PACK32]
|
||||
} else {
|
||||
Vec::new()
|
||||
};
|
||||
|
||||
resources
|
||||
.create_image(
|
||||
ImageCreateInfo {
|
||||
flags: ImageCreateFlags::MUTABLE_FORMAT,
|
||||
image_type: ImageType::Dim2d,
|
||||
format: Format::R32_UINT,
|
||||
view_formats,
|
||||
extent,
|
||||
mip_levels: bloom_image_mip_levels,
|
||||
usage: ImageUsage::TRANSFER_DST
|
||||
| ImageUsage::SAMPLED
|
||||
| ImageUsage::STORAGE
|
||||
| ImageUsage::COLOR_ATTACHMENT,
|
||||
..Default::default()
|
||||
},
|
||||
AllocationCreateInfo::default(),
|
||||
)
|
||||
.unwrap()
|
||||
};
|
||||
|
||||
let bloom_image_state = resources.image(bloom_image_id).unwrap();
|
||||
let bloom_image = bloom_image_state.image();
|
||||
|
||||
let bloom_texture_view = ImageView::new(
|
||||
bloom_image.clone(),
|
||||
ImageViewCreateInfo {
|
||||
format: Format::E5B9G9R9_UFLOAT_PACK32,
|
||||
subresource_range: bloom_image.subresource_range(),
|
||||
usage: ImageUsage::SAMPLED,
|
||||
..Default::default()
|
||||
},
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let bloom_mip_chain_views = (0..MAX_BLOOM_MIP_LEVELS).map(|mip_level| {
|
||||
let mip_level = cmp::min(mip_level, max_mip_levels(extent) - 1);
|
||||
|
||||
ImageView::new(
|
||||
bloom_image.clone(),
|
||||
ImageViewCreateInfo {
|
||||
format: Format::R32_UINT,
|
||||
subresource_range: ImageSubresourceRange {
|
||||
aspects: ImageAspects::COLOR,
|
||||
mip_levels: mip_level..mip_level + 1,
|
||||
array_layers: 0..1,
|
||||
},
|
||||
usage: ImageUsage::STORAGE,
|
||||
..Default::default()
|
||||
},
|
||||
)
|
||||
.unwrap()
|
||||
});
|
||||
|
||||
let descriptor_set = DescriptorSet::new(
|
||||
descriptor_set_allocator.clone(),
|
||||
pipeline_layout.set_layouts()[0].clone(),
|
||||
[
|
||||
WriteDescriptorSet::sampler(0, sampler.clone()),
|
||||
WriteDescriptorSet::image_view_with_layout(
|
||||
1,
|
||||
DescriptorImageViewInfo {
|
||||
image_view: bloom_texture_view,
|
||||
image_layout: ImageLayout::General,
|
||||
},
|
||||
),
|
||||
WriteDescriptorSet::image_view_with_layout_array(
|
||||
2,
|
||||
0,
|
||||
bloom_mip_chain_views.map(|image_view| DescriptorImageViewInfo {
|
||||
image_view,
|
||||
image_layout: ImageLayout::General,
|
||||
}),
|
||||
),
|
||||
],
|
||||
[],
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
(bloom_image_id, descriptor_set.into())
|
||||
}
|
259
examples/bloom/scene.rs
Normal file
259
examples/bloom/scene.rs
Normal file
@ -0,0 +1,259 @@
|
||||
use crate::RenderContext;
|
||||
use std::{alloc::Layout, mem, slice, sync::Arc};
|
||||
use vulkano::{
|
||||
buffer::{Buffer, BufferContents, BufferCreateInfo, BufferUsage},
|
||||
command_buffer::RenderPassBeginInfo,
|
||||
format::Format,
|
||||
image::{
|
||||
view::{ImageView, ImageViewCreateInfo},
|
||||
ImageAspects, ImageSubresourceRange, ImageUsage,
|
||||
},
|
||||
memory::allocator::{AllocationCreateInfo, DeviceLayout, MemoryTypeFilter},
|
||||
pipeline::{
|
||||
graphics::{
|
||||
color_blend::{ColorBlendAttachmentState, ColorBlendState},
|
||||
input_assembly::InputAssemblyState,
|
||||
multisample::MultisampleState,
|
||||
rasterization::RasterizationState,
|
||||
vertex_input::{Vertex, VertexDefinition},
|
||||
viewport::ViewportState,
|
||||
GraphicsPipelineCreateInfo,
|
||||
},
|
||||
DynamicState, GraphicsPipeline, PipelineShaderStageCreateInfo,
|
||||
},
|
||||
render_pass::{Framebuffer, FramebufferCreateInfo, RenderPass, Subpass},
|
||||
};
|
||||
use vulkano_taskgraph::{
|
||||
command_buffer::RecordingCommandBuffer, resource::HostAccessType, Id, Task, TaskContext,
|
||||
TaskResult,
|
||||
};
|
||||
|
||||
pub struct SceneTask {
|
||||
render_pass: Arc<RenderPass>,
|
||||
pipeline: Arc<GraphicsPipeline>,
|
||||
framebuffer: Arc<Framebuffer>,
|
||||
vertex_buffer_id: Id<Buffer>,
|
||||
}
|
||||
|
||||
impl SceneTask {
|
||||
pub fn new(rcx: &RenderContext) -> Self {
|
||||
let render_pass = vulkano::single_pass_renderpass!(
|
||||
rcx.device.clone(),
|
||||
attachments: {
|
||||
color: {
|
||||
format: Format::R32_UINT,
|
||||
samples: 1,
|
||||
load_op: Clear,
|
||||
store_op: Store,
|
||||
},
|
||||
},
|
||||
pass: {
|
||||
color: [color],
|
||||
depth_stencil: {},
|
||||
},
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let pipeline = {
|
||||
let vs = vs::load(rcx.device.clone())
|
||||
.unwrap()
|
||||
.entry_point("main")
|
||||
.unwrap();
|
||||
let fs = fs::load(rcx.device.clone())
|
||||
.unwrap()
|
||||
.entry_point("main")
|
||||
.unwrap();
|
||||
let vertex_input_state = MyVertex::per_vertex().definition(&vs).unwrap();
|
||||
let stages = [
|
||||
PipelineShaderStageCreateInfo::new(vs),
|
||||
PipelineShaderStageCreateInfo::new(fs),
|
||||
];
|
||||
let subpass = Subpass::from(render_pass.clone(), 0).unwrap();
|
||||
|
||||
GraphicsPipeline::new(
|
||||
rcx.device.clone(),
|
||||
None,
|
||||
GraphicsPipelineCreateInfo {
|
||||
stages: stages.into_iter().collect(),
|
||||
vertex_input_state: Some(vertex_input_state),
|
||||
input_assembly_state: Some(InputAssemblyState::default()),
|
||||
viewport_state: Some(ViewportState::default()),
|
||||
rasterization_state: Some(RasterizationState::default()),
|
||||
multisample_state: Some(MultisampleState::default()),
|
||||
color_blend_state: Some(ColorBlendState::with_attachment_states(
|
||||
subpass.num_color_attachments(),
|
||||
ColorBlendAttachmentState::default(),
|
||||
)),
|
||||
dynamic_state: [DynamicState::Viewport].into_iter().collect(),
|
||||
subpass: Some(subpass.into()),
|
||||
..GraphicsPipelineCreateInfo::layout(rcx.pipeline_layout.clone())
|
||||
},
|
||||
)
|
||||
.unwrap()
|
||||
};
|
||||
|
||||
let framebuffer = window_size_dependent_setup(rcx, &render_pass);
|
||||
|
||||
let vertices = [
|
||||
MyVertex {
|
||||
position: [-0.5, 0.5],
|
||||
},
|
||||
MyVertex {
|
||||
position: [0.5, 0.5],
|
||||
},
|
||||
MyVertex {
|
||||
position: [0.0, -0.5],
|
||||
},
|
||||
];
|
||||
let vertex_buffer_id = rcx
|
||||
.resources
|
||||
.create_buffer(
|
||||
BufferCreateInfo {
|
||||
usage: BufferUsage::VERTEX_BUFFER,
|
||||
..Default::default()
|
||||
},
|
||||
AllocationCreateInfo {
|
||||
memory_type_filter: MemoryTypeFilter::PREFER_DEVICE
|
||||
| MemoryTypeFilter::HOST_SEQUENTIAL_WRITE,
|
||||
..Default::default()
|
||||
},
|
||||
DeviceLayout::from_layout(Layout::for_value(&vertices)).unwrap(),
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
unsafe {
|
||||
vulkano_taskgraph::execute(
|
||||
&rcx.queue,
|
||||
&rcx.resources,
|
||||
rcx.flight_id,
|
||||
|_cbf, tcx| {
|
||||
tcx.write_buffer::<[MyVertex]>(vertex_buffer_id, ..)?
|
||||
.copy_from_slice(&vertices);
|
||||
|
||||
Ok(())
|
||||
},
|
||||
[(vertex_buffer_id, HostAccessType::Write)],
|
||||
[],
|
||||
[],
|
||||
)
|
||||
}
|
||||
.unwrap();
|
||||
|
||||
SceneTask {
|
||||
render_pass,
|
||||
pipeline,
|
||||
framebuffer,
|
||||
vertex_buffer_id,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn handle_resize(&mut self, rcx: &RenderContext) {
|
||||
let framebuffer = window_size_dependent_setup(rcx, &self.render_pass);
|
||||
|
||||
let flight = rcx.resources.flight(rcx.flight_id).unwrap();
|
||||
flight.destroy_object(mem::replace(&mut self.framebuffer, framebuffer));
|
||||
}
|
||||
|
||||
pub fn cleanup(&mut self, rcx: &RenderContext) {
|
||||
let flight = rcx.resources.flight(rcx.flight_id).unwrap();
|
||||
flight.destroy_object(self.framebuffer.clone());
|
||||
}
|
||||
}
|
||||
|
||||
impl Task for SceneTask {
|
||||
type World = RenderContext;
|
||||
|
||||
unsafe fn execute(
|
||||
&self,
|
||||
cbf: &mut RecordingCommandBuffer<'_>,
|
||||
_tcx: &mut TaskContext<'_>,
|
||||
rcx: &Self::World,
|
||||
) -> TaskResult {
|
||||
cbf.as_raw().begin_render_pass(
|
||||
&RenderPassBeginInfo {
|
||||
clear_values: vec![Some([0u32; 4].into())],
|
||||
..RenderPassBeginInfo::framebuffer(self.framebuffer.clone())
|
||||
},
|
||||
&Default::default(),
|
||||
)?;
|
||||
cbf.set_viewport(0, slice::from_ref(&rcx.viewport))?;
|
||||
cbf.bind_pipeline_graphics(&self.pipeline)?;
|
||||
cbf.bind_vertex_buffers(0, &[self.vertex_buffer_id], &[0], &[], &[])?;
|
||||
|
||||
unsafe { cbf.draw(3, 1, 0, 0) }?;
|
||||
|
||||
cbf.as_raw().end_render_pass(&Default::default())?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, BufferContents, Vertex)]
|
||||
#[repr(C)]
|
||||
struct MyVertex {
|
||||
#[format(R32G32_SFLOAT)]
|
||||
position: [f32; 2],
|
||||
}
|
||||
|
||||
mod vs {
|
||||
vulkano_shaders::shader! {
|
||||
ty: "vertex",
|
||||
src: r"
|
||||
#version 450
|
||||
|
||||
layout(location = 0) in vec2 position;
|
||||
|
||||
void main() {
|
||||
gl_Position = vec4(position, 0.0, 1.0);
|
||||
}
|
||||
",
|
||||
}
|
||||
}
|
||||
|
||||
mod fs {
|
||||
vulkano_shaders::shader! {
|
||||
ty: "fragment",
|
||||
src: r"
|
||||
#version 450
|
||||
#include <shared_exponent.glsl>
|
||||
|
||||
layout(location = 0) out uint f_color;
|
||||
|
||||
void main() {
|
||||
f_color = convertToSharedExponent(vec3(2.0, 0.0, 0.0));
|
||||
}
|
||||
",
|
||||
include: ["."],
|
||||
}
|
||||
}
|
||||
|
||||
fn window_size_dependent_setup(
|
||||
rcx: &RenderContext,
|
||||
render_pass: &Arc<RenderPass>,
|
||||
) -> Arc<Framebuffer> {
|
||||
let image_state = rcx.resources.image(rcx.bloom_image_id).unwrap();
|
||||
let image = image_state.image();
|
||||
let view = ImageView::new(
|
||||
image.clone(),
|
||||
ImageViewCreateInfo {
|
||||
format: Format::R32_UINT,
|
||||
subresource_range: ImageSubresourceRange {
|
||||
aspects: ImageAspects::COLOR,
|
||||
mip_levels: 0..1,
|
||||
array_layers: 0..1,
|
||||
},
|
||||
usage: ImageUsage::COLOR_ATTACHMENT,
|
||||
..Default::default()
|
||||
},
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
Framebuffer::new(
|
||||
render_pass.clone(),
|
||||
FramebufferCreateInfo {
|
||||
attachments: vec![view],
|
||||
..Default::default()
|
||||
},
|
||||
)
|
||||
.unwrap()
|
||||
}
|
26
examples/bloom/shared_exponent.glsl
Normal file
26
examples/bloom/shared_exponent.glsl
Normal file
@ -0,0 +1,26 @@
|
||||
// https://registry.khronos.org/OpenGL/extensions/EXT/EXT_texture_shared_exponent.txt
|
||||
uint convertToSharedExponent(vec3 color) {
|
||||
const int MAX = 65408;
|
||||
const int BIAS = 15;
|
||||
const int MANTISSA_BITS = 9;
|
||||
const int MANTISSA_VALUES = 512;
|
||||
|
||||
vec3 clamped_color = clamp(color, vec3(0.0), vec3(MAX));
|
||||
float max_clamped_component = max(clamped_color.r, max(clamped_color.g, clamped_color.b));
|
||||
int max_clamped_exponent = int((floatBitsToUint(max_clamped_component) >> 23) & 0xFF) - 127;
|
||||
int shared_exponent = max(-BIAS - 1, max_clamped_exponent) + 1 + BIAS;
|
||||
float divisor = exp2(float(shared_exponent - BIAS - MANTISSA_BITS));
|
||||
int max_shared_component = int(floor(max_clamped_component / divisor + 0.5));
|
||||
|
||||
if (max_shared_component == MANTISSA_VALUES) {
|
||||
shared_exponent += 1;
|
||||
divisor *= 2;
|
||||
}
|
||||
|
||||
vec3 shared_color = floor(clamped_color / divisor + 0.5);
|
||||
|
||||
return (uint(shared_exponent) << 27)
|
||||
| (uint(shared_color.b) << 18)
|
||||
| (uint(shared_color.g) << 9)
|
||||
| (uint(shared_color.r) << 0);
|
||||
}
|
40
examples/bloom/tonemap.glsl
Normal file
40
examples/bloom/tonemap.glsl
Normal file
@ -0,0 +1,40 @@
|
||||
#version 450
|
||||
|
||||
layout(location = 0) in vec2 v_tex_coords;
|
||||
|
||||
layout(location = 0) out vec4 f_color;
|
||||
|
||||
layout(set = 0, binding = 0) uniform sampler bloom_sampler;
|
||||
layout(set = 0, binding = 1) uniform texture2D bloom_texture;
|
||||
|
||||
layout(push_constant) uniform PushConstants {
|
||||
float exposure;
|
||||
};
|
||||
|
||||
const mat3 ACES_INPUT_MATRIX = {
|
||||
{ 0.59719, 0.07600, 0.02840 },
|
||||
{ 0.35458, 0.90834, 0.13383 },
|
||||
{ 0.04823, 0.01566, 0.83777 },
|
||||
};
|
||||
|
||||
const mat3 ACES_OUTPUT_MATRIX = {
|
||||
{ 1.60475, -0.10208, -0.00327 },
|
||||
{ -0.53108, 1.10813, -0.07276 },
|
||||
{ -0.07367, -0.00605, 1.07602 },
|
||||
};
|
||||
|
||||
vec3 rrtAndOdtFit(vec3 v) {
|
||||
vec3 a = v * (v + 0.0245786) - 0.000090537;
|
||||
vec3 b = v * (0.983729 * v + 0.4329510) + 0.238081;
|
||||
return a / b;
|
||||
}
|
||||
|
||||
void main() {
|
||||
vec4 hdr_color = exposure * texture(sampler2D(bloom_texture, bloom_sampler), v_tex_coords);
|
||||
|
||||
vec3 color = ACES_INPUT_MATRIX * hdr_color.rgb;
|
||||
color = rrtAndOdtFit(color);
|
||||
color = ACES_OUTPUT_MATRIX * color;
|
||||
|
||||
f_color = vec4(color, 1.0);
|
||||
}
|
212
examples/bloom/tonemap.rs
Normal file
212
examples/bloom/tonemap.rs
Normal file
@ -0,0 +1,212 @@
|
||||
use crate::RenderContext;
|
||||
use std::{mem, slice, sync::Arc};
|
||||
use vulkano::{
|
||||
command_buffer::RenderPassBeginInfo,
|
||||
image::view::ImageView,
|
||||
pipeline::{
|
||||
graphics::{
|
||||
color_blend::{ColorBlendAttachmentState, ColorBlendState},
|
||||
input_assembly::InputAssemblyState,
|
||||
multisample::MultisampleState,
|
||||
rasterization::RasterizationState,
|
||||
vertex_input::VertexInputState,
|
||||
viewport::ViewportState,
|
||||
GraphicsPipelineCreateInfo,
|
||||
},
|
||||
DynamicState, GraphicsPipeline, PipelineBindPoint, PipelineShaderStageCreateInfo,
|
||||
},
|
||||
render_pass::{Framebuffer, FramebufferCreateInfo, RenderPass, Subpass},
|
||||
swapchain::Swapchain,
|
||||
};
|
||||
use vulkano_taskgraph::{
|
||||
command_buffer::RecordingCommandBuffer, Id, Task, TaskContext, TaskResult,
|
||||
};
|
||||
|
||||
const EXPOSURE: f32 = 1.0;
|
||||
|
||||
pub struct TonemapTask {
|
||||
render_pass: Arc<RenderPass>,
|
||||
pipeline: Arc<GraphicsPipeline>,
|
||||
framebuffers: Vec<Arc<Framebuffer>>,
|
||||
swapchain_id: Id<Swapchain>,
|
||||
}
|
||||
|
||||
impl TonemapTask {
|
||||
pub fn new(rcx: &RenderContext, swapchain_id: Id<Swapchain>) -> Self {
|
||||
let render_pass = vulkano::single_pass_renderpass!(
|
||||
rcx.device.clone(),
|
||||
attachments: {
|
||||
color: {
|
||||
format: rcx.swapchain_format,
|
||||
samples: 1,
|
||||
load_op: DontCare,
|
||||
store_op: Store,
|
||||
},
|
||||
},
|
||||
pass: {
|
||||
color: [color],
|
||||
depth_stencil: {},
|
||||
},
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
let pipeline = {
|
||||
let vs = vs::load(rcx.device.clone())
|
||||
.unwrap()
|
||||
.entry_point("main")
|
||||
.unwrap();
|
||||
let fs = fs::load(rcx.device.clone())
|
||||
.unwrap()
|
||||
.entry_point("main")
|
||||
.unwrap();
|
||||
let stages = [
|
||||
PipelineShaderStageCreateInfo::new(vs),
|
||||
PipelineShaderStageCreateInfo::new(fs),
|
||||
];
|
||||
let subpass = Subpass::from(render_pass.clone(), 0).unwrap();
|
||||
|
||||
GraphicsPipeline::new(
|
||||
rcx.device.clone(),
|
||||
None,
|
||||
GraphicsPipelineCreateInfo {
|
||||
stages: stages.into_iter().collect(),
|
||||
vertex_input_state: Some(VertexInputState::default()),
|
||||
input_assembly_state: Some(InputAssemblyState::default()),
|
||||
viewport_state: Some(ViewportState::default()),
|
||||
rasterization_state: Some(RasterizationState::default()),
|
||||
multisample_state: Some(MultisampleState::default()),
|
||||
color_blend_state: Some(ColorBlendState::with_attachment_states(
|
||||
subpass.num_color_attachments(),
|
||||
ColorBlendAttachmentState::default(),
|
||||
)),
|
||||
dynamic_state: [DynamicState::Viewport].into_iter().collect(),
|
||||
subpass: Some(subpass.into()),
|
||||
..GraphicsPipelineCreateInfo::layout(rcx.pipeline_layout.clone())
|
||||
},
|
||||
)
|
||||
.unwrap()
|
||||
};
|
||||
|
||||
let framebuffers = window_size_dependent_setup(rcx, &render_pass);
|
||||
|
||||
TonemapTask {
|
||||
render_pass,
|
||||
pipeline,
|
||||
framebuffers,
|
||||
swapchain_id,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn handle_resize(&mut self, rcx: &RenderContext) {
|
||||
let framebuffers = window_size_dependent_setup(rcx, &self.render_pass);
|
||||
|
||||
let flight = rcx.resources.flight(rcx.flight_id).unwrap();
|
||||
flight.destroy_objects(mem::replace(&mut self.framebuffers, framebuffers));
|
||||
}
|
||||
|
||||
pub fn cleanup(&mut self, rcx: &RenderContext) {
|
||||
let flight = rcx.resources.flight(rcx.flight_id).unwrap();
|
||||
flight.destroy_objects(self.framebuffers.drain(..));
|
||||
}
|
||||
}
|
||||
|
||||
impl Task for TonemapTask {
|
||||
type World = RenderContext;
|
||||
|
||||
unsafe fn execute(
|
||||
&self,
|
||||
cbf: &mut RecordingCommandBuffer<'_>,
|
||||
tcx: &mut TaskContext<'_>,
|
||||
rcx: &Self::World,
|
||||
) -> TaskResult {
|
||||
cbf.as_raw().bind_descriptor_sets(
|
||||
PipelineBindPoint::Graphics,
|
||||
&rcx.pipeline_layout,
|
||||
0,
|
||||
slice::from_ref(&rcx.descriptor_set),
|
||||
)?;
|
||||
|
||||
let swapchain_state = tcx.swapchain(self.swapchain_id)?;
|
||||
let image_index = swapchain_state.current_image_index().unwrap();
|
||||
|
||||
cbf.as_raw().begin_render_pass(
|
||||
&RenderPassBeginInfo {
|
||||
clear_values: vec![None],
|
||||
..RenderPassBeginInfo::framebuffer(self.framebuffers[image_index as usize].clone())
|
||||
},
|
||||
&Default::default(),
|
||||
)?;
|
||||
cbf.set_viewport(0, slice::from_ref(&rcx.viewport))?;
|
||||
cbf.bind_pipeline_graphics(&self.pipeline)?;
|
||||
cbf.push_constants(
|
||||
&rcx.pipeline_layout,
|
||||
0,
|
||||
&fs::PushConstants { exposure: EXPOSURE },
|
||||
)?;
|
||||
|
||||
unsafe { cbf.draw(3, 1, 0, 0) }?;
|
||||
|
||||
cbf.as_raw().end_render_pass(&Default::default())?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
mod vs {
|
||||
vulkano_shaders::shader! {
|
||||
ty: "vertex",
|
||||
src: r"
|
||||
#version 450
|
||||
|
||||
const vec2[3] POSITIONS = {
|
||||
vec2(-1.0, -1.0),
|
||||
vec2(-1.0, 3.0),
|
||||
vec2( 3.0, -1.0),
|
||||
};
|
||||
|
||||
const vec2[3] TEX_COORDS = {
|
||||
vec2(0.0, 0.0),
|
||||
vec2(0.0, 2.0),
|
||||
vec2(2.0, 0.0),
|
||||
};
|
||||
|
||||
layout(location = 0) out vec2 v_tex_coords;
|
||||
|
||||
void main() {
|
||||
gl_Position = vec4(POSITIONS[gl_VertexIndex], 0.0, 1.0);
|
||||
v_tex_coords = TEX_COORDS[gl_VertexIndex];
|
||||
}
|
||||
",
|
||||
}
|
||||
}
|
||||
|
||||
mod fs {
|
||||
vulkano_shaders::shader! {
|
||||
ty: "fragment",
|
||||
path: "tonemap.glsl",
|
||||
include: ["."],
|
||||
}
|
||||
}
|
||||
|
||||
fn window_size_dependent_setup(
|
||||
rcx: &RenderContext,
|
||||
render_pass: &Arc<RenderPass>,
|
||||
) -> Vec<Arc<Framebuffer>> {
|
||||
let swapchain_state = rcx.resources.swapchain(rcx.swapchain_id).unwrap();
|
||||
let images = swapchain_state.images();
|
||||
|
||||
images
|
||||
.iter()
|
||||
.map(|image| {
|
||||
let view = ImageView::new_default(image.clone()).unwrap();
|
||||
Framebuffer::new(
|
||||
render_pass.clone(),
|
||||
FramebufferCreateInfo {
|
||||
attachments: vec![view],
|
||||
..Default::default()
|
||||
},
|
||||
)
|
||||
.unwrap()
|
||||
})
|
||||
.collect::<Vec<_>>()
|
||||
}
|
61
examples/bloom/upsample.glsl
Normal file
61
examples/bloom/upsample.glsl
Normal file
@ -0,0 +1,61 @@
|
||||
#version 450
|
||||
#include <shared_exponent.glsl>
|
||||
|
||||
const uint MAX_BLOOM_MIP_LEVELS = 6;
|
||||
|
||||
layout(local_size_x = 8, local_size_y = 8, local_size_z = 1) in;
|
||||
|
||||
layout(set = 0, binding = 0) uniform sampler bloom_sampler;
|
||||
layout(set = 0, binding = 1) uniform texture2D bloom_texture;
|
||||
layout(set = 0, binding = 2, r32ui) uniform uimage2D bloom_mip_chain[MAX_BLOOM_MIP_LEVELS];
|
||||
|
||||
layout(push_constant) uniform PushConstants {
|
||||
uint dst_mip_level;
|
||||
float intensity;
|
||||
};
|
||||
|
||||
uint src_mip_level = dst_mip_level + 1;
|
||||
|
||||
vec3 sample1(vec2 uv) {
|
||||
return textureLod(sampler2D(bloom_texture, bloom_sampler), uv, src_mip_level).rgb;
|
||||
}
|
||||
|
||||
// 9-tap tent filter.
|
||||
vec3 upsampleTent9(vec2 uv, vec2 src_texel_size) {
|
||||
vec3 color;
|
||||
color = sample1(uv + vec2(-1.0, -1.0) * src_texel_size) * 1.0;
|
||||
color += sample1(uv + vec2( 0.0, -1.0) * src_texel_size) * 2.0;
|
||||
color += sample1(uv + vec2( 1.0, -1.0) * src_texel_size) * 1.0;
|
||||
color += sample1(uv + vec2(-1.0, 0.0) * src_texel_size) * 2.0;
|
||||
color += sample1(uv + vec2( 0.0, 0.0) * src_texel_size) * 4.0;
|
||||
color += sample1(uv + vec2( 1.0, 0.0) * src_texel_size) * 2.0;
|
||||
color += sample1(uv + vec2(-1.0, 1.0) * src_texel_size) * 1.0;
|
||||
color += sample1(uv + vec2( 0.0, 1.0) * src_texel_size) * 2.0;
|
||||
color += sample1(uv + vec2( 1.0, 1.0) * src_texel_size) * 1.0;
|
||||
|
||||
return color * (1.0 / 16.0);
|
||||
}
|
||||
|
||||
void blend(vec2 uv, ivec2 dst_coord, vec3 color) {
|
||||
color += textureLod(sampler2D(bloom_texture, bloom_sampler), uv, dst_mip_level).rgb;
|
||||
uint packed = convertToSharedExponent(color);
|
||||
imageStore(bloom_mip_chain[dst_mip_level], dst_coord, uvec4(packed, 0, 0, 0));
|
||||
}
|
||||
|
||||
void main() {
|
||||
ivec2 dst_coord = ivec2(gl_GlobalInvocationID.xy);
|
||||
ivec2 dst_size = imageSize(bloom_mip_chain[dst_mip_level]);
|
||||
|
||||
if (dst_coord.x > dst_size.x || dst_coord.y > dst_size.y) {
|
||||
return;
|
||||
}
|
||||
|
||||
ivec2 src_size = textureSize(sampler2D(bloom_texture, bloom_sampler), int(src_mip_level));
|
||||
vec2 src_texel_size = 1.0 / vec2(src_size);
|
||||
vec2 uv = (vec2(dst_coord) + 0.5) / vec2(dst_size);
|
||||
vec3 color = upsampleTent9(uv, src_texel_size);
|
||||
|
||||
color *= intensity;
|
||||
|
||||
blend(uv, dst_coord, color);
|
||||
}
|
278
vulkano-taskgraph/src/command_buffer/commands/bind_push.rs
Normal file
278
vulkano-taskgraph/src/command_buffer/commands/bind_push.rs
Normal file
@ -0,0 +1,278 @@
|
||||
use crate::{
|
||||
command_buffer::{RecordingCommandBuffer, Result},
|
||||
Id,
|
||||
};
|
||||
use ash::vk;
|
||||
use smallvec::SmallVec;
|
||||
use std::{ffi::c_void, mem, ptr, sync::Arc};
|
||||
use vulkano::{
|
||||
self,
|
||||
buffer::{Buffer, BufferContents, IndexType},
|
||||
device::DeviceOwned,
|
||||
pipeline::{ComputePipeline, GraphicsPipeline, PipelineLayout},
|
||||
DeviceSize, Version, VulkanObject,
|
||||
};
|
||||
|
||||
/// # Commands to bind or push state for pipeline execution commands
|
||||
///
|
||||
/// These commands require a queue with a pipeline type that uses the given state.
|
||||
impl RecordingCommandBuffer<'_> {
|
||||
/// Binds an index buffer for future indexed draw calls.
|
||||
pub unsafe fn bind_index_buffer(
|
||||
&mut self,
|
||||
buffer: Id<Buffer>,
|
||||
offset: DeviceSize,
|
||||
size: DeviceSize,
|
||||
index_type: IndexType,
|
||||
) -> Result<&mut Self> {
|
||||
Ok(unsafe { self.bind_index_buffer_unchecked(buffer, offset, size, index_type) })
|
||||
}
|
||||
|
||||
pub unsafe fn bind_index_buffer_unchecked(
|
||||
&mut self,
|
||||
buffer: Id<Buffer>,
|
||||
offset: DeviceSize,
|
||||
size: DeviceSize,
|
||||
index_type: IndexType,
|
||||
) -> &mut Self {
|
||||
let buffer = unsafe { self.accesses.buffer_unchecked(buffer) };
|
||||
|
||||
let fns = self.device().fns();
|
||||
|
||||
if self.device().enabled_extensions().khr_maintenance5 {
|
||||
unsafe {
|
||||
(fns.khr_maintenance5.cmd_bind_index_buffer2_khr)(
|
||||
self.handle(),
|
||||
buffer.handle(),
|
||||
offset,
|
||||
size,
|
||||
index_type.into(),
|
||||
)
|
||||
};
|
||||
} else {
|
||||
unsafe {
|
||||
(fns.v1_0.cmd_bind_index_buffer)(
|
||||
self.handle(),
|
||||
buffer.handle(),
|
||||
offset,
|
||||
index_type.into(),
|
||||
)
|
||||
};
|
||||
}
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
/// Binds a compute pipeline for future dispatch calls.
|
||||
pub unsafe fn bind_pipeline_compute(
|
||||
&mut self,
|
||||
pipeline: &Arc<ComputePipeline>,
|
||||
) -> Result<&mut Self> {
|
||||
Ok(unsafe { self.bind_pipeline_compute_unchecked(pipeline) })
|
||||
}
|
||||
|
||||
pub unsafe fn bind_pipeline_compute_unchecked(
|
||||
&mut self,
|
||||
pipeline: &Arc<ComputePipeline>,
|
||||
) -> &mut Self {
|
||||
let fns = self.device().fns();
|
||||
unsafe {
|
||||
(fns.v1_0.cmd_bind_pipeline)(
|
||||
self.handle(),
|
||||
vk::PipelineBindPoint::COMPUTE,
|
||||
pipeline.handle(),
|
||||
)
|
||||
};
|
||||
|
||||
self.death_row.push(pipeline.clone());
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
/// Binds a graphics pipeline for future draw calls.
|
||||
pub unsafe fn bind_pipeline_graphics(
|
||||
&mut self,
|
||||
pipeline: &Arc<GraphicsPipeline>,
|
||||
) -> Result<&mut Self> {
|
||||
Ok(unsafe { self.bind_pipeline_graphics_unchecked(pipeline) })
|
||||
}
|
||||
|
||||
pub unsafe fn bind_pipeline_graphics_unchecked(
|
||||
&mut self,
|
||||
pipeline: &Arc<GraphicsPipeline>,
|
||||
) -> &mut Self {
|
||||
let fns = self.device().fns();
|
||||
unsafe {
|
||||
(fns.v1_0.cmd_bind_pipeline)(
|
||||
self.handle(),
|
||||
vk::PipelineBindPoint::GRAPHICS,
|
||||
pipeline.handle(),
|
||||
)
|
||||
};
|
||||
|
||||
self.death_row.push(pipeline.clone());
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
/// Binds vertex buffers for future draw calls.
|
||||
pub unsafe fn bind_vertex_buffers(
|
||||
&mut self,
|
||||
first_binding: u32,
|
||||
buffers: &[Id<Buffer>],
|
||||
offsets: &[DeviceSize],
|
||||
sizes: &[DeviceSize],
|
||||
strides: &[DeviceSize],
|
||||
) -> Result<&mut Self> {
|
||||
Ok(unsafe {
|
||||
self.bind_vertex_buffers_unchecked(first_binding, buffers, offsets, sizes, strides)
|
||||
})
|
||||
}
|
||||
|
||||
pub unsafe fn bind_vertex_buffers_unchecked(
|
||||
&mut self,
|
||||
first_binding: u32,
|
||||
buffers: &[Id<Buffer>],
|
||||
offsets: &[DeviceSize],
|
||||
sizes: &[DeviceSize],
|
||||
strides: &[DeviceSize],
|
||||
) -> &mut Self {
|
||||
if buffers.is_empty() {
|
||||
return self;
|
||||
}
|
||||
|
||||
let buffers_vk = buffers
|
||||
.iter()
|
||||
.map(|&buffer| unsafe { self.accesses.buffer_unchecked(buffer) }.handle())
|
||||
.collect::<SmallVec<[_; 2]>>();
|
||||
|
||||
let device = self.device();
|
||||
let fns = self.device().fns();
|
||||
|
||||
if device.api_version() >= Version::V1_3
|
||||
|| device.enabled_extensions().ext_extended_dynamic_state
|
||||
|| device.enabled_extensions().ext_shader_object
|
||||
{
|
||||
let cmd_bind_vertex_buffers2 = if device.api_version() >= Version::V1_3 {
|
||||
fns.v1_3.cmd_bind_vertex_buffers2
|
||||
} else if device.enabled_extensions().ext_extended_dynamic_state {
|
||||
fns.ext_extended_dynamic_state.cmd_bind_vertex_buffers2_ext
|
||||
} else {
|
||||
fns.ext_shader_object.cmd_bind_vertex_buffers2_ext
|
||||
};
|
||||
|
||||
unsafe {
|
||||
cmd_bind_vertex_buffers2(
|
||||
self.handle(),
|
||||
first_binding,
|
||||
buffers_vk.len() as u32,
|
||||
buffers_vk.as_ptr(),
|
||||
offsets.as_ptr(),
|
||||
if sizes.is_empty() {
|
||||
ptr::null()
|
||||
} else {
|
||||
sizes.as_ptr()
|
||||
},
|
||||
if strides.is_empty() {
|
||||
ptr::null()
|
||||
} else {
|
||||
strides.as_ptr()
|
||||
},
|
||||
)
|
||||
};
|
||||
} else {
|
||||
unsafe {
|
||||
(fns.v1_0.cmd_bind_vertex_buffers)(
|
||||
self.handle(),
|
||||
first_binding,
|
||||
buffers_vk.len() as u32,
|
||||
buffers_vk.as_ptr(),
|
||||
offsets.as_ptr(),
|
||||
)
|
||||
};
|
||||
}
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets push constants for future dispatch or draw calls.
|
||||
pub unsafe fn push_constants(
|
||||
&mut self,
|
||||
layout: &Arc<PipelineLayout>,
|
||||
offset: u32,
|
||||
values: &(impl BufferContents + ?Sized),
|
||||
) -> Result<&mut Self> {
|
||||
Ok(unsafe { self.push_constants_unchecked(layout, offset, values) })
|
||||
}
|
||||
|
||||
pub unsafe fn push_constants_unchecked(
|
||||
&mut self,
|
||||
layout: &Arc<PipelineLayout>,
|
||||
offset: u32,
|
||||
values: &(impl BufferContents + ?Sized),
|
||||
) -> &mut Self {
|
||||
unsafe {
|
||||
self.push_constants_unchecked_inner(
|
||||
layout,
|
||||
offset,
|
||||
<*const _>::cast(values),
|
||||
mem::size_of_val(values) as u32,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn push_constants_unchecked_inner(
|
||||
&mut self,
|
||||
layout: &Arc<PipelineLayout>,
|
||||
offset: u32,
|
||||
values: *const c_void,
|
||||
size: u32,
|
||||
) -> &mut Self {
|
||||
if size == 0 {
|
||||
return self;
|
||||
}
|
||||
|
||||
let fns = self.device().fns();
|
||||
let mut current_offset = offset;
|
||||
let mut remaining_size = size;
|
||||
|
||||
for range in layout
|
||||
.push_constant_ranges_disjoint()
|
||||
.iter()
|
||||
.skip_while(|range| range.offset + range.size <= offset)
|
||||
{
|
||||
// There is a gap between ranges, but the passed `values` contain some bytes in this
|
||||
// gap.
|
||||
if range.offset > current_offset {
|
||||
std::process::abort();
|
||||
}
|
||||
|
||||
// Push the minimum of the whole remaining data and the part until the end of this
|
||||
// range.
|
||||
let push_size = remaining_size.min(range.offset + range.size - current_offset);
|
||||
let push_offset = (current_offset - offset) as usize;
|
||||
debug_assert!(push_offset < size as usize);
|
||||
let push_values = unsafe { values.add(push_offset) };
|
||||
|
||||
unsafe {
|
||||
(fns.v1_0.cmd_push_constants)(
|
||||
self.handle(),
|
||||
layout.handle(),
|
||||
range.stages.into(),
|
||||
current_offset,
|
||||
push_size,
|
||||
push_values,
|
||||
)
|
||||
};
|
||||
|
||||
current_offset += push_size;
|
||||
remaining_size -= push_size;
|
||||
|
||||
if remaining_size == 0 {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
self
|
||||
}
|
||||
}
|
345
vulkano-taskgraph/src/command_buffer/commands/clear.rs
Normal file
345
vulkano-taskgraph/src/command_buffer/commands/clear.rs
Normal file
@ -0,0 +1,345 @@
|
||||
use crate::{
|
||||
command_buffer::{RecordingCommandBuffer, Result},
|
||||
resource::{AccessType, ImageLayoutType},
|
||||
Id,
|
||||
};
|
||||
use ash::vk;
|
||||
use smallvec::SmallVec;
|
||||
use std::{ffi::c_void, mem};
|
||||
use vulkano::{
|
||||
buffer::{Buffer, BufferContents},
|
||||
device::DeviceOwned,
|
||||
format::{ClearColorValue, ClearDepthStencilValue},
|
||||
image::{Image, ImageSubresourceRange},
|
||||
DeviceSize, VulkanObject,
|
||||
};
|
||||
|
||||
/// # Commands to fill resources with new data
|
||||
impl RecordingCommandBuffer<'_> {
|
||||
/// Clears a color image with a specific value.
|
||||
pub unsafe fn clear_color_image(
|
||||
&mut self,
|
||||
clear_info: &ClearColorImageInfo<'_>,
|
||||
) -> Result<&mut Self> {
|
||||
Ok(unsafe { self.clear_color_image_unchecked(clear_info) })
|
||||
}
|
||||
|
||||
pub unsafe fn clear_color_image_unchecked(
|
||||
&mut self,
|
||||
clear_info: &ClearColorImageInfo<'_>,
|
||||
) -> &mut Self {
|
||||
let &ClearColorImageInfo {
|
||||
image,
|
||||
image_layout,
|
||||
clear_value,
|
||||
regions,
|
||||
_ne: _,
|
||||
} = clear_info;
|
||||
|
||||
let image = unsafe { self.accesses.image_unchecked(image) };
|
||||
let image_layout = AccessType::ClearTransferWrite.image_layout(image_layout);
|
||||
|
||||
let fns = self.device().fns();
|
||||
let cmd_clear_color_image = fns.v1_0.cmd_clear_color_image;
|
||||
|
||||
if regions.is_empty() {
|
||||
let region_vk = image.subresource_range().into();
|
||||
|
||||
unsafe {
|
||||
cmd_clear_color_image(
|
||||
self.handle(),
|
||||
image.handle(),
|
||||
image_layout.into(),
|
||||
&clear_value.into(),
|
||||
1,
|
||||
®ion_vk,
|
||||
)
|
||||
};
|
||||
} else {
|
||||
let regions_vk = regions
|
||||
.iter()
|
||||
.cloned()
|
||||
.map(vk::ImageSubresourceRange::from)
|
||||
.collect::<SmallVec<[_; 8]>>();
|
||||
|
||||
unsafe {
|
||||
cmd_clear_color_image(
|
||||
self.handle(),
|
||||
image.handle(),
|
||||
image_layout.into(),
|
||||
&clear_value.into(),
|
||||
regions_vk.len() as u32,
|
||||
regions_vk.as_ptr(),
|
||||
)
|
||||
};
|
||||
}
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
/// Clears a depth/stencil image with a specific value.
|
||||
pub unsafe fn clear_depth_stencil_image(
|
||||
&mut self,
|
||||
clear_info: &ClearDepthStencilImageInfo<'_>,
|
||||
) -> Result<&mut Self> {
|
||||
Ok(unsafe { self.clear_depth_stencil_image_unchecked(clear_info) })
|
||||
}
|
||||
|
||||
pub unsafe fn clear_depth_stencil_image_unchecked(
|
||||
&mut self,
|
||||
clear_info: &ClearDepthStencilImageInfo<'_>,
|
||||
) -> &mut Self {
|
||||
let &ClearDepthStencilImageInfo {
|
||||
image,
|
||||
image_layout,
|
||||
clear_value,
|
||||
regions,
|
||||
_ne: _,
|
||||
} = clear_info;
|
||||
|
||||
let image = unsafe { self.accesses.image_unchecked(image) };
|
||||
let image_layout = AccessType::ClearTransferWrite.image_layout(image_layout);
|
||||
|
||||
let fns = self.device().fns();
|
||||
let cmd_clear_depth_stencil_image = fns.v1_0.cmd_clear_depth_stencil_image;
|
||||
|
||||
if regions.is_empty() {
|
||||
let region_vk = image.subresource_range().into();
|
||||
|
||||
unsafe {
|
||||
cmd_clear_depth_stencil_image(
|
||||
self.handle(),
|
||||
image.handle(),
|
||||
image_layout.into(),
|
||||
&clear_value.into(),
|
||||
1,
|
||||
®ion_vk,
|
||||
)
|
||||
};
|
||||
} else {
|
||||
let regions_vk = regions
|
||||
.iter()
|
||||
.cloned()
|
||||
.map(vk::ImageSubresourceRange::from)
|
||||
.collect::<SmallVec<[_; 8]>>();
|
||||
|
||||
unsafe {
|
||||
cmd_clear_depth_stencil_image(
|
||||
self.handle(),
|
||||
image.handle(),
|
||||
image_layout.into(),
|
||||
&clear_value.into(),
|
||||
regions_vk.len() as u32,
|
||||
regions_vk.as_ptr(),
|
||||
)
|
||||
};
|
||||
}
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
/// Fills a region of a buffer with repeated copies of a value.
|
||||
///
|
||||
/// This function is similar to the `memset` function in C. The `data` parameter is a number
|
||||
/// that will be repeatedly written through the entire buffer.
|
||||
pub unsafe fn fill_buffer(&mut self, fill_info: &FillBufferInfo<'_>) -> Result<&mut Self> {
|
||||
Ok(unsafe { self.fill_buffer_unchecked(fill_info) })
|
||||
}
|
||||
|
||||
pub unsafe fn fill_buffer_unchecked(&mut self, fill_info: &FillBufferInfo<'_>) -> &mut Self {
|
||||
let &FillBufferInfo {
|
||||
dst_buffer,
|
||||
dst_offset,
|
||||
mut size,
|
||||
data,
|
||||
_ne: _,
|
||||
} = fill_info;
|
||||
|
||||
let dst_buffer = unsafe { self.accesses.buffer_unchecked(dst_buffer) };
|
||||
|
||||
if size == 0 {
|
||||
size = dst_buffer.size() & !3;
|
||||
}
|
||||
|
||||
let fns = self.device().fns();
|
||||
let cmd_fill_buffer = fns.v1_0.cmd_fill_buffer;
|
||||
unsafe { cmd_fill_buffer(self.handle(), dst_buffer.handle(), dst_offset, size, data) };
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
/// Writes data to a region of a buffer.
|
||||
pub unsafe fn update_buffer(
|
||||
&mut self,
|
||||
dst_buffer: Id<Buffer>,
|
||||
dst_offset: DeviceSize,
|
||||
data: &(impl BufferContents + ?Sized),
|
||||
) -> Result<&mut Self> {
|
||||
Ok(unsafe { self.update_buffer_unchecked(dst_buffer, dst_offset, data) })
|
||||
}
|
||||
|
||||
pub unsafe fn update_buffer_unchecked(
|
||||
&mut self,
|
||||
dst_buffer: Id<Buffer>,
|
||||
dst_offset: DeviceSize,
|
||||
data: &(impl BufferContents + ?Sized),
|
||||
) -> &mut Self {
|
||||
unsafe {
|
||||
self.update_buffer_inner(
|
||||
dst_buffer,
|
||||
dst_offset,
|
||||
<*const _>::cast(data),
|
||||
mem::size_of_val(data) as DeviceSize,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn update_buffer_inner(
|
||||
&mut self,
|
||||
dst_buffer: Id<Buffer>,
|
||||
dst_offset: DeviceSize,
|
||||
data: *const c_void,
|
||||
data_size: DeviceSize,
|
||||
) -> &mut Self {
|
||||
if data_size == 0 {
|
||||
return self;
|
||||
}
|
||||
|
||||
let dst_buffer = unsafe { self.accesses.buffer_unchecked(dst_buffer) };
|
||||
|
||||
let fns = self.device().fns();
|
||||
let cmd_update_buffer = fns.v1_0.cmd_update_buffer;
|
||||
unsafe {
|
||||
cmd_update_buffer(
|
||||
self.handle(),
|
||||
dst_buffer.handle(),
|
||||
dst_offset,
|
||||
data_size,
|
||||
data,
|
||||
)
|
||||
};
|
||||
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
/// Parameters to clear a color image.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct ClearColorImageInfo<'a> {
|
||||
/// The image to clear.
|
||||
///
|
||||
/// The default value is [`Id::INVALID`], which must be overridden.
|
||||
pub image: Id<Image>,
|
||||
|
||||
/// The layout used for `image` during the clear operation.
|
||||
///
|
||||
/// The default value is [`ImageLayoutType::Optimal`].
|
||||
pub image_layout: ImageLayoutType,
|
||||
|
||||
/// The color value to clear the image to.
|
||||
///
|
||||
/// The default value is `ClearColorValue::Float([0.0; 4])`.
|
||||
pub clear_value: ClearColorValue,
|
||||
|
||||
/// The subresource ranges of `image` to clear.
|
||||
///
|
||||
/// The default value is a single region, covering the whole image.
|
||||
pub regions: &'a [ImageSubresourceRange],
|
||||
|
||||
pub _ne: crate::NonExhaustive<'a>,
|
||||
}
|
||||
|
||||
impl Default for ClearColorImageInfo<'_> {
|
||||
#[inline]
|
||||
fn default() -> Self {
|
||||
ClearColorImageInfo {
|
||||
image: Id::INVALID,
|
||||
image_layout: ImageLayoutType::Optimal,
|
||||
clear_value: ClearColorValue::Float([0.0; 4]),
|
||||
regions: &[],
|
||||
_ne: crate::NE,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Parameters to clear a depth/stencil image.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct ClearDepthStencilImageInfo<'a> {
|
||||
/// The image to clear.
|
||||
///
|
||||
/// The default value is [`Id::INVALID`], which must be overridden.
|
||||
pub image: Id<Image>,
|
||||
|
||||
/// The layout used for `image` during the clear operation.
|
||||
///
|
||||
/// The default value is [`ImageLayoutType::Optimal`].
|
||||
pub image_layout: ImageLayoutType,
|
||||
|
||||
/// The depth/stencil values to clear the image to.
|
||||
///
|
||||
/// The default value is zero for both.
|
||||
pub clear_value: ClearDepthStencilValue,
|
||||
|
||||
/// The subresource ranges of `image` to clear.
|
||||
///
|
||||
/// The default value is a single region, covering the whole image.
|
||||
pub regions: &'a [ImageSubresourceRange],
|
||||
|
||||
pub _ne: crate::NonExhaustive<'a>,
|
||||
}
|
||||
|
||||
impl Default for ClearDepthStencilImageInfo<'_> {
|
||||
#[inline]
|
||||
fn default() -> Self {
|
||||
ClearDepthStencilImageInfo {
|
||||
image: Id::INVALID,
|
||||
image_layout: ImageLayoutType::Optimal,
|
||||
clear_value: ClearDepthStencilValue::default(),
|
||||
regions: &[],
|
||||
_ne: crate::NE,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Parameters to fill a region of a buffer with repeated copies of a value.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct FillBufferInfo<'a> {
|
||||
/// The buffer to fill.
|
||||
///
|
||||
/// The default value is [`Id::INVALID`], which must be overridden.
|
||||
pub dst_buffer: Id<Buffer>,
|
||||
|
||||
/// The offset in bytes from the start of `dst_buffer` that filling will start from.
|
||||
///
|
||||
/// This must be a multiple of 4.
|
||||
///
|
||||
/// The default value is `0`.
|
||||
pub dst_offset: DeviceSize,
|
||||
|
||||
/// The number of bytes to fill.
|
||||
///
|
||||
/// This must be a multiple of 4.
|
||||
///
|
||||
/// The default value is the size of `dst_buffer`, rounded down to the nearest multiple of 4.
|
||||
pub size: DeviceSize,
|
||||
|
||||
/// The data to fill with.
|
||||
///
|
||||
/// The default value is `0`.
|
||||
pub data: u32,
|
||||
|
||||
pub _ne: crate::NonExhaustive<'a>,
|
||||
}
|
||||
|
||||
impl Default for FillBufferInfo<'_> {
|
||||
#[inline]
|
||||
fn default() -> Self {
|
||||
FillBufferInfo {
|
||||
dst_buffer: Id::INVALID,
|
||||
dst_offset: 0,
|
||||
size: 0,
|
||||
data: 0,
|
||||
_ne: crate::NE,
|
||||
}
|
||||
}
|
||||
}
|
1531
vulkano-taskgraph/src/command_buffer/commands/copy.rs
Normal file
1531
vulkano-taskgraph/src/command_buffer/commands/copy.rs
Normal file
File diff suppressed because it is too large
Load Diff
781
vulkano-taskgraph/src/command_buffer/commands/dynamic_state.rs
Normal file
781
vulkano-taskgraph/src/command_buffer/commands/dynamic_state.rs
Normal file
@ -0,0 +1,781 @@
|
||||
use crate::command_buffer::{RecordingCommandBuffer, Result};
|
||||
use ash::vk;
|
||||
use smallvec::SmallVec;
|
||||
use std::ops::RangeInclusive;
|
||||
use vulkano::{
|
||||
device::DeviceOwned,
|
||||
pipeline::graphics::{
|
||||
color_blend::LogicOp,
|
||||
conservative_rasterization::ConservativeRasterizationMode,
|
||||
depth_stencil::{CompareOp, StencilFaces, StencilOp},
|
||||
input_assembly::PrimitiveTopology,
|
||||
rasterization::{CullMode, FrontFace},
|
||||
vertex_input::{
|
||||
VertexInputAttributeDescription, VertexInputBindingDescription, VertexInputRate,
|
||||
VertexInputState,
|
||||
},
|
||||
viewport::{Scissor, Viewport},
|
||||
},
|
||||
Version, VulkanObject,
|
||||
};
|
||||
|
||||
/// # Commands to set dynamic state for pipelines
|
||||
///
|
||||
/// These commands require a queue with a pipeline type that uses the given state.
|
||||
impl RecordingCommandBuffer<'_> {
|
||||
/// Sets the dynamic blend constants for future draw calls.
|
||||
pub unsafe fn set_blend_constants(&mut self, constants: &[f32; 4]) -> Result<&mut Self> {
|
||||
Ok(unsafe { self.set_blend_constants_unchecked(constants) })
|
||||
}
|
||||
|
||||
pub unsafe fn set_blend_constants_unchecked(&mut self, constants: &[f32; 4]) -> &mut Self {
|
||||
let fns = self.device().fns();
|
||||
unsafe { (fns.v1_0.cmd_set_blend_constants)(self.handle(), constants) };
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets whether dynamic color writes should be enabled for each attachment in the framebuffer.
|
||||
pub unsafe fn set_color_write_enable(&mut self, enables: &[bool]) -> Result<&mut Self> {
|
||||
Ok(unsafe { self.set_color_write_enable_unchecked(enables) })
|
||||
}
|
||||
|
||||
pub unsafe fn set_color_write_enable_unchecked(&mut self, enables: &[bool]) -> &mut Self {
|
||||
if enables.is_empty() {
|
||||
return self;
|
||||
}
|
||||
|
||||
let enables_vk = enables
|
||||
.iter()
|
||||
.copied()
|
||||
.map(|v| v as vk::Bool32)
|
||||
.collect::<SmallVec<[_; 4]>>();
|
||||
|
||||
let fns = self.device().fns();
|
||||
unsafe {
|
||||
(fns.ext_color_write_enable.cmd_set_color_write_enable_ext)(
|
||||
self.handle(),
|
||||
enables_vk.len() as u32,
|
||||
enables_vk.as_ptr(),
|
||||
)
|
||||
};
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the dynamic cull mode for future draw calls.
|
||||
pub unsafe fn set_cull_mode(&mut self, cull_mode: CullMode) -> Result<&mut Self> {
|
||||
Ok(unsafe { self.set_cull_mode_unchecked(cull_mode) })
|
||||
}
|
||||
|
||||
pub unsafe fn set_cull_mode_unchecked(&mut self, cull_mode: CullMode) -> &mut Self {
|
||||
let fns = self.device().fns();
|
||||
let cmd_set_cull_mode = if self.device().api_version() >= Version::V1_3 {
|
||||
fns.v1_3.cmd_set_cull_mode
|
||||
} else {
|
||||
fns.ext_extended_dynamic_state.cmd_set_cull_mode_ext
|
||||
};
|
||||
|
||||
unsafe { cmd_set_cull_mode(self.handle(), cull_mode.into()) };
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the dynamic depth bias values for future draw calls.
|
||||
pub unsafe fn set_depth_bias(
|
||||
&mut self,
|
||||
constant_factor: f32,
|
||||
clamp: f32,
|
||||
slope_factor: f32,
|
||||
) -> Result<&mut Self> {
|
||||
Ok(unsafe { self.set_depth_bias_unchecked(constant_factor, clamp, slope_factor) })
|
||||
}
|
||||
|
||||
pub unsafe fn set_depth_bias_unchecked(
|
||||
&mut self,
|
||||
constant_factor: f32,
|
||||
clamp: f32,
|
||||
slope_factor: f32,
|
||||
) -> &mut Self {
|
||||
let fns = self.device().fns();
|
||||
unsafe {
|
||||
(fns.v1_0.cmd_set_depth_bias)(self.handle(), constant_factor, clamp, slope_factor)
|
||||
};
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets whether dynamic depth bias is enabled for future draw calls.
|
||||
pub unsafe fn set_depth_bias_enable(&mut self, enable: bool) -> Result<&mut Self> {
|
||||
Ok(unsafe { self.set_depth_bias_enable_unchecked(enable) })
|
||||
}
|
||||
|
||||
pub unsafe fn set_depth_bias_enable_unchecked(&mut self, enable: bool) -> &mut Self {
|
||||
let fns = self.device().fns();
|
||||
let cmd_set_depth_bias_enable = if self.device().api_version() >= Version::V1_3 {
|
||||
fns.v1_3.cmd_set_depth_bias_enable
|
||||
} else {
|
||||
fns.ext_extended_dynamic_state2
|
||||
.cmd_set_depth_bias_enable_ext
|
||||
};
|
||||
|
||||
unsafe { cmd_set_depth_bias_enable(self.handle(), enable.into()) };
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the dynamic depth bounds for future draw calls.
|
||||
pub unsafe fn set_depth_bounds(&mut self, bounds: RangeInclusive<f32>) -> Result<&mut Self> {
|
||||
Ok(unsafe { self.set_depth_bounds_unchecked(bounds.clone()) })
|
||||
}
|
||||
|
||||
pub unsafe fn set_depth_bounds_unchecked(&mut self, bounds: RangeInclusive<f32>) -> &mut Self {
|
||||
let fns = self.device().fns();
|
||||
unsafe { (fns.v1_0.cmd_set_depth_bounds)(self.handle(), *bounds.start(), *bounds.end()) };
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets whether dynamic depth bounds testing is enabled for future draw calls.
|
||||
pub unsafe fn set_depth_bounds_test_enable(&mut self, enable: bool) -> Result<&mut Self> {
|
||||
Ok(unsafe { self.set_depth_bounds_test_enable_unchecked(enable) })
|
||||
}
|
||||
|
||||
pub unsafe fn set_depth_bounds_test_enable_unchecked(&mut self, enable: bool) -> &mut Self {
|
||||
let fns = self.device().fns();
|
||||
let cmd_set_depth_bounds_test_enable = if self.device().api_version() >= Version::V1_3 {
|
||||
fns.v1_3.cmd_set_depth_bounds_test_enable
|
||||
} else {
|
||||
fns.ext_extended_dynamic_state
|
||||
.cmd_set_depth_bounds_test_enable_ext
|
||||
};
|
||||
|
||||
unsafe { cmd_set_depth_bounds_test_enable(self.handle(), enable.into()) };
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the dynamic depth compare op for future draw calls.
|
||||
pub unsafe fn set_depth_compare_op(&mut self, compare_op: CompareOp) -> Result<&mut Self> {
|
||||
Ok(unsafe { self.set_depth_compare_op_unchecked(compare_op) })
|
||||
}
|
||||
|
||||
pub unsafe fn set_depth_compare_op_unchecked(&mut self, compare_op: CompareOp) -> &mut Self {
|
||||
let fns = self.device().fns();
|
||||
let cmd_set_depth_compare_op = if self.device().api_version() >= Version::V1_3 {
|
||||
fns.v1_3.cmd_set_depth_compare_op
|
||||
} else {
|
||||
fns.ext_extended_dynamic_state.cmd_set_depth_compare_op_ext
|
||||
};
|
||||
|
||||
unsafe { cmd_set_depth_compare_op(self.handle(), compare_op.into()) };
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets whether dynamic depth testing is enabled for future draw calls.
|
||||
pub unsafe fn set_depth_test_enable(&mut self, enable: bool) -> Result<&mut Self> {
|
||||
Ok(unsafe { self.set_depth_test_enable_unchecked(enable) })
|
||||
}
|
||||
|
||||
pub unsafe fn set_depth_test_enable_unchecked(&mut self, enable: bool) -> &mut Self {
|
||||
let fns = self.device().fns();
|
||||
let cmd_set_depth_test_enable = if self.device().api_version() >= Version::V1_3 {
|
||||
fns.v1_3.cmd_set_depth_test_enable
|
||||
} else {
|
||||
fns.ext_extended_dynamic_state.cmd_set_depth_test_enable_ext
|
||||
};
|
||||
|
||||
unsafe { cmd_set_depth_test_enable(self.handle(), enable.into()) };
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets whether dynamic depth write is enabled for future draw calls.
|
||||
pub unsafe fn set_depth_write_enable(&mut self, enable: bool) -> Result<&mut Self> {
|
||||
Ok(unsafe { self.set_depth_write_enable_unchecked(enable) })
|
||||
}
|
||||
|
||||
pub unsafe fn set_depth_write_enable_unchecked(&mut self, enable: bool) -> &mut Self {
|
||||
let fns = self.device().fns();
|
||||
let cmd_set_depth_write_enable = if self.device().api_version() >= Version::V1_3 {
|
||||
fns.v1_3.cmd_set_depth_write_enable
|
||||
} else {
|
||||
fns.ext_extended_dynamic_state
|
||||
.cmd_set_depth_write_enable_ext
|
||||
};
|
||||
|
||||
unsafe { cmd_set_depth_write_enable(self.handle(), enable.into()) };
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the dynamic discard rectangles for future draw calls.
|
||||
pub unsafe fn set_discard_rectangle(
|
||||
&mut self,
|
||||
first_rectangle: u32,
|
||||
rectangles: &[Scissor],
|
||||
) -> Result<&mut Self> {
|
||||
Ok(unsafe { self.set_discard_rectangle_unchecked(first_rectangle, rectangles) })
|
||||
}
|
||||
|
||||
pub unsafe fn set_discard_rectangle_unchecked(
|
||||
&mut self,
|
||||
first_rectangle: u32,
|
||||
rectangles: &[Scissor],
|
||||
) -> &mut Self {
|
||||
if rectangles.is_empty() {
|
||||
return self;
|
||||
}
|
||||
|
||||
let rectangles_vk = rectangles
|
||||
.iter()
|
||||
.map(|v| v.into())
|
||||
.collect::<SmallVec<[_; 2]>>();
|
||||
|
||||
let fns = self.device().fns();
|
||||
unsafe {
|
||||
(fns.ext_discard_rectangles.cmd_set_discard_rectangle_ext)(
|
||||
self.handle(),
|
||||
first_rectangle,
|
||||
rectangles_vk.len() as u32,
|
||||
rectangles_vk.as_ptr(),
|
||||
)
|
||||
};
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the dynamic front face for future draw calls.
|
||||
pub unsafe fn set_front_face(&mut self, face: FrontFace) -> Result<&mut Self> {
|
||||
Ok(unsafe { self.set_front_face_unchecked(face) })
|
||||
}
|
||||
|
||||
pub unsafe fn set_front_face_unchecked(&mut self, face: FrontFace) -> &mut Self {
|
||||
let fns = self.device().fns();
|
||||
let cmd_set_front_face = if self.device().api_version() >= Version::V1_3 {
|
||||
fns.v1_3.cmd_set_front_face
|
||||
} else {
|
||||
fns.ext_extended_dynamic_state.cmd_set_front_face_ext
|
||||
};
|
||||
|
||||
unsafe { cmd_set_front_face(self.handle(), face.into()) };
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the dynamic line stipple values for future draw calls.
|
||||
pub unsafe fn set_line_stipple(&mut self, factor: u32, pattern: u16) -> Result<&mut Self> {
|
||||
Ok(unsafe { self.set_line_stipple_unchecked(factor, pattern) })
|
||||
}
|
||||
|
||||
pub unsafe fn set_line_stipple_unchecked(&mut self, factor: u32, pattern: u16) -> &mut Self {
|
||||
let fns = self.device().fns();
|
||||
unsafe {
|
||||
(fns.ext_line_rasterization.cmd_set_line_stipple_ext)(self.handle(), factor, pattern)
|
||||
};
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the dynamic line width for future draw calls.
|
||||
pub unsafe fn set_line_width(&mut self, line_width: f32) -> Result<&mut Self> {
|
||||
Ok(unsafe { self.set_line_width_unchecked(line_width) })
|
||||
}
|
||||
|
||||
pub unsafe fn set_line_width_unchecked(&mut self, line_width: f32) -> &mut Self {
|
||||
let fns = self.device().fns();
|
||||
unsafe { (fns.v1_0.cmd_set_line_width)(self.handle(), line_width) };
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the dynamic logic op for future draw calls.
|
||||
pub unsafe fn set_logic_op(&mut self, logic_op: LogicOp) -> Result<&mut Self> {
|
||||
Ok(unsafe { self.set_logic_op_unchecked(logic_op) })
|
||||
}
|
||||
|
||||
pub unsafe fn set_logic_op_unchecked(&mut self, logic_op: LogicOp) -> &mut Self {
|
||||
let fns = self.device().fns();
|
||||
unsafe {
|
||||
(fns.ext_extended_dynamic_state2.cmd_set_logic_op_ext)(self.handle(), logic_op.into())
|
||||
};
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the dynamic number of patch control points for future draw calls.
|
||||
pub unsafe fn set_patch_control_points(&mut self, num: u32) -> Result<&mut Self> {
|
||||
Ok(unsafe { self.set_patch_control_points_unchecked(num) })
|
||||
}
|
||||
|
||||
pub unsafe fn set_patch_control_points_unchecked(&mut self, num: u32) -> &mut Self {
|
||||
let fns = self.device().fns();
|
||||
unsafe {
|
||||
(fns.ext_extended_dynamic_state2
|
||||
.cmd_set_patch_control_points_ext)(self.handle(), num)
|
||||
};
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets whether dynamic primitive restart is enabled for future draw calls.
|
||||
pub unsafe fn set_primitive_restart_enable(&mut self, enable: bool) -> Result<&mut Self> {
|
||||
Ok(unsafe { self.set_primitive_restart_enable_unchecked(enable) })
|
||||
}
|
||||
|
||||
pub unsafe fn set_primitive_restart_enable_unchecked(&mut self, enable: bool) -> &mut Self {
|
||||
let fns = self.device().fns();
|
||||
let cmd_set_primitive_restart_enable = if self.device().api_version() >= Version::V1_3 {
|
||||
fns.v1_3.cmd_set_primitive_restart_enable
|
||||
} else {
|
||||
fns.ext_extended_dynamic_state2
|
||||
.cmd_set_primitive_restart_enable_ext
|
||||
};
|
||||
|
||||
unsafe { cmd_set_primitive_restart_enable(self.handle(), enable.into()) };
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the dynamic primitive topology for future draw calls.
|
||||
pub unsafe fn set_primitive_topology(
|
||||
&mut self,
|
||||
topology: PrimitiveTopology,
|
||||
) -> Result<&mut Self> {
|
||||
Ok(unsafe { self.set_primitive_topology_unchecked(topology) })
|
||||
}
|
||||
|
||||
pub unsafe fn set_primitive_topology_unchecked(
|
||||
&mut self,
|
||||
topology: PrimitiveTopology,
|
||||
) -> &mut Self {
|
||||
let fns = self.device().fns();
|
||||
let cmd_set_primitive_topology = if self.device().api_version() >= Version::V1_3 {
|
||||
fns.v1_3.cmd_set_primitive_topology
|
||||
} else {
|
||||
fns.ext_extended_dynamic_state
|
||||
.cmd_set_primitive_topology_ext
|
||||
};
|
||||
|
||||
unsafe { cmd_set_primitive_topology(self.handle(), topology.into()) };
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets whether dynamic rasterizer discard is enabled for future draw calls.
|
||||
pub unsafe fn set_rasterizer_discard_enable(&mut self, enable: bool) -> Result<&mut Self> {
|
||||
Ok(unsafe { self.set_rasterizer_discard_enable_unchecked(enable) })
|
||||
}
|
||||
|
||||
pub unsafe fn set_rasterizer_discard_enable_unchecked(&mut self, enable: bool) -> &mut Self {
|
||||
let fns = self.device().fns();
|
||||
let cmd_set_rasterizer_discard_enable = if self.device().api_version() >= Version::V1_3 {
|
||||
fns.v1_3.cmd_set_rasterizer_discard_enable
|
||||
} else {
|
||||
fns.ext_extended_dynamic_state2
|
||||
.cmd_set_rasterizer_discard_enable_ext
|
||||
};
|
||||
|
||||
unsafe { cmd_set_rasterizer_discard_enable(self.handle(), enable.into()) };
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the dynamic scissors for future draw calls.
|
||||
pub unsafe fn set_scissor(
|
||||
&mut self,
|
||||
first_scissor: u32,
|
||||
scissors: &[Scissor],
|
||||
) -> Result<&mut Self> {
|
||||
Ok(unsafe { self.set_scissor_unchecked(first_scissor, scissors) })
|
||||
}
|
||||
|
||||
pub unsafe fn set_scissor_unchecked(
|
||||
&mut self,
|
||||
first_scissor: u32,
|
||||
scissors: &[Scissor],
|
||||
) -> &mut Self {
|
||||
if scissors.is_empty() {
|
||||
return self;
|
||||
}
|
||||
|
||||
let scissors_vk = scissors
|
||||
.iter()
|
||||
.map(vk::Rect2D::from)
|
||||
.collect::<SmallVec<[_; 2]>>();
|
||||
|
||||
let fns = self.device().fns();
|
||||
unsafe {
|
||||
(fns.v1_0.cmd_set_scissor)(
|
||||
self.handle(),
|
||||
first_scissor,
|
||||
scissors_vk.len() as u32,
|
||||
scissors_vk.as_ptr(),
|
||||
)
|
||||
};
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the dynamic scissors with count for future draw calls.
|
||||
pub unsafe fn set_scissor_with_count(&mut self, scissors: &[Scissor]) -> Result<&mut Self> {
|
||||
Ok(unsafe { self.set_scissor_with_count_unchecked(scissors) })
|
||||
}
|
||||
|
||||
pub unsafe fn set_scissor_with_count_unchecked(&mut self, scissors: &[Scissor]) -> &mut Self {
|
||||
if scissors.is_empty() {
|
||||
return self;
|
||||
}
|
||||
|
||||
let scissors_vk = scissors
|
||||
.iter()
|
||||
.map(vk::Rect2D::from)
|
||||
.collect::<SmallVec<[_; 2]>>();
|
||||
|
||||
let fns = self.device().fns();
|
||||
let cmd_set_scissor_with_count = if self.device().api_version() >= Version::V1_3 {
|
||||
fns.v1_3.cmd_set_scissor_with_count
|
||||
} else {
|
||||
fns.ext_extended_dynamic_state
|
||||
.cmd_set_scissor_with_count_ext
|
||||
};
|
||||
|
||||
unsafe {
|
||||
cmd_set_scissor_with_count(
|
||||
self.handle(),
|
||||
scissors_vk.len() as u32,
|
||||
scissors_vk.as_ptr(),
|
||||
)
|
||||
};
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the dynamic stencil compare mask on one or both faces for future draw calls.
|
||||
pub unsafe fn set_stencil_compare_mask(
|
||||
&mut self,
|
||||
faces: StencilFaces,
|
||||
compare_mask: u32,
|
||||
) -> Result<&mut Self> {
|
||||
Ok(unsafe { self.set_stencil_compare_mask_unchecked(faces, compare_mask) })
|
||||
}
|
||||
|
||||
pub unsafe fn set_stencil_compare_mask_unchecked(
|
||||
&mut self,
|
||||
faces: StencilFaces,
|
||||
compare_mask: u32,
|
||||
) -> &mut Self {
|
||||
let fns = self.device().fns();
|
||||
unsafe {
|
||||
(fns.v1_0.cmd_set_stencil_compare_mask)(self.handle(), faces.into(), compare_mask)
|
||||
};
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the dynamic stencil ops on one or both faces for future draw calls.
|
||||
pub unsafe fn set_stencil_op(
|
||||
&mut self,
|
||||
faces: StencilFaces,
|
||||
fail_op: StencilOp,
|
||||
pass_op: StencilOp,
|
||||
depth_fail_op: StencilOp,
|
||||
compare_op: CompareOp,
|
||||
) -> Result<&mut Self> {
|
||||
Ok(unsafe {
|
||||
self.set_stencil_op_unchecked(faces, fail_op, pass_op, depth_fail_op, compare_op)
|
||||
})
|
||||
}
|
||||
|
||||
pub unsafe fn set_stencil_op_unchecked(
|
||||
&mut self,
|
||||
faces: StencilFaces,
|
||||
fail_op: StencilOp,
|
||||
pass_op: StencilOp,
|
||||
depth_fail_op: StencilOp,
|
||||
compare_op: CompareOp,
|
||||
) -> &mut Self {
|
||||
let fns = self.device().fns();
|
||||
let cmd_set_stencil_op = if self.device().api_version() >= Version::V1_3 {
|
||||
fns.v1_3.cmd_set_stencil_op
|
||||
} else {
|
||||
fns.ext_extended_dynamic_state.cmd_set_stencil_op_ext
|
||||
};
|
||||
|
||||
unsafe {
|
||||
cmd_set_stencil_op(
|
||||
self.handle(),
|
||||
faces.into(),
|
||||
fail_op.into(),
|
||||
pass_op.into(),
|
||||
depth_fail_op.into(),
|
||||
compare_op.into(),
|
||||
)
|
||||
};
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the dynamic stencil reference on one or both faces for future draw calls.
|
||||
pub unsafe fn set_stencil_reference(
|
||||
&mut self,
|
||||
faces: StencilFaces,
|
||||
reference: u32,
|
||||
) -> Result<&mut Self> {
|
||||
Ok(unsafe { self.set_stencil_reference_unchecked(faces, reference) })
|
||||
}
|
||||
|
||||
pub unsafe fn set_stencil_reference_unchecked(
|
||||
&mut self,
|
||||
faces: StencilFaces,
|
||||
reference: u32,
|
||||
) -> &mut Self {
|
||||
let fns = self.device().fns();
|
||||
unsafe { (fns.v1_0.cmd_set_stencil_reference)(self.handle(), faces.into(), reference) };
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets whether dynamic stencil testing is enabled for future draw calls.
|
||||
pub unsafe fn set_stencil_test_enable(&mut self, enable: bool) -> Result<&mut Self> {
|
||||
Ok(unsafe { self.set_stencil_test_enable_unchecked(enable) })
|
||||
}
|
||||
|
||||
pub unsafe fn set_stencil_test_enable_unchecked(&mut self, enable: bool) -> &mut Self {
|
||||
let fns = self.device().fns();
|
||||
let cmd_set_stencil_test_enable = if self.device().api_version() >= Version::V1_3 {
|
||||
fns.v1_3.cmd_set_stencil_test_enable
|
||||
} else {
|
||||
fns.ext_extended_dynamic_state
|
||||
.cmd_set_stencil_test_enable_ext
|
||||
};
|
||||
|
||||
unsafe { cmd_set_stencil_test_enable(self.handle(), enable.into()) };
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the dynamic stencil write mask on one or both faces for future draw calls.
|
||||
pub unsafe fn set_stencil_write_mask(
|
||||
&mut self,
|
||||
faces: StencilFaces,
|
||||
write_mask: u32,
|
||||
) -> Result<&mut Self> {
|
||||
Ok(unsafe { self.set_stencil_write_mask_unchecked(faces, write_mask) })
|
||||
}
|
||||
|
||||
pub unsafe fn set_stencil_write_mask_unchecked(
|
||||
&mut self,
|
||||
faces: StencilFaces,
|
||||
write_mask: u32,
|
||||
) -> &mut Self {
|
||||
let fns = self.device().fns();
|
||||
unsafe { (fns.v1_0.cmd_set_stencil_write_mask)(self.handle(), faces.into(), write_mask) };
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the dynamic vertex input for future draw calls.
|
||||
pub unsafe fn set_vertex_input(
|
||||
&mut self,
|
||||
vertex_input_state: &VertexInputState,
|
||||
) -> Result<&mut Self> {
|
||||
Ok(unsafe { self.set_vertex_input_unchecked(vertex_input_state) })
|
||||
}
|
||||
|
||||
pub unsafe fn set_vertex_input_unchecked(
|
||||
&mut self,
|
||||
vertex_input_state: &VertexInputState,
|
||||
) -> &mut Self {
|
||||
let mut vertex_binding_descriptions_vk: SmallVec<[_; 8]> = SmallVec::new();
|
||||
let mut vertex_attribute_descriptions_vk: SmallVec<[_; 8]> = SmallVec::new();
|
||||
|
||||
let VertexInputState {
|
||||
bindings,
|
||||
attributes,
|
||||
_ne: _,
|
||||
} = vertex_input_state;
|
||||
|
||||
vertex_binding_descriptions_vk.extend(bindings.iter().map(|(&binding, binding_desc)| {
|
||||
let &VertexInputBindingDescription {
|
||||
stride,
|
||||
input_rate,
|
||||
_ne: _,
|
||||
} = binding_desc;
|
||||
|
||||
let divisor = match input_rate {
|
||||
// VUID-VkVertexInputBindingDescription2EXT-divisor-06227
|
||||
VertexInputRate::Vertex => 1,
|
||||
VertexInputRate::Instance { divisor } => divisor,
|
||||
};
|
||||
|
||||
vk::VertexInputBindingDescription2EXT {
|
||||
binding,
|
||||
stride,
|
||||
input_rate: input_rate.into(),
|
||||
divisor,
|
||||
..Default::default()
|
||||
}
|
||||
}));
|
||||
|
||||
vertex_attribute_descriptions_vk.extend(attributes.iter().map(
|
||||
|(&location, attribute_desc)| {
|
||||
let &VertexInputAttributeDescription {
|
||||
binding,
|
||||
format,
|
||||
offset,
|
||||
_ne: _,
|
||||
} = attribute_desc;
|
||||
|
||||
vk::VertexInputAttributeDescription2EXT {
|
||||
location,
|
||||
binding,
|
||||
format: format.into(),
|
||||
offset,
|
||||
..Default::default()
|
||||
}
|
||||
},
|
||||
));
|
||||
|
||||
let fns = self.device().fns();
|
||||
unsafe {
|
||||
(fns.ext_vertex_input_dynamic_state.cmd_set_vertex_input_ext)(
|
||||
self.handle(),
|
||||
vertex_binding_descriptions_vk.len() as u32,
|
||||
vertex_binding_descriptions_vk.as_ptr(),
|
||||
vertex_attribute_descriptions_vk.len() as u32,
|
||||
vertex_attribute_descriptions_vk.as_ptr(),
|
||||
)
|
||||
};
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the dynamic viewports for future draw calls.
|
||||
pub unsafe fn set_viewport(
|
||||
&mut self,
|
||||
first_viewport: u32,
|
||||
viewports: &[Viewport],
|
||||
) -> Result<&mut Self> {
|
||||
Ok(unsafe { self.set_viewport_unchecked(first_viewport, viewports) })
|
||||
}
|
||||
|
||||
pub unsafe fn set_viewport_unchecked(
|
||||
&mut self,
|
||||
first_viewport: u32,
|
||||
viewports: &[Viewport],
|
||||
) -> &mut Self {
|
||||
if viewports.is_empty() {
|
||||
return self;
|
||||
}
|
||||
|
||||
let viewports_vk = viewports
|
||||
.iter()
|
||||
.map(|v| v.into())
|
||||
.collect::<SmallVec<[_; 2]>>();
|
||||
|
||||
let fns = self.device().fns();
|
||||
unsafe {
|
||||
(fns.v1_0.cmd_set_viewport)(
|
||||
self.handle(),
|
||||
first_viewport,
|
||||
viewports_vk.len() as u32,
|
||||
viewports_vk.as_ptr(),
|
||||
)
|
||||
};
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the dynamic viewports with count for future draw calls.
|
||||
pub unsafe fn set_viewport_with_count(&mut self, viewports: &[Viewport]) -> Result<&mut Self> {
|
||||
Ok(unsafe { self.set_viewport_with_count_unchecked(viewports) })
|
||||
}
|
||||
|
||||
pub unsafe fn set_viewport_with_count_unchecked(
|
||||
&mut self,
|
||||
viewports: &[Viewport],
|
||||
) -> &mut Self {
|
||||
if viewports.is_empty() {
|
||||
return self;
|
||||
}
|
||||
|
||||
let viewports_vk = viewports
|
||||
.iter()
|
||||
.map(|v| v.into())
|
||||
.collect::<SmallVec<[_; 2]>>();
|
||||
|
||||
let fns = self.device().fns();
|
||||
let cmd_set_viewport_with_count = if self.device().api_version() >= Version::V1_3 {
|
||||
fns.v1_3.cmd_set_viewport_with_count
|
||||
} else {
|
||||
fns.ext_extended_dynamic_state
|
||||
.cmd_set_viewport_with_count_ext
|
||||
};
|
||||
|
||||
unsafe {
|
||||
cmd_set_viewport_with_count(
|
||||
self.handle(),
|
||||
viewports_vk.len() as u32,
|
||||
viewports_vk.as_ptr(),
|
||||
)
|
||||
};
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the dynamic conservative rasterization mode for future draw calls.
|
||||
pub unsafe fn set_conservative_rasterization_mode(
|
||||
&mut self,
|
||||
conservative_rasterization_mode: ConservativeRasterizationMode,
|
||||
) -> Result<&mut Self> {
|
||||
Ok(unsafe {
|
||||
self.set_conservative_rasterization_mode_unchecked(conservative_rasterization_mode)
|
||||
})
|
||||
}
|
||||
|
||||
pub unsafe fn set_conservative_rasterization_mode_unchecked(
|
||||
&mut self,
|
||||
conservative_rasterization_mode: ConservativeRasterizationMode,
|
||||
) -> &mut Self {
|
||||
let fns = self.device().fns();
|
||||
unsafe {
|
||||
(fns.ext_extended_dynamic_state3
|
||||
.cmd_set_conservative_rasterization_mode_ext)(
|
||||
self.handle(),
|
||||
conservative_rasterization_mode.into(),
|
||||
)
|
||||
};
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
/// Sets the dynamic extra primitive overestimation size for future draw calls.
|
||||
pub unsafe fn set_extra_primitive_overestimation_size(
|
||||
&mut self,
|
||||
extra_primitive_overestimation_size: f32,
|
||||
) -> Result<&mut Self> {
|
||||
Ok(unsafe {
|
||||
self.set_extra_primitive_overestimation_size_unchecked(
|
||||
extra_primitive_overestimation_size,
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
pub unsafe fn set_extra_primitive_overestimation_size_unchecked(
|
||||
&mut self,
|
||||
extra_primitive_overestimation_size: f32,
|
||||
) -> &mut Self {
|
||||
let fns = self.device().fns();
|
||||
unsafe {
|
||||
(fns.ext_extended_dynamic_state3
|
||||
.cmd_set_extra_primitive_overestimation_size_ext)(
|
||||
self.handle(),
|
||||
extra_primitive_overestimation_size,
|
||||
)
|
||||
};
|
||||
|
||||
self
|
||||
}
|
||||
}
|
6
vulkano-taskgraph/src/command_buffer/commands/mod.rs
Normal file
6
vulkano-taskgraph/src/command_buffer/commands/mod.rs
Normal file
@ -0,0 +1,6 @@
|
||||
pub(super) mod bind_push;
|
||||
pub(super) mod clear;
|
||||
pub(super) mod copy;
|
||||
pub(super) mod dynamic_state;
|
||||
pub(super) mod pipeline;
|
||||
pub(super) mod sync;
|
661
vulkano-taskgraph/src/command_buffer/commands/pipeline.rs
Normal file
661
vulkano-taskgraph/src/command_buffer/commands/pipeline.rs
Normal file
@ -0,0 +1,661 @@
|
||||
use crate::{
|
||||
command_buffer::{RecordingCommandBuffer, Result},
|
||||
Id,
|
||||
};
|
||||
#[cfg(doc)]
|
||||
use vulkano::command_buffer::{
|
||||
DispatchIndirectCommand, DrawIndexedIndirectCommand, DrawIndirectCommand,
|
||||
DrawMeshTasksIndirectCommand,
|
||||
};
|
||||
use vulkano::{buffer::Buffer, device::DeviceOwned, DeviceSize, Version, VulkanObject};
|
||||
|
||||
/// # Commands to execute a bound pipeline
|
||||
///
|
||||
/// Dispatch commands require a compute queue, draw commands require a graphics queue.
|
||||
impl RecordingCommandBuffer<'_> {
|
||||
/// Performs a single compute operation using a compute pipeline.
|
||||
///
|
||||
/// A compute pipeline must have been bound using [`bind_pipeline_compute`]. Any resources used
|
||||
/// by the compute pipeline, such as descriptor sets, must have been set beforehand.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// - The general [shader safety requirements] apply.
|
||||
///
|
||||
/// [`bind_pipeline_compute`]: Self::bind_pipeline_compute
|
||||
/// [shader safety requirements]: vulkano::shader#safety
|
||||
pub unsafe fn dispatch(&mut self, group_counts: [u32; 3]) -> Result<&mut Self> {
|
||||
Ok(unsafe { self.dispatch_unchecked(group_counts) })
|
||||
}
|
||||
|
||||
pub unsafe fn dispatch_unchecked(&mut self, group_counts: [u32; 3]) -> &mut Self {
|
||||
let fns = self.device().fns();
|
||||
unsafe {
|
||||
(fns.v1_0.cmd_dispatch)(
|
||||
self.handle(),
|
||||
group_counts[0],
|
||||
group_counts[1],
|
||||
group_counts[2],
|
||||
)
|
||||
};
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
/// Performs a single compute operation using a compute pipeline. One dispatch is performed
|
||||
/// for the [`DispatchIndirectCommand`] struct that is read from `buffer` starting at `offset`.
|
||||
///
|
||||
/// A compute pipeline must have been bound using [`bind_pipeline_compute`]. Any resources used
|
||||
/// by the compute pipeline, such as descriptor sets, must have been set beforehand.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// - The general [shader safety requirements] apply.
|
||||
/// - The [safety requirements for `DispatchIndirectCommand`] apply.
|
||||
///
|
||||
/// [`bind_pipeline_compute`]: Self::bind_pipeline_compute
|
||||
/// [shader safety requirements]: vulkano::shader#safety
|
||||
/// [safety requirements for `DispatchIndirectCommand`]: DispatchIndirectCommand#safety
|
||||
pub unsafe fn dispatch_indirect(
|
||||
&mut self,
|
||||
buffer: Id<Buffer>,
|
||||
offset: DeviceSize,
|
||||
) -> Result<&mut Self> {
|
||||
Ok(unsafe { self.dispatch_indirect_unchecked(buffer, offset) })
|
||||
}
|
||||
|
||||
pub unsafe fn dispatch_indirect_unchecked(
|
||||
&mut self,
|
||||
buffer: Id<Buffer>,
|
||||
offset: DeviceSize,
|
||||
) -> &mut Self {
|
||||
let buffer = unsafe { self.accesses.buffer_unchecked(buffer) };
|
||||
|
||||
let fns = self.device().fns();
|
||||
unsafe { (fns.v1_0.cmd_dispatch_indirect)(self.handle(), buffer.handle(), offset) };
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
/// Performs a single draw operation using a primitive shading graphics pipeline.
|
||||
///
|
||||
/// The parameters specify the first vertex and the number of vertices to draw, and the first
|
||||
/// instance and number of instances. For non-instanced drawing, specify `instance_count` as 1
|
||||
/// and `first_instance` as 0.
|
||||
///
|
||||
/// A primitive shading graphics pipeline must have been bound using
|
||||
/// [`bind_pipeline_graphics`]. Any resources used by the graphics pipeline, such as descriptor
|
||||
/// sets, vertex buffers and dynamic state, must have been set beforehand. If the bound
|
||||
/// graphics pipeline uses vertex buffers, then the provided vertex and instance ranges must be
|
||||
/// in range of the bound vertex buffers.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// - The general [shader safety requirements] apply.
|
||||
///
|
||||
/// [`bind_pipeline_graphics`]: Self::bind_pipeline_graphics
|
||||
/// [shader safety requirements]: vulkano::shader#safety
|
||||
pub unsafe fn draw(
|
||||
&mut self,
|
||||
vertex_count: u32,
|
||||
instance_count: u32,
|
||||
first_vertex: u32,
|
||||
first_instance: u32,
|
||||
) -> Result<&mut Self> {
|
||||
Ok(unsafe {
|
||||
self.draw_unchecked(vertex_count, instance_count, first_vertex, first_instance)
|
||||
})
|
||||
}
|
||||
|
||||
pub unsafe fn draw_unchecked(
|
||||
&mut self,
|
||||
vertex_count: u32,
|
||||
instance_count: u32,
|
||||
first_vertex: u32,
|
||||
first_instance: u32,
|
||||
) -> &mut Self {
|
||||
let fns = self.device().fns();
|
||||
unsafe {
|
||||
(fns.v1_0.cmd_draw)(
|
||||
self.handle(),
|
||||
vertex_count,
|
||||
instance_count,
|
||||
first_vertex,
|
||||
first_instance,
|
||||
)
|
||||
};
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
/// Performs multiple draw operations using a primitive shading graphics pipeline.
|
||||
///
|
||||
/// One draw is performed for each [`DrawIndirectCommand`] struct that is read from `buffer`
|
||||
/// starting at `offset`, with the offset increasing by `stride` bytes for each successive
|
||||
/// draw. `draw_count` draw commands are performed. The maximum number of draw commands in the
|
||||
/// buffer is limited by the [`max_draw_indirect_count`] limit. This limit is 1 unless the
|
||||
/// [`multi_draw_indirect`] feature has been enabled.
|
||||
///
|
||||
/// A primitive shading graphics pipeline must have been bound using
|
||||
/// [`bind_pipeline_graphics`]. Any resources used by the graphics pipeline, such as descriptor
|
||||
/// sets, vertex buffers and dynamic state, must have been set beforehand. If the bound
|
||||
/// graphics pipeline uses vertex buffers, then the vertex and instance ranges of each
|
||||
/// `DrawIndirectCommand` in the indirect buffer must be in range of the bound vertex buffers.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// - The general [shader safety requirements] apply.
|
||||
/// - The [safety requirements for `DrawIndirectCommand`] apply.
|
||||
///
|
||||
/// [`max_draw_indirect_count`]: vulkano::device::DeviceProperties::max_draw_indirect_count
|
||||
/// [`multi_draw_indirect`]: vulkano::device::DeviceFeatures::multi_draw_indirect
|
||||
/// [`bind_pipeline_graphics`]: Self::bind_pipeline_graphics
|
||||
/// [shader safety requirements]: vulkano::shader#safety
|
||||
/// [safety requirements for `DrawIndirectCommand`]: DrawIndirectCommand#safety
|
||||
pub unsafe fn draw_indirect(
|
||||
&mut self,
|
||||
buffer: Id<Buffer>,
|
||||
offset: DeviceSize,
|
||||
draw_count: u32,
|
||||
stride: u32,
|
||||
) -> Result<&mut Self> {
|
||||
Ok(unsafe { self.draw_indirect_unchecked(buffer, offset, draw_count, stride) })
|
||||
}
|
||||
|
||||
pub unsafe fn draw_indirect_unchecked(
|
||||
&mut self,
|
||||
buffer: Id<Buffer>,
|
||||
offset: DeviceSize,
|
||||
draw_count: u32,
|
||||
stride: u32,
|
||||
) -> &mut Self {
|
||||
let buffer = unsafe { self.accesses.buffer_unchecked(buffer) };
|
||||
|
||||
let fns = self.device().fns();
|
||||
unsafe {
|
||||
(fns.v1_0.cmd_draw_indirect)(self.handle(), buffer.handle(), offset, draw_count, stride)
|
||||
};
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
/// Performs multiple draw operations using a primitive shading graphics pipeline, reading the
|
||||
/// number of draw operations from a separate buffer.
|
||||
///
|
||||
/// One draw is performed for each [`DrawIndirectCommand`] struct that is read from `buffer`
|
||||
/// starting at `offset`, with the offset increasing by `stride` bytes for each successive
|
||||
/// draw. The number of draws to perform is read from `count_buffer` at `count_buffer_offset`,
|
||||
/// or specified by `max_draw_count`, whichever is lower. This number is limited by the
|
||||
/// [`max_draw_indirect_count`] limit.
|
||||
///
|
||||
/// A primitive shading graphics pipeline must have been bound using
|
||||
/// [`bind_pipeline_graphics`]. Any resources used by the graphics pipeline, such as descriptor
|
||||
/// sets, vertex buffers and dynamic state, must have been set beforehand. If the bound
|
||||
/// graphics pipeline uses vertex buffers, then the vertex and instance ranges of each
|
||||
/// `DrawIndirectCommand` in the indirect buffer must be in range of the bound vertex buffers.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// - The general [shader safety requirements] apply.
|
||||
/// - The [safety requirements for `DrawIndirectCommand`] apply.
|
||||
/// - The count stored in `count_buffer` must not be greater than the
|
||||
/// [`max_draw_indirect_count`] device limit.
|
||||
/// - The count stored in `count_buffer` must fall within the range of `buffer` starting at
|
||||
/// `offset`.
|
||||
///
|
||||
/// [`max_draw_indirect_count`]: vulkano::device::DeviceProperties::max_draw_indirect_count
|
||||
/// [`bind_pipeline_graphics`]: Self::bind_pipeline_graphics
|
||||
/// [shader safety requirements]: vulkano::shader#safety
|
||||
/// [safety requirements for `DrawIndirectCommand`]: DrawIndirectCommand#safety
|
||||
pub unsafe fn draw_indirect_count(
|
||||
&mut self,
|
||||
buffer: Id<Buffer>,
|
||||
offset: DeviceSize,
|
||||
count_buffer: Id<Buffer>,
|
||||
count_buffer_offset: DeviceSize,
|
||||
max_draw_count: u32,
|
||||
stride: u32,
|
||||
) -> Result<&mut Self> {
|
||||
Ok(unsafe {
|
||||
self.draw_indirect_count_unchecked(
|
||||
buffer,
|
||||
offset,
|
||||
count_buffer,
|
||||
count_buffer_offset,
|
||||
max_draw_count,
|
||||
stride,
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
pub unsafe fn draw_indirect_count_unchecked(
|
||||
&mut self,
|
||||
buffer: Id<Buffer>,
|
||||
offset: DeviceSize,
|
||||
count_buffer: Id<Buffer>,
|
||||
count_buffer_offset: DeviceSize,
|
||||
max_draw_count: u32,
|
||||
stride: u32,
|
||||
) -> &mut Self {
|
||||
let buffer = unsafe { self.accesses.buffer_unchecked(buffer) };
|
||||
let count_buffer = unsafe { self.accesses.buffer_unchecked(count_buffer) };
|
||||
|
||||
let device = self.device();
|
||||
let fns = device.fns();
|
||||
let cmd_draw_indirect_count = if device.api_version() >= Version::V1_2 {
|
||||
fns.v1_2.cmd_draw_indirect_count
|
||||
} else if device.enabled_extensions().khr_draw_indirect_count {
|
||||
fns.khr_draw_indirect_count.cmd_draw_indirect_count_khr
|
||||
} else if device.enabled_extensions().amd_draw_indirect_count {
|
||||
fns.amd_draw_indirect_count.cmd_draw_indirect_count_amd
|
||||
} else {
|
||||
std::process::abort();
|
||||
};
|
||||
|
||||
unsafe {
|
||||
cmd_draw_indirect_count(
|
||||
self.handle(),
|
||||
buffer.handle(),
|
||||
offset,
|
||||
count_buffer.handle(),
|
||||
count_buffer_offset,
|
||||
max_draw_count,
|
||||
stride,
|
||||
)
|
||||
};
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
/// Performs a single draw operation using a primitive shading graphics pipeline, using an
|
||||
/// index buffer.
|
||||
///
|
||||
/// The parameters specify the first index and the number of indices in the index buffer that
|
||||
/// should be used, and the first instance and number of instances. For non-instanced drawing,
|
||||
/// specify `instance_count` as 1 and `first_instance` as 0. The `vertex_offset` is a constant
|
||||
/// value that should be added to each index in the index buffer to produce the final vertex
|
||||
/// number to be used.
|
||||
///
|
||||
/// An index buffer must have been bound using [`bind_index_buffer`], and the provided index
|
||||
/// range must be in range of the bound index buffer.
|
||||
///
|
||||
/// A primitive shading graphics pipeline must have been bound using
|
||||
/// [`bind_pipeline_graphics`]. Any resources used by the graphics pipeline, such as descriptor
|
||||
/// sets, vertex buffers and dynamic state, must have been set beforehand. If the bound
|
||||
/// graphics pipeline uses vertex buffers, then the provided instance range must be in range of
|
||||
/// the bound vertex buffers. The vertex indices in the index buffer must be in range of the
|
||||
/// bound vertex buffers.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// - The general [shader safety requirements] apply.
|
||||
/// - Every vertex number that is retrieved from the index buffer must fall within the range of
|
||||
/// the bound vertex-rate vertex buffers.
|
||||
/// - Every vertex number that is retrieved from the index buffer, if it is not the special
|
||||
/// primitive restart value, must be no greater than the [`max_draw_indexed_index_value`]
|
||||
/// device limit.
|
||||
///
|
||||
/// [`bind_index_buffer`]: Self::bind_index_buffer
|
||||
/// [`bind_pipeline_graphics`]: Self::bind_pipeline_graphics
|
||||
/// [shader safety requirements]: vulkano::shader#safety
|
||||
/// [`max_draw_indexed_index_value`]: vulkano::device::DeviceProperties::max_draw_indexed_index_value
|
||||
pub unsafe fn draw_indexed(
|
||||
&mut self,
|
||||
index_count: u32,
|
||||
instance_count: u32,
|
||||
first_index: u32,
|
||||
vertex_offset: i32,
|
||||
first_instance: u32,
|
||||
) -> Result<&mut Self> {
|
||||
Ok(unsafe {
|
||||
self.draw_indexed_unchecked(
|
||||
index_count,
|
||||
instance_count,
|
||||
first_index,
|
||||
vertex_offset,
|
||||
first_instance,
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
pub unsafe fn draw_indexed_unchecked(
|
||||
&mut self,
|
||||
index_count: u32,
|
||||
instance_count: u32,
|
||||
first_index: u32,
|
||||
vertex_offset: i32,
|
||||
first_instance: u32,
|
||||
) -> &mut Self {
|
||||
let fns = self.device().fns();
|
||||
unsafe {
|
||||
(fns.v1_0.cmd_draw_indexed)(
|
||||
self.handle(),
|
||||
index_count,
|
||||
instance_count,
|
||||
first_index,
|
||||
vertex_offset,
|
||||
first_instance,
|
||||
)
|
||||
};
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
/// Performs multiple draw operations using a primitive shading graphics pipeline, using an
|
||||
/// index buffer.
|
||||
///
|
||||
/// One draw is performed for each [`DrawIndexedIndirectCommand`] struct that is read from
|
||||
/// `buffer` starting at `offset`, with the offset increasing by `stride` bytes with each
|
||||
/// successive draw. `draw_count` draw commands are performed. The maximum number of draw
|
||||
/// commands in the buffer is limited by the [`max_draw_indirect_count`] limit. This limit is 1
|
||||
/// unless the [`multi_draw_indirect`] feature has been enabled.
|
||||
///
|
||||
/// An index buffer must have been bound using [`bind_index_buffer`], and the index ranges of
|
||||
/// each `DrawIndexedIndirectCommand` in the indirect buffer must be in range of the bound
|
||||
/// index buffer.
|
||||
///
|
||||
/// A primitive shading graphics pipeline must have been bound using
|
||||
/// [`bind_pipeline_graphics`]. Any resources used by the graphics pipeline, such as descriptor
|
||||
/// sets, vertex buffers and dynamic state, must have been set beforehand. If the bound
|
||||
/// graphics pipeline uses vertex buffers, then the instance ranges of each
|
||||
/// `DrawIndexedIndirectCommand` in the indirect buffer must be in range of the bound vertex
|
||||
/// buffers.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// - The general [shader safety requirements] apply.
|
||||
/// - The [safety requirements for `DrawIndexedIndirectCommand`] apply.
|
||||
///
|
||||
/// [`max_draw_indirect_count`]: vulkano::device::DeviceProperties::max_draw_indirect_count
|
||||
/// [`multi_draw_indirect`]: vulkano::device::DeviceFeatures::multi_draw_indirect
|
||||
/// [`bind_index_buffer`]: Self::bind_index_buffer
|
||||
/// [`bind_pipeline_graphics`]: Self::bind_pipeline_graphics
|
||||
/// [shader safety requirements]: vulkano::shader#safety
|
||||
/// [safety requirements for `DrawIndexedIndirectCommand`]: DrawIndexedIndirectCommand#safety
|
||||
pub unsafe fn draw_indexed_indirect(
|
||||
&mut self,
|
||||
buffer: Id<Buffer>,
|
||||
offset: DeviceSize,
|
||||
draw_count: u32,
|
||||
stride: u32,
|
||||
) -> Result<&mut Self> {
|
||||
Ok(unsafe { self.draw_indexed_indirect_unchecked(buffer, offset, draw_count, stride) })
|
||||
}
|
||||
|
||||
pub unsafe fn draw_indexed_indirect_unchecked(
|
||||
&mut self,
|
||||
buffer: Id<Buffer>,
|
||||
offset: DeviceSize,
|
||||
draw_count: u32,
|
||||
stride: u32,
|
||||
) -> &mut Self {
|
||||
let buffer = unsafe { self.accesses.buffer_unchecked(buffer) };
|
||||
|
||||
let fns = self.device().fns();
|
||||
unsafe {
|
||||
(fns.v1_0.cmd_draw_indexed_indirect)(
|
||||
self.handle(),
|
||||
buffer.handle(),
|
||||
offset,
|
||||
draw_count,
|
||||
stride,
|
||||
)
|
||||
};
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
/// Performs multiple draw operations using a primitive shading graphics pipeline, using an
|
||||
/// index buffer, and reading the number of draw operations from a separate buffer.
|
||||
///
|
||||
/// One draw is performed for each [`DrawIndexedIndirectCommand`] struct that is read from
|
||||
/// `buffer` starting at `offset`, with the offset increasing by `stride` bytes for each
|
||||
/// successive draw. The number of draws to perform is read from `count_buffer` at
|
||||
/// `count_buffer_offset`, or specified by `max_draw_count`, whichever is lower. This number is
|
||||
/// limited by the [`max_draw_indirect_count`] limit.
|
||||
///
|
||||
/// An index buffer must have been bound using [`bind_index_buffer`], and the index ranges of
|
||||
/// each `DrawIndexedIndirectCommand` in the indirect buffer must be in range of the bound
|
||||
/// index buffer.
|
||||
///
|
||||
/// A primitive shading graphics pipeline must have been bound using
|
||||
/// [`bind_pipeline_graphics`]. Any resources used by the graphics pipeline, such as descriptor
|
||||
/// sets, vertex buffers and dynamic state, must have been set beforehand. If the bound
|
||||
/// graphics pipeline uses vertex buffers, then the instance ranges of each
|
||||
/// `DrawIndexedIndirectCommand` in the indirect buffer must be in range of the bound vertex
|
||||
/// buffers.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// - The general [shader safety requirements] apply.
|
||||
/// - The [safety requirements for `DrawIndexedIndirectCommand`] apply.
|
||||
/// - The count stored in `count_buffer` must not be greater than the
|
||||
/// [`max_draw_indirect_count`] device limit.
|
||||
/// - The count stored in `count_buffer` must fall within the range of `buffer`.
|
||||
///
|
||||
/// [`max_draw_indirect_count`]: vulkano::device::DeviceProperties::max_draw_indirect_count
|
||||
/// [`bind_index_buffer`]: Self::bind_index_buffer
|
||||
/// [`bind_pipeline_graphics`]: Self::bind_pipeline_graphics
|
||||
/// [shader safety requirements]: vulkano::shader#safety
|
||||
/// [safety requirements for `DrawIndexedIndirectCommand`]: DrawIndexedIndirectCommand#safety
|
||||
pub unsafe fn draw_indexed_indirect_count(
|
||||
&mut self,
|
||||
buffer: Id<Buffer>,
|
||||
offset: DeviceSize,
|
||||
count_buffer: Id<Buffer>,
|
||||
count_buffer_offset: DeviceSize,
|
||||
max_draw_count: u32,
|
||||
stride: u32,
|
||||
) -> Result<&mut Self> {
|
||||
Ok(unsafe {
|
||||
self.draw_indexed_indirect_count_unchecked(
|
||||
buffer,
|
||||
offset,
|
||||
count_buffer,
|
||||
count_buffer_offset,
|
||||
max_draw_count,
|
||||
stride,
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
pub unsafe fn draw_indexed_indirect_count_unchecked(
|
||||
&mut self,
|
||||
buffer: Id<Buffer>,
|
||||
offset: DeviceSize,
|
||||
count_buffer: Id<Buffer>,
|
||||
count_buffer_offset: DeviceSize,
|
||||
max_draw_count: u32,
|
||||
stride: u32,
|
||||
) -> &mut Self {
|
||||
let buffer = unsafe { self.accesses.buffer_unchecked(buffer) };
|
||||
let count_buffer = unsafe { self.accesses.buffer_unchecked(count_buffer) };
|
||||
|
||||
let device = self.device();
|
||||
let fns = device.fns();
|
||||
let cmd_draw_indexed_indirect_count = if device.api_version() >= Version::V1_2 {
|
||||
fns.v1_2.cmd_draw_indexed_indirect_count
|
||||
} else if device.enabled_extensions().khr_draw_indirect_count {
|
||||
fns.khr_draw_indirect_count
|
||||
.cmd_draw_indexed_indirect_count_khr
|
||||
} else if device.enabled_extensions().amd_draw_indirect_count {
|
||||
fns.amd_draw_indirect_count
|
||||
.cmd_draw_indexed_indirect_count_amd
|
||||
} else {
|
||||
std::process::abort();
|
||||
};
|
||||
|
||||
unsafe {
|
||||
cmd_draw_indexed_indirect_count(
|
||||
self.handle(),
|
||||
buffer.handle(),
|
||||
offset,
|
||||
count_buffer.handle(),
|
||||
count_buffer_offset,
|
||||
max_draw_count,
|
||||
stride,
|
||||
)
|
||||
};
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
/// Perform a single draw operation using a mesh shading graphics pipeline.
|
||||
///
|
||||
/// A mesh shading graphics pipeline must have been bound using [`bind_pipeline_graphics`]. Any
|
||||
/// resources used by the graphics pipeline, such as descriptor sets and dynamic state, must
|
||||
/// have been set beforehand.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// - The general [shader safety requirements] apply.
|
||||
///
|
||||
/// [`bind_pipeline_graphics`]: Self::bind_pipeline_graphics
|
||||
/// [shader safety requirements]: vulkano::shader#safety
|
||||
pub unsafe fn draw_mesh_tasks(&mut self, group_counts: [u32; 3]) -> Result<&mut Self> {
|
||||
Ok(unsafe { self.draw_mesh_tasks_unchecked(group_counts) })
|
||||
}
|
||||
|
||||
pub unsafe fn draw_mesh_tasks_unchecked(&mut self, group_counts: [u32; 3]) -> &mut Self {
|
||||
let fns = self.device().fns();
|
||||
unsafe {
|
||||
(fns.ext_mesh_shader.cmd_draw_mesh_tasks_ext)(
|
||||
self.handle(),
|
||||
group_counts[0],
|
||||
group_counts[1],
|
||||
group_counts[2],
|
||||
)
|
||||
};
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
/// Perform multiple draw operations using a mesh shading graphics pipeline.
|
||||
///
|
||||
/// One draw is performed for each [`DrawMeshTasksIndirectCommand`] struct that is read from
|
||||
/// `buffer` starting at `offset`, with the offset increasing by `stride` bytes for each
|
||||
/// successive draw. `draw_count` draw commands are performed. The maximum number of draw
|
||||
/// commands in the buffer is limited by the [`max_draw_indirect_count`] limit. This limit is 1
|
||||
/// unless the [`multi_draw_indirect`] feature has been enabled.
|
||||
///
|
||||
/// A mesh shading graphics pipeline must have been bound using [`bind_pipeline_graphics`]. Any
|
||||
/// resources used by the graphics pipeline, such as descriptor sets and dynamic state, must
|
||||
/// have been set beforehand.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// - The general [shader safety requirements] apply.
|
||||
/// - The [safety requirements for `DrawMeshTasksIndirectCommand`] apply.
|
||||
///
|
||||
/// [`max_draw_indirect_count`]: vulkano::device::DeviceProperties::max_draw_indirect_count
|
||||
/// [`multi_draw_indirect`]: vulkano::device::DeviceFeatures::multi_draw_indirect
|
||||
/// [`bind_pipeline_graphics`]: Self::bind_pipeline_graphics
|
||||
/// [shader safety requirements]: vulkano::shader#safety
|
||||
/// [safety requirements for `DrawMeshTasksIndirectCommand`]: DrawMeshTasksIndirectCommand#safety
|
||||
pub unsafe fn draw_mesh_tasks_indirect(
|
||||
&mut self,
|
||||
buffer: Id<Buffer>,
|
||||
offset: DeviceSize,
|
||||
draw_count: u32,
|
||||
stride: u32,
|
||||
) -> Result<&mut Self> {
|
||||
Ok(unsafe { self.draw_mesh_tasks_indirect_unchecked(buffer, offset, draw_count, stride) })
|
||||
}
|
||||
|
||||
pub unsafe fn draw_mesh_tasks_indirect_unchecked(
|
||||
&mut self,
|
||||
buffer: Id<Buffer>,
|
||||
offset: DeviceSize,
|
||||
draw_count: u32,
|
||||
stride: u32,
|
||||
) -> &mut Self {
|
||||
let buffer = unsafe { self.accesses.buffer_unchecked(buffer) };
|
||||
|
||||
let fns = self.device().fns();
|
||||
unsafe {
|
||||
(fns.ext_mesh_shader.cmd_draw_mesh_tasks_indirect_ext)(
|
||||
self.handle(),
|
||||
buffer.handle(),
|
||||
offset,
|
||||
draw_count,
|
||||
stride,
|
||||
)
|
||||
};
|
||||
|
||||
self
|
||||
}
|
||||
|
||||
/// Performs multiple draw operations using a mesh shading graphics pipeline, reading the
|
||||
/// number of draw operations from a separate buffer.
|
||||
///
|
||||
/// One draw is performed for each [`DrawMeshTasksIndirectCommand`] struct that is read from
|
||||
/// `buffer` starting at `offset`, with the offset increasing by `stride` bytes after each
|
||||
/// successive draw. The number of draws to perform is read from `count_buffer`, or specified
|
||||
/// by `max_draw_count`, whichever is lower. This number is limited by the
|
||||
/// [`max_draw_indirect_count`] limit.
|
||||
///
|
||||
/// A mesh shading graphics pipeline must have been bound using [`bind_pipeline_graphics`]. Any
|
||||
/// resources used by the graphics pipeline, such as descriptor sets and dynamic state, must
|
||||
/// have been set beforehand.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// - The general [shader safety requirements] apply.
|
||||
/// - The [safety requirements for `DrawMeshTasksIndirectCommand`] apply.
|
||||
/// - The count stored in `count_buffer` must not be greater than the
|
||||
/// [`max_draw_indirect_count`] device limit.
|
||||
/// - The count stored in `count_buffer` must fall within the range of `buffer`.
|
||||
///
|
||||
/// [`max_draw_indirect_count`]: vulkano::device::DeviceProperties::max_draw_indirect_count
|
||||
/// [`bind_pipeline_graphics`]: Self::bind_pipeline_graphics
|
||||
/// [shader safety requirements]: vulkano::shader#safety
|
||||
/// [safety requirements for `DrawMeshTasksIndirectCommand`]: DrawMeshTasksIndirectCommand#safety
|
||||
pub unsafe fn draw_mesh_tasks_indirect_count(
|
||||
&mut self,
|
||||
buffer: Id<Buffer>,
|
||||
offset: DeviceSize,
|
||||
count_buffer: Id<Buffer>,
|
||||
count_buffer_offset: DeviceSize,
|
||||
max_draw_count: u32,
|
||||
stride: u32,
|
||||
) -> Result<&mut Self> {
|
||||
Ok(unsafe {
|
||||
self.draw_mesh_tasks_indirect_count_unchecked(
|
||||
buffer,
|
||||
offset,
|
||||
count_buffer,
|
||||
count_buffer_offset,
|
||||
max_draw_count,
|
||||
stride,
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
pub unsafe fn draw_mesh_tasks_indirect_count_unchecked(
|
||||
&mut self,
|
||||
buffer: Id<Buffer>,
|
||||
offset: DeviceSize,
|
||||
count_buffer: Id<Buffer>,
|
||||
count_buffer_offset: DeviceSize,
|
||||
max_draw_count: u32,
|
||||
stride: u32,
|
||||
) -> &mut Self {
|
||||
let buffer = unsafe { self.accesses.buffer_unchecked(buffer) };
|
||||
let count_buffer = unsafe { self.accesses.buffer_unchecked(count_buffer) };
|
||||
|
||||
let fns = self.device().fns();
|
||||
unsafe {
|
||||
(fns.ext_mesh_shader.cmd_draw_mesh_tasks_indirect_count_ext)(
|
||||
self.handle(),
|
||||
buffer.handle(),
|
||||
offset,
|
||||
count_buffer.handle(),
|
||||
count_buffer_offset,
|
||||
max_draw_count,
|
||||
stride,
|
||||
)
|
||||
};
|
||||
|
||||
self
|
||||
}
|
||||
}
|
474
vulkano-taskgraph/src/command_buffer/commands/sync.rs
Normal file
474
vulkano-taskgraph/src/command_buffer/commands/sync.rs
Normal file
@ -0,0 +1,474 @@
|
||||
use crate::{
|
||||
command_buffer::{RecordingCommandBuffer, Result},
|
||||
Id,
|
||||
};
|
||||
use ash::vk;
|
||||
use smallvec::SmallVec;
|
||||
use std::ops::Range;
|
||||
use vulkano::{
|
||||
buffer::Buffer,
|
||||
device::DeviceOwned,
|
||||
image::{Image, ImageAspects, ImageLayout, ImageSubresourceRange},
|
||||
sync::{AccessFlags, DependencyFlags, PipelineStages},
|
||||
DeviceSize, Version, VulkanObject,
|
||||
};
|
||||
|
||||
/// # Commands to synchronize resource accesses
|
||||
impl RecordingCommandBuffer<'_> {
|
||||
pub unsafe fn pipeline_barrier(
|
||||
&mut self,
|
||||
dependency_info: &DependencyInfo<'_>,
|
||||
) -> Result<&mut Self> {
|
||||
Ok(unsafe { self.pipeline_barrier_unchecked(dependency_info) })
|
||||
}
|
||||
|
||||
pub unsafe fn pipeline_barrier_unchecked(
|
||||
&mut self,
|
||||
dependency_info: &DependencyInfo<'_>,
|
||||
) -> &mut Self {
|
||||
if dependency_info.is_empty() {
|
||||
return self;
|
||||
}
|
||||
|
||||
let &DependencyInfo {
|
||||
dependency_flags,
|
||||
memory_barriers,
|
||||
buffer_memory_barriers,
|
||||
image_memory_barriers,
|
||||
_ne: _,
|
||||
} = dependency_info;
|
||||
|
||||
if self.device().enabled_features().synchronization2 {
|
||||
let memory_barriers_vk: SmallVec<[_; 2]> = memory_barriers
|
||||
.iter()
|
||||
.map(|barrier| {
|
||||
let &MemoryBarrier {
|
||||
src_stages,
|
||||
src_access,
|
||||
dst_stages,
|
||||
dst_access,
|
||||
_ne: _,
|
||||
} = barrier;
|
||||
|
||||
vk::MemoryBarrier2::default()
|
||||
.src_stage_mask(src_stages.into())
|
||||
.src_access_mask(src_access.into())
|
||||
.dst_stage_mask(dst_stages.into())
|
||||
.dst_access_mask(dst_access.into())
|
||||
})
|
||||
.collect();
|
||||
|
||||
let buffer_memory_barriers_vk: SmallVec<[_; 8]> = buffer_memory_barriers
|
||||
.iter()
|
||||
.map(|barrier| {
|
||||
let &BufferMemoryBarrier {
|
||||
src_stages,
|
||||
src_access,
|
||||
dst_stages,
|
||||
dst_access,
|
||||
buffer,
|
||||
ref range,
|
||||
_ne: _,
|
||||
} = barrier;
|
||||
|
||||
let buffer = unsafe { self.accesses.buffer_unchecked(buffer) };
|
||||
|
||||
vk::BufferMemoryBarrier2::default()
|
||||
.src_stage_mask(src_stages.into())
|
||||
.src_access_mask(src_access.into())
|
||||
.dst_stage_mask(dst_stages.into())
|
||||
.dst_access_mask(dst_access.into())
|
||||
.src_queue_family_index(vk::QUEUE_FAMILY_IGNORED)
|
||||
.dst_queue_family_index(vk::QUEUE_FAMILY_IGNORED)
|
||||
.buffer(buffer.handle())
|
||||
.offset(range.start)
|
||||
.size(range.end - range.start)
|
||||
})
|
||||
.collect();
|
||||
|
||||
let image_memory_barriers_vk: SmallVec<[_; 8]> = image_memory_barriers
|
||||
.iter()
|
||||
.map(|barrier| {
|
||||
let &ImageMemoryBarrier {
|
||||
src_stages,
|
||||
src_access,
|
||||
dst_stages,
|
||||
dst_access,
|
||||
old_layout,
|
||||
new_layout,
|
||||
image,
|
||||
ref subresource_range,
|
||||
_ne: _,
|
||||
} = barrier;
|
||||
|
||||
let image = unsafe { self.accesses.image_unchecked(image) };
|
||||
|
||||
vk::ImageMemoryBarrier2::default()
|
||||
.src_stage_mask(src_stages.into())
|
||||
.src_access_mask(src_access.into())
|
||||
.dst_stage_mask(dst_stages.into())
|
||||
.dst_access_mask(dst_access.into())
|
||||
.old_layout(old_layout.into())
|
||||
.new_layout(new_layout.into())
|
||||
.src_queue_family_index(vk::QUEUE_FAMILY_IGNORED)
|
||||
.dst_queue_family_index(vk::QUEUE_FAMILY_IGNORED)
|
||||
.image(image.handle())
|
||||
.subresource_range(subresource_range.clone().into())
|
||||
})
|
||||
.collect();
|
||||
|
||||
let dependency_info_vk = vk::DependencyInfo::default()
|
||||
.dependency_flags(dependency_flags.into())
|
||||
.memory_barriers(&memory_barriers_vk)
|
||||
.buffer_memory_barriers(&buffer_memory_barriers_vk)
|
||||
.image_memory_barriers(&image_memory_barriers_vk);
|
||||
|
||||
let fns = self.device().fns();
|
||||
let cmd_pipeline_barrier2 = if self.device().api_version() >= Version::V1_3 {
|
||||
fns.v1_3.cmd_pipeline_barrier2
|
||||
} else {
|
||||
fns.khr_synchronization2.cmd_pipeline_barrier2_khr
|
||||
};
|
||||
|
||||
unsafe { cmd_pipeline_barrier2(self.handle(), &dependency_info_vk) };
|
||||
} else {
|
||||
let mut src_stage_mask = vk::PipelineStageFlags::empty();
|
||||
let mut dst_stage_mask = vk::PipelineStageFlags::empty();
|
||||
|
||||
let memory_barriers_vk: SmallVec<[_; 2]> = memory_barriers
|
||||
.iter()
|
||||
.map(|barrier| {
|
||||
let &MemoryBarrier {
|
||||
src_stages,
|
||||
src_access,
|
||||
dst_stages,
|
||||
dst_access,
|
||||
_ne: _,
|
||||
} = barrier;
|
||||
|
||||
src_stage_mask |= src_stages.into();
|
||||
dst_stage_mask |= dst_stages.into();
|
||||
|
||||
vk::MemoryBarrier::default()
|
||||
.src_access_mask(src_access.into())
|
||||
.dst_access_mask(dst_access.into())
|
||||
})
|
||||
.collect();
|
||||
|
||||
let buffer_memory_barriers_vk: SmallVec<[_; 8]> = buffer_memory_barriers
|
||||
.iter()
|
||||
.map(|barrier| {
|
||||
let &BufferMemoryBarrier {
|
||||
src_stages,
|
||||
src_access,
|
||||
dst_stages,
|
||||
dst_access,
|
||||
buffer,
|
||||
ref range,
|
||||
_ne: _,
|
||||
} = barrier;
|
||||
|
||||
src_stage_mask |= src_stages.into();
|
||||
dst_stage_mask |= dst_stages.into();
|
||||
|
||||
let buffer = unsafe { self.accesses.buffer_unchecked(buffer) };
|
||||
|
||||
vk::BufferMemoryBarrier::default()
|
||||
.src_access_mask(src_access.into())
|
||||
.dst_access_mask(dst_access.into())
|
||||
.src_queue_family_index(vk::QUEUE_FAMILY_IGNORED)
|
||||
.dst_queue_family_index(vk::QUEUE_FAMILY_IGNORED)
|
||||
.buffer(buffer.handle())
|
||||
.offset(range.start)
|
||||
.size(range.end - range.start)
|
||||
})
|
||||
.collect();
|
||||
|
||||
let image_memory_barriers_vk: SmallVec<[_; 8]> = image_memory_barriers
|
||||
.iter()
|
||||
.map(|barrier| {
|
||||
let &ImageMemoryBarrier {
|
||||
src_stages,
|
||||
src_access,
|
||||
dst_stages,
|
||||
dst_access,
|
||||
old_layout,
|
||||
new_layout,
|
||||
image,
|
||||
ref subresource_range,
|
||||
_ne: _,
|
||||
} = barrier;
|
||||
|
||||
src_stage_mask |= src_stages.into();
|
||||
dst_stage_mask |= dst_stages.into();
|
||||
|
||||
let image = unsafe { self.accesses.image_unchecked(image) };
|
||||
|
||||
vk::ImageMemoryBarrier::default()
|
||||
.src_access_mask(src_access.into())
|
||||
.dst_access_mask(dst_access.into())
|
||||
.old_layout(old_layout.into())
|
||||
.new_layout(new_layout.into())
|
||||
.src_queue_family_index(vk::QUEUE_FAMILY_IGNORED)
|
||||
.dst_queue_family_index(vk::QUEUE_FAMILY_IGNORED)
|
||||
.image(image.handle())
|
||||
.subresource_range(subresource_range.clone().into())
|
||||
})
|
||||
.collect();
|
||||
|
||||
if src_stage_mask.is_empty() {
|
||||
// "VK_PIPELINE_STAGE_2_TOP_OF_PIPE_BIT is [...] equivalent to
|
||||
// VK_PIPELINE_STAGE_2_NONE in the first scope."
|
||||
src_stage_mask |= vk::PipelineStageFlags::TOP_OF_PIPE;
|
||||
}
|
||||
|
||||
if dst_stage_mask.is_empty() {
|
||||
// "VK_PIPELINE_STAGE_2_BOTTOM_OF_PIPE_BIT is [...] equivalent to
|
||||
// VK_PIPELINE_STAGE_2_NONE in the second scope."
|
||||
dst_stage_mask |= vk::PipelineStageFlags::BOTTOM_OF_PIPE;
|
||||
}
|
||||
|
||||
let fns = self.device().fns();
|
||||
unsafe {
|
||||
(fns.v1_0.cmd_pipeline_barrier)(
|
||||
self.handle(),
|
||||
src_stage_mask,
|
||||
dst_stage_mask,
|
||||
dependency_flags.into(),
|
||||
memory_barriers_vk.len() as u32,
|
||||
memory_barriers_vk.as_ptr(),
|
||||
buffer_memory_barriers_vk.len() as u32,
|
||||
buffer_memory_barriers_vk.as_ptr(),
|
||||
image_memory_barriers_vk.len() as u32,
|
||||
image_memory_barriers_vk.as_ptr(),
|
||||
)
|
||||
};
|
||||
}
|
||||
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
/// Dependency info for barriers in a pipeline barrier command.
|
||||
///
|
||||
/// A pipeline barrier creates a dependency between commands submitted before the barrier (the
|
||||
/// source scope) and commands submitted after it (the destination scope). Each `DependencyInfo`
|
||||
/// consists of multiple individual barriers that concern either a single resource or operate
|
||||
/// globally.
|
||||
///
|
||||
/// Each barrier has a set of source/destination pipeline stages and source/destination memory
|
||||
/// access types. The pipeline stages create an *execution dependency*: the `src_stages` of
|
||||
/// commands submitted before the barrier must be completely finished before any of the
|
||||
/// `dst_stages` of commands after the barrier are allowed to start. The memory access types create
|
||||
/// a *memory dependency*: in addition to the execution dependency, any `src_access`
|
||||
/// performed before the barrier must be made available and visible before any `dst_access`
|
||||
/// are made after the barrier.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct DependencyInfo<'a> {
|
||||
/// Flags to modify how the execution and memory dependencies are formed.
|
||||
///
|
||||
/// The default value is empty.
|
||||
pub dependency_flags: DependencyFlags,
|
||||
|
||||
/// Memory barriers for global operations and accesses, not limited to a single resource.
|
||||
///
|
||||
/// The default value is empty.
|
||||
pub memory_barriers: &'a [MemoryBarrier<'a>],
|
||||
|
||||
/// Memory barriers for individual buffers.
|
||||
///
|
||||
/// The default value is empty.
|
||||
pub buffer_memory_barriers: &'a [BufferMemoryBarrier<'a>],
|
||||
|
||||
/// Memory barriers for individual images.
|
||||
///
|
||||
/// The default value is empty.
|
||||
pub image_memory_barriers: &'a [ImageMemoryBarrier<'a>],
|
||||
|
||||
pub _ne: crate::NonExhaustive<'a>,
|
||||
}
|
||||
|
||||
impl DependencyInfo<'_> {
|
||||
/// Returns `true` if `self` doesn't contain any barriers.
|
||||
#[inline]
|
||||
pub fn is_empty(&self) -> bool {
|
||||
self.memory_barriers.is_empty()
|
||||
&& self.buffer_memory_barriers.is_empty()
|
||||
&& self.image_memory_barriers.is_empty()
|
||||
}
|
||||
}
|
||||
|
||||
impl Default for DependencyInfo<'_> {
|
||||
#[inline]
|
||||
fn default() -> Self {
|
||||
DependencyInfo {
|
||||
dependency_flags: DependencyFlags::default(),
|
||||
memory_barriers: &[],
|
||||
buffer_memory_barriers: &[],
|
||||
image_memory_barriers: &[],
|
||||
_ne: crate::NE,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A memory barrier that is applied globally.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct MemoryBarrier<'a> {
|
||||
/// The pipeline stages in the source scope to wait for.
|
||||
///
|
||||
/// The default value is [`PipelineStages::empty()`].
|
||||
pub src_stages: PipelineStages,
|
||||
|
||||
/// The memory accesses in the source scope to make available and visible.
|
||||
///
|
||||
/// The default value is [`AccessFlags::empty()`].
|
||||
pub src_access: AccessFlags,
|
||||
|
||||
/// The pipeline stages in the destination scope that must wait for `src_stages`.
|
||||
///
|
||||
/// The default value is [`PipelineStages::empty()`].
|
||||
pub dst_stages: PipelineStages,
|
||||
|
||||
/// The memory accesses in the destination scope that must wait for `src_access` to be made
|
||||
/// available and visible.
|
||||
///
|
||||
/// The default value is [`AccessFlags::empty()`].
|
||||
pub dst_access: AccessFlags,
|
||||
|
||||
pub _ne: crate::NonExhaustive<'a>,
|
||||
}
|
||||
|
||||
impl Default for MemoryBarrier<'_> {
|
||||
#[inline]
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
src_stages: PipelineStages::empty(),
|
||||
src_access: AccessFlags::empty(),
|
||||
dst_stages: PipelineStages::empty(),
|
||||
dst_access: AccessFlags::empty(),
|
||||
_ne: crate::NE,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A memory barrier that is applied to a single buffer.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct BufferMemoryBarrier<'a> {
|
||||
/// The pipeline stages in the source scope to wait for.
|
||||
///
|
||||
/// The default value is [`PipelineStages::empty()`].
|
||||
pub src_stages: PipelineStages,
|
||||
|
||||
/// The memory accesses in the source scope to make available and visible.
|
||||
///
|
||||
/// The default value is [`AccessFlags::empty()`].
|
||||
pub src_access: AccessFlags,
|
||||
|
||||
/// The pipeline stages in the destination scope that must wait for `src_stages`.
|
||||
///
|
||||
/// The default value is [`PipelineStages::empty()`].
|
||||
pub dst_stages: PipelineStages,
|
||||
|
||||
/// The memory accesses in the destination scope that must wait for `src_access` to be made
|
||||
/// available and visible.
|
||||
///
|
||||
/// The default value is [`AccessFlags::empty()`].
|
||||
pub dst_access: AccessFlags,
|
||||
|
||||
/// The buffer to apply the barrier to.
|
||||
///
|
||||
/// The default value is [`Id::INVALID`], which must be overridden.
|
||||
pub buffer: Id<Buffer>,
|
||||
|
||||
/// The byte range of `buffer` to apply the barrier to.
|
||||
///
|
||||
/// The default value is empty, which must be overridden.
|
||||
pub range: Range<DeviceSize>,
|
||||
|
||||
pub _ne: crate::NonExhaustive<'a>,
|
||||
}
|
||||
|
||||
impl Default for BufferMemoryBarrier<'_> {
|
||||
#[inline]
|
||||
fn default() -> Self {
|
||||
BufferMemoryBarrier {
|
||||
src_stages: PipelineStages::empty(),
|
||||
src_access: AccessFlags::empty(),
|
||||
dst_stages: PipelineStages::empty(),
|
||||
dst_access: AccessFlags::empty(),
|
||||
buffer: Id::INVALID,
|
||||
range: 0..0,
|
||||
_ne: crate::NE,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A memory barrier that is applied to a single image.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct ImageMemoryBarrier<'a> {
|
||||
/// The pipeline stages in the source scope to wait for.
|
||||
///
|
||||
/// The default value is [`PipelineStages::empty()`].
|
||||
pub src_stages: PipelineStages,
|
||||
|
||||
/// The memory accesses in the source scope to make available and visible.
|
||||
///
|
||||
/// The default value is [`AccessFlags::empty()`].
|
||||
pub src_access: AccessFlags,
|
||||
|
||||
/// The pipeline stages in the destination scope that must wait for `src_stages`.
|
||||
///
|
||||
/// The default value is [`PipelineStages::empty()`].
|
||||
pub dst_stages: PipelineStages,
|
||||
|
||||
/// The memory accesses in the destination scope that must wait for `src_access` to be made
|
||||
/// available and visible.
|
||||
///
|
||||
/// The default value is [`AccessFlags::empty()`].
|
||||
pub dst_access: AccessFlags,
|
||||
|
||||
/// The layout that the specified `subresource_range` of `image` is expected to be in when the
|
||||
/// source scope completes.
|
||||
///
|
||||
/// The default value is [`ImageLayout::Undefined`].
|
||||
pub old_layout: ImageLayout,
|
||||
|
||||
/// The layout that the specified `subresource_range` of `image` will be transitioned to before
|
||||
/// the destination scope begins.
|
||||
///
|
||||
/// The default value is [`ImageLayout::Undefined`].
|
||||
pub new_layout: ImageLayout,
|
||||
|
||||
/// The image to apply the barrier to.
|
||||
///
|
||||
/// The default value is [`Id::INVALID`], which must be overridden.
|
||||
pub image: Id<Image>,
|
||||
|
||||
/// The subresource range of `image` to apply the barrier to.
|
||||
///
|
||||
/// The default value is empty, which must be overridden.
|
||||
pub subresource_range: ImageSubresourceRange,
|
||||
|
||||
pub _ne: crate::NonExhaustive<'a>,
|
||||
}
|
||||
|
||||
impl Default for ImageMemoryBarrier<'_> {
|
||||
#[inline]
|
||||
fn default() -> Self {
|
||||
ImageMemoryBarrier {
|
||||
src_stages: PipelineStages::empty(),
|
||||
src_access: AccessFlags::empty(),
|
||||
dst_stages: PipelineStages::empty(),
|
||||
dst_access: AccessFlags::empty(),
|
||||
old_layout: ImageLayout::Undefined,
|
||||
new_layout: ImageLayout::Undefined,
|
||||
image: Id::INVALID,
|
||||
subresource_range: ImageSubresourceRange {
|
||||
aspects: ImageAspects::empty(),
|
||||
mip_levels: 0..0,
|
||||
array_layers: 0..0,
|
||||
},
|
||||
_ne: crate::NE,
|
||||
}
|
||||
}
|
||||
}
|
110
vulkano-taskgraph/src/command_buffer/mod.rs
Normal file
110
vulkano-taskgraph/src/command_buffer/mod.rs
Normal file
@ -0,0 +1,110 @@
|
||||
//! Recording commands to execute on the device.
|
||||
|
||||
#[allow(unused_imports)] // everything is exported for future-proofing
|
||||
pub use self::commands::{clear::*, copy::*, dynamic_state::*, pipeline::*, sync::*};
|
||||
use crate::{graph::ResourceMap, resource::DeathRow, Id};
|
||||
use ash::vk;
|
||||
use std::sync::Arc;
|
||||
use vulkano::{
|
||||
buffer::Buffer,
|
||||
command_buffer::sys::RawRecordingCommandBuffer,
|
||||
device::{Device, DeviceOwned},
|
||||
image::Image,
|
||||
VulkanObject,
|
||||
};
|
||||
|
||||
mod commands;
|
||||
|
||||
/// A command buffer in the recording state.
|
||||
///
|
||||
/// Unlike [`RawRecordingCommandBuffer`], this type has knowledge of the current task context and
|
||||
/// can therefore validate resource accesses. (TODO)
|
||||
pub struct RecordingCommandBuffer<'a> {
|
||||
inner: &'a mut RawRecordingCommandBuffer,
|
||||
accesses: ResourceAccesses<'a>,
|
||||
death_row: &'a mut DeathRow,
|
||||
}
|
||||
|
||||
struct ResourceAccesses<'a> {
|
||||
resource_map: &'a ResourceMap<'a>,
|
||||
}
|
||||
|
||||
impl<'a> RecordingCommandBuffer<'a> {
|
||||
pub(crate) unsafe fn new(
|
||||
inner: &'a mut RawRecordingCommandBuffer,
|
||||
resource_map: &'a ResourceMap<'a>,
|
||||
death_row: &'a mut DeathRow,
|
||||
) -> Self {
|
||||
RecordingCommandBuffer {
|
||||
inner,
|
||||
accesses: ResourceAccesses { resource_map },
|
||||
death_row,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the underlying raw command buffer.
|
||||
///
|
||||
/// While this method is safe, using the command buffer isn't. You must guarantee that any
|
||||
/// subresources you use while recording commands are either accounted for in the [task's
|
||||
/// access set], or that those subresources don't require any synchronization (including layout
|
||||
/// transitions and queue family ownership transfers), or that no other task is accessing the
|
||||
/// subresources at the same time without appropriate synchronization.
|
||||
#[inline]
|
||||
pub fn as_raw(&mut self) -> &mut RawRecordingCommandBuffer {
|
||||
self.inner
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl DeviceOwned for RecordingCommandBuffer<'_> {
|
||||
#[inline]
|
||||
fn device(&self) -> &Arc<Device> {
|
||||
self.inner.device()
|
||||
}
|
||||
}
|
||||
|
||||
unsafe impl VulkanObject for RecordingCommandBuffer<'_> {
|
||||
type Handle = vk::CommandBuffer;
|
||||
|
||||
#[inline]
|
||||
fn handle(&self) -> Self::Handle {
|
||||
self.inner.handle()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> ResourceAccesses<'a> {
|
||||
unsafe fn buffer_unchecked(&self, id: Id<Buffer>) -> &'a Arc<Buffer> {
|
||||
if id.is_virtual() {
|
||||
// SAFETY:
|
||||
// * The caller of `Task::execute` must ensure that `self.resource_map` maps the virtual
|
||||
// IDs of the graph exhaustively.
|
||||
// * The caller must ensure that `id` is valid.
|
||||
unsafe { self.resource_map.buffer_unchecked(id) }.buffer()
|
||||
} else {
|
||||
let resources = self.resource_map.resources();
|
||||
|
||||
// SAFETY:
|
||||
// * `ResourceMap` owns an `epoch::Guard`.
|
||||
// * The caller must ensure that `id` is valid.
|
||||
unsafe { resources.buffer_unchecked_unprotected(id) }.buffer()
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn image_unchecked(&self, id: Id<Image>) -> &'a Arc<Image> {
|
||||
if id.is_virtual() {
|
||||
// SAFETY:
|
||||
// * The caller must ensure that `id` is valid.
|
||||
// * The caller of `Task::execute` must ensure that `self.resource_map` maps the virtual
|
||||
// IDs of the graph exhaustively.
|
||||
unsafe { self.resource_map.image_unchecked(id) }.image()
|
||||
} else {
|
||||
let resources = self.resource_map.resources();
|
||||
|
||||
// SAFETY:
|
||||
// * The caller must ensure that `id` is valid.
|
||||
// * `ResourceMap` owns an `epoch::Guard`.
|
||||
unsafe { resources.image_unchecked_unprotected(id) }.image()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type Result<T = (), E = Box<vulkano::ValidationError>> = ::std::result::Result<T, E>;
|
@ -47,9 +47,9 @@ impl<W: ?Sized> TaskGraph<W> {
|
||||
/// [directed cycles]: https://en.wikipedia.org/wiki/Cycle_(graph_theory)#Directed_circuit_and_directed_cycle
|
||||
pub unsafe fn compile(
|
||||
mut self,
|
||||
compile_info: CompileInfo,
|
||||
compile_info: &CompileInfo<'_>,
|
||||
) -> Result<ExecutableTaskGraph<W>, CompileError<W>> {
|
||||
let CompileInfo {
|
||||
let &CompileInfo {
|
||||
queues,
|
||||
present_queue,
|
||||
flight_id,
|
||||
@ -60,7 +60,7 @@ impl<W: ?Sized> TaskGraph<W> {
|
||||
|
||||
let device = &self.device().clone();
|
||||
|
||||
for queue in &queues {
|
||||
for queue in queues {
|
||||
assert_eq!(queue.device(), device);
|
||||
assert_eq!(
|
||||
queues
|
||||
@ -86,14 +86,14 @@ impl<W: ?Sized> TaskGraph<W> {
|
||||
};
|
||||
unsafe { self.dependency_levels(&topological_order) };
|
||||
let queue_family_indices =
|
||||
match unsafe { self.queue_family_indices(device, &queues, &topological_order) } {
|
||||
match unsafe { self.queue_family_indices(device, queues, &topological_order) } {
|
||||
Ok(queue_family_indices) => queue_family_indices,
|
||||
Err(kind) => return Err(CompileError::new(self, kind)),
|
||||
};
|
||||
let mut queues_by_queue_family_index: SmallVec<[_; 8]> =
|
||||
smallvec![None; *queue_family_indices.iter().max().unwrap() as usize + 1];
|
||||
|
||||
for queue in &queues {
|
||||
for &queue in queues {
|
||||
if let Some(x) =
|
||||
queues_by_queue_family_index.get_mut(queue.queue_family_index() as usize)
|
||||
{
|
||||
@ -262,7 +262,7 @@ impl<W: ?Sized> TaskGraph<W> {
|
||||
if should_submit {
|
||||
let queue = queues_by_queue_family_index[task_node.queue_family_index as usize]
|
||||
.unwrap();
|
||||
state.submit(queue.clone());
|
||||
state.submit(queue);
|
||||
prev_submission_end = i + 1;
|
||||
break;
|
||||
}
|
||||
@ -278,7 +278,7 @@ impl<W: ?Sized> TaskGraph<W> {
|
||||
}
|
||||
|
||||
state.flush_submit();
|
||||
state.submit(state.present_queue.clone().unwrap());
|
||||
state.submit(state.present_queue.unwrap());
|
||||
}
|
||||
|
||||
let semaphores = match (0..semaphore_count)
|
||||
@ -304,7 +304,7 @@ impl<W: ?Sized> TaskGraph<W> {
|
||||
image_barriers: state.image_barriers,
|
||||
semaphores: RefCell::new(semaphores),
|
||||
swapchains,
|
||||
present_queue: state.present_queue,
|
||||
present_queue: state.present_queue.cloned(),
|
||||
last_accesses: prev_accesses,
|
||||
})
|
||||
}
|
||||
@ -447,7 +447,7 @@ impl<W: ?Sized> TaskGraph<W> {
|
||||
unsafe fn queue_family_indices(
|
||||
&mut self,
|
||||
device: &Device,
|
||||
queues: &[Arc<Queue>],
|
||||
queues: &[&Arc<Queue>],
|
||||
topological_order: &[NodeIndex],
|
||||
) -> Result<SmallVec<[u32; 3]>, CompileErrorKind> {
|
||||
let queue_family_properties = device.physical_device().queue_family_properties();
|
||||
@ -625,7 +625,7 @@ struct CompileState<'a> {
|
||||
submissions: Vec<Submission>,
|
||||
buffer_barriers: Vec<super::BufferMemoryBarrier>,
|
||||
image_barriers: Vec<super::ImageMemoryBarrier>,
|
||||
present_queue: Option<Arc<Queue>>,
|
||||
present_queue: Option<&'a Arc<Queue>>,
|
||||
initial_buffer_barrier_range: Range<BarrierIndex>,
|
||||
initial_image_barrier_range: Range<BarrierIndex>,
|
||||
has_flushed_submit: bool,
|
||||
@ -636,7 +636,7 @@ struct CompileState<'a> {
|
||||
}
|
||||
|
||||
impl<'a> CompileState<'a> {
|
||||
fn new(prev_accesses: &'a mut [ResourceAccess], present_queue: Option<Arc<Queue>>) -> Self {
|
||||
fn new(prev_accesses: &'a mut [ResourceAccess], present_queue: Option<&'a Arc<Queue>>) -> Self {
|
||||
CompileState {
|
||||
prev_accesses,
|
||||
instructions: Vec::new(),
|
||||
@ -932,7 +932,7 @@ impl<'a> CompileState<'a> {
|
||||
self.should_flush_submit = false;
|
||||
}
|
||||
|
||||
fn submit(&mut self, queue: Arc<Queue>) {
|
||||
fn submit(&mut self, queue: &Arc<Queue>) {
|
||||
self.instructions.push(Instruction::Submit);
|
||||
|
||||
let prev_instruction_range_end = self
|
||||
@ -941,7 +941,7 @@ impl<'a> CompileState<'a> {
|
||||
.map(|s| s.instruction_range.end)
|
||||
.unwrap_or(0);
|
||||
self.submissions.push(Submission {
|
||||
queue,
|
||||
queue: queue.clone(),
|
||||
initial_buffer_barrier_range: self.initial_buffer_barrier_range.clone(),
|
||||
initial_image_barrier_range: self.initial_image_barrier_range.clone(),
|
||||
instruction_range: prev_instruction_range_end..self.instructions.len(),
|
||||
@ -961,13 +961,13 @@ impl<W: ?Sized> ExecutableTaskGraph<W> {
|
||||
///
|
||||
/// [compile]: TaskGraph::compile
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct CompileInfo {
|
||||
pub struct CompileInfo<'a> {
|
||||
/// The queues to work with.
|
||||
///
|
||||
/// You must supply at least one queue and all queues must be from unique queue families.
|
||||
///
|
||||
/// The default value is empty, which must be overridden.
|
||||
pub queues: Vec<Arc<Queue>>,
|
||||
pub queues: &'a [&'a Arc<Queue>],
|
||||
|
||||
/// The queue to use for swapchain presentation, if any.
|
||||
///
|
||||
@ -977,21 +977,21 @@ pub struct CompileInfo {
|
||||
/// The default value is `None`.
|
||||
///
|
||||
/// [`queues`]: Self::queues
|
||||
pub present_queue: Option<Arc<Queue>>,
|
||||
pub present_queue: Option<&'a Arc<Queue>>,
|
||||
|
||||
/// The flight which will be executed.
|
||||
///
|
||||
/// The default value is `Id::INVALID`, which must be overridden.
|
||||
pub flight_id: Id<Flight>,
|
||||
|
||||
pub _ne: vulkano::NonExhaustive,
|
||||
pub _ne: crate::NonExhaustive<'a>,
|
||||
}
|
||||
|
||||
impl Default for CompileInfo {
|
||||
impl Default for CompileInfo<'_> {
|
||||
#[inline]
|
||||
fn default() -> Self {
|
||||
CompileInfo {
|
||||
queues: Vec::new(),
|
||||
queues: &[],
|
||||
present_queue: None,
|
||||
flight_id: Id::INVALID,
|
||||
_ne: crate::NE,
|
||||
@ -1112,7 +1112,7 @@ mod tests {
|
||||
fn unconnected() {
|
||||
let (resources, queues) = test_queues!();
|
||||
let compile_info = CompileInfo {
|
||||
queues,
|
||||
queues: &queues.iter().collect::<Vec<_>>(),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
@ -1124,7 +1124,7 @@ mod tests {
|
||||
// ┌───┐
|
||||
// │ B │
|
||||
// └───┘
|
||||
let mut graph = TaskGraph::<()>::new(resources.clone(), 10, 0);
|
||||
let mut graph = TaskGraph::<()>::new(&resources, 10, 0);
|
||||
graph
|
||||
.create_task_node("A", QueueFamilyType::Graphics, PhantomData)
|
||||
.build();
|
||||
@ -1133,7 +1133,7 @@ mod tests {
|
||||
.build();
|
||||
|
||||
assert!(matches!(
|
||||
unsafe { graph.compile(compile_info.clone()) },
|
||||
unsafe { graph.compile(&compile_info) },
|
||||
Err(CompileError {
|
||||
kind: CompileErrorKind::Unconnected,
|
||||
..
|
||||
@ -1155,7 +1155,7 @@ mod tests {
|
||||
// │ ┌───┐
|
||||
// └─►│ D │
|
||||
// └───┘
|
||||
let mut graph = TaskGraph::<()>::new(resources.clone(), 10, 0);
|
||||
let mut graph = TaskGraph::<()>::new(&resources, 10, 0);
|
||||
let a = graph
|
||||
.create_task_node("A", QueueFamilyType::Graphics, PhantomData)
|
||||
.build();
|
||||
@ -1172,7 +1172,7 @@ mod tests {
|
||||
graph.add_edge(b, d).unwrap();
|
||||
|
||||
assert!(matches!(
|
||||
unsafe { graph.compile(compile_info.clone()) },
|
||||
unsafe { graph.compile(&compile_info) },
|
||||
Err(CompileError {
|
||||
kind: CompileErrorKind::Unconnected,
|
||||
..
|
||||
@ -1190,7 +1190,7 @@ mod tests {
|
||||
// └───┘│ └───┘│ └───┘┌─►│ G │
|
||||
// │ └──────┘┌►│ │
|
||||
// └──────────────┘ └───┘
|
||||
let mut graph = TaskGraph::<()>::new(resources.clone(), 10, 0);
|
||||
let mut graph = TaskGraph::<()>::new(&resources, 10, 0);
|
||||
let a = graph
|
||||
.create_task_node("A", QueueFamilyType::Graphics, PhantomData)
|
||||
.build();
|
||||
@ -1221,7 +1221,7 @@ mod tests {
|
||||
graph.add_edge(f, g).unwrap();
|
||||
|
||||
assert!(matches!(
|
||||
unsafe { graph.compile(compile_info) },
|
||||
unsafe { graph.compile(&compile_info) },
|
||||
Err(CompileError {
|
||||
kind: CompileErrorKind::Unconnected,
|
||||
..
|
||||
@ -1234,7 +1234,7 @@ mod tests {
|
||||
fn cycle() {
|
||||
let (resources, queues) = test_queues!();
|
||||
let compile_info = CompileInfo {
|
||||
queues,
|
||||
queues: &queues.iter().collect::<Vec<_>>(),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
@ -1243,7 +1243,7 @@ mod tests {
|
||||
// ┌►│ A ├─►│ B ├─►│ C ├┐
|
||||
// │ └───┘ └───┘ └───┘│
|
||||
// └────────────────────┘
|
||||
let mut graph = TaskGraph::<()>::new(resources.clone(), 10, 0);
|
||||
let mut graph = TaskGraph::<()>::new(&resources, 10, 0);
|
||||
let a = graph
|
||||
.create_task_node("A", QueueFamilyType::Graphics, PhantomData)
|
||||
.build();
|
||||
@ -1258,7 +1258,7 @@ mod tests {
|
||||
graph.add_edge(c, a).unwrap();
|
||||
|
||||
assert!(matches!(
|
||||
unsafe { graph.compile(compile_info.clone()) },
|
||||
unsafe { graph.compile(&compile_info) },
|
||||
Err(CompileError {
|
||||
kind: CompileErrorKind::Cycle,
|
||||
..
|
||||
@ -1275,7 +1275,7 @@ mod tests {
|
||||
// │ └►│ D ├─►│ E ├┴►│ F ├┐
|
||||
// │ └───┘ └───┘ └───┘│
|
||||
// └───────────────────────────┘
|
||||
let mut graph = TaskGraph::<()>::new(resources.clone(), 10, 0);
|
||||
let mut graph = TaskGraph::<()>::new(&resources, 10, 0);
|
||||
let a = graph
|
||||
.create_task_node("A", QueueFamilyType::Graphics, PhantomData)
|
||||
.build();
|
||||
@ -1303,7 +1303,7 @@ mod tests {
|
||||
graph.add_edge(f, a).unwrap();
|
||||
|
||||
assert!(matches!(
|
||||
unsafe { graph.compile(compile_info.clone()) },
|
||||
unsafe { graph.compile(&compile_info) },
|
||||
Err(CompileError {
|
||||
kind: CompileErrorKind::Cycle,
|
||||
..
|
||||
@ -1322,7 +1322,7 @@ mod tests {
|
||||
// │ ┌►└───┘ └───┘ └───┘││
|
||||
// │ └────────────────────┘│
|
||||
// └───────────────────────────┘
|
||||
let mut graph = TaskGraph::<()>::new(resources.clone(), 10, 0);
|
||||
let mut graph = TaskGraph::<()>::new(&resources, 10, 0);
|
||||
let a = graph
|
||||
.create_task_node("A", QueueFamilyType::Graphics, PhantomData)
|
||||
.build();
|
||||
@ -1351,7 +1351,7 @@ mod tests {
|
||||
graph.add_edge(f, b).unwrap();
|
||||
|
||||
assert!(matches!(
|
||||
unsafe { graph.compile(compile_info) },
|
||||
unsafe { graph.compile(&compile_info) },
|
||||
Err(CompileError {
|
||||
kind: CompileErrorKind::Cycle,
|
||||
..
|
||||
@ -1364,12 +1364,12 @@ mod tests {
|
||||
fn initial_pipeline_barrier() {
|
||||
let (resources, queues) = test_queues!();
|
||||
let compile_info = CompileInfo {
|
||||
queues,
|
||||
queues: &queues.iter().collect::<Vec<_>>(),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
{
|
||||
let mut graph = TaskGraph::<()>::new(resources.clone(), 10, 10);
|
||||
let mut graph = TaskGraph::<()>::new(&resources, 10, 10);
|
||||
let buffer = graph.add_buffer(&BufferCreateInfo::default());
|
||||
let image = graph.add_image(&ImageCreateInfo::default());
|
||||
let node = graph
|
||||
@ -1382,7 +1382,7 @@ mod tests {
|
||||
)
|
||||
.build();
|
||||
|
||||
let graph = unsafe { graph.compile(compile_info.clone()) }.unwrap();
|
||||
let graph = unsafe { graph.compile(&compile_info) }.unwrap();
|
||||
|
||||
assert_matches_instructions!(
|
||||
graph,
|
||||
@ -1429,7 +1429,7 @@ mod tests {
|
||||
}
|
||||
|
||||
let compile_info = CompileInfo {
|
||||
queues,
|
||||
queues: &queues.iter().collect::<Vec<_>>(),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
@ -1444,7 +1444,7 @@ mod tests {
|
||||
// │└►┌───┐
|
||||
// └─►│ C │
|
||||
// └───┘
|
||||
let mut graph = TaskGraph::<()>::new(resources.clone(), 10, 10);
|
||||
let mut graph = TaskGraph::<()>::new(&resources, 10, 10);
|
||||
let a = graph
|
||||
.create_task_node("A", QueueFamilyType::Graphics, PhantomData)
|
||||
.build();
|
||||
@ -1457,7 +1457,7 @@ mod tests {
|
||||
graph.add_edge(a, c).unwrap();
|
||||
graph.add_edge(b, c).unwrap();
|
||||
|
||||
let graph = unsafe { graph.compile(compile_info.clone()) }.unwrap();
|
||||
let graph = unsafe { graph.compile(&compile_info) }.unwrap();
|
||||
|
||||
assert_matches_instructions!(
|
||||
graph,
|
||||
@ -1500,7 +1500,7 @@ mod tests {
|
||||
// │ ┌───┐
|
||||
// └►│ C │
|
||||
// └───┘
|
||||
let mut graph = TaskGraph::<()>::new(resources.clone(), 10, 10);
|
||||
let mut graph = TaskGraph::<()>::new(&resources, 10, 10);
|
||||
let a = graph
|
||||
.create_task_node("A", QueueFamilyType::Graphics, PhantomData)
|
||||
.build();
|
||||
@ -1513,7 +1513,7 @@ mod tests {
|
||||
graph.add_edge(a, b).unwrap();
|
||||
graph.add_edge(a, c).unwrap();
|
||||
|
||||
let graph = unsafe { graph.compile(compile_info.clone()) }.unwrap();
|
||||
let graph = unsafe { graph.compile(&compile_info) }.unwrap();
|
||||
|
||||
assert_matches_instructions!(
|
||||
graph,
|
||||
@ -1556,7 +1556,7 @@ mod tests {
|
||||
// │ ┌───┐└►┌───┐│
|
||||
// └►│ C ├─►│ D ├┘
|
||||
// └───┘ └───┘
|
||||
let mut graph = TaskGraph::<()>::new(resources.clone(), 10, 10);
|
||||
let mut graph = TaskGraph::<()>::new(&resources, 10, 10);
|
||||
let a = graph
|
||||
.create_task_node("A", QueueFamilyType::Graphics, PhantomData)
|
||||
.build();
|
||||
@ -1578,7 +1578,7 @@ mod tests {
|
||||
graph.add_edge(c, d).unwrap();
|
||||
graph.add_edge(d, e).unwrap();
|
||||
|
||||
let graph = unsafe { graph.compile(compile_info.clone()) }.unwrap();
|
||||
let graph = unsafe { graph.compile(&compile_info) }.unwrap();
|
||||
|
||||
// TODO: This could be brought down to 3 submissions with task reordering.
|
||||
assert_matches_instructions!(
|
||||
@ -1645,12 +1645,12 @@ mod tests {
|
||||
}
|
||||
|
||||
let compile_info = CompileInfo {
|
||||
queues,
|
||||
queues: &queues.iter().collect::<Vec<_>>(),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
{
|
||||
let mut graph = TaskGraph::<()>::new(resources.clone(), 10, 10);
|
||||
let mut graph = TaskGraph::<()>::new(&resources, 10, 10);
|
||||
let buffer1 = graph.add_buffer(&BufferCreateInfo::default());
|
||||
let buffer2 = graph.add_buffer(&BufferCreateInfo::default());
|
||||
let image1 = graph.add_image(&ImageCreateInfo::default());
|
||||
@ -1687,7 +1687,7 @@ mod tests {
|
||||
.build();
|
||||
graph.add_edge(compute_node, graphics_node).unwrap();
|
||||
|
||||
let graph = unsafe { graph.compile(compile_info.clone()) }.unwrap();
|
||||
let graph = unsafe { graph.compile(&compile_info) }.unwrap();
|
||||
|
||||
assert_matches_instructions!(
|
||||
graph,
|
||||
@ -1789,7 +1789,7 @@ mod tests {
|
||||
}
|
||||
|
||||
{
|
||||
let mut graph = TaskGraph::<()>::new(resources.clone(), 10, 10);
|
||||
let mut graph = TaskGraph::<()>::new(&resources, 10, 10);
|
||||
let sharing = Sharing::Concurrent(
|
||||
compile_info
|
||||
.queues
|
||||
@ -1845,7 +1845,7 @@ mod tests {
|
||||
.build();
|
||||
graph.add_edge(compute_node, graphics_node).unwrap();
|
||||
|
||||
let graph = unsafe { graph.compile(compile_info.clone()) }.unwrap();
|
||||
let graph = unsafe { graph.compile(&compile_info) }.unwrap();
|
||||
|
||||
assert_matches_instructions!(
|
||||
graph,
|
||||
@ -1900,13 +1900,13 @@ mod tests {
|
||||
queue_flags.contains(QueueFlags::GRAPHICS)
|
||||
});
|
||||
let compile_info = CompileInfo {
|
||||
queues: queues.clone(),
|
||||
present_queue: Some(present_queue.unwrap().clone()),
|
||||
queues: &queues.iter().collect::<Vec<_>>(),
|
||||
present_queue: Some(present_queue.unwrap()),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
{
|
||||
let mut graph = TaskGraph::<()>::new(resources.clone(), 10, 10);
|
||||
let mut graph = TaskGraph::<()>::new(&resources, 10, 10);
|
||||
let swapchain1 = graph.add_swapchain(&SwapchainCreateInfo::default());
|
||||
let swapchain2 = graph.add_swapchain(&SwapchainCreateInfo::default());
|
||||
let node = graph
|
||||
@ -1923,7 +1923,7 @@ mod tests {
|
||||
)
|
||||
.build();
|
||||
|
||||
let graph = unsafe { graph.compile(compile_info.clone()) }.unwrap();
|
||||
let graph = unsafe { graph.compile(&compile_info) }.unwrap();
|
||||
|
||||
assert_matches_instructions!(
|
||||
graph,
|
||||
@ -2006,13 +2006,13 @@ mod tests {
|
||||
}
|
||||
|
||||
let compile_info = CompileInfo {
|
||||
queues: queues.clone(),
|
||||
present_queue: Some(present_queue.unwrap().clone()),
|
||||
queues: &queues.iter().collect::<Vec<_>>(),
|
||||
present_queue: Some(present_queue.unwrap()),
|
||||
..Default::default()
|
||||
};
|
||||
|
||||
{
|
||||
let mut graph = TaskGraph::<()>::new(resources.clone(), 10, 10);
|
||||
let mut graph = TaskGraph::<()>::new(&resources, 10, 10);
|
||||
let concurrent_sharing = Sharing::Concurrent(
|
||||
compile_info
|
||||
.queues
|
||||
@ -2054,7 +2054,7 @@ mod tests {
|
||||
)
|
||||
.build();
|
||||
|
||||
let graph = unsafe { graph.compile(compile_info.clone()) }.unwrap();
|
||||
let graph = unsafe { graph.compile(&compile_info) }.unwrap();
|
||||
|
||||
assert_matches_instructions!(
|
||||
graph,
|
||||
|
@ -2,6 +2,7 @@ use super::{
|
||||
BarrierIndex, ExecutableTaskGraph, Instruction, NodeIndex, ResourceAccess, SemaphoreIndex,
|
||||
};
|
||||
use crate::{
|
||||
command_buffer::RecordingCommandBuffer,
|
||||
resource::{
|
||||
BufferAccess, BufferState, DeathRow, ImageAccess, ImageState, Resources, SwapchainState,
|
||||
},
|
||||
@ -11,7 +12,6 @@ use ash::vk;
|
||||
use concurrent_slotmap::epoch;
|
||||
use smallvec::SmallVec;
|
||||
use std::{
|
||||
cell::Cell,
|
||||
error::Error,
|
||||
fmt, mem,
|
||||
ops::Range,
|
||||
@ -48,7 +48,7 @@ impl<W: ?Sized + 'static> ExecutableTaskGraph<W> {
|
||||
/// - Panics if `resource_map` maps to any swapchain that isn't owned by the flight.
|
||||
/// - Panics if the oldest frame of the flight wasn't [waited] on.
|
||||
///
|
||||
/// [waited]: Flight::wait
|
||||
/// [waited]: crate::resource::Flight::wait
|
||||
pub unsafe fn execute(
|
||||
&self,
|
||||
resource_map: ResourceMap<'_>,
|
||||
@ -280,7 +280,7 @@ impl<W: ?Sized + 'static> ExecutableTaskGraph<W> {
|
||||
|
||||
for instruction in self.instructions.iter().cloned() {
|
||||
if execute_initial_barriers {
|
||||
let submission = state.current_submission();
|
||||
let submission = current_submission!(state);
|
||||
state.initial_pipeline_barrier(
|
||||
submission.initial_buffer_barrier_range.clone(),
|
||||
submission.initial_image_barrier_range.clone(),
|
||||
@ -308,7 +308,7 @@ impl<W: ?Sized + 'static> ExecutableTaskGraph<W> {
|
||||
buffer_barrier_range,
|
||||
image_barrier_range,
|
||||
} => {
|
||||
state.pipeline_barrier(buffer_barrier_range, image_barrier_range);
|
||||
state.pipeline_barrier(buffer_barrier_range, image_barrier_range)?;
|
||||
}
|
||||
Instruction::SignalSemaphore {
|
||||
semaphore_index,
|
||||
@ -369,7 +369,7 @@ impl<W: ?Sized + 'static> ExecutableTaskGraph<W> {
|
||||
|
||||
for instruction in self.instructions.iter().cloned() {
|
||||
if execute_initial_barriers {
|
||||
let submission = state.current_submission();
|
||||
let submission = current_submission!(state);
|
||||
state.initial_pipeline_barrier(
|
||||
submission.initial_buffer_barrier_range.clone(),
|
||||
submission.initial_image_barrier_range.clone(),
|
||||
@ -397,7 +397,7 @@ impl<W: ?Sized + 'static> ExecutableTaskGraph<W> {
|
||||
buffer_barrier_range,
|
||||
image_barrier_range,
|
||||
} => {
|
||||
state.pipeline_barrier(buffer_barrier_range, image_barrier_range);
|
||||
state.pipeline_barrier(buffer_barrier_range, image_barrier_range)?;
|
||||
}
|
||||
Instruction::SignalSemaphore {
|
||||
semaphore_index,
|
||||
@ -653,9 +653,6 @@ impl<'a, W: ?Sized + 'static> ExecuteState2<'a, W> {
|
||||
queue_submit2 = fns.khr_synchronization2.queue_submit2_khr;
|
||||
}
|
||||
|
||||
let current_command_buffer =
|
||||
create_command_buffer(resource_map, &executable.submissions[0].queue)?;
|
||||
|
||||
Ok(ExecuteState2 {
|
||||
executable,
|
||||
resource_map,
|
||||
@ -668,17 +665,13 @@ impl<'a, W: ?Sized + 'static> ExecuteState2<'a, W> {
|
||||
queue_submit2,
|
||||
per_submits: SmallVec::new(),
|
||||
current_per_submit: PerSubmitInfo2::default(),
|
||||
current_command_buffer: Some(current_command_buffer),
|
||||
current_command_buffer: None,
|
||||
command_buffers: Vec::new(),
|
||||
current_buffer_barriers: Vec::new(),
|
||||
current_image_barriers: Vec::new(),
|
||||
})
|
||||
}
|
||||
|
||||
fn current_submission(&self) -> &super::Submission {
|
||||
&self.executable.submissions[*self.submission_count]
|
||||
}
|
||||
|
||||
fn initial_pipeline_barrier(
|
||||
&mut self,
|
||||
buffer_barrier_range: Range<BarrierIndex>,
|
||||
@ -690,7 +683,7 @@ impl<'a, W: ?Sized + 'static> ExecuteState2<'a, W> {
|
||||
|
||||
fn convert_initial_buffer_barriers(&mut self, barrier_range: Range<BarrierIndex>) {
|
||||
let barrier_range = barrier_range.start as usize..barrier_range.end as usize;
|
||||
let queue_family_index = self.current_submission().queue.queue_family_index();
|
||||
let queue_family_index = current_submission!(self).queue.queue_family_index();
|
||||
|
||||
for barrier in &self.executable.buffer_barriers[barrier_range] {
|
||||
let state = unsafe { self.resource_map.buffer_unchecked(barrier.buffer) };
|
||||
@ -732,7 +725,7 @@ impl<'a, W: ?Sized + 'static> ExecuteState2<'a, W> {
|
||||
|
||||
fn convert_initial_image_barriers(&mut self, barrier_range: Range<BarrierIndex>) {
|
||||
let barrier_range = barrier_range.start as usize..barrier_range.end as usize;
|
||||
let queue_family_index = self.current_submission().queue.queue_family_index();
|
||||
let queue_family_index = current_submission!(self).queue.queue_family_index();
|
||||
|
||||
for barrier in &self.executable.image_barriers[barrier_range] {
|
||||
let (image, access) = match barrier.image.object_type() {
|
||||
@ -810,20 +803,25 @@ impl<'a, W: ?Sized + 'static> ExecuteState2<'a, W> {
|
||||
|
||||
fn execute_task(&mut self, node_index: NodeIndex) -> Result {
|
||||
if !self.current_buffer_barriers.is_empty() || !self.current_image_barriers.is_empty() {
|
||||
self.flush_barriers();
|
||||
self.flush_barriers()?;
|
||||
}
|
||||
|
||||
let task_node = unsafe { self.executable.graph.nodes.task_node_unchecked(node_index) };
|
||||
let task = &task_node.task;
|
||||
let current_command_buffer = self.current_command_buffer.as_mut().unwrap();
|
||||
let mut current_command_buffer = unsafe {
|
||||
RecordingCommandBuffer::new(
|
||||
current_command_buffer!(self),
|
||||
self.resource_map,
|
||||
self.death_row,
|
||||
)
|
||||
};
|
||||
let mut context = TaskContext {
|
||||
resource_map: self.resource_map,
|
||||
death_row: Cell::new(Some(self.death_row)),
|
||||
current_frame_index: self.current_frame_index,
|
||||
command_buffers: Cell::new(Some(&mut self.command_buffers)),
|
||||
command_buffers: &mut self.command_buffers,
|
||||
};
|
||||
|
||||
unsafe { task.execute(current_command_buffer, &mut context, self.world) }
|
||||
unsafe { task.execute(&mut current_command_buffer, &mut context, self.world) }
|
||||
.map_err(|error| ExecuteError::Task { node_index, error })?;
|
||||
|
||||
if !self.command_buffers.is_empty() {
|
||||
@ -844,11 +842,11 @@ impl<'a, W: ?Sized + 'static> ExecuteState2<'a, W> {
|
||||
&mut self,
|
||||
buffer_barrier_range: Range<BarrierIndex>,
|
||||
image_barrier_range: Range<BarrierIndex>,
|
||||
) {
|
||||
) -> Result {
|
||||
self.convert_buffer_barriers(buffer_barrier_range);
|
||||
self.convert_image_barriers(image_barrier_range);
|
||||
|
||||
self.flush_barriers();
|
||||
self.flush_barriers()
|
||||
}
|
||||
|
||||
fn convert_buffer_barriers(&mut self, barrier_range: Range<BarrierIndex>) {
|
||||
@ -951,10 +949,10 @@ impl<'a, W: ?Sized + 'static> ExecuteState2<'a, W> {
|
||||
);
|
||||
}
|
||||
|
||||
fn flush_barriers(&mut self) {
|
||||
fn flush_barriers(&mut self) -> Result {
|
||||
unsafe {
|
||||
(self.cmd_pipeline_barrier2)(
|
||||
self.current_command_buffer.as_ref().unwrap().handle(),
|
||||
current_command_buffer!(self).handle(),
|
||||
&vk::DependencyInfo::default()
|
||||
.buffer_memory_barriers(&self.current_buffer_barriers)
|
||||
.image_memory_barriers(&self.current_image_barriers),
|
||||
@ -963,6 +961,8 @@ impl<'a, W: ?Sized + 'static> ExecuteState2<'a, W> {
|
||||
|
||||
self.current_buffer_barriers.clear();
|
||||
self.current_image_barriers.clear();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn flush_submit(&mut self) -> Result {
|
||||
@ -980,7 +980,7 @@ impl<'a, W: ?Sized + 'static> ExecuteState2<'a, W> {
|
||||
.flush_mapped_memory_ranges(self.resource_map)
|
||||
}?;
|
||||
|
||||
let submission = self.current_submission();
|
||||
let submission = current_submission!(self);
|
||||
|
||||
let mut submit_infos = SmallVec::<[_; 4]>::with_capacity(self.per_submits.len());
|
||||
submit_infos.extend(self.per_submits.iter().map(|per_submit| {
|
||||
@ -1010,23 +1010,21 @@ impl<'a, W: ?Sized + 'static> ExecuteState2<'a, W> {
|
||||
.map_err(VulkanError::from)
|
||||
})?;
|
||||
|
||||
drop(submit_infos);
|
||||
self.per_submits.clear();
|
||||
|
||||
*self.submission_count += 1;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
unsafe fn flush_current_command_buffer(&mut self) -> Result {
|
||||
if let Some(command_buffer) = self.current_command_buffer.take() {
|
||||
let command_buffer = unsafe { command_buffer.end() }?;
|
||||
self.current_per_submit.command_buffer_infos.push(
|
||||
vk::CommandBufferSubmitInfo::default().command_buffer(command_buffer.handle()),
|
||||
);
|
||||
let current_command_buffer = self.current_command_buffer.take().unwrap();
|
||||
let command_buffer = unsafe { current_command_buffer.end() }?;
|
||||
self.current_per_submit
|
||||
.command_buffer_infos
|
||||
.push(vk::CommandBufferSubmitInfo::default().command_buffer(command_buffer.handle()));
|
||||
self.death_row.push(Arc::new(command_buffer));
|
||||
self.current_command_buffer = Some(create_command_buffer(
|
||||
self.resource_map,
|
||||
&self.current_submission().queue,
|
||||
)?);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@ -1074,9 +1072,6 @@ impl<'a, W: ?Sized + 'static> ExecuteState<'a, W> {
|
||||
let cmd_pipeline_barrier = fns.v1_0.cmd_pipeline_barrier;
|
||||
let queue_submit = fns.v1_0.queue_submit;
|
||||
|
||||
let current_command_buffer =
|
||||
create_command_buffer(resource_map, &executable.submissions[0].queue)?;
|
||||
|
||||
Ok(ExecuteState {
|
||||
executable,
|
||||
resource_map,
|
||||
@ -1089,7 +1084,7 @@ impl<'a, W: ?Sized + 'static> ExecuteState<'a, W> {
|
||||
queue_submit,
|
||||
per_submits: SmallVec::new(),
|
||||
current_per_submit: PerSubmitInfo::default(),
|
||||
current_command_buffer: Some(current_command_buffer),
|
||||
current_command_buffer: None,
|
||||
command_buffers: Vec::new(),
|
||||
current_buffer_barriers: Vec::new(),
|
||||
current_image_barriers: Vec::new(),
|
||||
@ -1098,10 +1093,6 @@ impl<'a, W: ?Sized + 'static> ExecuteState<'a, W> {
|
||||
})
|
||||
}
|
||||
|
||||
fn current_submission(&self) -> &super::Submission {
|
||||
&self.executable.submissions[*self.submission_count]
|
||||
}
|
||||
|
||||
fn initial_pipeline_barrier(
|
||||
&mut self,
|
||||
buffer_barrier_range: Range<BarrierIndex>,
|
||||
@ -1113,7 +1104,7 @@ impl<'a, W: ?Sized + 'static> ExecuteState<'a, W> {
|
||||
|
||||
fn convert_initial_buffer_barriers(&mut self, barrier_range: Range<BarrierIndex>) {
|
||||
let barrier_range = barrier_range.start as usize..barrier_range.end as usize;
|
||||
let queue_family_index = self.current_submission().queue.queue_family_index();
|
||||
let queue_family_index = current_submission!(self).queue.queue_family_index();
|
||||
|
||||
for barrier in &self.executable.buffer_barriers[barrier_range] {
|
||||
let state = unsafe { self.resource_map.buffer_unchecked(barrier.buffer) };
|
||||
@ -1156,7 +1147,7 @@ impl<'a, W: ?Sized + 'static> ExecuteState<'a, W> {
|
||||
|
||||
fn convert_initial_image_barriers(&mut self, barrier_range: Range<BarrierIndex>) {
|
||||
let barrier_range = barrier_range.start as usize..barrier_range.end as usize;
|
||||
let queue_family_index = self.current_submission().queue.queue_family_index();
|
||||
let queue_family_index = current_submission!(self).queue.queue_family_index();
|
||||
|
||||
for barrier in &self.executable.image_barriers[barrier_range] {
|
||||
let (image, access) = match barrier.image.object_type() {
|
||||
@ -1237,20 +1228,25 @@ impl<'a, W: ?Sized + 'static> ExecuteState<'a, W> {
|
||||
|
||||
fn execute_task(&mut self, node_index: NodeIndex) -> Result {
|
||||
if !self.current_buffer_barriers.is_empty() || !self.current_image_barriers.is_empty() {
|
||||
self.flush_barriers();
|
||||
self.flush_barriers()?;
|
||||
}
|
||||
|
||||
let task_node = unsafe { self.executable.graph.nodes.task_node_unchecked(node_index) };
|
||||
let task = &task_node.task;
|
||||
let current_command_buffer = self.current_command_buffer.as_mut().unwrap();
|
||||
let mut current_command_buffer = unsafe {
|
||||
RecordingCommandBuffer::new(
|
||||
current_command_buffer!(self),
|
||||
self.resource_map,
|
||||
self.death_row,
|
||||
)
|
||||
};
|
||||
let mut context = TaskContext {
|
||||
resource_map: self.resource_map,
|
||||
death_row: Cell::new(Some(self.death_row)),
|
||||
current_frame_index: self.current_frame_index,
|
||||
command_buffers: Cell::new(Some(&mut self.command_buffers)),
|
||||
command_buffers: &mut self.command_buffers,
|
||||
};
|
||||
|
||||
unsafe { task.execute(current_command_buffer, &mut context, self.world) }
|
||||
unsafe { task.execute(&mut current_command_buffer, &mut context, self.world) }
|
||||
.map_err(|error| ExecuteError::Task { node_index, error })?;
|
||||
|
||||
if !self.command_buffers.is_empty() {
|
||||
@ -1271,11 +1267,11 @@ impl<'a, W: ?Sized + 'static> ExecuteState<'a, W> {
|
||||
&mut self,
|
||||
buffer_barrier_range: Range<BarrierIndex>,
|
||||
image_barrier_range: Range<BarrierIndex>,
|
||||
) {
|
||||
) -> Result {
|
||||
self.convert_buffer_barriers(buffer_barrier_range);
|
||||
self.convert_image_barriers(image_barrier_range);
|
||||
|
||||
self.flush_barriers();
|
||||
self.flush_barriers()
|
||||
}
|
||||
|
||||
fn convert_buffer_barriers(&mut self, barrier_range: Range<BarrierIndex>) {
|
||||
@ -1384,7 +1380,7 @@ impl<'a, W: ?Sized + 'static> ExecuteState<'a, W> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn flush_barriers(&mut self) {
|
||||
fn flush_barriers(&mut self) -> Result {
|
||||
if self.current_src_stage_mask.is_empty() {
|
||||
self.current_src_stage_mask = vk::PipelineStageFlags::TOP_OF_PIPE;
|
||||
}
|
||||
@ -1395,7 +1391,7 @@ impl<'a, W: ?Sized + 'static> ExecuteState<'a, W> {
|
||||
|
||||
unsafe {
|
||||
(self.cmd_pipeline_barrier)(
|
||||
self.current_command_buffer.as_ref().unwrap().handle(),
|
||||
current_command_buffer!(self).handle(),
|
||||
self.current_src_stage_mask,
|
||||
self.current_dst_stage_mask,
|
||||
vk::DependencyFlags::empty(),
|
||||
@ -1412,6 +1408,8 @@ impl<'a, W: ?Sized + 'static> ExecuteState<'a, W> {
|
||||
self.current_image_barriers.clear();
|
||||
self.current_src_stage_mask = vk::PipelineStageFlags::empty();
|
||||
self.current_dst_stage_mask = vk::PipelineStageFlags::empty();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn submit(&mut self) -> Result {
|
||||
@ -1420,7 +1418,7 @@ impl<'a, W: ?Sized + 'static> ExecuteState<'a, W> {
|
||||
.flush_mapped_memory_ranges(self.resource_map)
|
||||
}?;
|
||||
|
||||
let submission = self.current_submission();
|
||||
let submission = current_submission!(self);
|
||||
|
||||
let mut submit_infos = SmallVec::<[_; 4]>::with_capacity(self.per_submits.len());
|
||||
submit_infos.extend(self.per_submits.iter().map(|per_submit| {
|
||||
@ -1451,39 +1449,57 @@ impl<'a, W: ?Sized + 'static> ExecuteState<'a, W> {
|
||||
.map_err(VulkanError::from)
|
||||
})?;
|
||||
|
||||
drop(submit_infos);
|
||||
self.per_submits.clear();
|
||||
|
||||
*self.submission_count += 1;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
unsafe fn flush_current_command_buffer(&mut self) -> Result {
|
||||
if let Some(command_buffer) = self.current_command_buffer.take() {
|
||||
let command_buffer = unsafe { command_buffer.end() }?;
|
||||
let current_command_buffer = self.current_command_buffer.take().unwrap();
|
||||
let command_buffer = unsafe { current_command_buffer.end() }?;
|
||||
self.current_per_submit
|
||||
.command_buffers
|
||||
.push(command_buffer.handle());
|
||||
self.death_row.push(Arc::new(command_buffer));
|
||||
self.current_command_buffer = Some(create_command_buffer(
|
||||
self.resource_map,
|
||||
&self.current_submission().queue,
|
||||
)?);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! current_submission {
|
||||
($state:expr) => {
|
||||
&$state.executable.submissions[*$state.submission_count]
|
||||
};
|
||||
}
|
||||
use current_submission;
|
||||
|
||||
macro_rules! current_command_buffer {
|
||||
($state:expr) => {{
|
||||
if $state.current_command_buffer.is_none() {
|
||||
$state.current_command_buffer = Some(create_command_buffer(
|
||||
$state.resource_map,
|
||||
¤t_submission!($state).queue,
|
||||
)?);
|
||||
}
|
||||
|
||||
$state.current_command_buffer.as_mut().unwrap()
|
||||
}};
|
||||
}
|
||||
use current_command_buffer;
|
||||
|
||||
fn create_command_buffer(
|
||||
resource_map: &ResourceMap<'_>,
|
||||
queue: &Queue,
|
||||
) -> Result<RawRecordingCommandBuffer, VulkanError> {
|
||||
let allocator = resource_map.physical_resources.command_buffer_allocator();
|
||||
|
||||
// SAFETY: The parameters are valid.
|
||||
unsafe {
|
||||
RawRecordingCommandBuffer::new_unchecked(
|
||||
resource_map
|
||||
.physical_resources
|
||||
.command_buffer_allocator()
|
||||
.clone(),
|
||||
allocator.clone(),
|
||||
queue.queue_family_index(),
|
||||
CommandBufferLevel::Primary,
|
||||
CommandBufferBeginInfo {
|
||||
@ -1965,6 +1981,11 @@ impl<'a> ResourceMap<'a> {
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn buffer_unchecked(&self, id: Id<Buffer>) -> &BufferState {
|
||||
#[cfg(debug_assertions)]
|
||||
if self.virtual_resources.get(id.erase()).is_err() {
|
||||
std::process::abort();
|
||||
}
|
||||
|
||||
// SAFETY: The caller must ensure that `id` is a valid virtual ID.
|
||||
let &slot = unsafe { self.map.get_unchecked(id.index() as usize) };
|
||||
|
||||
@ -1980,6 +2001,11 @@ impl<'a> ResourceMap<'a> {
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn image_unchecked(&self, id: Id<Image>) -> &ImageState {
|
||||
#[cfg(debug_assertions)]
|
||||
if self.virtual_resources.get(id.erase()).is_err() {
|
||||
std::process::abort();
|
||||
}
|
||||
|
||||
// SAFETY: The caller must ensure that `id` is a valid virtual ID.
|
||||
let &slot = unsafe { self.map.get_unchecked(id.index() as usize) };
|
||||
|
||||
@ -1998,6 +2024,11 @@ impl<'a> ResourceMap<'a> {
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn swapchain_unchecked(&self, id: Id<Swapchain>) -> &SwapchainState {
|
||||
#[cfg(debug_assertions)]
|
||||
if self.virtual_resources.get(id.erase()).is_err() {
|
||||
std::process::abort();
|
||||
}
|
||||
|
||||
// SAFETY: The caller must ensure that `id` is a valid virtual ID.
|
||||
let &slot = unsafe { self.map.get_unchecked(id.index() as usize) };
|
||||
|
||||
|
@ -72,7 +72,7 @@ impl<W: ?Sized> TaskGraph<W> {
|
||||
/// maximum number of virtual resources the graph can ever have.
|
||||
#[must_use]
|
||||
pub fn new(
|
||||
physical_resources: Arc<resource::Resources>,
|
||||
physical_resources: &Arc<resource::Resources>,
|
||||
max_nodes: u32,
|
||||
max_resources: u32,
|
||||
) -> Self {
|
||||
@ -82,7 +82,7 @@ impl<W: ?Sized> TaskGraph<W> {
|
||||
},
|
||||
resources: Resources {
|
||||
inner: SlotMap::new(max_resources),
|
||||
physical_resources,
|
||||
physical_resources: physical_resources.clone(),
|
||||
physical_map: HashMap::default(),
|
||||
host_reads: Vec::new(),
|
||||
host_writes: Vec::new(),
|
||||
@ -565,7 +565,7 @@ struct ResourceAccess {
|
||||
impl<W: ?Sized> TaskNode<W> {
|
||||
fn new(queue_family_type: QueueFamilyType, task: impl Task<World = W>) -> Self {
|
||||
TaskNode {
|
||||
accesses: ResourceAccesses { inner: Vec::new() },
|
||||
accesses: ResourceAccesses::new(),
|
||||
queue_family_type,
|
||||
queue_family_index: 0,
|
||||
dependency_level_index: 0,
|
||||
@ -596,6 +596,10 @@ impl<W: ?Sized> TaskNode<W> {
|
||||
}
|
||||
|
||||
impl ResourceAccesses {
|
||||
pub(crate) const fn new() -> Self {
|
||||
ResourceAccesses { inner: Vec::new() }
|
||||
}
|
||||
|
||||
fn get_mut(
|
||||
&mut self,
|
||||
resources: &mut Resources,
|
||||
@ -1022,7 +1026,7 @@ mod tests {
|
||||
#[test]
|
||||
fn basic_usage1() {
|
||||
let (resources, _) = test_queues!();
|
||||
let mut graph = TaskGraph::<()>::new(resources, 10, 0);
|
||||
let mut graph = TaskGraph::<()>::new(&resources, 10, 0);
|
||||
|
||||
let x = graph
|
||||
.create_task_node("X", QueueFamilyType::Graphics, PhantomData)
|
||||
@ -1057,7 +1061,7 @@ mod tests {
|
||||
#[test]
|
||||
fn basic_usage2() {
|
||||
let (resources, _) = test_queues!();
|
||||
let mut graph = TaskGraph::<()>::new(resources, 10, 0);
|
||||
let mut graph = TaskGraph::<()>::new(&resources, 10, 0);
|
||||
|
||||
let x = graph
|
||||
.create_task_node("X", QueueFamilyType::Graphics, PhantomData)
|
||||
@ -1094,7 +1098,7 @@ mod tests {
|
||||
#[test]
|
||||
fn self_referential_node() {
|
||||
let (resources, _) = test_queues!();
|
||||
let mut graph = TaskGraph::<()>::new(resources, 10, 0);
|
||||
let mut graph = TaskGraph::<()>::new(&resources, 10, 0);
|
||||
|
||||
let x = graph
|
||||
.create_task_node("X", QueueFamilyType::Graphics, PhantomData)
|
||||
|
@ -1,10 +1,11 @@
|
||||
#![forbid(unsafe_op_in_unsafe_fn)]
|
||||
|
||||
use command_buffer::RecordingCommandBuffer;
|
||||
use concurrent_slotmap::SlotId;
|
||||
use graph::{CompileInfo, ExecuteError, ResourceMap, TaskGraph};
|
||||
use resource::{
|
||||
AccessType, BufferState, DeathRow, Flight, HostAccessType, ImageLayoutType, ImageState,
|
||||
Resources, SwapchainState,
|
||||
AccessType, BufferState, Flight, HostAccessType, ImageLayoutType, ImageState, Resources,
|
||||
SwapchainState,
|
||||
};
|
||||
use std::{
|
||||
any::{Any, TypeId},
|
||||
@ -20,29 +21,30 @@ use std::{
|
||||
};
|
||||
use vulkano::{
|
||||
buffer::{Buffer, BufferContents, BufferMemory, Subbuffer},
|
||||
command_buffer::sys::{RawCommandBuffer, RawRecordingCommandBuffer},
|
||||
command_buffer::sys::RawCommandBuffer,
|
||||
device::Queue,
|
||||
image::Image,
|
||||
swapchain::Swapchain,
|
||||
DeviceSize, ValidationError,
|
||||
};
|
||||
|
||||
pub mod command_buffer;
|
||||
pub mod graph;
|
||||
pub mod resource;
|
||||
|
||||
/// Creates a [`TaskGraph`] with one task node, compiles it, and executes it.
|
||||
pub unsafe fn execute(
|
||||
queue: Arc<Queue>,
|
||||
resources: Arc<Resources>,
|
||||
queue: &Arc<Queue>,
|
||||
resources: &Arc<Resources>,
|
||||
flight_id: Id<Flight>,
|
||||
task: impl FnOnce(&mut RawRecordingCommandBuffer, &mut TaskContext<'_>) -> TaskResult,
|
||||
task: impl FnOnce(&mut RecordingCommandBuffer<'_>, &mut TaskContext<'_>) -> TaskResult,
|
||||
host_buffer_accesses: impl IntoIterator<Item = (Id<Buffer>, HostAccessType)>,
|
||||
buffer_accesses: impl IntoIterator<Item = (Id<Buffer>, AccessType)>,
|
||||
image_accesses: impl IntoIterator<Item = (Id<Image>, AccessType, ImageLayoutType)>,
|
||||
) -> Result<(), ExecuteError> {
|
||||
#[repr(transparent)]
|
||||
struct OnceTask<'a>(
|
||||
&'a dyn Fn(&mut RawRecordingCommandBuffer, &mut TaskContext<'_>) -> TaskResult,
|
||||
&'a dyn Fn(&mut RecordingCommandBuffer<'_>, &mut TaskContext<'_>) -> TaskResult,
|
||||
);
|
||||
|
||||
// SAFETY: The task is constructed inside this function and never leaves its scope, so there is
|
||||
@ -58,7 +60,7 @@ pub unsafe fn execute(
|
||||
|
||||
unsafe fn execute(
|
||||
&self,
|
||||
cbf: &mut RawRecordingCommandBuffer,
|
||||
cbf: &mut RecordingCommandBuffer<'_>,
|
||||
tcx: &mut TaskContext<'_>,
|
||||
_: &Self::World,
|
||||
) -> TaskResult {
|
||||
@ -67,7 +69,7 @@ pub unsafe fn execute(
|
||||
}
|
||||
|
||||
let task = Cell::new(Some(task));
|
||||
let trampoline = move |cbf: &mut RawRecordingCommandBuffer, tcx: &mut TaskContext<'_>| {
|
||||
let trampoline = move |cbf: &mut RecordingCommandBuffer<'_>, tcx: &mut TaskContext<'_>| {
|
||||
// `ExecutableTaskGraph::execute` calls each task exactly once, and we only execute the
|
||||
// task graph once.
|
||||
(Cell::take(&task).unwrap())(cbf, tcx)
|
||||
@ -101,8 +103,8 @@ pub unsafe fn execute(
|
||||
// * The user must ensure that there are no accesses that are incompatible with the queue.
|
||||
// * The user must ensure that there are no accesses incompatible with the device.
|
||||
let task_graph = unsafe {
|
||||
task_graph.compile(CompileInfo {
|
||||
queues: vec![queue],
|
||||
task_graph.compile(&CompileInfo {
|
||||
queues: &[queue],
|
||||
present_queue: None,
|
||||
flight_id,
|
||||
_ne: crate::NE,
|
||||
@ -139,7 +141,7 @@ pub trait Task: Any + Send + Sync {
|
||||
/// [sharing mode]: vulkano::sync::Sharing
|
||||
unsafe fn execute(
|
||||
&self,
|
||||
cbf: &mut RawRecordingCommandBuffer,
|
||||
cbf: &mut RecordingCommandBuffer<'_>,
|
||||
tcx: &mut TaskContext<'_>,
|
||||
world: &Self::World,
|
||||
) -> TaskResult;
|
||||
@ -213,7 +215,7 @@ impl<W: ?Sized + 'static> Task for PhantomData<fn() -> W> {
|
||||
|
||||
unsafe fn execute(
|
||||
&self,
|
||||
_cbf: &mut RawRecordingCommandBuffer,
|
||||
_cbf: &mut RecordingCommandBuffer<'_>,
|
||||
_tcx: &mut TaskContext<'_>,
|
||||
_world: &Self::World,
|
||||
) -> TaskResult {
|
||||
@ -223,12 +225,11 @@ impl<W: ?Sized + 'static> Task for PhantomData<fn() -> W> {
|
||||
|
||||
/// The context of a task.
|
||||
///
|
||||
/// This gives you access to the current command buffer, resources, as well as resource cleanup.
|
||||
/// This gives you access to the resources.
|
||||
pub struct TaskContext<'a> {
|
||||
resource_map: &'a ResourceMap<'a>,
|
||||
death_row: Cell<Option<&'a mut DeathRow>>,
|
||||
current_frame_index: u32,
|
||||
command_buffers: Cell<Option<&'a mut Vec<Arc<RawCommandBuffer>>>>,
|
||||
command_buffers: &'a mut Vec<Arc<RawCommandBuffer>>,
|
||||
}
|
||||
|
||||
impl<'a> TaskContext<'a> {
|
||||
@ -496,48 +497,6 @@ impl<'a> TaskContext<'a> {
|
||||
Ok(data)
|
||||
}
|
||||
|
||||
/// Queues the destruction of the buffer corresponding to `id` after the destruction of the
|
||||
/// command buffer(s) for this task.
|
||||
// FIXME: unsafe
|
||||
#[inline]
|
||||
pub unsafe fn destroy_buffer(&self, id: Id<Buffer>) -> TaskResult {
|
||||
let state = unsafe { self.resource_map.resources().remove_buffer(id) }?;
|
||||
let death_row = self.death_row.take().unwrap();
|
||||
// FIXME:
|
||||
death_row.push(state.buffer().clone());
|
||||
self.death_row.set(Some(death_row));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Queues the destruction of the image corresponding to `id` after the destruction of the
|
||||
/// command buffer(s) for this task.
|
||||
// FIXME: unsafe
|
||||
#[inline]
|
||||
pub unsafe fn destroy_image(&self, id: Id<Image>) -> TaskResult {
|
||||
let state = unsafe { self.resource_map.resources().remove_image(id) }?;
|
||||
let death_row = self.death_row.take().unwrap();
|
||||
// FIXME:
|
||||
death_row.push(state.image().clone());
|
||||
self.death_row.set(Some(death_row));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Queues the destruction of the swapchain corresponding to `id` after the destruction of the
|
||||
/// command buffer(s) for this task.
|
||||
// FIXME: unsafe
|
||||
#[inline]
|
||||
pub unsafe fn destroy_swapchain(&self, id: Id<Swapchain>) -> TaskResult {
|
||||
let state = unsafe { self.resource_map.resources().remove_swapchain(id) }?;
|
||||
let death_row = self.death_row.take().unwrap();
|
||||
// FIXME:
|
||||
death_row.push(state.swapchain().clone());
|
||||
self.death_row.set(Some(death_row));
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Pushes a command buffer into the list of command buffers to be executed on the queue.
|
||||
///
|
||||
/// All command buffers will be executed in the order in which they are pushed after the task
|
||||
@ -551,10 +510,8 @@ impl<'a> TaskContext<'a> {
|
||||
/// buffer must not do any accesses not accounted for in the [task's access set], or ensure
|
||||
/// that such accesses are appropriately synchronized.
|
||||
#[inline]
|
||||
pub unsafe fn push_command_buffer(&self, command_buffer: Arc<RawCommandBuffer>) {
|
||||
let vec = self.command_buffers.take().unwrap();
|
||||
vec.push(command_buffer);
|
||||
self.command_buffers.set(Some(vec));
|
||||
pub unsafe fn push_command_buffer(&mut self, command_buffer: Arc<RawCommandBuffer>) {
|
||||
self.command_buffers.push(command_buffer);
|
||||
}
|
||||
|
||||
/// Extends the list of command buffers to be executed on the queue.
|
||||
@ -569,12 +526,10 @@ impl<'a> TaskContext<'a> {
|
||||
/// [`push_command_buffer`]: Self::push_command_buffer
|
||||
#[inline]
|
||||
pub unsafe fn extend_command_buffers(
|
||||
&self,
|
||||
&mut self,
|
||||
command_buffers: impl IntoIterator<Item = Arc<RawCommandBuffer>>,
|
||||
) {
|
||||
let vec = self.command_buffers.take().unwrap();
|
||||
vec.extend(command_buffers);
|
||||
self.command_buffers.set(Some(vec));
|
||||
self.command_buffers.extend(command_buffers);
|
||||
}
|
||||
}
|
||||
|
||||
@ -886,10 +841,10 @@ enum ObjectType {
|
||||
Flight = 3,
|
||||
}
|
||||
|
||||
// SAFETY: ZSTs can always be safely produced out of thin air, barring any safety invariants they
|
||||
// might impose, which in the case of `NonExhaustive` are none.
|
||||
const NE: vulkano::NonExhaustive =
|
||||
unsafe { ::std::mem::transmute::<(), ::vulkano::NonExhaustive>(()) };
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct NonExhaustive<'a>(PhantomData<&'a ()>);
|
||||
|
||||
const NE: NonExhaustive<'static> = NonExhaustive(PhantomData);
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
@ -932,7 +887,7 @@ mod tests {
|
||||
};
|
||||
|
||||
(
|
||||
$crate::resource::Resources::new(device, Default::default()),
|
||||
$crate::resource::Resources::new(&device, &Default::default()),
|
||||
queues.collect::<Vec<_>>(),
|
||||
)
|
||||
}};
|
||||
|
@ -121,9 +121,9 @@ impl Resources {
|
||||
///
|
||||
/// - Panics if `device` already has a `Resources` collection associated with it.
|
||||
#[must_use]
|
||||
pub fn new(device: Arc<Device>, create_info: ResourcesCreateInfo) -> Arc<Self> {
|
||||
pub fn new(device: &Arc<Device>, create_info: &ResourcesCreateInfo<'_>) -> Arc<Self> {
|
||||
let mut registered_devices = REGISTERED_DEVICES.lock();
|
||||
let device_addr = Arc::as_ptr(&device) as usize;
|
||||
let device_addr = Arc::as_ptr(device) as usize;
|
||||
|
||||
assert!(
|
||||
!registered_devices.contains(&device_addr),
|
||||
@ -141,7 +141,7 @@ impl Resources {
|
||||
let global = epoch::GlobalHandle::new();
|
||||
|
||||
Arc::new(Resources {
|
||||
device,
|
||||
device: device.clone(),
|
||||
memory_allocator,
|
||||
command_buffer_allocator,
|
||||
locals: ThreadLocal::new(),
|
||||
@ -570,6 +570,11 @@ impl Resources {
|
||||
|
||||
#[inline]
|
||||
pub(crate) unsafe fn buffer_unchecked_unprotected(&self, id: Id<Buffer>) -> &BufferState {
|
||||
#[cfg(debug_assertions)]
|
||||
if unsafe { self.buffers.get_unprotected(id.slot) }.is_none() {
|
||||
std::process::abort();
|
||||
}
|
||||
|
||||
// SAFETY: Enforced by the caller.
|
||||
unsafe { self.buffers.index_unchecked_unprotected(id.index()) }
|
||||
}
|
||||
@ -591,6 +596,11 @@ impl Resources {
|
||||
|
||||
#[inline]
|
||||
pub(crate) unsafe fn image_unchecked_unprotected(&self, id: Id<Image>) -> &ImageState {
|
||||
#[cfg(debug_assertions)]
|
||||
if unsafe { self.images.get_unprotected(id.slot) }.is_none() {
|
||||
std::process::abort();
|
||||
}
|
||||
|
||||
// SAFETY: Enforced by the caller.
|
||||
unsafe { self.images.index_unchecked_unprotected(id.index()) }
|
||||
}
|
||||
@ -618,6 +628,11 @@ impl Resources {
|
||||
&self,
|
||||
id: Id<Swapchain>,
|
||||
) -> &SwapchainState {
|
||||
#[cfg(debug_assertions)]
|
||||
if unsafe { self.swapchains.get_unprotected(id.slot) }.is_none() {
|
||||
std::process::abort();
|
||||
}
|
||||
|
||||
// SAFETY: Enforced by the caller.
|
||||
unsafe { self.swapchains.index_unchecked_unprotected(id.index()) }
|
||||
}
|
||||
@ -1065,7 +1080,7 @@ impl Flight {
|
||||
|
||||
/// Parameters to create a new [`Resources`] collection.
|
||||
#[derive(Debug)]
|
||||
pub struct ResourcesCreateInfo {
|
||||
pub struct ResourcesCreateInfo<'a> {
|
||||
/// The maximum number of [`Buffer`]s that the collection can hold at once.
|
||||
pub max_buffers: u32,
|
||||
|
||||
@ -1078,10 +1093,10 @@ pub struct ResourcesCreateInfo {
|
||||
/// The maximum number of [`Flight`]s that the collection can hold at once.
|
||||
pub max_flights: u32,
|
||||
|
||||
pub _ne: vulkano::NonExhaustive,
|
||||
pub _ne: crate::NonExhaustive<'a>,
|
||||
}
|
||||
|
||||
impl Default for ResourcesCreateInfo {
|
||||
impl Default for ResourcesCreateInfo<'_> {
|
||||
#[inline]
|
||||
fn default() -> Self {
|
||||
ResourcesCreateInfo {
|
||||
|
@ -278,8 +278,9 @@ impl PipelineLayout {
|
||||
///
|
||||
/// The ranges are guaranteed to be sorted deterministically by offset, and
|
||||
/// guaranteed to be disjoint, meaning that there is no overlap between the ranges.
|
||||
#[doc(hidden)]
|
||||
#[inline]
|
||||
pub(crate) fn push_constant_ranges_disjoint(&self) -> &[PushConstantRange] {
|
||||
pub fn push_constant_ranges_disjoint(&self) -> &[PushConstantRange] {
|
||||
&self.push_constant_ranges_disjoint
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user