render: implement a vulkan renderer
This commit is contained in:
parent
4ba8550da8
commit
cf332e8436
66 changed files with 4287 additions and 239 deletions
128
src/gfx_apis/vulkan/allocator.rs
Normal file
128
src/gfx_apis/vulkan/allocator.rs
Normal file
|
|
@ -0,0 +1,128 @@
|
|||
use {
|
||||
crate::{
|
||||
gfx_apis::vulkan::{device::VulkanDevice, instance::API_VERSION, VulkanError},
|
||||
utils::{numcell::NumCell, ptr_ext::MutPtrExt},
|
||||
},
|
||||
ash::vk::{DeviceMemory, DeviceSize, MemoryRequirements},
|
||||
gpu_alloc::{Config, GpuAllocator, MemoryBlock, Request, UsageFlags},
|
||||
gpu_alloc_ash::AshMemoryDevice,
|
||||
std::{
|
||||
cell::{Cell, UnsafeCell},
|
||||
rc::Rc,
|
||||
},
|
||||
};
|
||||
|
||||
pub struct VulkanAllocator {
|
||||
pub(super) device: Rc<VulkanDevice>,
|
||||
pub(super) non_coherent_atom_mask: u64,
|
||||
allocator: UnsafeCell<GpuAllocator<DeviceMemory>>,
|
||||
total: NumCell<u64>,
|
||||
}
|
||||
|
||||
pub struct VulkanAllocation {
|
||||
pub(super) allocator: Rc<VulkanAllocator>,
|
||||
pub(super) memory: DeviceMemory,
|
||||
pub(super) offset: DeviceSize,
|
||||
pub(super) mem: Option<*mut u8>,
|
||||
pub(super) size: DeviceSize,
|
||||
block: Cell<Option<MemoryBlock<DeviceMemory>>>,
|
||||
}
|
||||
|
||||
impl Drop for VulkanAllocation {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
self.allocator.total.fetch_sub(self.size);
|
||||
let mut block = self.block.take().unwrap();
|
||||
if let Some(_ptr) = self.mem {
|
||||
// log::info!("free = {:?} - {:?} ({})", ptr, ptr.add(block.size() as usize), block.size());
|
||||
block.unmap(AshMemoryDevice::wrap(&self.allocator.device.device));
|
||||
}
|
||||
self.allocator
|
||||
.allocator
|
||||
.get()
|
||||
.deref_mut()
|
||||
.dealloc(AshMemoryDevice::wrap(&self.allocator.device.device), block);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl VulkanDevice {
|
||||
pub fn create_allocator(self: &Rc<Self>) -> Result<Rc<VulkanAllocator>, VulkanError> {
|
||||
let config = Config::i_am_prototyping();
|
||||
let props = unsafe {
|
||||
gpu_alloc_ash::device_properties(
|
||||
&self.instance.instance,
|
||||
API_VERSION,
|
||||
self.physical_device,
|
||||
)
|
||||
};
|
||||
let mut props = props.map_err(VulkanError::GetDeviceProperties)?;
|
||||
props.buffer_device_address = false;
|
||||
let non_coherent_atom_size = props.non_coherent_atom_size;
|
||||
let allocator = GpuAllocator::new(config, props);
|
||||
Ok(Rc::new(VulkanAllocator {
|
||||
device: self.clone(),
|
||||
non_coherent_atom_mask: non_coherent_atom_size - 1,
|
||||
allocator: UnsafeCell::new(allocator),
|
||||
total: Default::default(),
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
impl VulkanAllocator {
|
||||
fn allocator(&self) -> &mut GpuAllocator<DeviceMemory> {
|
||||
unsafe { self.allocator.get().deref_mut() }
|
||||
}
|
||||
|
||||
pub fn alloc(
|
||||
self: &Rc<Self>,
|
||||
req: &MemoryRequirements,
|
||||
usage: UsageFlags,
|
||||
map: bool,
|
||||
) -> Result<VulkanAllocation, VulkanError> {
|
||||
let request = Request {
|
||||
size: req.size,
|
||||
align_mask: req.alignment - 1,
|
||||
usage,
|
||||
memory_types: req.memory_type_bits,
|
||||
};
|
||||
let block = unsafe {
|
||||
self.allocator()
|
||||
.alloc(AshMemoryDevice::wrap(&self.device.device), request)
|
||||
};
|
||||
let mut block = block.map_err(VulkanError::AllocateMemory2)?;
|
||||
let ptr = match map {
|
||||
true => {
|
||||
let ptr = unsafe {
|
||||
block.map(
|
||||
AshMemoryDevice::wrap(&self.device.device),
|
||||
0,
|
||||
block.size() as usize,
|
||||
)
|
||||
};
|
||||
Some(ptr.map_err(VulkanError::MapMemory)?.as_ptr())
|
||||
}
|
||||
false => None,
|
||||
};
|
||||
self.total.fetch_add(block.size());
|
||||
Ok(VulkanAllocation {
|
||||
allocator: self.clone(),
|
||||
memory: *block.memory(),
|
||||
offset: block.offset(),
|
||||
mem: ptr,
|
||||
size: block.size(),
|
||||
block: Cell::new(Some(block)),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for VulkanAllocator {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
self.allocator
|
||||
.get()
|
||||
.deref_mut()
|
||||
.cleanup(AshMemoryDevice::wrap(&self.device.device));
|
||||
}
|
||||
}
|
||||
}
|
||||
69
src/gfx_apis/vulkan/command.rs
Normal file
69
src/gfx_apis/vulkan/command.rs
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
use {
|
||||
crate::gfx_apis::vulkan::{device::VulkanDevice, VulkanError},
|
||||
ash::vk::{
|
||||
CommandBuffer, CommandBufferAllocateInfo, CommandBufferLevel, CommandPool,
|
||||
CommandPoolCreateFlags, CommandPoolCreateInfo,
|
||||
},
|
||||
std::rc::Rc,
|
||||
};
|
||||
|
||||
pub struct VulkanCommandPool {
|
||||
pub(super) device: Rc<VulkanDevice>,
|
||||
pub(super) pool: CommandPool,
|
||||
}
|
||||
|
||||
pub struct VulkanCommandBuffer {
|
||||
pub(super) pool: Rc<VulkanCommandPool>,
|
||||
pub(super) buffer: CommandBuffer,
|
||||
}
|
||||
|
||||
impl Drop for VulkanCommandPool {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
self.device.device.destroy_command_pool(self.pool, None);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for VulkanCommandBuffer {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
self.pool
|
||||
.device
|
||||
.device
|
||||
.free_command_buffers(self.pool.pool, &[self.buffer]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl VulkanCommandPool {
|
||||
pub fn allocate_buffer(self: &Rc<Self>) -> Result<Rc<VulkanCommandBuffer>, VulkanError> {
|
||||
let create_info = CommandBufferAllocateInfo::builder()
|
||||
.command_pool(self.pool)
|
||||
.command_buffer_count(1)
|
||||
.level(CommandBufferLevel::PRIMARY);
|
||||
let buffer = unsafe { self.device.device.allocate_command_buffers(&create_info) };
|
||||
let mut buffer = buffer.map_err(VulkanError::AllocateCommandBuffer)?;
|
||||
assert_eq!(buffer.len(), 1);
|
||||
Ok(Rc::new(VulkanCommandBuffer {
|
||||
pool: self.clone(),
|
||||
buffer: buffer.pop().unwrap(),
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
impl VulkanDevice {
|
||||
pub fn create_command_pool(self: &Rc<Self>) -> Result<Rc<VulkanCommandPool>, VulkanError> {
|
||||
let info = CommandPoolCreateInfo::builder()
|
||||
.queue_family_index(self.graphics_queue_idx)
|
||||
.flags(
|
||||
CommandPoolCreateFlags::TRANSIENT | CommandPoolCreateFlags::RESET_COMMAND_BUFFER,
|
||||
);
|
||||
let pool = unsafe { self.device.create_command_pool(&info, None) };
|
||||
let pool = pool.map_err(VulkanError::AllocateCommandPool)?;
|
||||
Ok(Rc::new(VulkanCommandPool {
|
||||
device: self.clone(),
|
||||
pool,
|
||||
}))
|
||||
}
|
||||
}
|
||||
49
src/gfx_apis/vulkan/descriptor.rs
Normal file
49
src/gfx_apis/vulkan/descriptor.rs
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
use {
|
||||
crate::gfx_apis::vulkan::{device::VulkanDevice, sampler::VulkanSampler, VulkanError},
|
||||
ash::vk::{
|
||||
DescriptorSetLayout, DescriptorSetLayoutBinding, DescriptorSetLayoutCreateFlags,
|
||||
DescriptorSetLayoutCreateInfo, DescriptorType, ShaderStageFlags,
|
||||
},
|
||||
std::{rc::Rc, slice},
|
||||
};
|
||||
|
||||
pub(super) struct VulkanDescriptorSetLayout {
|
||||
pub(super) device: Rc<VulkanDevice>,
|
||||
pub(super) layout: DescriptorSetLayout,
|
||||
pub(super) _sampler: Rc<VulkanSampler>,
|
||||
}
|
||||
|
||||
impl Drop for VulkanDescriptorSetLayout {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
self.device
|
||||
.device
|
||||
.destroy_descriptor_set_layout(self.layout, None);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl VulkanDevice {
|
||||
pub(super) fn create_descriptor_set_layout(
|
||||
&self,
|
||||
sampler: &Rc<VulkanSampler>,
|
||||
) -> Result<Rc<VulkanDescriptorSetLayout>, VulkanError> {
|
||||
let immutable_sampler = [sampler.sampler];
|
||||
let binding = DescriptorSetLayoutBinding::builder()
|
||||
.stage_flags(ShaderStageFlags::FRAGMENT)
|
||||
.immutable_samplers(&immutable_sampler)
|
||||
.descriptor_count(1)
|
||||
.descriptor_type(DescriptorType::COMBINED_IMAGE_SAMPLER)
|
||||
.build();
|
||||
let create_info = DescriptorSetLayoutCreateInfo::builder()
|
||||
.bindings(slice::from_ref(&binding))
|
||||
.flags(DescriptorSetLayoutCreateFlags::PUSH_DESCRIPTOR_KHR);
|
||||
let layout = unsafe { self.device.create_descriptor_set_layout(&create_info, None) };
|
||||
let layout = layout.map_err(VulkanError::CreateDescriptorSetLayout)?;
|
||||
Ok(Rc::new(VulkanDescriptorSetLayout {
|
||||
device: sampler.device.clone(),
|
||||
layout,
|
||||
_sampler: sampler.clone(),
|
||||
}))
|
||||
}
|
||||
}
|
||||
343
src/gfx_apis/vulkan/device.rs
Normal file
343
src/gfx_apis/vulkan/device.rs
Normal file
|
|
@ -0,0 +1,343 @@
|
|||
use {
|
||||
crate::{
|
||||
format::XRGB8888,
|
||||
gfx_apis::vulkan::{
|
||||
format::VulkanFormat,
|
||||
instance::{
|
||||
map_extension_properties, ApiVersionDisplay, Extensions, VulkanInstance,
|
||||
API_VERSION,
|
||||
},
|
||||
util::OnDrop,
|
||||
VulkanError,
|
||||
},
|
||||
video::{drm::Drm, gbm::GbmDevice},
|
||||
},
|
||||
ahash::AHashMap,
|
||||
arrayvec::ArrayVec,
|
||||
ash::{
|
||||
extensions::khr::{ExternalFenceFd, ExternalMemoryFd, ExternalSemaphoreFd, PushDescriptor},
|
||||
vk::{
|
||||
DeviceCreateInfo, DeviceMemory, DeviceQueueCreateInfo, ExtExternalMemoryDmaBufFn,
|
||||
ExtImageDrmFormatModifierFn, ExtPhysicalDeviceDrmFn, ExtQueueFamilyForeignFn,
|
||||
ExternalSemaphoreFeatureFlags, ExternalSemaphoreHandleTypeFlags,
|
||||
ExternalSemaphoreProperties, KhrDriverPropertiesFn, KhrExternalFenceFdFn,
|
||||
KhrExternalMemoryFdFn, KhrExternalSemaphoreFdFn, KhrPushDescriptorFn,
|
||||
MemoryPropertyFlags, MemoryType, PhysicalDevice, PhysicalDeviceDriverProperties,
|
||||
PhysicalDeviceDriverPropertiesKHR, PhysicalDeviceDrmPropertiesEXT,
|
||||
PhysicalDeviceDynamicRenderingFeatures, PhysicalDeviceExternalSemaphoreInfo,
|
||||
PhysicalDeviceProperties, PhysicalDeviceProperties2,
|
||||
PhysicalDeviceSynchronization2Features, PhysicalDeviceTimelineSemaphoreFeatures, Queue,
|
||||
QueueFlags, MAX_MEMORY_TYPES,
|
||||
},
|
||||
Device,
|
||||
},
|
||||
isnt::std_1::collections::IsntHashMap2Ext,
|
||||
std::{
|
||||
ffi::{CStr, CString},
|
||||
rc::Rc,
|
||||
},
|
||||
uapi::Ustr,
|
||||
};
|
||||
|
||||
pub struct VulkanDevice {
|
||||
pub(super) physical_device: PhysicalDevice,
|
||||
pub(super) render_node: Rc<CString>,
|
||||
pub(super) gbm: GbmDevice,
|
||||
pub(super) instance: Rc<VulkanInstance>,
|
||||
pub(super) device: Device,
|
||||
pub(super) external_memory_fd: ExternalMemoryFd,
|
||||
pub(super) external_semaphore_fd: ExternalSemaphoreFd,
|
||||
pub(super) external_fence_fd: ExternalFenceFd,
|
||||
pub(super) push_descriptor: PushDescriptor,
|
||||
pub(super) formats: AHashMap<u32, VulkanFormat>,
|
||||
pub(super) memory_types: ArrayVec<MemoryType, MAX_MEMORY_TYPES>,
|
||||
pub(super) graphics_queue: Queue,
|
||||
pub(super) graphics_queue_idx: u32,
|
||||
}
|
||||
|
||||
impl Drop for VulkanDevice {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
self.device.destroy_device(None);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl VulkanDevice {
|
||||
pub(super) fn find_memory_type(
|
||||
&self,
|
||||
flags: MemoryPropertyFlags,
|
||||
memory_type_bits: u32,
|
||||
) -> Option<u32> {
|
||||
for (idx, ty) in self.memory_types.iter().enumerate() {
|
||||
if memory_type_bits & (1 << idx as u32) != 0 {
|
||||
if ty.property_flags.contains(flags) {
|
||||
return Some(idx as _);
|
||||
}
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
struct FreeMem<'a>(&'a Device, DeviceMemory);
|
||||
|
||||
impl<'a> Drop for FreeMem<'a> {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
self.0.free_memory(self.1, None);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl VulkanInstance {
|
||||
fn get_device_extensions(&self, phy_dev: PhysicalDevice) -> Result<Extensions, VulkanError> {
|
||||
unsafe {
|
||||
self.instance
|
||||
.enumerate_device_extension_properties(phy_dev)
|
||||
.map(map_extension_properties)
|
||||
.map_err(VulkanError::DeviceExtensions)
|
||||
}
|
||||
}
|
||||
|
||||
fn find_dev(&self, drm: &Drm) -> Result<PhysicalDevice, VulkanError> {
|
||||
let stat = match uapi::fstat(drm.raw()) {
|
||||
Ok(s) => s,
|
||||
Err(e) => return Err(VulkanError::Fstat(e.into())),
|
||||
};
|
||||
let dev = stat.st_rdev;
|
||||
log::info!(
|
||||
"Searching for vulkan device with devnum {}:{}",
|
||||
uapi::major(dev),
|
||||
uapi::minor(dev)
|
||||
);
|
||||
let phy_devs = unsafe { self.instance.enumerate_physical_devices() };
|
||||
let phy_devs = match phy_devs {
|
||||
Ok(d) => d,
|
||||
Err(e) => return Err(VulkanError::EnumeratePhysicalDevices(e)),
|
||||
};
|
||||
let mut devices = vec![];
|
||||
for phy_dev in phy_devs {
|
||||
let props = unsafe { self.instance.get_physical_device_properties(phy_dev) };
|
||||
if props.api_version < API_VERSION {
|
||||
devices.push((props, None, None));
|
||||
continue;
|
||||
}
|
||||
let extensions = match self.get_device_extensions(phy_dev) {
|
||||
Ok(e) => e,
|
||||
Err(e) => {
|
||||
log::error!(
|
||||
"Could not enumerate extensions of device with id {}: {:#}",
|
||||
props.device_id,
|
||||
e
|
||||
);
|
||||
devices.push((props, None, None));
|
||||
continue;
|
||||
}
|
||||
};
|
||||
if !extensions.contains_key(ExtPhysicalDeviceDrmFn::name()) {
|
||||
devices.push((props, Some(extensions), None));
|
||||
continue;
|
||||
}
|
||||
let has_driver_props = extensions.contains_key(KhrDriverPropertiesFn::name());
|
||||
let mut drm_props = PhysicalDeviceDrmPropertiesEXT::builder().build();
|
||||
let mut driver_props = PhysicalDeviceDriverPropertiesKHR::builder().build();
|
||||
let mut props2 = PhysicalDeviceProperties2::builder().push_next(&mut drm_props);
|
||||
if has_driver_props {
|
||||
props2 = props2.push_next(&mut driver_props);
|
||||
}
|
||||
unsafe {
|
||||
self.instance
|
||||
.get_physical_device_properties2(phy_dev, &mut props2);
|
||||
}
|
||||
let primary_dev =
|
||||
uapi::makedev(drm_props.primary_major as _, drm_props.primary_minor as _);
|
||||
let render_dev =
|
||||
uapi::makedev(drm_props.render_major as _, drm_props.render_minor as _);
|
||||
if primary_dev == dev || render_dev == dev {
|
||||
log::info!("Device with id {} matches", props.device_id);
|
||||
log_device(&props, Some(&extensions), Some(&driver_props));
|
||||
return Ok(phy_dev);
|
||||
}
|
||||
devices.push((props, Some(extensions), Some(driver_props)));
|
||||
}
|
||||
if devices.is_empty() {
|
||||
log::warn!("Found no devices");
|
||||
} else {
|
||||
log::warn!("Found the following devices but none matches:");
|
||||
for (props, extensions, driver_props) in devices.iter() {
|
||||
log::warn!("Found the following devices but none matches:");
|
||||
log::warn!("-----");
|
||||
log_device(props, extensions.as_ref(), driver_props.as_ref());
|
||||
}
|
||||
}
|
||||
Err(VulkanError::NoDeviceFound(dev))
|
||||
}
|
||||
|
||||
fn find_graphics_queue(&self, phy_dev: PhysicalDevice) -> Result<u32, VulkanError> {
|
||||
let props = unsafe {
|
||||
self.instance
|
||||
.get_physical_device_queue_family_properties(phy_dev)
|
||||
};
|
||||
props
|
||||
.iter()
|
||||
.position(|p| p.queue_flags.contains(QueueFlags::GRAPHICS))
|
||||
.map(|v| v as _)
|
||||
.ok_or(VulkanError::NoGraphicsQueue)
|
||||
}
|
||||
|
||||
fn supports_semaphore_import(&self, phy_dev: PhysicalDevice) -> bool {
|
||||
let mut props = ExternalSemaphoreProperties::builder().build();
|
||||
let info = PhysicalDeviceExternalSemaphoreInfo::builder()
|
||||
.handle_type(ExternalSemaphoreHandleTypeFlags::OPAQUE_FD)
|
||||
.build();
|
||||
unsafe {
|
||||
self.instance
|
||||
.get_physical_device_external_semaphore_properties(phy_dev, &info, &mut props);
|
||||
}
|
||||
props
|
||||
.external_semaphore_features
|
||||
.contains(ExternalSemaphoreFeatureFlags::IMPORTABLE)
|
||||
}
|
||||
|
||||
pub fn create_device(self: &Rc<Self>, drm: &Drm) -> Result<Rc<VulkanDevice>, VulkanError> {
|
||||
let render_node = drm
|
||||
.get_render_node()
|
||||
.map_err(VulkanError::FetchRenderNode)?
|
||||
.ok_or(VulkanError::NoRenderNode)
|
||||
.map(Rc::new)?;
|
||||
let gbm = GbmDevice::new(drm).map_err(VulkanError::Gbm)?;
|
||||
let phy_dev = self.find_dev(drm)?;
|
||||
let extensions = self.get_device_extensions(phy_dev)?;
|
||||
for &ext in REQUIRED_DEVICE_EXTENSIONS {
|
||||
if extensions.not_contains_key(ext) {
|
||||
return Err(VulkanError::MissingDeviceExtension(ext));
|
||||
}
|
||||
}
|
||||
let graphics_queue_idx = self.find_graphics_queue(phy_dev)?;
|
||||
if !self.supports_semaphore_import(phy_dev) {
|
||||
return Err(VulkanError::SyncobjImport);
|
||||
}
|
||||
let enabled_extensions: Vec<_> = REQUIRED_DEVICE_EXTENSIONS
|
||||
.iter()
|
||||
.map(|n| n.as_ptr())
|
||||
.collect();
|
||||
let mut semaphore_features =
|
||||
PhysicalDeviceTimelineSemaphoreFeatures::builder().timeline_semaphore(true);
|
||||
let mut synchronization2_features =
|
||||
PhysicalDeviceSynchronization2Features::builder().synchronization2(true);
|
||||
let mut dynamic_rendering_features =
|
||||
PhysicalDeviceDynamicRenderingFeatures::builder().dynamic_rendering(true);
|
||||
let queue_create_info = DeviceQueueCreateInfo::builder()
|
||||
.queue_family_index(graphics_queue_idx)
|
||||
.queue_priorities(&[1.0])
|
||||
.build();
|
||||
let device_create_info = DeviceCreateInfo::builder()
|
||||
.push_next(&mut semaphore_features)
|
||||
.push_next(&mut synchronization2_features)
|
||||
.push_next(&mut dynamic_rendering_features)
|
||||
.queue_create_infos(std::slice::from_ref(&queue_create_info))
|
||||
.enabled_extension_names(&enabled_extensions);
|
||||
let device = unsafe {
|
||||
self.instance
|
||||
.create_device(phy_dev, &device_create_info, None)
|
||||
};
|
||||
let device = match device {
|
||||
Ok(d) => d,
|
||||
Err(e) => return Err(VulkanError::CreateDevice(e)),
|
||||
};
|
||||
let destroy_device = OnDrop(|| unsafe { device.destroy_device(None) });
|
||||
let formats = self.load_formats(phy_dev)?;
|
||||
let supports_xrgb8888 = formats
|
||||
.get(&XRGB8888.drm)
|
||||
.map(|f| {
|
||||
let mut supports_rendering = false;
|
||||
let mut supports_texturing = false;
|
||||
f.modifiers.values().for_each(|v| {
|
||||
supports_rendering |= v.render_max_extents.is_some();
|
||||
supports_texturing |= v.texture_max_extents.is_some();
|
||||
});
|
||||
supports_rendering && supports_texturing
|
||||
})
|
||||
.unwrap_or(false);
|
||||
if !supports_xrgb8888 {
|
||||
return Err(VulkanError::XRGB8888);
|
||||
}
|
||||
destroy_device.forget();
|
||||
let external_memory_fd = ExternalMemoryFd::new(&self.instance, &device);
|
||||
let external_semaphore_fd = ExternalSemaphoreFd::new(&self.instance, &device);
|
||||
let external_fence_fd = ExternalFenceFd::new(&self.instance, &device);
|
||||
let push_descriptor = PushDescriptor::new(&self.instance, &device);
|
||||
let memory_properties =
|
||||
unsafe { self.instance.get_physical_device_memory_properties(phy_dev) };
|
||||
let memory_types = memory_properties.memory_types
|
||||
[..memory_properties.memory_type_count as _]
|
||||
.iter()
|
||||
.copied()
|
||||
.collect();
|
||||
let graphics_queue = unsafe { device.get_device_queue(graphics_queue_idx, 0) };
|
||||
Ok(Rc::new(VulkanDevice {
|
||||
physical_device: phy_dev,
|
||||
render_node,
|
||||
gbm,
|
||||
instance: self.clone(),
|
||||
device,
|
||||
external_memory_fd,
|
||||
external_semaphore_fd,
|
||||
external_fence_fd,
|
||||
push_descriptor,
|
||||
formats,
|
||||
memory_types,
|
||||
graphics_queue,
|
||||
graphics_queue_idx,
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
const REQUIRED_DEVICE_EXTENSIONS: &[&CStr] = &[
|
||||
KhrExternalMemoryFdFn::name(),
|
||||
KhrExternalSemaphoreFdFn::name(),
|
||||
KhrExternalFenceFdFn::name(),
|
||||
ExtExternalMemoryDmaBufFn::name(),
|
||||
ExtQueueFamilyForeignFn::name(),
|
||||
ExtImageDrmFormatModifierFn::name(),
|
||||
KhrPushDescriptorFn::name(),
|
||||
];
|
||||
|
||||
fn log_device(
|
||||
props: &PhysicalDeviceProperties,
|
||||
extensions: Option<&Extensions>,
|
||||
driver_props: Option<&PhysicalDeviceDriverProperties>,
|
||||
) {
|
||||
log::info!(" api version: {}", ApiVersionDisplay(props.api_version));
|
||||
log::info!(
|
||||
" driver version: {}",
|
||||
ApiVersionDisplay(props.driver_version)
|
||||
);
|
||||
log::info!(" vendor id: {}", props.vendor_id);
|
||||
log::info!(" device id: {}", props.device_id);
|
||||
log::info!(" device type: {:?}", props.device_type);
|
||||
unsafe {
|
||||
log::info!(
|
||||
" device name: {}",
|
||||
Ustr::from_ptr(props.device_name.as_ptr()).display()
|
||||
);
|
||||
}
|
||||
if props.api_version < API_VERSION {
|
||||
log::warn!(" device does not support vulkan 1.3");
|
||||
}
|
||||
if let Some(extensions) = extensions {
|
||||
if !extensions.contains_key(ExtPhysicalDeviceDrmFn::name()) {
|
||||
log::warn!(" device does support not the VK_EXT_physical_device_drm extension");
|
||||
}
|
||||
}
|
||||
if let Some(driver_props) = driver_props {
|
||||
unsafe {
|
||||
log::info!(
|
||||
" driver: {} ({})",
|
||||
Ustr::from_ptr(driver_props.driver_name.as_ptr()).display(),
|
||||
Ustr::from_ptr(driver_props.driver_info.as_ptr()).display()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
49
src/gfx_apis/vulkan/fence.rs
Normal file
49
src/gfx_apis/vulkan/fence.rs
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
use {
|
||||
crate::gfx_apis::vulkan::{device::VulkanDevice, VulkanError},
|
||||
ash::vk::{
|
||||
ExportFenceCreateInfo, ExternalFenceHandleTypeFlags, Fence, FenceCreateInfo,
|
||||
FenceGetFdInfoKHR,
|
||||
},
|
||||
std::rc::Rc,
|
||||
uapi::OwnedFd,
|
||||
};
|
||||
|
||||
pub struct VulkanFence {
|
||||
pub(super) device: Rc<VulkanDevice>,
|
||||
pub(super) fence: Fence,
|
||||
}
|
||||
|
||||
impl Drop for VulkanFence {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
self.device.device.destroy_fence(self.fence, None);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl VulkanDevice {
|
||||
pub fn create_fence(self: &Rc<Self>) -> Result<Rc<VulkanFence>, VulkanError> {
|
||||
let fence = {
|
||||
let mut export_info = ExportFenceCreateInfo::builder()
|
||||
.handle_types(ExternalFenceHandleTypeFlags::SYNC_FD);
|
||||
let create_info = FenceCreateInfo::builder().push_next(&mut export_info);
|
||||
let fence = unsafe { self.device.create_fence(&create_info, None) };
|
||||
fence.map_err(VulkanError::CreateFence)?
|
||||
};
|
||||
Ok(Rc::new(VulkanFence {
|
||||
device: self.clone(),
|
||||
fence,
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
impl VulkanFence {
|
||||
pub fn export_syncfile(&self) -> Result<Rc<OwnedFd>, VulkanError> {
|
||||
let info = FenceGetFdInfoKHR::builder()
|
||||
.fence(self.fence)
|
||||
.handle_type(ExternalFenceHandleTypeFlags::SYNC_FD);
|
||||
let res = unsafe { self.device.external_fence_fd.get_fence_fd(&info) };
|
||||
res.map_err(VulkanError::ExportSyncFile)
|
||||
.map(|fd| Rc::new(OwnedFd::new(fd)))
|
||||
}
|
||||
}
|
||||
277
src/gfx_apis/vulkan/format.rs
Normal file
277
src/gfx_apis/vulkan/format.rs
Normal file
|
|
@ -0,0 +1,277 @@
|
|||
use {
|
||||
crate::{
|
||||
format::{Format, FORMATS},
|
||||
gfx_apis::vulkan::{instance::VulkanInstance, VulkanError},
|
||||
video::Modifier,
|
||||
},
|
||||
ahash::AHashMap,
|
||||
ash::{
|
||||
vk,
|
||||
vk::{
|
||||
DrmFormatModifierPropertiesEXT, DrmFormatModifierPropertiesListEXT,
|
||||
ExternalImageFormatProperties, ExternalMemoryFeatureFlags,
|
||||
ExternalMemoryHandleTypeFlags, FormatFeatureFlags, FormatProperties2,
|
||||
ImageFormatProperties2, ImageTiling, ImageType, ImageUsageFlags, PhysicalDevice,
|
||||
PhysicalDeviceExternalImageFormatInfo, PhysicalDeviceImageDrmFormatModifierInfoEXT,
|
||||
PhysicalDeviceImageFormatInfo2, SharingMode,
|
||||
},
|
||||
},
|
||||
isnt::std_1::collections::IsntHashMapExt,
|
||||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct VulkanFormat {
|
||||
pub format: &'static Format,
|
||||
pub modifiers: AHashMap<Modifier, VulkanModifier>,
|
||||
pub shm: Option<VulkanShmFormat>,
|
||||
pub features: FormatFeatureFlags,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct VulkanFormatFeatures {
|
||||
pub linear_sampling: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct VulkanModifier {
|
||||
pub modifier: Modifier,
|
||||
pub planes: usize,
|
||||
pub features: FormatFeatureFlags,
|
||||
pub render_max_extents: Option<VulkanMaxExtents>,
|
||||
pub texture_max_extents: Option<VulkanMaxExtents>,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct VulkanMaxExtents {
|
||||
pub width: u32,
|
||||
pub height: u32,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct VulkanShmFormat {
|
||||
pub max_extents: VulkanMaxExtents,
|
||||
}
|
||||
|
||||
const FRAMEBUFFER_FEATURES: FormatFeatureFlags = FormatFeatureFlags::from_raw(
|
||||
0 | FormatFeatureFlags::COLOR_ATTACHMENT.as_raw()
|
||||
| FormatFeatureFlags::COLOR_ATTACHMENT_BLEND.as_raw(),
|
||||
);
|
||||
const TEX_FEATURES: FormatFeatureFlags = FormatFeatureFlags::from_raw(
|
||||
0 | FormatFeatureFlags::SAMPLED_IMAGE.as_raw()
|
||||
| FormatFeatureFlags::TRANSFER_SRC.as_raw()
|
||||
| FormatFeatureFlags::SAMPLED_IMAGE_FILTER_LINEAR.as_raw(),
|
||||
);
|
||||
const SHM_FEATURES: FormatFeatureFlags = FormatFeatureFlags::from_raw(
|
||||
0 | FormatFeatureFlags::TRANSFER_SRC.as_raw()
|
||||
| FormatFeatureFlags::TRANSFER_DST.as_raw()
|
||||
| TEX_FEATURES.as_raw(),
|
||||
);
|
||||
|
||||
const FRAMEBUFFER_USAGE: ImageUsageFlags = ImageUsageFlags::from_raw(
|
||||
0 | ImageUsageFlags::COLOR_ATTACHMENT.as_raw() | ImageUsageFlags::TRANSFER_SRC.as_raw(),
|
||||
);
|
||||
const TEX_USAGE: ImageUsageFlags = ImageUsageFlags::from_raw(
|
||||
0 | ImageUsageFlags::SAMPLED.as_raw() | ImageUsageFlags::TRANSFER_SRC.as_raw(),
|
||||
);
|
||||
const SHM_USAGE: ImageUsageFlags = ImageUsageFlags::from_raw(
|
||||
0 | ImageUsageFlags::TRANSFER_SRC.as_raw()
|
||||
| ImageUsageFlags::TRANSFER_DST.as_raw()
|
||||
| TEX_USAGE.as_raw(),
|
||||
);
|
||||
|
||||
impl VulkanInstance {
|
||||
pub(super) fn load_formats(
|
||||
&self,
|
||||
phy_dev: PhysicalDevice,
|
||||
) -> Result<AHashMap<u32, VulkanFormat>, VulkanError> {
|
||||
let mut res = AHashMap::new();
|
||||
for format in FORMATS {
|
||||
self.load_format(phy_dev, format, &mut res)?;
|
||||
}
|
||||
Ok(res)
|
||||
}
|
||||
|
||||
fn load_format(
|
||||
&self,
|
||||
phy_dev: PhysicalDevice,
|
||||
format: &'static Format,
|
||||
dst: &mut AHashMap<u32, VulkanFormat>,
|
||||
) -> Result<(), VulkanError> {
|
||||
let mut modifier_props = DrmFormatModifierPropertiesListEXT::builder().build();
|
||||
let mut format_properties = FormatProperties2::builder()
|
||||
.push_next(&mut modifier_props)
|
||||
.build();
|
||||
unsafe {
|
||||
self.instance.get_physical_device_format_properties2(
|
||||
phy_dev,
|
||||
format.vk_format,
|
||||
&mut format_properties,
|
||||
);
|
||||
}
|
||||
let shm = self.load_shm_format(phy_dev, format, &format_properties)?;
|
||||
let modifiers = self.load_drm_format(phy_dev, format, &modifier_props)?;
|
||||
if shm.is_some() || modifiers.is_not_empty() {
|
||||
dst.insert(
|
||||
format.drm,
|
||||
VulkanFormat {
|
||||
format,
|
||||
modifiers,
|
||||
shm,
|
||||
features: format_properties.format_properties.optimal_tiling_features,
|
||||
},
|
||||
);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn load_shm_format(
|
||||
&self,
|
||||
phy_dev: PhysicalDevice,
|
||||
format: &Format,
|
||||
props: &FormatProperties2,
|
||||
) -> Result<Option<VulkanShmFormat>, VulkanError> {
|
||||
if !props
|
||||
.format_properties
|
||||
.optimal_tiling_features
|
||||
.contains(SHM_FEATURES)
|
||||
{
|
||||
return Ok(None);
|
||||
}
|
||||
let format_info = PhysicalDeviceImageFormatInfo2::builder()
|
||||
.ty(ImageType::TYPE_2D)
|
||||
.format(format.vk_format)
|
||||
.tiling(ImageTiling::OPTIMAL)
|
||||
.usage(SHM_USAGE);
|
||||
let mut format_properties = ImageFormatProperties2::builder();
|
||||
let res = unsafe {
|
||||
self.instance.get_physical_device_image_format_properties2(
|
||||
phy_dev,
|
||||
&format_info,
|
||||
&mut format_properties,
|
||||
)
|
||||
};
|
||||
if let Err(e) = res {
|
||||
return match e {
|
||||
vk::Result::ERROR_FORMAT_NOT_SUPPORTED => Ok(None),
|
||||
_ => Err(VulkanError::LoadImageProperties(e)),
|
||||
};
|
||||
}
|
||||
Ok(Some(VulkanShmFormat {
|
||||
max_extents: VulkanMaxExtents {
|
||||
width: format_properties.image_format_properties.max_extent.width,
|
||||
height: format_properties.image_format_properties.max_extent.height,
|
||||
},
|
||||
}))
|
||||
}
|
||||
|
||||
fn load_drm_format(
|
||||
&self,
|
||||
phy_dev: PhysicalDevice,
|
||||
format: &Format,
|
||||
props: &DrmFormatModifierPropertiesListEXT,
|
||||
) -> Result<AHashMap<Modifier, VulkanModifier>, VulkanError> {
|
||||
if props.drm_format_modifier_count == 0 {
|
||||
return Ok(AHashMap::new());
|
||||
}
|
||||
let mut drm_mods = vec![
|
||||
DrmFormatModifierPropertiesEXT::default();
|
||||
props.drm_format_modifier_count as usize
|
||||
];
|
||||
let mut modifier_props = DrmFormatModifierPropertiesListEXT::builder()
|
||||
.drm_format_modifier_properties(&mut drm_mods)
|
||||
.build();
|
||||
let mut format_properties = FormatProperties2::builder()
|
||||
.push_next(&mut modifier_props)
|
||||
.build();
|
||||
unsafe {
|
||||
self.instance.get_physical_device_format_properties2(
|
||||
phy_dev,
|
||||
format.vk_format,
|
||||
&mut format_properties,
|
||||
);
|
||||
};
|
||||
let mut mods = AHashMap::new();
|
||||
for modifier in drm_mods {
|
||||
let render_max_extents = self.get_max_extents(
|
||||
phy_dev,
|
||||
format,
|
||||
FRAMEBUFFER_FEATURES,
|
||||
FRAMEBUFFER_USAGE,
|
||||
&modifier,
|
||||
)?;
|
||||
let texture_max_extents =
|
||||
self.get_max_extents(phy_dev, format, TEX_FEATURES, TEX_USAGE, &modifier)?;
|
||||
mods.insert(
|
||||
modifier.drm_format_modifier,
|
||||
VulkanModifier {
|
||||
modifier: modifier.drm_format_modifier,
|
||||
planes: modifier.drm_format_modifier_plane_count as _,
|
||||
features: modifier.drm_format_modifier_tiling_features,
|
||||
render_max_extents,
|
||||
texture_max_extents,
|
||||
},
|
||||
);
|
||||
}
|
||||
Ok(mods)
|
||||
}
|
||||
|
||||
fn get_max_extents(
|
||||
&self,
|
||||
phy_dev: PhysicalDevice,
|
||||
format: &Format,
|
||||
features: FormatFeatureFlags,
|
||||
usage: ImageUsageFlags,
|
||||
props: &DrmFormatModifierPropertiesEXT,
|
||||
) -> Result<Option<VulkanMaxExtents>, VulkanError> {
|
||||
if !props.drm_format_modifier_tiling_features.contains(features) {
|
||||
return Ok(None);
|
||||
}
|
||||
let mut modifier_info = PhysicalDeviceImageDrmFormatModifierInfoEXT::builder()
|
||||
.drm_format_modifier(props.drm_format_modifier)
|
||||
.sharing_mode(SharingMode::EXCLUSIVE)
|
||||
.build();
|
||||
let mut external_image_format_info = PhysicalDeviceExternalImageFormatInfo::builder()
|
||||
.handle_type(ExternalMemoryHandleTypeFlags::DMA_BUF_EXT)
|
||||
.build();
|
||||
let image_format_info = PhysicalDeviceImageFormatInfo2::builder()
|
||||
.ty(ImageType::TYPE_2D)
|
||||
.format(format.vk_format)
|
||||
.usage(usage)
|
||||
.tiling(ImageTiling::DRM_FORMAT_MODIFIER_EXT)
|
||||
.push_next(&mut external_image_format_info)
|
||||
.push_next(&mut modifier_info)
|
||||
.build();
|
||||
|
||||
let mut external_image_format_props = ExternalImageFormatProperties::builder().build();
|
||||
let mut image_format_props = ImageFormatProperties2::builder()
|
||||
.push_next(&mut external_image_format_props)
|
||||
.build();
|
||||
|
||||
let res = unsafe {
|
||||
self.instance.get_physical_device_image_format_properties2(
|
||||
phy_dev,
|
||||
&image_format_info,
|
||||
&mut image_format_props,
|
||||
)
|
||||
};
|
||||
|
||||
if let Err(e) = res {
|
||||
return match e {
|
||||
vk::Result::ERROR_FORMAT_NOT_SUPPORTED => Ok(None),
|
||||
_ => Err(VulkanError::LoadImageProperties(e)),
|
||||
};
|
||||
}
|
||||
let importable = external_image_format_props
|
||||
.external_memory_properties
|
||||
.external_memory_features
|
||||
.contains(ExternalMemoryFeatureFlags::IMPORTABLE);
|
||||
if !importable {
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
Ok(Some(VulkanMaxExtents {
|
||||
width: image_format_props.image_format_properties.max_extent.width,
|
||||
height: image_format_props.image_format_properties.max_extent.height,
|
||||
}))
|
||||
}
|
||||
}
|
||||
582
src/gfx_apis/vulkan/image.rs
Normal file
582
src/gfx_apis/vulkan/image.rs
Normal file
|
|
@ -0,0 +1,582 @@
|
|||
use {
|
||||
crate::{
|
||||
format::Format,
|
||||
gfx_api::{GfxApiOpt, GfxError, GfxFramebuffer, GfxImage, GfxTexture},
|
||||
gfx_apis::vulkan::{
|
||||
allocator::VulkanAllocation, device::VulkanDevice, format::VulkanMaxExtents,
|
||||
renderer::VulkanRenderer, util::OnDrop, VulkanError,
|
||||
},
|
||||
theme::Color,
|
||||
utils::clonecell::CloneCell,
|
||||
video::{
|
||||
dmabuf::{DmaBuf, DmaBufPlane, PlaneVec},
|
||||
Modifier,
|
||||
},
|
||||
},
|
||||
ash::vk::{
|
||||
BindImageMemoryInfo, BindImagePlaneMemoryInfo, ComponentMapping, ComponentSwizzle,
|
||||
DeviceMemory, DeviceSize, Extent3D, ExternalMemoryHandleTypeFlags,
|
||||
ExternalMemoryImageCreateInfo, FormatFeatureFlags, Image, ImageAspectFlags,
|
||||
ImageCreateFlags, ImageCreateInfo, ImageDrmFormatModifierExplicitCreateInfoEXT,
|
||||
ImageLayout, ImageMemoryRequirementsInfo2, ImagePlaneMemoryRequirementsInfo,
|
||||
ImageSubresourceRange, ImageTiling, ImageType, ImageUsageFlags, ImageView,
|
||||
ImageViewCreateInfo, ImageViewType, ImportMemoryFdInfoKHR, MemoryAllocateInfo,
|
||||
MemoryDedicatedAllocateInfo, MemoryPropertyFlags, MemoryRequirements2, SampleCountFlags,
|
||||
SharingMode, SubresourceLayout,
|
||||
},
|
||||
gpu_alloc::UsageFlags,
|
||||
std::{
|
||||
any::Any,
|
||||
cell::{Cell, RefCell},
|
||||
fmt::{Debug, Formatter},
|
||||
mem,
|
||||
rc::Rc,
|
||||
},
|
||||
};
|
||||
|
||||
pub struct VulkanDmaBufImageTemplate {
|
||||
pub(super) renderer: Rc<VulkanRenderer>,
|
||||
pub(super) format: &'static Format,
|
||||
pub(super) width: u32,
|
||||
pub(super) height: u32,
|
||||
pub(super) modifier: Modifier,
|
||||
pub(super) disjoint: bool,
|
||||
pub(super) planes: PlaneVec<DmaBufPlane>,
|
||||
pub(super) render_max_extents: Option<VulkanMaxExtents>,
|
||||
pub(super) texture_max_extents: Option<VulkanMaxExtents>,
|
||||
}
|
||||
|
||||
pub struct VulkanImage {
|
||||
pub(super) renderer: Rc<VulkanRenderer>,
|
||||
pub(super) format: &'static Format,
|
||||
pub(super) width: u32,
|
||||
pub(super) height: u32,
|
||||
pub(super) stride: u32,
|
||||
pub(super) texture_view: ImageView,
|
||||
pub(super) render_view: Option<ImageView>,
|
||||
pub(super) image: Image,
|
||||
pub(super) is_undefined: Cell<bool>,
|
||||
pub(super) ty: VulkanImageMemory,
|
||||
pub(super) render_ops: CloneCell<Vec<GfxApiOpt>>,
|
||||
}
|
||||
|
||||
pub enum VulkanImageMemory {
|
||||
DmaBuf(VulkanDmaBufImage),
|
||||
Internal(VulkanShmImage),
|
||||
}
|
||||
|
||||
pub struct VulkanDmaBufImage {
|
||||
pub(super) template: Rc<VulkanDmaBufImageTemplate>,
|
||||
pub(super) mems: PlaneVec<DeviceMemory>,
|
||||
}
|
||||
|
||||
pub struct VulkanShmImage {
|
||||
pub(super) to_flush: RefCell<Option<Vec<u8>>>,
|
||||
pub(super) size: DeviceSize,
|
||||
pub(super) stride: u32,
|
||||
pub(super) _allocation: VulkanAllocation,
|
||||
}
|
||||
|
||||
impl Drop for VulkanDmaBufImage {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
for &mem in &self.mems {
|
||||
self.template.renderer.device.device.free_memory(mem, None);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for VulkanImage {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
self.renderer
|
||||
.device
|
||||
.device
|
||||
.destroy_image_view(self.texture_view, None);
|
||||
if let Some(render_view) = self.render_view {
|
||||
self.renderer
|
||||
.device
|
||||
.device
|
||||
.destroy_image_view(render_view, None);
|
||||
}
|
||||
self.renderer.device.device.destroy_image(self.image, None);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl VulkanShmImage {
|
||||
pub fn upload(&self, buffer: &[Cell<u8>]) -> Result<(), VulkanError> {
|
||||
let buffer = unsafe {
|
||||
std::slice::from_raw_parts(buffer.as_ptr() as *const u8, buffer.len()).to_vec()
|
||||
};
|
||||
*self.to_flush.borrow_mut() = Some(buffer);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl VulkanRenderer {
|
||||
pub fn create_shm_texture(
|
||||
self: &Rc<Self>,
|
||||
format: &'static Format,
|
||||
width: i32,
|
||||
height: i32,
|
||||
stride: i32,
|
||||
data: &[Cell<u8>],
|
||||
for_download: bool,
|
||||
) -> Result<Rc<VulkanImage>, VulkanError> {
|
||||
if width <= 0 || height <= 0 || stride <= 0 {
|
||||
return Err(VulkanError::NonPositiveImageSize);
|
||||
}
|
||||
let width = width as u32;
|
||||
let height = height as u32;
|
||||
let stride = stride as u32;
|
||||
if stride % format.bpp != 0 || stride / format.bpp < width {
|
||||
return Err(VulkanError::InvalidStride);
|
||||
}
|
||||
let vk_format = self
|
||||
.device
|
||||
.formats
|
||||
.get(&format.drm)
|
||||
.ok_or(VulkanError::FormatNotSupported)?;
|
||||
let shm = vk_format.shm.as_ref().ok_or(VulkanError::ShmNotSupported)?;
|
||||
if width > shm.max_extents.width || height > shm.max_extents.height {
|
||||
return Err(VulkanError::ImageTooLarge);
|
||||
}
|
||||
let size = stride.checked_mul(height).ok_or(VulkanError::ShmOverflow)?;
|
||||
let usage = ImageUsageFlags::TRANSFER_SRC
|
||||
| match for_download {
|
||||
true => ImageUsageFlags::COLOR_ATTACHMENT,
|
||||
false => ImageUsageFlags::SAMPLED | ImageUsageFlags::TRANSFER_DST,
|
||||
};
|
||||
let create_info = ImageCreateInfo::builder()
|
||||
.image_type(ImageType::TYPE_2D)
|
||||
.format(format.vk_format)
|
||||
.mip_levels(1)
|
||||
.array_layers(1)
|
||||
.tiling(ImageTiling::OPTIMAL)
|
||||
.samples(SampleCountFlags::TYPE_1)
|
||||
.sharing_mode(SharingMode::EXCLUSIVE)
|
||||
.initial_layout(ImageLayout::UNDEFINED)
|
||||
.extent(Extent3D {
|
||||
width,
|
||||
height,
|
||||
depth: 1,
|
||||
})
|
||||
.usage(usage)
|
||||
.build();
|
||||
let image = unsafe { self.device.device.create_image(&create_info, None) };
|
||||
let image = image.map_err(VulkanError::CreateImage)?;
|
||||
let destroy_image = OnDrop(|| unsafe { self.device.device.destroy_image(image, None) });
|
||||
let memory_requirements =
|
||||
unsafe { self.device.device.get_image_memory_requirements(image) };
|
||||
let allocation =
|
||||
self.allocator
|
||||
.alloc(&memory_requirements, UsageFlags::FAST_DEVICE_ACCESS, false)?;
|
||||
let res = unsafe {
|
||||
self.device
|
||||
.device
|
||||
.bind_image_memory(image, allocation.memory, allocation.offset)
|
||||
};
|
||||
res.map_err(VulkanError::BindImageMemory)?;
|
||||
let image_view_create_info = ImageViewCreateInfo::builder()
|
||||
.image(image)
|
||||
.format(format.vk_format)
|
||||
.view_type(ImageViewType::TYPE_2D)
|
||||
.subresource_range(ImageSubresourceRange {
|
||||
aspect_mask: ImageAspectFlags::COLOR,
|
||||
base_mip_level: 0,
|
||||
level_count: 1,
|
||||
base_array_layer: 0,
|
||||
layer_count: 1,
|
||||
});
|
||||
let view = unsafe {
|
||||
self.device
|
||||
.device
|
||||
.create_image_view(&image_view_create_info, None)
|
||||
};
|
||||
let view = view.map_err(VulkanError::CreateImageView)?;
|
||||
let shm = VulkanShmImage {
|
||||
to_flush: Default::default(),
|
||||
size: size as u64,
|
||||
stride,
|
||||
_allocation: allocation,
|
||||
};
|
||||
shm.upload(data)?;
|
||||
destroy_image.forget();
|
||||
Ok(Rc::new(VulkanImage {
|
||||
renderer: self.clone(),
|
||||
format,
|
||||
width,
|
||||
height,
|
||||
stride,
|
||||
texture_view: view,
|
||||
render_view: None,
|
||||
image,
|
||||
is_undefined: Cell::new(true),
|
||||
ty: VulkanImageMemory::Internal(shm),
|
||||
render_ops: Default::default(),
|
||||
}))
|
||||
}
|
||||
|
||||
pub fn import_dmabuf(
|
||||
self: &Rc<Self>,
|
||||
dmabuf: &DmaBuf,
|
||||
) -> Result<Rc<VulkanDmaBufImageTemplate>, VulkanError> {
|
||||
let format = self
|
||||
.device
|
||||
.formats
|
||||
.get(&dmabuf.format.drm)
|
||||
.ok_or(VulkanError::FormatNotSupported)?;
|
||||
let modifier = format
|
||||
.modifiers
|
||||
.get(&dmabuf.modifier)
|
||||
.ok_or(VulkanError::ModifierNotSupported)?;
|
||||
if dmabuf.width <= 0 || dmabuf.height <= 0 {
|
||||
return Err(VulkanError::NonPositiveImageSize);
|
||||
}
|
||||
let width = dmabuf.width as u32;
|
||||
let height = dmabuf.height as u32;
|
||||
let can_render = match &modifier.render_max_extents {
|
||||
None => false,
|
||||
Some(t) => width <= t.width && height <= t.height,
|
||||
};
|
||||
let can_texture = match &modifier.texture_max_extents {
|
||||
None => false,
|
||||
Some(t) => width <= t.width && height <= t.height,
|
||||
};
|
||||
if !can_render && !can_texture {
|
||||
if modifier.render_max_extents.is_none() && modifier.texture_max_extents.is_none() {
|
||||
return Err(VulkanError::ModifierUseNotSupported);
|
||||
}
|
||||
return Err(VulkanError::ImageTooLarge);
|
||||
}
|
||||
if modifier.planes != dmabuf.planes.len() {
|
||||
return Err(VulkanError::BadPlaneCount);
|
||||
}
|
||||
let disjoint = dmabuf.is_disjoint();
|
||||
if disjoint && !modifier.features.contains(FormatFeatureFlags::DISJOINT) {
|
||||
return Err(VulkanError::DisjointNotSupported);
|
||||
}
|
||||
Ok(Rc::new(VulkanDmaBufImageTemplate {
|
||||
renderer: self.clone(),
|
||||
format: dmabuf.format,
|
||||
width,
|
||||
height,
|
||||
modifier: dmabuf.modifier,
|
||||
disjoint,
|
||||
planes: dmabuf.planes.clone(),
|
||||
render_max_extents: modifier.render_max_extents,
|
||||
texture_max_extents: modifier.texture_max_extents,
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
impl VulkanDevice {
|
||||
pub fn create_image_view(
|
||||
&self,
|
||||
image: Image,
|
||||
format: &'static Format,
|
||||
for_rendering: bool,
|
||||
) -> Result<ImageView, VulkanError> {
|
||||
let create_info = ImageViewCreateInfo::builder()
|
||||
.image(image)
|
||||
.view_type(ImageViewType::TYPE_2D)
|
||||
.format(format.vk_format)
|
||||
.components(ComponentMapping {
|
||||
r: ComponentSwizzle::IDENTITY,
|
||||
g: ComponentSwizzle::IDENTITY,
|
||||
b: ComponentSwizzle::IDENTITY,
|
||||
a: match format.has_alpha || for_rendering {
|
||||
true => ComponentSwizzle::IDENTITY,
|
||||
false => ComponentSwizzle::ONE,
|
||||
},
|
||||
})
|
||||
.subresource_range(ImageSubresourceRange {
|
||||
aspect_mask: ImageAspectFlags::COLOR,
|
||||
base_mip_level: 0,
|
||||
level_count: 1,
|
||||
base_array_layer: 0,
|
||||
layer_count: 1,
|
||||
});
|
||||
let view = unsafe { self.device.create_image_view(&create_info, None) };
|
||||
view.map_err(VulkanError::CreateImageView)
|
||||
}
|
||||
}
|
||||
|
||||
impl VulkanDmaBufImageTemplate {
|
||||
pub fn create_framebuffer(self: &Rc<Self>) -> Result<Rc<VulkanImage>, VulkanError> {
|
||||
self.create_image(true, None)
|
||||
}
|
||||
|
||||
pub fn create_texture(
|
||||
self: &Rc<Self>,
|
||||
shm: Option<VulkanShmImage>,
|
||||
) -> Result<Rc<VulkanImage>, VulkanError> {
|
||||
self.create_image(false, shm)
|
||||
}
|
||||
|
||||
fn create_image(
|
||||
self: &Rc<Self>,
|
||||
for_rendering: bool,
|
||||
shm: Option<VulkanShmImage>,
|
||||
) -> Result<Rc<VulkanImage>, VulkanError> {
|
||||
let device = &self.renderer.device;
|
||||
let max_extents = match for_rendering {
|
||||
true => self.render_max_extents,
|
||||
false => self.texture_max_extents,
|
||||
};
|
||||
let max_extents = max_extents.ok_or(VulkanError::ModifierUseNotSupported)?;
|
||||
if self.width > max_extents.width || self.height > max_extents.height {
|
||||
return Err(VulkanError::ImageTooLarge);
|
||||
}
|
||||
let image = {
|
||||
let plane_layouts: PlaneVec<_> = self
|
||||
.planes
|
||||
.iter()
|
||||
.map(|p| SubresourceLayout {
|
||||
offset: p.offset as _,
|
||||
row_pitch: p.stride as _,
|
||||
size: 0,
|
||||
array_pitch: 0,
|
||||
depth_pitch: 0,
|
||||
})
|
||||
.collect();
|
||||
let mut mod_info = ImageDrmFormatModifierExplicitCreateInfoEXT::builder()
|
||||
.drm_format_modifier(self.modifier)
|
||||
.plane_layouts(&plane_layouts)
|
||||
.build();
|
||||
let mut memory_image_create_info = ExternalMemoryImageCreateInfo::builder()
|
||||
.handle_types(ExternalMemoryHandleTypeFlags::DMA_BUF_EXT)
|
||||
.build();
|
||||
let flags = match self.disjoint {
|
||||
true => ImageCreateFlags::DISJOINT,
|
||||
false => ImageCreateFlags::empty(),
|
||||
};
|
||||
let usage = ImageUsageFlags::TRANSFER_SRC
|
||||
| match (for_rendering, shm.is_some()) {
|
||||
(true, _) => ImageUsageFlags::COLOR_ATTACHMENT,
|
||||
(false, false) => ImageUsageFlags::SAMPLED,
|
||||
(false, true) => ImageUsageFlags::SAMPLED | ImageUsageFlags::TRANSFER_DST,
|
||||
};
|
||||
let create_info = ImageCreateInfo::builder()
|
||||
.image_type(ImageType::TYPE_2D)
|
||||
.format(self.format.vk_format)
|
||||
.mip_levels(1)
|
||||
.array_layers(1)
|
||||
.tiling(ImageTiling::DRM_FORMAT_MODIFIER_EXT)
|
||||
.samples(SampleCountFlags::TYPE_1)
|
||||
.sharing_mode(SharingMode::EXCLUSIVE)
|
||||
.initial_layout(ImageLayout::UNDEFINED)
|
||||
.extent(Extent3D {
|
||||
width: self.width,
|
||||
height: self.height,
|
||||
depth: 1,
|
||||
})
|
||||
.usage(usage)
|
||||
.flags(flags)
|
||||
.push_next(&mut memory_image_create_info)
|
||||
.push_next(&mut mod_info)
|
||||
.build();
|
||||
let image = unsafe { device.device.create_image(&create_info, None) };
|
||||
image.map_err(VulkanError::CreateImage)?
|
||||
};
|
||||
let destroy_image = OnDrop(|| unsafe { device.device.destroy_image(image, None) });
|
||||
let num_device_memories = match self.disjoint {
|
||||
true => self.planes.len(),
|
||||
false => 1,
|
||||
};
|
||||
let mut device_memories = PlaneVec::new();
|
||||
let mut free_device_memories = PlaneVec::new();
|
||||
let mut bind_image_plane_memory_infos = PlaneVec::new();
|
||||
for plane_idx in 0..num_device_memories {
|
||||
let dma_buf_plane = &self.planes[plane_idx];
|
||||
let memory_fd_properties = unsafe {
|
||||
device.external_memory_fd.get_memory_fd_properties(
|
||||
ExternalMemoryHandleTypeFlags::DMA_BUF_EXT,
|
||||
dma_buf_plane.fd.raw(),
|
||||
)
|
||||
};
|
||||
let memory_fd_properties =
|
||||
memory_fd_properties.map_err(VulkanError::MemoryFdProperties)?;
|
||||
let mut image_memory_requirements_info =
|
||||
ImageMemoryRequirementsInfo2::builder().image(image);
|
||||
let mut image_plane_memory_requirements_info;
|
||||
if self.disjoint {
|
||||
let plane_aspect = match plane_idx {
|
||||
0 => ImageAspectFlags::MEMORY_PLANE_0_EXT,
|
||||
1 => ImageAspectFlags::MEMORY_PLANE_1_EXT,
|
||||
2 => ImageAspectFlags::MEMORY_PLANE_2_EXT,
|
||||
3 => ImageAspectFlags::MEMORY_PLANE_3_EXT,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
image_plane_memory_requirements_info =
|
||||
ImagePlaneMemoryRequirementsInfo::builder().plane_aspect(plane_aspect);
|
||||
image_memory_requirements_info = image_memory_requirements_info
|
||||
.push_next(&mut image_plane_memory_requirements_info);
|
||||
bind_image_plane_memory_infos.push(
|
||||
BindImagePlaneMemoryInfo::builder()
|
||||
.plane_aspect(plane_aspect)
|
||||
.build(),
|
||||
);
|
||||
}
|
||||
let mut memory_requirements = MemoryRequirements2::default();
|
||||
unsafe {
|
||||
device.device.get_image_memory_requirements2(
|
||||
&image_memory_requirements_info,
|
||||
&mut memory_requirements,
|
||||
);
|
||||
}
|
||||
let memory_type_bits = memory_requirements.memory_requirements.memory_type_bits
|
||||
& memory_fd_properties.memory_type_bits;
|
||||
let memory_type_index = self
|
||||
.renderer
|
||||
.device
|
||||
.find_memory_type(MemoryPropertyFlags::empty(), memory_type_bits)
|
||||
.ok_or(VulkanError::MemoryType)?;
|
||||
let fd = uapi::fcntl_dupfd_cloexec(dma_buf_plane.fd.raw(), 0)
|
||||
.map_err(|e| VulkanError::Dupfd(e.into()))?;
|
||||
let mut memory_dedicated_allocate_info =
|
||||
MemoryDedicatedAllocateInfo::builder().image(image).build();
|
||||
let mut import_memory_fd_info = ImportMemoryFdInfoKHR::builder()
|
||||
.fd(fd.raw())
|
||||
.handle_type(ExternalMemoryHandleTypeFlags::DMA_BUF_EXT)
|
||||
.build();
|
||||
let memory_allocate_info = MemoryAllocateInfo::builder()
|
||||
.allocation_size(memory_requirements.memory_requirements.size)
|
||||
.memory_type_index(memory_type_index)
|
||||
.push_next(&mut import_memory_fd_info)
|
||||
.push_next(&mut memory_dedicated_allocate_info)
|
||||
.build();
|
||||
let device_memory =
|
||||
unsafe { device.device.allocate_memory(&memory_allocate_info, None) };
|
||||
let device_memory = device_memory.map_err(VulkanError::AllocateMemory)?;
|
||||
fd.unwrap();
|
||||
device_memories.push(device_memory);
|
||||
free_device_memories.push(OnDrop(move || unsafe {
|
||||
device.device.free_memory(device_memory, None)
|
||||
}));
|
||||
}
|
||||
let mut bind_image_memory_infos = Vec::with_capacity(num_device_memories);
|
||||
for (i, mem) in device_memories.iter().copied().enumerate() {
|
||||
let mut info = BindImageMemoryInfo::builder().image(image).memory(mem);
|
||||
if self.disjoint {
|
||||
info = info.push_next(&mut bind_image_plane_memory_infos[i]);
|
||||
}
|
||||
bind_image_memory_infos.push(info.build());
|
||||
}
|
||||
let res = unsafe { device.device.bind_image_memory2(&bind_image_memory_infos) };
|
||||
res.map_err(VulkanError::BindImageMemory)?;
|
||||
let texture_view = device.create_image_view(image, self.format, false)?;
|
||||
let render_view = device.create_image_view(image, self.format, true)?;
|
||||
free_device_memories.drain(..).for_each(mem::forget);
|
||||
mem::forget(destroy_image);
|
||||
Ok(Rc::new(VulkanImage {
|
||||
renderer: self.renderer.clone(),
|
||||
texture_view,
|
||||
render_view: Some(render_view),
|
||||
image,
|
||||
width: self.width,
|
||||
height: self.height,
|
||||
stride: 0,
|
||||
render_ops: Default::default(),
|
||||
ty: VulkanImageMemory::DmaBuf(VulkanDmaBufImage {
|
||||
template: self.clone(),
|
||||
mems: device_memories,
|
||||
}),
|
||||
format: self.format,
|
||||
is_undefined: Cell::new(true),
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
impl GfxImage for VulkanDmaBufImageTemplate {
|
||||
fn to_framebuffer(self: Rc<Self>) -> Result<Rc<dyn GfxFramebuffer>, GfxError> {
|
||||
self.create_framebuffer()
|
||||
.map(|v| v as _)
|
||||
.map_err(|e| e.into())
|
||||
}
|
||||
|
||||
fn to_texture(self: Rc<Self>) -> Result<Rc<dyn GfxTexture>, GfxError> {
|
||||
self.create_texture(None)
|
||||
.map(|v| v as _)
|
||||
.map_err(|e| e.into())
|
||||
}
|
||||
|
||||
fn width(&self) -> i32 {
|
||||
self.width as i32
|
||||
}
|
||||
|
||||
fn height(&self) -> i32 {
|
||||
self.height as i32
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for VulkanImage {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
f.debug_struct("VulkanDmaBufImage").finish_non_exhaustive()
|
||||
}
|
||||
}
|
||||
|
||||
impl GfxFramebuffer for VulkanImage {
|
||||
fn as_any(&self) -> &dyn Any {
|
||||
self
|
||||
}
|
||||
|
||||
fn take_render_ops(&self) -> Vec<GfxApiOpt> {
|
||||
self.render_ops.take()
|
||||
}
|
||||
|
||||
fn size(&self) -> (i32, i32) {
|
||||
(self.width as _, self.height as _)
|
||||
}
|
||||
|
||||
fn render(&self, ops: Vec<GfxApiOpt>, clear: Option<&Color>) {
|
||||
self.renderer.execute(self, &ops, clear).unwrap();
|
||||
}
|
||||
|
||||
fn copy_to_shm(
|
||||
&self,
|
||||
_x: i32,
|
||||
_y: i32,
|
||||
_width: i32,
|
||||
_height: i32,
|
||||
_format: &Format,
|
||||
_shm: &[Cell<u8>],
|
||||
) -> Result<(), GfxError> {
|
||||
return Err(VulkanError::UnsupportedOperation.into());
|
||||
}
|
||||
|
||||
fn format(&self) -> &'static Format {
|
||||
self.format
|
||||
}
|
||||
}
|
||||
|
||||
impl GfxTexture for VulkanImage {
|
||||
fn size(&self) -> (i32, i32) {
|
||||
(self.width as _, self.height as _)
|
||||
}
|
||||
|
||||
fn as_any(&self) -> &dyn Any {
|
||||
self
|
||||
}
|
||||
|
||||
fn into_any(self: Rc<Self>) -> Rc<dyn Any> {
|
||||
self
|
||||
}
|
||||
|
||||
fn read_pixels(
|
||||
self: Rc<Self>,
|
||||
x: i32,
|
||||
y: i32,
|
||||
width: i32,
|
||||
height: i32,
|
||||
stride: i32,
|
||||
format: &'static Format,
|
||||
shm: &[Cell<u8>],
|
||||
) -> Result<(), GfxError> {
|
||||
self.renderer
|
||||
.read_pixels(&self, x, y, width, height, stride, format, shm)
|
||||
.map_err(|e| e.into())
|
||||
}
|
||||
}
|
||||
228
src/gfx_apis/vulkan/instance.rs
Normal file
228
src/gfx_apis/vulkan/instance.rs
Normal file
|
|
@ -0,0 +1,228 @@
|
|||
use {
|
||||
crate::{
|
||||
async_engine::AsyncEngine,
|
||||
gfx_apis::vulkan::{util::OnDrop, VulkanError, VULKAN_VALIDATION},
|
||||
io_uring::IoUring,
|
||||
},
|
||||
ahash::{AHashMap, AHashSet},
|
||||
ash::{
|
||||
extensions::ext::DebugUtils,
|
||||
vk::{
|
||||
api_version_major, api_version_minor, api_version_patch, api_version_variant,
|
||||
ApplicationInfo, Bool32, DebugUtilsMessageSeverityFlagsEXT,
|
||||
DebugUtilsMessageTypeFlagsEXT, DebugUtilsMessengerCallbackDataEXT,
|
||||
DebugUtilsMessengerCreateInfoEXT, DebugUtilsMessengerEXT, ExtDebugUtilsFn,
|
||||
ExtValidationFeaturesFn, ExtensionProperties, InstanceCreateInfo, LayerProperties,
|
||||
ValidationFeaturesEXT, API_VERSION_1_3, FALSE,
|
||||
},
|
||||
Entry, Instance, LoadingError,
|
||||
},
|
||||
isnt::std_1::collections::IsntHashMap2Ext,
|
||||
log::Level,
|
||||
once_cell::sync::Lazy,
|
||||
std::{
|
||||
ffi::{c_void, CStr, CString},
|
||||
fmt::{Display, Formatter},
|
||||
iter::IntoIterator,
|
||||
rc::Rc,
|
||||
slice,
|
||||
sync::Arc,
|
||||
},
|
||||
uapi::{ustr, Ustr},
|
||||
};
|
||||
|
||||
pub struct VulkanInstance {
|
||||
pub(super) _entry: &'static Entry,
|
||||
pub(super) instance: Instance,
|
||||
pub(super) debug_utils: DebugUtils,
|
||||
pub(super) messenger: DebugUtilsMessengerEXT,
|
||||
pub(super) eng: Rc<AsyncEngine>,
|
||||
pub(super) ring: Rc<IoUring>,
|
||||
}
|
||||
|
||||
impl VulkanInstance {
|
||||
pub fn new(
|
||||
eng: &Rc<AsyncEngine>,
|
||||
ring: &Rc<IoUring>,
|
||||
validation: bool,
|
||||
) -> Result<Rc<Self>, VulkanError> {
|
||||
static ENTRY: Lazy<Result<Entry, Arc<LoadingError>>> =
|
||||
Lazy::new(|| unsafe { Entry::load() }.map_err(Arc::new));
|
||||
let entry = match &*ENTRY {
|
||||
Ok(e) => e,
|
||||
Err(e) => return Err(VulkanError::Load(e.clone())),
|
||||
};
|
||||
let extensions = get_instance_extensions(entry, None)?;
|
||||
for &ext in REQUIRED_INSTANCE_EXTENSIONS {
|
||||
if extensions.not_contains_key(ext) {
|
||||
return Err(VulkanError::MissingInstanceExtension(ext));
|
||||
}
|
||||
}
|
||||
let mut enabled_extensions: Vec<_> = REQUIRED_INSTANCE_EXTENSIONS
|
||||
.iter()
|
||||
.map(|c| c.as_ptr())
|
||||
.collect();
|
||||
let app_info = ApplicationInfo::builder()
|
||||
.api_version(API_VERSION)
|
||||
.application_name(ustr!("jay").as_c_str().unwrap())
|
||||
.application_version(1);
|
||||
let mut severity = DebugUtilsMessageSeverityFlagsEXT::empty()
|
||||
| DebugUtilsMessageSeverityFlagsEXT::ERROR
|
||||
| DebugUtilsMessageSeverityFlagsEXT::WARNING;
|
||||
if *VULKAN_VALIDATION {
|
||||
severity |= DebugUtilsMessageSeverityFlagsEXT::INFO
|
||||
| DebugUtilsMessageSeverityFlagsEXT::VERBOSE;
|
||||
}
|
||||
let types = DebugUtilsMessageTypeFlagsEXT::empty()
|
||||
| DebugUtilsMessageTypeFlagsEXT::VALIDATION
|
||||
| DebugUtilsMessageTypeFlagsEXT::PERFORMANCE
|
||||
| DebugUtilsMessageTypeFlagsEXT::GENERAL;
|
||||
let mut debug_info = DebugUtilsMessengerCreateInfoEXT::builder()
|
||||
.message_severity(severity)
|
||||
.message_type(types)
|
||||
.pfn_user_callback(Some(debug_callback));
|
||||
let validation_features = [
|
||||
// ash::vk::ValidationFeatureEnableEXT::DEBUG_PRINTF,
|
||||
// ash::vk::ValidationFeatureEnableEXT::BEST_PRACTICES,
|
||||
// ash::vk::ValidationFeatureEnableEXT::SYNCHRONIZATION_VALIDATION,
|
||||
// ash::vk::ValidationFeatureEnableEXT::GPU_ASSISTED,
|
||||
];
|
||||
let mut validation_info =
|
||||
ValidationFeaturesEXT::builder().enabled_validation_features(&validation_features);
|
||||
let mut create_info = InstanceCreateInfo::builder()
|
||||
.application_info(&app_info)
|
||||
.push_next(&mut debug_info);
|
||||
let validation_layer_name = VALIDATION_LAYER.as_ptr();
|
||||
if validation {
|
||||
if get_available_layers(entry)?.contains(VALIDATION_LAYER) {
|
||||
create_info =
|
||||
create_info.enabled_layer_names(slice::from_ref(&validation_layer_name));
|
||||
let extensions = get_instance_extensions(entry, Some(VALIDATION_LAYER))?;
|
||||
if extensions.contains_key(ExtValidationFeaturesFn::name()) {
|
||||
enabled_extensions.push(ExtValidationFeaturesFn::name().as_ptr());
|
||||
create_info = create_info.push_next(&mut validation_info);
|
||||
} else {
|
||||
log::warn!("{:?} is not available", ExtValidationFeaturesFn::name(),);
|
||||
}
|
||||
} else {
|
||||
log::warn!(
|
||||
"Vulkan validation was requested but validation layers are not available"
|
||||
);
|
||||
}
|
||||
}
|
||||
create_info = create_info.enabled_extension_names(&enabled_extensions);
|
||||
let instance = match unsafe { entry.create_instance(&create_info, None) } {
|
||||
Ok(i) => i,
|
||||
Err(e) => return Err(VulkanError::CreateInstance(e)),
|
||||
};
|
||||
let destroy_instance = OnDrop(|| unsafe { instance.destroy_instance(None) });
|
||||
let debug_utils = DebugUtils::new(entry, &instance);
|
||||
let messenger = unsafe { debug_utils.create_debug_utils_messenger(&debug_info, None) };
|
||||
let messenger = match messenger {
|
||||
Ok(m) => m,
|
||||
Err(e) => return Err(VulkanError::Messenger(e)),
|
||||
};
|
||||
destroy_instance.forget();
|
||||
Ok(Rc::new(Self {
|
||||
_entry: entry,
|
||||
instance,
|
||||
debug_utils,
|
||||
messenger,
|
||||
eng: eng.clone(),
|
||||
ring: ring.clone(),
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for VulkanInstance {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
self.debug_utils
|
||||
.destroy_debug_utils_messenger(self.messenger, None);
|
||||
self.instance.destroy_instance(None);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const REQUIRED_INSTANCE_EXTENSIONS: &[&CStr] = &[ExtDebugUtilsFn::name()];
|
||||
|
||||
const VALIDATION_LAYER: &CStr = c"VK_LAYER_KHRONOS_validation";
|
||||
|
||||
pub type Extensions = AHashMap<CString, u32>;
|
||||
|
||||
fn get_instance_extensions(entry: &Entry, layer: Option<&CStr>) -> Result<Extensions, VulkanError> {
|
||||
entry
|
||||
.enumerate_instance_extension_properties(layer)
|
||||
.map_err(VulkanError::InstanceExtensions)
|
||||
.map(map_extension_properties)
|
||||
}
|
||||
|
||||
fn get_available_layers(entry: &Entry) -> Result<AHashSet<CString>, VulkanError> {
|
||||
entry
|
||||
.enumerate_instance_layer_properties()
|
||||
.map_err(VulkanError::InstanceLayers)
|
||||
.map(map_layer_properties)
|
||||
}
|
||||
|
||||
fn map_layer_properties(props: Vec<LayerProperties>) -> AHashSet<CString> {
|
||||
props
|
||||
.into_iter()
|
||||
.map(|e| unsafe { CStr::from_ptr(e.layer_name.as_ptr()).to_owned() })
|
||||
.collect()
|
||||
}
|
||||
|
||||
pub fn map_extension_properties(props: Vec<ExtensionProperties>) -> Extensions {
|
||||
props
|
||||
.into_iter()
|
||||
.map(|e| {
|
||||
let s = unsafe { CStr::from_ptr(e.extension_name.as_ptr()) };
|
||||
(s.to_owned(), e.spec_version)
|
||||
})
|
||||
.collect()
|
||||
}
|
||||
|
||||
unsafe extern "system" fn debug_callback(
|
||||
message_severity: DebugUtilsMessageSeverityFlagsEXT,
|
||||
_message_types: DebugUtilsMessageTypeFlagsEXT,
|
||||
p_callback_data: *const DebugUtilsMessengerCallbackDataEXT,
|
||||
_p_user_data: *mut c_void,
|
||||
) -> Bool32 {
|
||||
let _level = match message_severity {
|
||||
DebugUtilsMessageSeverityFlagsEXT::ERROR => Level::Error,
|
||||
DebugUtilsMessageSeverityFlagsEXT::WARNING => Level::Warn,
|
||||
DebugUtilsMessageSeverityFlagsEXT::INFO => Level::Info,
|
||||
DebugUtilsMessageSeverityFlagsEXT::VERBOSE => Level::Trace,
|
||||
_ => Level::Warn,
|
||||
};
|
||||
let data = &*p_callback_data;
|
||||
let message = Ustr::from_ptr(data.p_message);
|
||||
let message_id_name = if data.p_message_id_name.is_null() {
|
||||
ustr!("<null>")
|
||||
} else {
|
||||
Ustr::from_ptr(data.p_message_id_name)
|
||||
};
|
||||
log::log!(
|
||||
Level::Info,
|
||||
"VULKAN: {} ({})",
|
||||
message.display(),
|
||||
message_id_name.display()
|
||||
);
|
||||
FALSE
|
||||
}
|
||||
|
||||
pub struct ApiVersionDisplay(pub u32);
|
||||
|
||||
impl Display for ApiVersionDisplay {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
||||
write!(
|
||||
f,
|
||||
"{}.{}.{}.{}",
|
||||
api_version_variant(self.0),
|
||||
api_version_major(self.0),
|
||||
api_version_minor(self.0),
|
||||
api_version_patch(self.0),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
pub const API_VERSION: u32 = API_VERSION_1_3;
|
||||
182
src/gfx_apis/vulkan/pipeline.rs
Normal file
182
src/gfx_apis/vulkan/pipeline.rs
Normal file
|
|
@ -0,0 +1,182 @@
|
|||
use {
|
||||
crate::{
|
||||
format::ARGB8888,
|
||||
gfx_apis::vulkan::{
|
||||
descriptor::VulkanDescriptorSetLayout, device::VulkanDevice, shaders::VulkanShader,
|
||||
util::OnDrop, VulkanError,
|
||||
},
|
||||
},
|
||||
arrayvec::ArrayVec,
|
||||
ash::vk::{
|
||||
BlendFactor, BlendOp, ColorComponentFlags, CullModeFlags, DynamicState, FrontFace,
|
||||
GraphicsPipelineCreateInfo, Pipeline, PipelineCache, PipelineColorBlendAttachmentState,
|
||||
PipelineColorBlendStateCreateInfo, PipelineDynamicStateCreateInfo,
|
||||
PipelineInputAssemblyStateCreateInfo, PipelineLayout, PipelineLayoutCreateInfo,
|
||||
PipelineMultisampleStateCreateInfo, PipelineRasterizationStateCreateInfo,
|
||||
PipelineRenderingCreateInfo, PipelineShaderStageCreateInfo,
|
||||
PipelineVertexInputStateCreateInfo, PipelineViewportStateCreateInfo, PolygonMode,
|
||||
PrimitiveTopology, PushConstantRange, SampleCountFlags, ShaderStageFlags,
|
||||
},
|
||||
std::{mem, rc::Rc, slice},
|
||||
uapi::ustr,
|
||||
};
|
||||
|
||||
pub(super) struct VulkanPipeline {
|
||||
pub(super) vert: Rc<VulkanShader>,
|
||||
pub(super) _frag: Rc<VulkanShader>,
|
||||
pub(super) frag_push_offset: u32,
|
||||
pub(super) pipeline_layout: PipelineLayout,
|
||||
pub(super) pipeline: Pipeline,
|
||||
pub(super) _frag_descriptor_set_layout: Option<Rc<VulkanDescriptorSetLayout>>,
|
||||
}
|
||||
|
||||
pub(super) struct PipelineCreateInfo {
|
||||
pub(super) vert: Rc<VulkanShader>,
|
||||
pub(super) frag: Rc<VulkanShader>,
|
||||
pub(super) alpha: bool,
|
||||
pub(super) frag_descriptor_set_layout: Option<Rc<VulkanDescriptorSetLayout>>,
|
||||
}
|
||||
|
||||
impl VulkanDevice {
|
||||
pub(super) fn create_pipeline<V, F>(
|
||||
&self,
|
||||
info: PipelineCreateInfo,
|
||||
) -> Result<Rc<VulkanPipeline>, VulkanError> {
|
||||
self.create_pipeline_(info, mem::size_of::<V>() as _, mem::size_of::<F>() as _)
|
||||
}
|
||||
|
||||
fn create_pipeline_(
|
||||
&self,
|
||||
info: PipelineCreateInfo,
|
||||
vert_push_size: u32,
|
||||
frag_push_size: u32,
|
||||
) -> Result<Rc<VulkanPipeline>, VulkanError> {
|
||||
let pipeline_layout = {
|
||||
let mut push_constant_ranges = ArrayVec::<_, 2>::new();
|
||||
let mut push_constant_offset = 0;
|
||||
if vert_push_size > 0 {
|
||||
push_constant_ranges.push(
|
||||
PushConstantRange::builder()
|
||||
.stage_flags(ShaderStageFlags::VERTEX)
|
||||
.offset(0)
|
||||
.size(vert_push_size)
|
||||
.build(),
|
||||
);
|
||||
push_constant_offset += vert_push_size;
|
||||
}
|
||||
if frag_push_size > 0 {
|
||||
push_constant_ranges.push(
|
||||
PushConstantRange::builder()
|
||||
.stage_flags(ShaderStageFlags::FRAGMENT)
|
||||
.offset(push_constant_offset)
|
||||
.size(frag_push_size)
|
||||
.build(),
|
||||
);
|
||||
#[allow(unused_assignments)]
|
||||
{
|
||||
push_constant_offset += frag_push_size;
|
||||
}
|
||||
}
|
||||
let mut descriptor_set_layouts = ArrayVec::<_, 1>::new();
|
||||
descriptor_set_layouts
|
||||
.extend(info.frag_descriptor_set_layout.as_ref().map(|l| l.layout));
|
||||
let create_info = PipelineLayoutCreateInfo::builder()
|
||||
.push_constant_ranges(&push_constant_ranges)
|
||||
.set_layouts(&descriptor_set_layouts);
|
||||
let layout = unsafe { self.device.create_pipeline_layout(&create_info, None) };
|
||||
layout.map_err(VulkanError::CreatePipelineLayout)?
|
||||
};
|
||||
let destroy_layout =
|
||||
OnDrop(|| unsafe { self.device.destroy_pipeline_layout(pipeline_layout, None) });
|
||||
let pipeline = {
|
||||
let main = ustr!("main").as_c_str().unwrap();
|
||||
let stages = [
|
||||
PipelineShaderStageCreateInfo::builder()
|
||||
.stage(ShaderStageFlags::VERTEX)
|
||||
.module(info.vert.module)
|
||||
.name(main)
|
||||
.build(),
|
||||
PipelineShaderStageCreateInfo::builder()
|
||||
.stage(ShaderStageFlags::FRAGMENT)
|
||||
.module(info.frag.module)
|
||||
.name(main)
|
||||
.build(),
|
||||
];
|
||||
let input_assembly_state = PipelineInputAssemblyStateCreateInfo::builder()
|
||||
.topology(PrimitiveTopology::TRIANGLE_STRIP);
|
||||
let vertex_input_state = PipelineVertexInputStateCreateInfo::builder();
|
||||
let rasterization_state = PipelineRasterizationStateCreateInfo::builder()
|
||||
.polygon_mode(PolygonMode::FILL)
|
||||
.cull_mode(CullModeFlags::BACK)
|
||||
.line_width(1.0)
|
||||
.front_face(FrontFace::COUNTER_CLOCKWISE);
|
||||
let multisampling_state = PipelineMultisampleStateCreateInfo::builder()
|
||||
.sample_shading_enable(false)
|
||||
.rasterization_samples(SampleCountFlags::TYPE_1);
|
||||
let mut blending = PipelineColorBlendAttachmentState::builder()
|
||||
.color_write_mask(ColorComponentFlags::RGBA);
|
||||
if info.alpha {
|
||||
blending = blending
|
||||
.blend_enable(true)
|
||||
.src_color_blend_factor(BlendFactor::ONE)
|
||||
.dst_color_blend_factor(BlendFactor::ONE_MINUS_SRC_ALPHA)
|
||||
.color_blend_op(BlendOp::ADD)
|
||||
.src_alpha_blend_factor(BlendFactor::ONE)
|
||||
.dst_alpha_blend_factor(BlendFactor::ONE_MINUS_SRC_ALPHA)
|
||||
.alpha_blend_op(BlendOp::ADD);
|
||||
}
|
||||
let color_blend_state = PipelineColorBlendStateCreateInfo::builder()
|
||||
.attachments(slice::from_ref(&blending));
|
||||
let dynamic_states = [DynamicState::VIEWPORT, DynamicState::SCISSOR];
|
||||
let dynamic_state =
|
||||
PipelineDynamicStateCreateInfo::builder().dynamic_states(&dynamic_states);
|
||||
let viewport_state = PipelineViewportStateCreateInfo::builder()
|
||||
.viewport_count(1)
|
||||
.scissor_count(1);
|
||||
let mut pipeline_rendering_create_info = PipelineRenderingCreateInfo::builder()
|
||||
.color_attachment_formats(slice::from_ref(&ARGB8888.vk_format));
|
||||
let create_info = GraphicsPipelineCreateInfo::builder()
|
||||
.push_next(&mut pipeline_rendering_create_info)
|
||||
.stages(&stages)
|
||||
.input_assembly_state(&input_assembly_state)
|
||||
.vertex_input_state(&vertex_input_state)
|
||||
.rasterization_state(&rasterization_state)
|
||||
.multisample_state(&multisampling_state)
|
||||
.color_blend_state(&color_blend_state)
|
||||
.dynamic_state(&dynamic_state)
|
||||
.viewport_state(&viewport_state)
|
||||
.layout(pipeline_layout);
|
||||
let pipelines = unsafe {
|
||||
self.device.create_graphics_pipelines(
|
||||
PipelineCache::null(),
|
||||
slice::from_ref(&create_info),
|
||||
None,
|
||||
)
|
||||
};
|
||||
let mut pipelines = pipelines
|
||||
.map_err(|e| e.1)
|
||||
.map_err(VulkanError::CreatePipeline)?;
|
||||
assert_eq!(pipelines.len(), 1);
|
||||
pipelines.pop().unwrap()
|
||||
};
|
||||
destroy_layout.forget();
|
||||
Ok(Rc::new(VulkanPipeline {
|
||||
vert: info.vert,
|
||||
_frag: info.frag,
|
||||
frag_push_offset: vert_push_size,
|
||||
pipeline_layout,
|
||||
pipeline,
|
||||
_frag_descriptor_set_layout: info.frag_descriptor_set_layout,
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for VulkanPipeline {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
let device = &self.vert.device.device;
|
||||
device.destroy_pipeline(self.pipeline, None);
|
||||
device.destroy_pipeline_layout(self.pipeline_layout, None);
|
||||
}
|
||||
}
|
||||
}
|
||||
1019
src/gfx_apis/vulkan/renderer.rs
Normal file
1019
src/gfx_apis/vulkan/renderer.rs
Normal file
File diff suppressed because it is too large
Load diff
42
src/gfx_apis/vulkan/sampler.rs
Normal file
42
src/gfx_apis/vulkan/sampler.rs
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
use {
|
||||
crate::gfx_apis::vulkan::{device::VulkanDevice, VulkanError},
|
||||
ash::vk::{
|
||||
BorderColor, Filter, Sampler, SamplerAddressMode, SamplerCreateInfo, SamplerMipmapMode,
|
||||
},
|
||||
std::rc::Rc,
|
||||
};
|
||||
|
||||
pub struct VulkanSampler {
|
||||
pub(super) device: Rc<VulkanDevice>,
|
||||
pub(super) sampler: Sampler,
|
||||
}
|
||||
|
||||
impl VulkanDevice {
|
||||
pub(super) fn create_sampler(self: &Rc<Self>) -> Result<Rc<VulkanSampler>, VulkanError> {
|
||||
let create_info = SamplerCreateInfo::builder()
|
||||
.mag_filter(Filter::LINEAR)
|
||||
.min_filter(Filter::LINEAR)
|
||||
.mipmap_mode(SamplerMipmapMode::NEAREST)
|
||||
.address_mode_u(SamplerAddressMode::REPEAT)
|
||||
.address_mode_v(SamplerAddressMode::REPEAT)
|
||||
.address_mode_w(SamplerAddressMode::REPEAT)
|
||||
.max_anisotropy(1.0)
|
||||
.min_lod(0.0)
|
||||
.max_lod(0.25)
|
||||
.border_color(BorderColor::FLOAT_TRANSPARENT_BLACK);
|
||||
let sampler = unsafe { self.device.create_sampler(&create_info, None) };
|
||||
let sampler = sampler.map_err(VulkanError::CreateSampler)?;
|
||||
Ok(Rc::new(VulkanSampler {
|
||||
device: self.clone(),
|
||||
sampler,
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for VulkanSampler {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
self.device.device.destroy_sampler(self.sampler, None);
|
||||
}
|
||||
}
|
||||
}
|
||||
54
src/gfx_apis/vulkan/semaphore.rs
Normal file
54
src/gfx_apis/vulkan/semaphore.rs
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
use {
|
||||
crate::gfx_apis::vulkan::{device::VulkanDevice, VulkanError},
|
||||
ash::vk::{
|
||||
ExternalSemaphoreHandleTypeFlags, ImportSemaphoreFdInfoKHR, Semaphore, SemaphoreCreateInfo,
|
||||
SemaphoreImportFlags,
|
||||
},
|
||||
std::{mem, rc::Rc},
|
||||
uapi::OwnedFd,
|
||||
};
|
||||
|
||||
pub struct VulkanSemaphore {
|
||||
pub(super) device: Rc<VulkanDevice>,
|
||||
pub(super) semaphore: Semaphore,
|
||||
}
|
||||
|
||||
impl Drop for VulkanSemaphore {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
self.device.device.destroy_semaphore(self.semaphore, None);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl VulkanDevice {
|
||||
pub fn create_semaphore(self: &Rc<Self>) -> Result<Rc<VulkanSemaphore>, VulkanError> {
|
||||
let sem = {
|
||||
let create_info = SemaphoreCreateInfo::builder();
|
||||
let sem = unsafe { self.device.create_semaphore(&create_info, None) };
|
||||
sem.map_err(VulkanError::CreateSemaphore)?
|
||||
};
|
||||
Ok(Rc::new(VulkanSemaphore {
|
||||
device: self.clone(),
|
||||
semaphore: sem,
|
||||
}))
|
||||
}
|
||||
}
|
||||
|
||||
impl VulkanSemaphore {
|
||||
pub fn import_syncfile(&self, syncfile: OwnedFd) -> Result<(), VulkanError> {
|
||||
let fd_info = ImportSemaphoreFdInfoKHR::builder()
|
||||
.fd(syncfile.raw())
|
||||
.flags(SemaphoreImportFlags::TEMPORARY)
|
||||
.handle_type(ExternalSemaphoreHandleTypeFlags::SYNC_FD)
|
||||
.semaphore(self.semaphore);
|
||||
let res = unsafe {
|
||||
self.device
|
||||
.external_semaphore_fd
|
||||
.import_semaphore_fd(&fd_info)
|
||||
};
|
||||
mem::forget(syncfile);
|
||||
res.map_err(VulkanError::ImportSyncFile)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
67
src/gfx_apis/vulkan/shaders.rs
Normal file
67
src/gfx_apis/vulkan/shaders.rs
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
use {
|
||||
crate::gfx_apis::vulkan::{device::VulkanDevice, VulkanError},
|
||||
ash::vk::{ShaderModule, ShaderModuleCreateInfo},
|
||||
std::rc::Rc,
|
||||
uapi::Packed,
|
||||
};
|
||||
|
||||
pub const FILL_VERT: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/fill.vert.spv"));
|
||||
pub const FILL_FRAG: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/fill.frag.spv"));
|
||||
pub const TEX_VERT: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/tex.vert.spv"));
|
||||
pub const TEX_FRAG: &[u8] = include_bytes!(concat!(env!("OUT_DIR"), "/tex.frag.spv"));
|
||||
|
||||
pub struct VulkanShader {
|
||||
pub(super) device: Rc<VulkanDevice>,
|
||||
pub(super) module: ShaderModule,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
#[repr(C)]
|
||||
pub struct FillVertPushConstants {
|
||||
pub pos: [[f32; 2]; 4],
|
||||
}
|
||||
|
||||
unsafe impl Packed for FillVertPushConstants {}
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
#[repr(C)]
|
||||
pub struct FillFragPushConstants {
|
||||
pub color: [f32; 4],
|
||||
}
|
||||
|
||||
unsafe impl Packed for FillFragPushConstants {}
|
||||
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
#[repr(C)]
|
||||
pub struct TexVertPushConstants {
|
||||
pub pos: [[f32; 2]; 4],
|
||||
pub tex_pos: [[f32; 2]; 4],
|
||||
}
|
||||
|
||||
unsafe impl Packed for TexVertPushConstants {}
|
||||
|
||||
impl VulkanDevice {
|
||||
pub(super) fn create_shader(
|
||||
self: &Rc<Self>,
|
||||
src: &[u8],
|
||||
) -> Result<Rc<VulkanShader>, VulkanError> {
|
||||
let src: Vec<u32> = uapi::pod_iter(src).unwrap().collect();
|
||||
let create_info = ShaderModuleCreateInfo::builder().code(&src);
|
||||
let module = unsafe { self.device.create_shader_module(&create_info, None) };
|
||||
module
|
||||
.map_err(VulkanError::CreateShaderModule)
|
||||
.map(|m| VulkanShader {
|
||||
device: self.clone(),
|
||||
module: m,
|
||||
})
|
||||
.map(Rc::new)
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for VulkanShader {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
self.device.device.destroy_shader_module(self.module, None);
|
||||
}
|
||||
}
|
||||
}
|
||||
11
src/gfx_apis/vulkan/shaders/fill.frag
Normal file
11
src/gfx_apis/vulkan/shaders/fill.frag
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
#version 450
|
||||
|
||||
layout(push_constant, std430) uniform Data {
|
||||
layout(offset = 32) vec4 color;
|
||||
} data;
|
||||
|
||||
layout(location = 0) out vec4 out_color;
|
||||
|
||||
void main() {
|
||||
out_color = data.color;
|
||||
}
|
||||
18
src/gfx_apis/vulkan/shaders/fill.vert
Normal file
18
src/gfx_apis/vulkan/shaders/fill.vert
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
#version 450
|
||||
//#extension GL_EXT_debug_printf : enable
|
||||
|
||||
layout(push_constant, std430) uniform Data {
|
||||
layout(offset = 0) vec2 pos[4];
|
||||
} data;
|
||||
|
||||
void main() {
|
||||
vec2 pos;
|
||||
switch (gl_VertexIndex) {
|
||||
case 0: pos = data.pos[0]; break;
|
||||
case 1: pos = data.pos[1]; break;
|
||||
case 2: pos = data.pos[2]; break;
|
||||
case 3: pos = data.pos[3]; break;
|
||||
}
|
||||
gl_Position = vec4(pos, 0.0, 1.0);
|
||||
// debugPrintfEXT("gl_Position = %v4f", gl_Position);
|
||||
}
|
||||
9
src/gfx_apis/vulkan/shaders/tex.frag
Normal file
9
src/gfx_apis/vulkan/shaders/tex.frag
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
#version 450
|
||||
|
||||
layout(set = 0, binding = 0) uniform sampler2D tex;
|
||||
layout(location = 0) in vec2 tex_pos;
|
||||
layout(location = 0) out vec4 out_color;
|
||||
|
||||
void main() {
|
||||
out_color = textureLod(tex, tex_pos, 0);
|
||||
}
|
||||
21
src/gfx_apis/vulkan/shaders/tex.vert
Normal file
21
src/gfx_apis/vulkan/shaders/tex.vert
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
#version 450
|
||||
//#extension GL_EXT_debug_printf : enable
|
||||
|
||||
layout(push_constant, std430) uniform Data {
|
||||
layout(offset = 0) vec2 pos[4];
|
||||
layout(offset = 32) vec2 tex_pos[4];
|
||||
} data;
|
||||
|
||||
layout(location = 0) out vec2 tex_pos;
|
||||
|
||||
void main() {
|
||||
vec2 pos;
|
||||
switch (gl_VertexIndex) {
|
||||
case 0: pos = data.pos[0]; tex_pos = data.tex_pos[0]; break;
|
||||
case 1: pos = data.pos[1]; tex_pos = data.tex_pos[1]; break;
|
||||
case 2: pos = data.pos[2]; tex_pos = data.tex_pos[2]; break;
|
||||
case 3: pos = data.pos[3]; tex_pos = data.tex_pos[3]; break;
|
||||
}
|
||||
gl_Position = vec4(pos, 0.0, 1.0);
|
||||
// debugPrintfEXT("gl_Position = %v4f, tex_pos = %v2f", gl_Position, tex_pos);
|
||||
}
|
||||
103
src/gfx_apis/vulkan/staging.rs
Normal file
103
src/gfx_apis/vulkan/staging.rs
Normal file
|
|
@ -0,0 +1,103 @@
|
|||
use {
|
||||
crate::gfx_apis::vulkan::{
|
||||
allocator::VulkanAllocation, device::VulkanDevice, renderer::VulkanRenderer, util::OnDrop,
|
||||
VulkanError,
|
||||
},
|
||||
ash::vk::{Buffer, BufferCreateInfo, BufferUsageFlags, MappedMemoryRange},
|
||||
gpu_alloc::UsageFlags,
|
||||
std::rc::Rc,
|
||||
};
|
||||
|
||||
pub struct VulkanStagingBuffer {
|
||||
pub(super) device: Rc<VulkanDevice>,
|
||||
pub(super) allocation: VulkanAllocation,
|
||||
pub(super) buffer: Buffer,
|
||||
pub(super) size: u64,
|
||||
}
|
||||
|
||||
impl VulkanRenderer {
|
||||
pub(super) fn create_staging_buffer(
|
||||
self: &Rc<Self>,
|
||||
size: u64,
|
||||
upload: bool,
|
||||
download: bool,
|
||||
transient: bool,
|
||||
) -> Result<VulkanStagingBuffer, VulkanError> {
|
||||
let mut vk_usage = BufferUsageFlags::empty();
|
||||
let mut usage = UsageFlags::empty();
|
||||
if upload {
|
||||
vk_usage |= BufferUsageFlags::TRANSFER_SRC;
|
||||
usage |= UsageFlags::UPLOAD;
|
||||
}
|
||||
if download {
|
||||
vk_usage |= BufferUsageFlags::TRANSFER_DST;
|
||||
usage |= UsageFlags::DOWNLOAD;
|
||||
}
|
||||
if transient {
|
||||
usage |= UsageFlags::TRANSIENT;
|
||||
}
|
||||
let buffer = {
|
||||
let create_info = BufferCreateInfo::builder().size(size).usage(vk_usage);
|
||||
let buffer = unsafe { self.device.device.create_buffer(&create_info, None) };
|
||||
buffer.map_err(VulkanError::CreateBuffer)?
|
||||
};
|
||||
let destroy_buffer = OnDrop(|| unsafe { self.device.device.destroy_buffer(buffer, None) });
|
||||
let memory_requirements =
|
||||
unsafe { self.device.device.get_buffer_memory_requirements(buffer) };
|
||||
let allocation = self.allocator.alloc(&memory_requirements, usage, true)?;
|
||||
{
|
||||
let res = unsafe {
|
||||
self.device
|
||||
.device
|
||||
.bind_buffer_memory(buffer, allocation.memory, allocation.offset)
|
||||
};
|
||||
res.map_err(VulkanError::BindBufferMemory)?;
|
||||
}
|
||||
destroy_buffer.forget();
|
||||
Ok(VulkanStagingBuffer {
|
||||
device: self.device.clone(),
|
||||
allocation,
|
||||
buffer,
|
||||
size,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl VulkanStagingBuffer {
|
||||
pub fn upload<T, F>(&self, f: F) -> Result<T, VulkanError>
|
||||
where
|
||||
F: FnOnce(*mut u8, usize) -> T,
|
||||
{
|
||||
let t = f(self.allocation.mem.unwrap(), self.size as usize);
|
||||
let range = self.range();
|
||||
let res = unsafe { self.device.device.flush_mapped_memory_ranges(&[range]) };
|
||||
res.map_err(VulkanError::FlushMemory).map(|_| t)
|
||||
}
|
||||
|
||||
pub fn download<T, F>(&self, f: F) -> Result<T, VulkanError>
|
||||
where
|
||||
F: FnOnce(*const u8, usize) -> T,
|
||||
{
|
||||
let range = self.range();
|
||||
let res = unsafe { self.device.device.invalidate_mapped_memory_ranges(&[range]) };
|
||||
res.map_err(VulkanError::FlushMemory)?;
|
||||
Ok(f(self.allocation.mem.unwrap(), self.size as usize))
|
||||
}
|
||||
|
||||
fn range(&self) -> MappedMemoryRange {
|
||||
let atom_mask = self.allocation.allocator.non_coherent_atom_mask;
|
||||
MappedMemoryRange::builder()
|
||||
.memory(self.allocation.memory)
|
||||
.offset(self.allocation.offset & !atom_mask)
|
||||
.size((self.allocation.size + atom_mask) & !atom_mask)
|
||||
.build()
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for VulkanStagingBuffer {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
self.device.device.destroy_buffer(self.buffer, None);
|
||||
}
|
||||
}
|
||||
}
|
||||
17
src/gfx_apis/vulkan/util.rs
Normal file
17
src/gfx_apis/vulkan/util.rs
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
use std::mem;
|
||||
|
||||
pub struct OnDrop<F>(pub F)
|
||||
where
|
||||
F: FnMut() + Copy;
|
||||
|
||||
impl<F: FnMut() + Copy> OnDrop<F> {
|
||||
pub fn forget(self) {
|
||||
mem::forget(self);
|
||||
}
|
||||
}
|
||||
|
||||
impl<F: FnMut() + Copy> Drop for OnDrop<F> {
|
||||
fn drop(&mut self) {
|
||||
(self.0)();
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue