#[derive(Clone, Copy, PartialEq, Eq, Hash)]
pub struct Sampler(Handle);
+#[derive(Clone, Copy, PartialEq, Eq, Hash)]
+pub struct BindGroupLayout(Handle);
+
+#[derive(Clone, Copy, PartialEq, Eq, Hash)]
+pub struct BindGroup(Handle);
+
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
pub struct Pipeline(Handle);
pub max_lod: f32,
}
+#[derive(Clone, Copy, Debug, PartialEq, Eq)]
+pub enum BindingType {
+ Sampler,
+ Texture,
+ UniformBuffer,
+ StorageBuffer,
+ DynamicUniformBuffer,
+ DynamicStorageBuffer,
+}
+
+pub struct BindGroupLayoutEntryDesc {
+ pub slot: u32,
+ pub stages: ShaderStageFlags,
+ pub binding_type: BindingType,
+ pub count: u32,
+}
+
+pub struct BindGroupLayoutDesc<'a> {
+ pub entries: &'a [BindGroupLayoutEntryDesc],
+}
+
pub struct GraphicsPipelineLayout<'a> {
pub color_attachment_formats: &'a [TextureFormat],
pub depth_attachment_format: Option<TextureFormat>,
pub struct GraphicsPipelineDesc<'a> {
pub vertex_shader: ShaderDesc<'a>,
pub fragment_shader: ShaderDesc<'a>,
+ pub bind_group_layouts: &'a [BindGroupLayout],
pub layout: GraphicsPipelineLayout<'a>,
}
fn create_texture(&self, texture_desc: &TextureDesc) -> Texture;
fn create_texture_view(&self, desc: &TextureViewDesc) -> Texture;
fn create_sampler(&self, desc: &SamplerDesc) -> Sampler;
+ fn create_bind_group_layout(&self, desc: &BindGroupLayoutDesc) -> BindGroupLayout;
fn create_graphics_pipeline(&self, desc: &GraphicsPipelineDesc) -> Pipeline;
fn create_compute_pipeline(&self, desc: &ComputePipelineDesc) -> Pipeline;
fn destroy_buffer(&self, frame_token: &FrameToken, buffer: Buffer);
fn destroy_texture(&self, frame_token: &FrameToken, texture: Texture);
fn destroy_sampler(&self, frame_token: &FrameToken, sampler: Sampler);
+ fn destroy_bind_group_layout(
+ &self,
+ frame_token: &FrameToken,
+ bind_group_layout: BindGroupLayout,
+ );
fn destroy_pipeline(&self, frame_token: &FrameToken, pipeline: Pipeline);
fn acquire_swapchain(
use vulkan_sys as vk;
use crate::{
- Buffer, BufferDesc, BufferUsageFlags, ClearValue, CommandBufferToken, ComputePipelineDesc,
- Device, FrameToken, GpuConcurrent, GraphicsPipelineDesc, LoadOp, MemoryLocation, Pipeline,
- Sampler, SamplerAddressMode, SamplerCompareOp, SamplerDesc, SamplerFilter, Texture,
- TextureDesc, TextureDimension, TextureFormat, TextureUsageFlags, TextureViewDesc, ThreadToken,
+ BindGroupLayout, BindGroupLayoutDesc, BindingType, Buffer, BufferDesc, BufferUsageFlags,
+ ClearValue, CommandBufferToken, ComputePipelineDesc, Device, FrameToken, GpuConcurrent,
+ GraphicsPipelineDesc, LoadOp, MemoryLocation, Pipeline, Sampler, SamplerAddressMode,
+ SamplerCompareOp, SamplerDesc, SamplerFilter, ShaderStageFlags, Texture, TextureDesc,
+ TextureDimension, TextureFormat, TextureUsageFlags, TextureViewDesc, ThreadToken,
};
const NUM_FRAMES: usize = 2;
}
}
+#[must_use]
+fn vulkan_shader_stage_flags(stage_flags: ShaderStageFlags) -> vk::ShaderStageFlags {
+ let mut flags = vk::ShaderStageFlags::default();
+ if stage_flags.contains(ShaderStageFlags::COMPUTE) {
+ flags |= vk::ShaderStageFlags::COMPUTE;
+ }
+ if stage_flags.contains(ShaderStageFlags::FRAGMENT) {
+ flags |= vk::ShaderStageFlags::FRAGMENT;
+ }
+ if stage_flags.contains(ShaderStageFlags::VERTEX) {
+ flags |= vk::ShaderStageFlags::VERTEX;
+ }
+ flags
+}
+
+#[must_use]
+fn vulkan_descriptor_type(binding_type: BindingType) -> vk::DescriptorType {
+ match binding_type {
+ BindingType::Sampler => vk::DescriptorType::Sampler,
+ BindingType::Texture => vk::DescriptorType::SampledImage,
+ BindingType::UniformBuffer => vk::DescriptorType::UniformBuffer,
+ BindingType::StorageBuffer => vk::DescriptorType::StorageBuffer,
+ BindingType::DynamicUniformBuffer => vk::DescriptorType::UniformBufferDynamic,
+ BindingType::DynamicStorageBuffer => vk::DescriptorType::StorageBufferDynamic,
+ }
+}
+
struct DelayQueue<T> {
delay: u64,
counter: u64,
struct VulkanSampler(vk::Sampler);
+struct VulkanBindGroupLayout(vk::DescriptorSetLayout);
+
struct VulkanPipeline {
pipeline: vk::Pipeline,
pipeline_bind_point: vk::PipelineBindPoint,
textures: Pool<VulkanTextureHolder>,
buffers: Pool<VulkanBuffer>,
samplers: Pool<VulkanSampler>,
+ bind_group_layouts: Pool<VulkanBindGroupLayout>,
pipelines: Pool<VulkanPipeline>,
}
destroyed_images: Mutex<VecDeque<vk::Image>>,
destroyed_image_views: Mutex<VecDeque<vk::ImageView>>,
destroyed_samplers: Mutex<VecDeque<vk::Sampler>>,
+ destroyed_descriptor_set_layouts: Mutex<VecDeque<vk::DescriptorSetLayout>>,
destroyed_pipelines: Mutex<VecDeque<vk::Pipeline>>,
recycled_semaphores: Mutex<VecDeque<vk::Semaphore>>,
UnsafeCell::new(VulkanFrame {
command_buffer_pools: cmd_buffer_pools,
universal_queue_fence: AtomicU64::new(universal_queue_fence),
- present_swapchains: Mutex::new(HashMap::new()),
- destroyed_buffers: Mutex::new(VecDeque::new()),
- destroyed_buffer_views: Mutex::new(VecDeque::new()),
- destroyed_images: Mutex::new(VecDeque::new()),
- destroyed_image_views: Mutex::new(VecDeque::new()),
- destroyed_samplers: Mutex::new(VecDeque::new()),
- destroyed_allocations: Mutex::new(VecDeque::new()),
- destroyed_pipelines: Mutex::new(VecDeque::new()),
- recycled_semaphores: Mutex::new(VecDeque::new()),
+ present_swapchains: Default::default(),
+ destroyed_allocations: Default::default(),
+ destroyed_buffers: Default::default(),
+ destroyed_buffer_views: Default::default(),
+ destroyed_images: Default::default(),
+ destroyed_image_views: Default::default(),
+ destroyed_samplers: Default::default(),
+ destroyed_descriptor_set_layouts: Default::default(),
+ destroyed_pipelines: Default::default(),
+ recycled_semaphores: Default::default(),
})
}));
textures: Pool::new(),
buffers: Pool::new(),
samplers: Pool::new(),
+ bind_group_layouts: Pool::new(),
pipelines: Pool::new(),
}),
for pipeline in frame.destroyed_pipelines.get_mut().drain(..) {
unsafe { device_fn.destroy_pipeline(device, pipeline, None) }
}
+ for descriptor_set_layout in frame.destroyed_descriptor_set_layouts.get_mut().drain(..) {
+ unsafe { device_fn.destroy_descriptor_set_layout(device, descriptor_set_layout, None) }
+ }
for sampler in frame.destroyed_samplers.get_mut().drain(..) {
unsafe { device_fn.destroy_sampler(device, sampler, None) }
}
Sampler(handle)
}
+ fn create_bind_group_layout(&self, desc: &BindGroupLayoutDesc) -> BindGroupLayout {
+ let layout_bindings = desc
+ .entries
+ .iter()
+ .map(|x| vk::DescriptorSetLayoutBinding {
+ binding: x.slot,
+ descriptor_type: vulkan_descriptor_type(x.binding_type),
+ descriptor_count: x.count,
+ stage_flags: vulkan_shader_stage_flags(x.stages),
+ immutable_samplers: std::ptr::null(),
+ })
+ .collect::<Vec<_>>();
+
+ let create_info = &vk::DescriptorSetLayoutCreateInfo {
+ bindings: layout_bindings.as_slice().into(),
+ ..default()
+ };
+ let mut set_layout = vk::DescriptorSetLayout::null();
+ vk_check!(self.device_fn.create_descriptor_set_layout(
+ self.device,
+ create_info,
+ None,
+ &mut set_layout,
+ ));
+
+ let bind_group_layout = self
+ .pools
+ .lock()
+ .bind_group_layouts
+ .insert(VulkanBindGroupLayout(set_layout));
+
+ BindGroupLayout(bind_group_layout)
+ }
+
fn create_graphics_pipeline(&self, desc: &GraphicsPipelineDesc) -> Pipeline {
let layout = {
- let create_info = vk::PipelineLayoutCreateInfo::default();
+ let create_info = vk::PipelineLayoutCreateInfo {
+ //set_layouts: set_layouts.as_slice().into(),
+ ..default()
+ };
let mut pipeline_layout = vk::PipelineLayout::null();
vk_check!(self.device_fn.create_pipeline_layout(
self.device,
}
}
+ fn destroy_bind_group_layout(
+ &self,
+ frame_token: &FrameToken,
+ bind_group_layout: BindGroupLayout,
+ ) {
+ if let Some(bind_group_layout) = self
+ .pools
+ .lock()
+ .bind_group_layouts
+ .remove(bind_group_layout.0)
+ {
+ self.frame(frame_token)
+ .destroyed_descriptor_set_layouts
+ .lock()
+ .push_back(bind_group_layout.0)
+ }
+ }
+
fn destroy_pipeline(&self, frame_token: &FrameToken, pipeline: Pipeline) {
if let Some(pipeline) = self.pools.lock().pipelines.remove(pipeline.0) {
self.frame(frame_token)
textures,
buffers: _,
samplers: _,
+ bind_group_layouts: _,
pipelines: _,
} = pools.deref_mut();
textures,
buffers: _,
samplers: _,
+ bind_group_layouts: _,
pipelines: _,
} = pools.deref_mut();
unsafe { device_fn.destroy_pipeline(device, pipeline.pipeline, None) }
}
+ for descriptor_set_layout in self.pools.get_mut().bind_group_layouts.values() {
+ unsafe {
+ device_fn.destroy_descriptor_set_layout(device, descriptor_set_layout.0, None)
+ }
+ }
+
unsafe { device_fn.destroy_device(device, None) }
unsafe { self.instance_fn.destroy_instance(self.instance, None) };
}
use narcissus_app::{create_app, Event, Window, WindowDesc};
use narcissus_core::{cstr, obj, slice, Image};
use narcissus_gpu::{
- create_vulkan_device, ClearValue, Device, FrameToken, GraphicsPipelineDesc,
- GraphicsPipelineLayout, LoadOp, MemoryLocation, Pipeline, RenderingAttachment, RenderingDesc,
- Scissor, ShaderDesc, StoreOp, TextureDesc, TextureDimension, TextureFormat, TextureUsageFlags,
- TextureViewDesc, ThreadToken, Viewport,
+ create_vulkan_device, BindGroupLayoutDesc, BindGroupLayoutEntryDesc, BindingType, ClearValue,
+ Device, FrameToken, GraphicsPipelineDesc, GraphicsPipelineLayout, LoadOp, MemoryLocation,
+ Pipeline, RenderingAttachment, RenderingDesc, Scissor, ShaderDesc, ShaderStageFlags, StoreOp,
+ TextureDesc, TextureDimension, TextureFormat, TextureUsageFlags, TextureViewDesc, ThreadToken,
+ Viewport,
};
use narcissus_maths::{Vec2, Vec3};
let vert_shader_spv = Spirv(*include_bytes!("shaders/triangle.vert.spv"));
let frag_shader_spv = Spirv(*include_bytes!("shaders/triangle.frag.spv"));
+ let global_layout = device.create_bind_group_layout(&BindGroupLayoutDesc {
+ entries: &[
+ // Global uniforms.
+ BindGroupLayoutEntryDesc {
+ slot: 0,
+ stages: ShaderStageFlags::ALL,
+ binding_type: BindingType::UniformBuffer,
+ count: 1,
+ },
+ ],
+ });
+
+ let per_material_layout = device.create_bind_group_layout(&BindGroupLayoutDesc {
+ entries: &[
+ // Per-material uniforms.
+ BindGroupLayoutEntryDesc {
+ slot: 0,
+ stages: ShaderStageFlags::ALL,
+ binding_type: BindingType::UniformBuffer,
+ count: 1,
+ },
+ // Per-material textures.
+ BindGroupLayoutEntryDesc {
+ slot: 1,
+ stages: ShaderStageFlags::ALL,
+ binding_type: BindingType::Texture,
+ count: 1,
+ },
+ ],
+ });
+
+ let per_draw_layout = device.create_bind_group_layout(&BindGroupLayoutDesc {
+ entries: &[
+ // Per-draw Uniforms
+ BindGroupLayoutEntryDesc {
+ slot: 0,
+ stages: ShaderStageFlags::ALL,
+ binding_type: BindingType::DynamicUniformBuffer,
+ count: 1,
+ },
+ ],
+ });
+
let pipeline = device.create_graphics_pipeline(&GraphicsPipelineDesc {
vertex_shader: ShaderDesc {
entrypoint_name: cstr!("main"),
entrypoint_name: cstr!("main"),
code: &frag_shader_spv.0,
},
+ bind_group_layouts: &[
+ // Set 0
+ global_layout,
+ // Set 1
+ per_material_layout,
+ // Set 2
+ per_draw_layout,
+ ],
layout: GraphicsPipelineLayout {
color_attachment_formats: &[TextureFormat::BGRA8_SRGB],
depth_attachment_format: None,