test(vk): Add Vulkan render test

This commit is contained in:
chyyran 2024-08-25 00:03:59 -04:00 committed by Ronny Chan
parent 6de2de8d12
commit 79513a301e
9 changed files with 878 additions and 5 deletions

View file

@ -42,9 +42,12 @@ use std::sync::Arc;
/// A Vulkan device and metadata that is required by the shader runtime. /// A Vulkan device and metadata that is required by the shader runtime.
pub struct VulkanObjects { pub struct VulkanObjects {
pub(crate) device: Arc<ash::Device>, /// The handle to the initialized `ash::Device`
pub(crate) alloc: Arc<Mutex<Allocator>>, pub device: Arc<ash::Device>,
queue: vk::Queue, /// The instance of the `gpu-allocator` to use.
pub alloc: Arc<Mutex<Allocator>>,
/// The graphics queue to do work on.
pub queue: vk::Queue,
} }
/// A collection of handles needed to access the Vulkan instance. /// A collection of handles needed to access the Vulkan instance.

View file

@ -16,6 +16,10 @@ ash = "0.38.0+1.3.281"
pollster = "0.3.0" pollster = "0.3.0"
parking_lot = "0.12.3" parking_lot = "0.12.3"
image-compare = "0.4.1" image-compare = "0.4.1"
gpu-allocator = "0.27.0"
[features]
vulkan-debug = []
[target.'cfg(windows)'.dependencies.windows] [target.'cfg(windows)'.dependencies.windows]
workspace = true workspace = true

View file

@ -1,3 +1,2 @@
/// Render tests /// Render tests
pub mod render; pub mod render;

View file

@ -1,4 +1,5 @@
pub mod d3d11; pub mod d3d11;
pub mod vk;
pub mod wgpu; pub mod wgpu;
use std::path::Path; use std::path::Path;
@ -27,7 +28,10 @@ mod test {
use std::fs::File; use std::fs::File;
const IMAGE_PATH: &str = "../triangle.png"; const IMAGE_PATH: &str = "../triangle.png";
const FILTER_PATH: &str = "../test/shaders_slang/crt/crt-royale.slangp"; // const FILTER_PATH: &str = "../test/shaders_slang/crt/crt-royale.slangp";
const FILTER_PATH: &str =
"../test/shaders_slang/bezel/Mega_Bezel/Presets/MBZ__0__SMOOTH-ADV.slangp";
#[test] #[test]
pub fn test_d3d11() -> anyhow::Result<()> { pub fn test_d3d11() -> anyhow::Result<()> {
@ -49,6 +53,16 @@ mod test {
Ok(()) Ok(())
} }
#[test]
pub fn test_vk() -> anyhow::Result<()> {
let vulkan = super::vk::Vulkan::new(IMAGE_PATH)?;
let image = vulkan.render(FILTER_PATH, 100)?;
//
let out = File::create("out.png")?;
image.write_with_encoder(PngEncoder::new(out))?;
Ok(())
}
#[test] #[test]
pub fn compare() -> anyhow::Result<()> { pub fn compare() -> anyhow::Result<()> {
let d3d11 = super::d3d11::Direct3D11::new(IMAGE_PATH)?; let d3d11 = super::d3d11::Direct3D11::new(IMAGE_PATH)?;

View file

@ -0,0 +1,166 @@
use crate::render::vk::physical_device::QueueFamilyIndices;
use ash::prelude::VkResult;
use ash::vk;
use ash::vk::CommandBufferResetFlags;
use gpu_allocator::vulkan::Allocator;
use librashader::runtime::vk::VulkanObjects;
use parking_lot::Mutex;
use std::ffi::CStr;
use std::sync::Arc;
pub struct VulkanBase {
entry: ash::Entry,
instance: ash::Instance,
device: Arc<ash::Device>,
graphics_queue: vk::Queue,
// pub debug: VulkanDebug,
physical_device: vk::PhysicalDevice,
mem_props: vk::PhysicalDeviceMemoryProperties,
allocator: Arc<Mutex<Allocator>>,
_pool: vk::CommandPool,
cmd_buffer: vk::CommandBuffer,
}
impl From<&VulkanBase> for VulkanObjects {
fn from(value: &VulkanBase) -> Self {
VulkanObjects {
device: Arc::clone(&value.device),
alloc: Arc::clone(&value.allocator),
queue: value.graphics_queue.clone(),
}
}
}
const KHRONOS_VALIDATION: &[u8] = b"VK_LAYER_KHRONOS_validation\0";
const LIBRASHADER_VULKAN: &[u8] = b"librashader Vulkan\0";
impl VulkanBase {
pub fn new() -> anyhow::Result<Self> {
let entry = unsafe { ash::Entry::load() }?;
let layers = [KHRONOS_VALIDATION.as_ptr().cast()];
let app_info = vk::ApplicationInfo::default()
.application_name(unsafe { CStr::from_bytes_with_nul_unchecked(LIBRASHADER_VULKAN) })
.engine_name(unsafe { CStr::from_bytes_with_nul_unchecked(LIBRASHADER_VULKAN) })
.engine_version(0)
.application_version(0)
.api_version(vk::make_api_version(0, 1, 3, 0));
let create_info = vk::InstanceCreateInfo::default()
.application_info(&app_info)
.enabled_layer_names(&layers)
.enabled_extension_names(&[]);
let instance = unsafe { entry.create_instance(&create_info, None) }?;
let physical_device = super::physical_device::pick_physical_device(&instance)?;
let (device, queue, cmd_pool) = Self::create_device(&instance, &physical_device)?;
let mem_props = unsafe { instance.get_physical_device_memory_properties(physical_device) };
let alloc = super::memory::create_allocator(
device.clone(),
instance.clone(),
physical_device.clone(),
);
let buffer_info = vk::CommandBufferAllocateInfo::default()
.command_pool(cmd_pool)
.level(vk::CommandBufferLevel::PRIMARY)
.command_buffer_count(1);
let buffers = unsafe { device.allocate_command_buffers(&buffer_info) }?
.into_iter()
.next()
.unwrap();
Ok(Self {
entry,
instance,
device: Arc::new(device),
graphics_queue: queue,
physical_device,
mem_props,
// debug,
allocator: alloc,
_pool: cmd_pool,
cmd_buffer: buffers,
})
}
pub(crate) fn device(&self) -> &Arc<ash::Device> {
&self.device
}
pub(crate) fn allocator(&self) -> &Arc<Mutex<Allocator>> {
&self.allocator
}
fn create_device(
instance: &ash::Instance,
physical_device: &vk::PhysicalDevice,
) -> anyhow::Result<(ash::Device, vk::Queue, vk::CommandPool)> {
let _debug = [unsafe { CStr::from_bytes_with_nul_unchecked(KHRONOS_VALIDATION).as_ptr() }];
let indices = super::physical_device::find_queue_family(&instance, *physical_device);
let queue_info = [vk::DeviceQueueCreateInfo::default()
.queue_family_index(indices.graphics_family()?)
.queue_priorities(&[1.0f32])];
let mut physical_device_features =
vk::PhysicalDeviceVulkan13Features::default().dynamic_rendering(true);
let extensions = [ash::khr::dynamic_rendering::NAME.as_ptr()];
let device_create_info = vk::DeviceCreateInfo::default()
.queue_create_infos(&queue_info)
.enabled_extension_names(&extensions)
.push_next(&mut physical_device_features);
let device =
unsafe { instance.create_device(*physical_device, &device_create_info, None)? };
let queue = unsafe { device.get_device_queue(indices.graphics_family()?, 0) };
let create_info = vk::CommandPoolCreateInfo::default()
.flags(vk::CommandPoolCreateFlags::RESET_COMMAND_BUFFER)
.queue_family_index(indices.graphics_family()?);
let pool = unsafe { device.create_command_pool(&create_info, None) }?;
Ok((device, queue, pool))
}
/// Simple helper function to synchronously queue work on the graphics queue
pub fn queue_work<T>(&self, f: impl FnOnce(vk::CommandBuffer) -> T) -> anyhow::Result<T> {
unsafe {
self.device.begin_command_buffer(
self.cmd_buffer,
&vk::CommandBufferBeginInfo::default()
.flags(vk::CommandBufferUsageFlags::ONE_TIME_SUBMIT),
)?
}
let result = f(self.cmd_buffer);
unsafe {
self.device.end_command_buffer(self.cmd_buffer)?;
self.device.queue_submit(
self.graphics_queue,
&[vk::SubmitInfo::default().command_buffers(&[self.cmd_buffer])],
vk::Fence::null(),
)?;
self.device.queue_wait_idle(self.graphics_queue)?;
self.device
.reset_command_buffer(self.cmd_buffer, vk::CommandBufferResetFlags::empty())?;
}
Ok(result)
}
}
impl Drop for VulkanBase {
fn drop(&mut self) {
unsafe {
self.device.destroy_command_pool(self._pool, None);
}
}
}

View file

@ -0,0 +1,136 @@
use ash::vk;
use gpu_allocator::vulkan::{
Allocation, AllocationCreateDesc, AllocationScheme, Allocator, AllocatorCreateDesc,
};
use gpu_allocator::{AllocationSizes, MemoryLocation};
use parking_lot::Mutex;
use anyhow::anyhow;
use std::mem::ManuallyDrop;
use std::sync::Arc;
pub struct VulkanImageMemory {
pub(crate) allocation: ManuallyDrop<Allocation>,
allocator: Arc<Mutex<Allocator>>,
}
impl VulkanImageMemory {
pub fn new(
device: &Arc<ash::Device>,
allocator: &Arc<Mutex<Allocator>>,
requirements: vk::MemoryRequirements,
image: &vk::Image,
location: MemoryLocation,
) -> anyhow::Result<VulkanImageMemory> {
let allocation = allocator.lock().allocate(&AllocationCreateDesc {
name: "imagemem",
requirements,
location,
linear: false,
allocation_scheme: AllocationScheme::DedicatedImage(*image),
})?;
unsafe {
device.bind_image_memory(*image, allocation.memory(), 0)?;
Ok(VulkanImageMemory {
allocation: ManuallyDrop::new(allocation),
allocator: Arc::clone(allocator),
})
}
}
}
impl Drop for VulkanImageMemory {
fn drop(&mut self) {
let allocation = unsafe { ManuallyDrop::take(&mut self.allocation) };
if let Err(e) = self.allocator.lock().free(allocation) {
println!("librashader-runtime-vk: [warn] failed to deallocate image buffer {e}")
}
}
}
pub struct VulkanBuffer {
pub handle: vk::Buffer,
device: Arc<ash::Device>,
allocation: ManuallyDrop<Allocation>,
allocator: Arc<Mutex<Allocator>>,
size: vk::DeviceSize,
}
impl VulkanBuffer {
pub fn new(
device: &Arc<ash::Device>,
allocator: &Arc<Mutex<Allocator>>,
usage: vk::BufferUsageFlags,
size: usize,
) -> anyhow::Result<VulkanBuffer> {
unsafe {
let buffer_info = vk::BufferCreateInfo::default()
.size(size as vk::DeviceSize)
.usage(usage)
.sharing_mode(vk::SharingMode::EXCLUSIVE);
let buffer = device.create_buffer(&buffer_info, None)?;
let memory_reqs = device.get_buffer_memory_requirements(buffer);
let alloc = allocator.lock().allocate(&AllocationCreateDesc {
name: "buffer",
requirements: memory_reqs,
location: MemoryLocation::CpuToGpu,
linear: true,
allocation_scheme: AllocationScheme::DedicatedBuffer(buffer),
})?;
// let alloc = device.allocate_memory(&alloc_info, None)?;
device.bind_buffer_memory(buffer, alloc.memory(), 0)?;
Ok(VulkanBuffer {
handle: buffer,
allocation: ManuallyDrop::new(alloc),
allocator: Arc::clone(allocator),
size: size as vk::DeviceSize,
device: device.clone(),
})
}
}
pub fn as_mut_slice(&mut self) -> anyhow::Result<&mut [u8]> {
let Some(allocation) = self.allocation.mapped_slice_mut() else {
return Err(anyhow!("Allocation is not host visible"));
};
Ok(allocation)
}
}
impl Drop for VulkanBuffer {
fn drop(&mut self) {
unsafe {
// SAFETY: things can not be double dropped.
let allocation = ManuallyDrop::take(&mut self.allocation);
if let Err(e) = self.allocator.lock().free(allocation) {
println!("librashader-test-vk: [warn] failed to deallocate buffer memory {e}")
}
if self.handle != vk::Buffer::null() {
self.device.destroy_buffer(self.handle, None);
}
}
}
}
pub fn create_allocator(
device: ash::Device,
instance: ash::Instance,
physical_device: vk::PhysicalDevice,
) -> Arc<Mutex<Allocator>> {
let alloc = Allocator::new(&AllocatorCreateDesc {
instance,
device,
physical_device,
debug_settings: Default::default(),
buffer_device_address: false,
allocation_sizes: AllocationSizes::default(),
})
.unwrap();
Arc::new(Mutex::new(alloc))
}

View file

@ -0,0 +1,360 @@
use crate::render::vk::base::VulkanBase;
use crate::render::vk::memory::{VulkanBuffer, VulkanImageMemory};
use crate::render::RenderTest;
use anyhow::anyhow;
use ash::vk;
use gpu_allocator::MemoryLocation;
use image::RgbaImage;
use librashader::runtime::vk::{FilterChain, FilterChainOptions, VulkanImage};
use librashader::runtime::Viewport;
use librashader_runtime::image::{Image, UVDirection, BGRA8};
use std::io::{Cursor, Write};
use std::path::Path;
mod base;
mod memory;
mod physical_device;
mod util;
pub struct Vulkan {
vk: VulkanBase,
image_bytes: Image<BGRA8>,
image: vk::Image,
view: vk::ImageView,
image_alloc: VulkanImageMemory,
}
impl RenderTest for Vulkan {
fn render(&self, path: impl AsRef<Path>, frame_count: usize) -> anyhow::Result<RgbaImage> {
unsafe {
let mut filter_chain = FilterChain::load_from_path(
path,
&self.vk,
Some(&FilterChainOptions {
frames_in_flight: 3,
force_no_mipmaps: false,
use_dynamic_rendering: false,
disable_cache: true,
}),
)?;
let image_info = vk::ImageCreateInfo::default()
.image_type(vk::ImageType::TYPE_2D)
.format(vk::Format::B8G8R8A8_UNORM)
.extent(self.image_bytes.size.into())
.mip_levels(1)
.array_layers(1)
.samples(vk::SampleCountFlags::TYPE_1)
.tiling(vk::ImageTiling::OPTIMAL)
.usage(vk::ImageUsageFlags::COLOR_ATTACHMENT | vk::ImageUsageFlags::TRANSFER_SRC)
.initial_layout(vk::ImageLayout::UNDEFINED);
let render_texture = unsafe { self.vk.device().create_image(&image_info, None)? };
// This just needs to stay alive until the read.
let _memory = unsafe {
let mem_reqs = self
.vk
.device()
.get_image_memory_requirements(render_texture);
VulkanImageMemory::new(
self.vk.device(),
&self.vk.allocator(),
mem_reqs,
&render_texture,
MemoryLocation::GpuOnly,
)?
};
let transfer_texture = self.vk.device().create_image(
&vk::ImageCreateInfo::default()
.image_type(vk::ImageType::TYPE_2D)
.format(vk::Format::R8G8B8A8_UNORM)
.extent(self.image_bytes.size.into())
.mip_levels(1)
.array_layers(1)
.samples(vk::SampleCountFlags::TYPE_1)
.tiling(vk::ImageTiling::LINEAR)
.usage(vk::ImageUsageFlags::TRANSFER_DST)
.initial_layout(vk::ImageLayout::UNDEFINED),
None,
)?;
let mut transfer_memory = unsafe {
let mem_reqs = self
.vk
.device()
.get_image_memory_requirements(transfer_texture);
VulkanImageMemory::new(
self.vk.device(),
&self.vk.allocator(),
mem_reqs,
&transfer_texture,
MemoryLocation::GpuToCpu,
)?
};
self.vk.queue_work(|cmd| unsafe {
unsafe {
util::vulkan_image_layout_transition_levels(
&self.vk.device(),
cmd,
render_texture,
vk::REMAINING_MIP_LEVELS,
vk::ImageLayout::UNDEFINED,
vk::ImageLayout::COLOR_ATTACHMENT_OPTIMAL,
vk::AccessFlags::COLOR_ATTACHMENT_WRITE,
vk::AccessFlags::COLOR_ATTACHMENT_WRITE,
vk::PipelineStageFlags::ALL_GRAPHICS,
vk::PipelineStageFlags::ALL_GRAPHICS,
vk::QUEUE_FAMILY_IGNORED,
vk::QUEUE_FAMILY_IGNORED,
);
util::vulkan_image_layout_transition_levels(
&self.vk.device(),
cmd,
transfer_texture,
vk::REMAINING_MIP_LEVELS,
vk::ImageLayout::UNDEFINED,
vk::ImageLayout::GENERAL,
vk::AccessFlags::TRANSFER_WRITE | vk::AccessFlags::TRANSFER_READ,
vk::AccessFlags::TRANSFER_WRITE | vk::AccessFlags::TRANSFER_READ,
vk::PipelineStageFlags::ALL_GRAPHICS,
vk::PipelineStageFlags::TRANSFER,
vk::QUEUE_FAMILY_IGNORED,
vk::QUEUE_FAMILY_IGNORED,
)
}
filter_chain.frame(
&VulkanImage {
image: self.image,
size: self.image_bytes.size,
format: vk::Format::B8G8R8A8_UNORM,
},
&Viewport::new_render_target_sized_origin(
VulkanImage {
image: render_texture,
size: self.image_bytes.size.into(),
format: vk::Format::B8G8R8A8_UNORM,
},
None,
)?,
cmd,
frame_count,
None,
)?;
unsafe {
util::vulkan_image_layout_transition_levels(
&self.vk.device(),
cmd,
render_texture,
vk::REMAINING_MIP_LEVELS,
vk::ImageLayout::COLOR_ATTACHMENT_OPTIMAL,
vk::ImageLayout::TRANSFER_SRC_OPTIMAL,
vk::AccessFlags::COLOR_ATTACHMENT_WRITE,
vk::AccessFlags::TRANSFER_READ,
vk::PipelineStageFlags::ALL_GRAPHICS,
vk::PipelineStageFlags::TRANSFER,
vk::QUEUE_FAMILY_IGNORED,
vk::QUEUE_FAMILY_IGNORED,
)
}
let offsets = [
vk::Offset3D { x: 0, y: 0, z: 0 },
vk::Offset3D {
x: self.image_bytes.size.width as i32,
y: self.image_bytes.size.height as i32,
z: 1,
},
];
let subresource = vk::ImageSubresourceLayers::default()
.aspect_mask(vk::ImageAspectFlags::COLOR)
.base_array_layer(0)
.layer_count(1);
let image_blit = vk::ImageBlit::default()
.src_subresource(subresource.clone())
.src_offsets(offsets.clone())
.dst_subresource(subresource)
.dst_offsets(offsets);
self.vk.device().cmd_blit_image(
cmd,
render_texture,
vk::ImageLayout::TRANSFER_SRC_OPTIMAL,
transfer_texture,
vk::ImageLayout::GENERAL,
&[image_blit],
vk::Filter::NEAREST,
);
Ok::<_, anyhow::Error>(())
})??;
// should have read now.
let mut memory = transfer_memory
.allocation
.mapped_slice_mut()
.ok_or(anyhow!("readback buffer was not mapped"))?;
let layout = self.vk.device().get_image_subresource_layout(
transfer_texture,
vk::ImageSubresource::default()
.aspect_mask(vk::ImageAspectFlags::COLOR)
.array_layer(0),
);
memory = &mut memory[layout.offset as usize..];
// let mut pixels = Vec::with_capacity(layout.size as usize);
let image = RgbaImage::from_raw(
self.image_bytes.size.width,
self.image_bytes.size.height,
Vec::from(memory),
)
.ok_or(anyhow!("failed to create image from slice"));
self.vk.device().destroy_image(transfer_texture, None);
self.vk.device().destroy_image(render_texture, None);
Ok(image?)
}
}
}
impl Vulkan {
pub fn new(image_path: impl AsRef<Path>) -> anyhow::Result<Self> {
let vk = VulkanBase::new()?;
let (image_bytes, image_alloc, image, view) = Self::load_image(&vk, image_path)?;
Ok(Self {
vk,
image,
image_bytes,
view,
image_alloc,
})
}
pub fn load_image(
vk: &VulkanBase,
image_path: impl AsRef<Path>,
) -> anyhow::Result<(Image<BGRA8>, VulkanImageMemory, vk::Image, vk::ImageView)> {
let image: Image<BGRA8> = Image::load(image_path, UVDirection::TopLeft)?;
let image_info = vk::ImageCreateInfo::default()
.image_type(vk::ImageType::TYPE_2D)
.format(vk::Format::B8G8R8A8_UNORM)
.extent(image.size.into())
.mip_levels(1)
.array_layers(1)
.samples(vk::SampleCountFlags::TYPE_1)
.tiling(vk::ImageTiling::OPTIMAL)
.usage(
vk::ImageUsageFlags::SAMPLED
| vk::ImageUsageFlags::TRANSFER_SRC
| vk::ImageUsageFlags::TRANSFER_DST,
)
.initial_layout(vk::ImageLayout::UNDEFINED);
let texture = unsafe { vk.device().create_image(&image_info, None)? };
let memory = unsafe {
let mem_reqs = vk.device().get_image_memory_requirements(texture);
VulkanImageMemory::new(
vk.device(),
&vk.allocator(),
mem_reqs,
&texture,
MemoryLocation::GpuOnly,
)?
};
let image_subresource = vk::ImageSubresourceRange::default()
.level_count(image_info.mip_levels)
.layer_count(1)
.aspect_mask(vk::ImageAspectFlags::COLOR);
let swizzle_components = vk::ComponentMapping::default()
.r(vk::ComponentSwizzle::R)
.g(vk::ComponentSwizzle::G)
.b(vk::ComponentSwizzle::B)
.a(vk::ComponentSwizzle::A);
let view_info = vk::ImageViewCreateInfo::default()
.view_type(vk::ImageViewType::TYPE_2D)
.format(vk::Format::B8G8R8A8_UNORM)
.image(texture)
.subresource_range(image_subresource)
.components(swizzle_components);
let texture_view = unsafe { vk.device().create_image_view(&view_info, None)? };
let mut staging = VulkanBuffer::new(
&vk.device(),
&vk.allocator(),
vk::BufferUsageFlags::TRANSFER_SRC,
image.bytes.len(),
)?;
staging.as_mut_slice()?.copy_from_slice(&image.bytes);
vk.queue_work(|cmd| unsafe {
util::vulkan_image_layout_transition_levels(
&vk.device(),
cmd,
texture,
vk::REMAINING_MIP_LEVELS,
vk::ImageLayout::UNDEFINED,
vk::ImageLayout::TRANSFER_DST_OPTIMAL,
vk::AccessFlags::empty(),
vk::AccessFlags::TRANSFER_WRITE,
vk::PipelineStageFlags::TOP_OF_PIPE,
vk::PipelineStageFlags::TRANSFER,
vk::QUEUE_FAMILY_IGNORED,
vk::QUEUE_FAMILY_IGNORED,
);
let builder = vk::BufferImageCopy::default()
.image_subresource(
vk::ImageSubresourceLayers::default()
.aspect_mask(vk::ImageAspectFlags::COLOR)
.mip_level(0)
.base_array_layer(0)
.layer_count(1),
)
.image_extent(image.size.into());
vk.device().cmd_copy_buffer_to_image(
cmd,
staging.handle,
texture,
vk::ImageLayout::TRANSFER_DST_OPTIMAL,
&[builder],
);
util::vulkan_image_layout_transition_levels(
&vk.device(),
cmd,
texture,
vk::REMAINING_MIP_LEVELS,
vk::ImageLayout::TRANSFER_DST_OPTIMAL,
vk::ImageLayout::SHADER_READ_ONLY_OPTIMAL,
vk::AccessFlags::TRANSFER_WRITE,
vk::AccessFlags::SHADER_READ,
vk::PipelineStageFlags::TRANSFER,
vk::PipelineStageFlags::ALL_GRAPHICS,
vk::QUEUE_FAMILY_IGNORED,
vk::QUEUE_FAMILY_IGNORED,
);
})?;
Ok((image, memory, texture, texture_view))
}
}

View file

@ -0,0 +1,144 @@
use anyhow::anyhow;
use ash::vk;
use std::ffi::CStr;
pub struct QueueFamilyIndices {
graphics_family: Option<u32>,
}
impl QueueFamilyIndices {
pub fn is_complete(&self) -> bool {
self.graphics_family.is_some()
}
pub fn graphics_family(&self) -> anyhow::Result<u32> {
self.graphics_family
.ok_or(anyhow!("The queue family is not complete"))
}
}
pub(crate) fn pick_physical_device(instance: &ash::Instance) -> anyhow::Result<vk::PhysicalDevice> {
let physical_devices = unsafe { instance.enumerate_physical_devices()? };
let mut result = None;
for &physical_device in physical_devices.iter() {
if is_physical_device_suitable(instance, physical_device) && result.is_none() {
result = Some(physical_device)
}
}
result.ok_or(anyhow!("Failed to find a suitable GPU"))
}
fn is_physical_device_suitable(
instance: &ash::Instance,
physical_device: vk::PhysicalDevice,
) -> bool {
let device_properties = unsafe { instance.get_physical_device_properties(physical_device) };
let device_features = unsafe { instance.get_physical_device_features(physical_device) };
let device_queue_families =
unsafe { instance.get_physical_device_queue_family_properties(physical_device) };
if cfg!(feature = "vulkan-debug") {
let device_type = match device_properties.device_type {
vk::PhysicalDeviceType::CPU => "Cpu",
vk::PhysicalDeviceType::INTEGRATED_GPU => "Integrated GPU",
vk::PhysicalDeviceType::DISCRETE_GPU => "Discrete GPU",
vk::PhysicalDeviceType::VIRTUAL_GPU => "Virtual GPU",
vk::PhysicalDeviceType::OTHER => "Unknown",
_ => panic!(),
};
let device_name = unsafe { CStr::from_ptr(device_properties.device_name.as_ptr()) };
let device_name = device_name.to_string_lossy();
println!(
"\tDevice Name: {}, id: {}, type: {}",
device_name, device_properties.device_id, device_type
);
println!("\tAPI Version: {}", device_properties.api_version);
println!("\tSupport Queue Family: {}", device_queue_families.len());
println!("\t\tQueue Count | Graphics, Compute, Transfer, Sparse Binding");
for queue_family in device_queue_families.iter() {
let is_graphics_support = if queue_family.queue_flags.contains(vk::QueueFlags::GRAPHICS)
{
"support"
} else {
"unsupport"
};
let is_compute_support = if queue_family.queue_flags.contains(vk::QueueFlags::COMPUTE) {
"support"
} else {
"unsupport"
};
let is_transfer_support = if queue_family.queue_flags.contains(vk::QueueFlags::TRANSFER)
{
"support"
} else {
"unsupport"
};
let is_sparse_support = if queue_family
.queue_flags
.contains(vk::QueueFlags::SPARSE_BINDING)
{
"support"
} else {
"unsupport"
};
println!(
"\t\t{}\t | {}, {}, {}, {}",
queue_family.queue_count,
is_graphics_support,
is_compute_support,
is_transfer_support,
is_sparse_support
);
}
// there are plenty of features
println!(
"\tGeometry Shader support: {}",
if device_features.geometry_shader == 1 {
"support"
} else {
"unsupported"
}
);
}
let indices = find_queue_family(instance, physical_device);
indices.is_complete()
}
pub fn find_queue_family(
instance: &ash::Instance,
physical_device: vk::PhysicalDevice,
) -> QueueFamilyIndices {
let queue_families =
unsafe { instance.get_physical_device_queue_family_properties(physical_device) };
let mut queue_family_indices = QueueFamilyIndices {
graphics_family: None,
};
let mut index = 0;
for queue_family in queue_families.iter() {
if queue_family.queue_count > 0
&& queue_family.queue_flags.contains(vk::QueueFlags::GRAPHICS)
{
queue_family_indices.graphics_family = Some(index);
}
if queue_family_indices.is_complete() {
break;
}
index += 1;
}
queue_family_indices
}

View file

@ -0,0 +1,47 @@
use ash::vk;
use gpu_allocator::vulkan::{Allocator, AllocatorCreateDesc};
use gpu_allocator::AllocationSizes;
use parking_lot::Mutex;
use std::sync::Arc;
#[inline(always)]
pub unsafe fn vulkan_image_layout_transition_levels(
device: &ash::Device,
cmd: vk::CommandBuffer,
image: vk::Image,
levels: u32,
old_layout: vk::ImageLayout,
new_layout: vk::ImageLayout,
src_access: vk::AccessFlags,
dst_access: vk::AccessFlags,
src_stage: vk::PipelineStageFlags,
dst_stage: vk::PipelineStageFlags,
src_queue_family_index: u32,
dst_queue_family_index: u32,
) {
let mut barrier = vk::ImageMemoryBarrier::default();
barrier.s_type = vk::StructureType::IMAGE_MEMORY_BARRIER;
barrier.p_next = std::ptr::null();
barrier.src_access_mask = src_access;
barrier.dst_access_mask = dst_access;
barrier.old_layout = old_layout;
barrier.new_layout = new_layout;
barrier.src_queue_family_index = src_queue_family_index;
barrier.dst_queue_family_index = dst_queue_family_index;
barrier.image = image;
barrier.subresource_range.aspect_mask = vk::ImageAspectFlags::COLOR;
barrier.subresource_range.base_array_layer = 0;
barrier.subresource_range.level_count = levels;
barrier.subresource_range.layer_count = vk::REMAINING_ARRAY_LAYERS;
device.cmd_pipeline_barrier(
cmd,
src_stage,
dst_stage,
vk::DependencyFlags::empty(),
&[],
&[],
&[barrier],
)
}