mod allocator; mod blend_buffer; mod bo_allocator; mod buffer_cache; mod command; mod descriptor; mod descriptor_buffer; mod device; mod dmabuf_buffer; mod eotfs; mod fence; mod format; mod gpu_alloc_ash; mod image; mod instance; mod pipeline; mod renderer; mod sampler; mod semaphore; mod shaders; mod shm_image; mod staging; mod transfer; use { crate::{ allocator::{Allocator, AllocatorError}, async_engine::AsyncEngine, cpu_worker::{CpuWorker, jobs::read_write::ReadWriteJobError}, format::Format, gfx_api::{ AsyncShmGfxTexture, GfxBlendBuffer, GfxBuffer, GfxContext, GfxError, GfxFormat, GfxImage, GfxInternalFramebuffer, GfxStagingBuffer, GfxTexture, ResetStatus, STAGING_DOWNLOAD, STAGING_UPLOAD, ShmGfxTexture, StagingBufferUsecase, }, gfx_apis::vulkan::{ image::VulkanImageMemory, instance::VulkanInstance, renderer::VulkanRenderer, }, io_uring::IoUring, pr_caps::PrCapsThread, rect::Rect, utils::{errorfmt::ErrorFmt, oserror::OsError}, video::{ dmabuf::DmaBuf, drm::{Drm, DrmError, sync_obj::SyncObjCtx}, gbm::GbmError, }, vulkan_core::VulkanCoreError, }, ahash::AHashMap, ash::vk, gpu_alloc::{AllocationError, MapError}, jay_config::video::GfxApi, log::Level, std::{ cell::Cell, ffi::{CStr, CString}, rc::Rc, }, thiserror::Error, uapi::{OwnedFd, c::dev_t}, }; #[derive(Debug, Error)] pub enum VulkanError { #[error(transparent)] Core(#[from] VulkanCoreError), #[error("Could not create a GBM device")] Gbm(#[source] GbmError), #[error("Could not list device extensions")] DeviceExtensions(#[source] vk::Result), #[error("Could not create the device")] CreateDevice(#[source] vk::Result), #[error("Could not create a semaphore")] CreateSemaphore(#[source] vk::Result), #[error("Could not create a fence")] CreateFence(#[source] vk::Result), #[error("Could not create the buffer")] CreateBuffer(#[source] vk::Result), #[error("Could not create a shader module")] CreateShaderModule(#[source] vk::Result), #[error("Could not allocate a command pool")] AllocateCommandPool(#[source] vk::Result), #[error("Could not allocate a command buffer")] AllocateCommandBuffer(#[source] vk::Result), #[error("Device does not have a graphics queue")] NoGraphicsQueue, #[error("Missing required device extension {0:?}")] MissingDeviceExtension(&'static CStr), #[error("Could not enumerate the physical devices")] EnumeratePhysicalDevices(#[source] vk::Result), #[error("Could not find a vulkan device that matches dev_t {0}")] NoDeviceFound(dev_t), #[error("There is no vulkan software renderer")] NoSoftwareRenderer, #[error("Could not load image properties")] LoadImageProperties(#[source] vk::Result), #[error("Device does not support rending and texturing from the XRGB8888 format")] XRGB8888, #[error("Device does not support sync file import")] SyncFileImport, #[error("Could not start a command buffer")] BeginCommandBuffer(vk::Result), #[error("Could not end a command buffer")] EndCommandBuffer(vk::Result), #[error("Could not submit a command buffer")] Submit(vk::Result), #[error("Could not create a sampler")] CreateSampler(#[source] vk::Result), #[error("Could not create a pipeline layout")] CreatePipelineLayout(#[source] vk::Result), #[error("Could not create a descriptor set layout")] CreateDescriptorSetLayout(#[source] vk::Result), #[error("Could not create a pipeline")] CreatePipeline(#[source] vk::Result), #[error("The format is not supported")] FormatNotSupported, #[error("The modifier is not supported")] ModifierNotSupported, #[error("The modifier does not support this use-case")] ModifierUseNotSupported, #[error("The image has a non-positive size")] NonPositiveImageSize, #[error("The image is too large")] ImageTooLarge, #[error("Could not retrieve device properties")] GetDeviceProperties(#[source] vk::Result), #[error("The dmabuf has an incorrect number of planes")] BadPlaneCount, #[error("The dmabuf is disjoint but the modifier does not support disjoint buffers")] DisjointNotSupported, #[error("Could not create the image")] CreateImage(#[source] vk::Result), #[error("Could not create an image view")] CreateImageView(#[source] vk::Result), #[error("Could not query the memory fd properties")] MemoryFdProperties(#[source] vk::Result), #[error("There is no matching memory type")] MemoryType, #[error("Could not duplicate the DRM fd")] Dupfd(#[source] OsError), #[error("Could not allocate memory")] AllocateMemory(#[source] vk::Result), #[error("Could not allocate memory")] AllocateMemory2(#[source] AllocationError), #[error("Could not bind memory to the image")] BindImageMemory(#[source] vk::Result), #[error("The format does not support shared memory images")] ShmNotSupported, #[error("Could not bind memory to the buffer")] BindBufferMemory(#[source] vk::Result), #[error("Could not map the memory")] MapMemory(#[source] MapError), #[error("Could not flush modified memory")] FlushMemory(#[source] vk::Result), #[error("Could not export a sync file from a dma-buf")] IoctlExportSyncFile(#[source] OsError), #[error("Could not import a sync file into a semaphore")] ImportSyncFile(#[source] vk::Result), #[error("Could not export a sync file from a semaphore")] ExportSyncFile(#[source] vk::Result), #[error("Could not fetch the render node of the device")] FetchRenderNode(#[source] DrmError), #[error("Device has no render node")] NoRenderNode, #[error("Overflow while calculating shm buffer size")] ShmOverflow, #[error("Shm stride does not match format or width")] InvalidStride, #[error("Shm stride and height do not match buffer size")] InvalidBufferSize, #[error("Only BO_USE_RENDERING and BO_USE_WRITE are supported")] UnsupportedBufferUsage, #[error("None of the supplied modifiers are supported")] NoSupportedModifiers, #[error("Could not retrieve the image modifier")] GetModifier(#[source] vk::Result), #[error("Vulkan allocated the image with an invalid modifier")] InvalidModifier, #[error("Could not export the DmaBuf")] GetDmaBuf(#[source] vk::Result), #[error("Could not wait for the device to become idle")] WaitIdle(#[source] vk::Result), #[error("Could not dup a DRM device")] DupDrm(#[source] DrmError), #[error("Graphics context has already been dropped")] Defunct, #[error("Could not perform an async copy to the staging buffer")] AsyncCopyToStaging(#[source] ReadWriteJobError), #[error("The async shm texture is busy")] AsyncCopyBusy, #[error("The staging buffer is busy")] StagingBufferBusy, #[error("The staging buffer does not support uploads")] StagingBufferNoUpload, #[error("The staging buffer does not support downloads")] StagingBufferNoDownload, #[error("Image contents are undefined")] UndefinedContents, #[error("The framebuffer is being used by the transfer queue")] BusyInTransfer, #[error("Driver does not support descriptor buffers")] NoDescriptorBuffer, #[error("A non-vulkan buffer was passed into the vulkan renderer")] NonVulkanBuffer, #[error("Mixed vulkan device use")] MixedVulkanDeviceUse, #[error("Could not allocate GBM BO")] AllocGbm(#[source] GbmError), #[error("Could not retrieve file description flags")] GetFl(#[source] OsError), #[error("GBM implementation cannot be used with software renderer")] SoftwareRendererNotUsable, #[error("DMABUF buffer offsets must be aligned to 4 bytes and the pixel size")] DmaBufBufferOffsetAlignment, } impl From for GfxError { fn from(value: VulkanError) -> Self { Self(Box::new(value)) } } pub fn create_graphics_context( eng: &Rc, ring: &Rc, drm: &Drm, caps_thread: Option<&PrCapsThread>, software: bool, ) -> Result, GfxError> { let instance = VulkanInstance::new(Level::Info)?; let device = 'device: { if let Some(t) = caps_thread { match unsafe { t.run(|| instance.create_device(drm, true, software)) } { Ok(d) => break 'device d, Err(e) => { log::warn!("Could not create high-priority device: {}", ErrorFmt(e)); } } } instance.create_device(drm, false, software)? }; let renderer = device.create_renderer(eng, ring)?; Ok(Rc::new(Context(renderer))) } pub fn create_vulkan_allocator(drm: &Drm) -> Result, AllocatorError> { let instance = VulkanInstance::new(Level::Debug)?; let device = instance.create_device(drm, false, false)?; let allocator = device.create_bo_allocator(drm)?; Ok(Rc::new(allocator)) } #[derive(Debug)] struct Context(Rc); impl GfxContext for Context { fn reset_status(&self) -> Option { self.0.device.lost.get().then_some(ResetStatus::Unknown) } fn render_node(&self) -> Option> { Some(self.0.device.render_node.clone()) } fn formats(&self) -> &Rc> { &self.0.formats } fn fast_ram_access(&self) -> bool { self.0.device.fast_ram_access } fn dmabuf_img(self: Rc, buf: &DmaBuf) -> Result, GfxError> { self.0 .import_dmabuf(buf) .map(|v| v as _) .map_err(|e| e.into()) } fn shmem_texture( self: Rc, old: Option>, data: &[Cell], format: &'static Format, width: i32, height: i32, stride: i32, damage: Option<&[Rect]>, ) -> Result, GfxError> { if let Some(old) = old { let old = (old as Rc).into_vk(&self.0.device.device)?; let shm = match &old.ty { VulkanImageMemory::DmaBuf(_) => unreachable!(), VulkanImageMemory::Blend(_) => unreachable!(), VulkanImageMemory::Internal(shm) => shm, }; if old.width as i32 == width && old.height as i32 == height && shm.stride as i32 == stride && old.format.vk_format == format.vk_format { shm.upload(&old, data, damage)?; return Ok(old); } } let tex = self .0 .create_shm_texture(format, width, height, stride, data, false, None)?; Ok(tex as _) } fn async_shmem_texture( self: Rc, format: &'static Format, width: i32, height: i32, stride: i32, cpu_worker: &Rc, ) -> Result, GfxError> { let tex = self.0.create_shm_texture( format, width, height, stride, &[], false, Some(cpu_worker), )?; Ok(tex) } fn allocator(&self) -> Rc { self.0.device.gbm.clone() } fn gfx_api(&self) -> GfxApi { GfxApi::Vulkan } fn create_internal_fb( self: Rc, cpu_worker: &Rc, width: i32, height: i32, stride: i32, format: &'static Format, ) -> Result, GfxError> { let fb = self.0.create_shm_texture( format, width, height, stride, &[], true, Some(cpu_worker), )?; Ok(fb) } fn sync_obj_ctx(&self) -> Option<&Rc> { Some(&self.0.device.sync_ctx) } fn create_staging_buffer( &self, size: usize, usecase: StagingBufferUsecase, ) -> Rc { let upload = usecase.contains(STAGING_UPLOAD); let download = usecase.contains(STAGING_DOWNLOAD); self.0 .device .create_staging_shell(size as u64, upload, download) } fn acquire_blend_buffer( &self, width: i32, height: i32, ) -> Result, GfxError> { let buffer = self.0.acquire_blend_buffer(width, height)?; Ok(buffer) } fn supports_color_management(&self) -> bool { self.0.device.descriptor_buffer.is_some() } fn create_dmabuf_buffer( &self, dmabuf: &OwnedFd, offset: usize, size: usize, format: &'static Format, ) -> Result, GfxError> { self.0.check_defunct()?; let buffer = self.0 .device .create_dmabuf_buffer(dmabuf, offset as u64, size as u64, format)?; Ok(buffer) } } impl Drop for Context { fn drop(&mut self) { self.0.on_drop(); } }