diff --git a/docs/features.txt b/docs/features.txt index b087784a761..698a49fc143 100644 --- a/docs/features.txt +++ b/docs/features.txt @@ -835,7 +835,7 @@ Rusticl extensions that are not part of any OpenCL version: cl_khr_kernel_clock DONE (freedreno, iris, llvmpipe, nvc0, panfrost, radeonsi, zink, needs llvm-19) cl_khr_mipmap_image not started cl_khr_pci_bus_info DONE (iris, nvc0, radeonsi, zink) - cl_khr_priority_hints not started + cl_khr_priority_hints DONE (asahi, freedreno, iris, panfrost, radeonsi) cl_khr_spirv_extended_debug_info not started cl_khr_spirv_linkonce_odr DONE cl_khr_spirv_no_integer_wrap_decoration DONE diff --git a/docs/relnotes/new_features.txt b/docs/relnotes/new_features.txt index 47b6f2793e8..2620110da79 100644 --- a/docs/relnotes/new_features.txt +++ b/docs/relnotes/new_features.txt @@ -49,3 +49,4 @@ VK_KHR_unified_image_layouts on RADV (RDNA3+) VK_KHR_vulkan_memory_model on panvk VK_KHR_present_wait2 VK_EXT_descriptor_indexing on panvk/v10+ +cl_khr_priority_hints diff --git a/src/gallium/frontends/rusticl/api/queue.rs b/src/gallium/frontends/rusticl/api/queue.rs index 1b6b887a9fb..b8952ed1565 100644 --- a/src/gallium/frontends/rusticl/api/queue.rs +++ b/src/gallium/frontends/rusticl/api/queue.rs @@ -129,12 +129,23 @@ fn create_command_queue_with_properties( properties: *const cl_queue_properties, ) -> CLResult { let mut queue_properties = cl_command_queue_properties::default(); + let d = Device::ref_from_raw(device)?; // SAFETY: properties is a 0 terminated array by spec. let properties = unsafe { Properties::new(properties) }.ok_or(CL_INVALID_PROPERTY)?; for (&key, &val) in properties.iter() { match u32::try_from(key).or(Err(CL_INVALID_PROPERTY))? { CL_QUEUE_PROPERTIES => queue_properties = val, + CL_QUEUE_PRIORITY_KHR if d.context_priority_supported() != 0 => { + let valid_props: cl_queue_properties = (CL_QUEUE_PRIORITY_LOW_KHR + | CL_QUEUE_PRIORITY_MED_KHR + | CL_QUEUE_PRIORITY_HIGH_KHR) + .into(); + + if val & !valid_props != 0 { + return Err(CL_INVALID_VALUE); + } + } // CL_INVALID_QUEUE_PROPERTIES if values specified in properties are valid but are not // supported by the device. CL_QUEUE_SIZE => return Err(CL_INVALID_QUEUE_PROPERTIES), diff --git a/src/gallium/frontends/rusticl/core/device.rs b/src/gallium/frontends/rusticl/core/device.rs index d7ded752491..73c1b80cbad 100644 --- a/src/gallium/frontends/rusticl/core/device.rs +++ b/src/gallium/frontends/rusticl/core/device.rs @@ -707,6 +707,10 @@ impl DeviceBase { add_ext(1, 0, 0, "cl_khr_pci_bus_info"); } + if self.context_priority_supported() != 0 { + add_ext(1, 0, 0, "cl_khr_priority_hints"); + } + if self.screen().device_uuid().is_some() && self.screen().driver_uuid().is_some() { static_assert!(PIPE_UUID_SIZE == CL_UUID_SIZE_KHR); static_assert!(PIPE_LUID_SIZE == CL_LUID_SIZE_KHR); @@ -912,6 +916,25 @@ impl DeviceBase { self.screen.caps().int64 } + pub fn context_priority_supported(&self) -> cl_queue_priority_khr { + let mut res = 0; + let prio_mask = self.screen().caps().context_priority_mask; + + if prio_mask & PIPE_CONTEXT_PRIORITY_LOW != 0 { + res |= CL_QUEUE_PRIORITY_LOW_KHR; + } + if prio_mask & PIPE_CONTEXT_PRIORITY_MEDIUM != 0 { + res |= CL_QUEUE_PRIORITY_MED_KHR; + } + if prio_mask & PIPE_CONTEXT_PRIORITY_HIGH != 0 { + res |= CL_QUEUE_PRIORITY_HIGH_KHR; + } + + debug_assert!(prio_mask == 0 || prio_mask & CL_QUEUE_PRIORITY_MED_KHR != 0); + + res + } + pub fn global_mem_size(&self) -> cl_ulong { if let Some(memory_info) = self.screen.query_memory_info() { let memory: cl_ulong = if memory_info.total_device_memory != 0 { @@ -1082,14 +1105,20 @@ impl DeviceBase { &self.screen } - pub fn create_context(&self) -> Option { - self.reusable_ctx() - .pop() - .or_else(|| self.screen.create_context()) + pub fn create_context(&self, prio: PipeContextPrio) -> Option { + // We only cache for Med prio contexts for now. + let res = (prio == PipeContextPrio::Med) + .then(|| self.reusable_ctx().pop()) + .flatten() + .or_else(|| self.screen.create_context(prio))?; + + debug_assert_eq!(res.prio, prio); + + Some(res) } pub fn recycle_context(&self, ctx: PipeContext) { - if Platform::dbg().reuse_context { + if Platform::dbg().reuse_context && ctx.prio == PipeContextPrio::Med { self.reusable_ctx().push(ctx); } } @@ -1211,7 +1240,7 @@ impl Device { let screen = Arc::new(screen); // Create before loading libclc as llvmpipe only creates the shader cache with the first // context being created. - let helper_ctx = screen.create_context()?; + let helper_ctx = screen.create_context(PipeContextPrio::Med)?; let mut dev_base = DeviceBase { caps: DeviceCaps::new(&screen), helper_ctx: Mutex::new(helper_ctx), diff --git a/src/gallium/frontends/rusticl/core/queue.rs b/src/gallium/frontends/rusticl/core/queue.rs index fb8f06fdac4..c1ca2b8db7d 100644 --- a/src/gallium/frontends/rusticl/core/queue.rs +++ b/src/gallium/frontends/rusticl/core/queue.rs @@ -8,6 +8,7 @@ use crate::impl_cl_type_trait; use mesa_rust::compiler::nir::NirShader; use mesa_rust::pipe::context::PipeContext; +use mesa_rust::pipe::context::PipeContextPrio; use mesa_rust_gen::*; use mesa_rust_util::properties::*; use rusticl_opencl_gen::*; @@ -150,9 +151,17 @@ struct SendableQueueContext { } impl SendableQueueContext { - fn new(device: &'static Device) -> CLResult { + fn new(device: &'static Device, prio: cl_queue_priority_khr) -> CLResult { + let prio = if prio & CL_QUEUE_PRIORITY_HIGH_KHR != 0 { + PipeContextPrio::High + } else if prio & CL_QUEUE_PRIORITY_MED_KHR != 0 { + PipeContextPrio::Med + } else { + PipeContextPrio::Low + }; + Ok(Self { - ctx: ManuallyDrop::new(device.create_context().ok_or(CL_OUT_OF_HOST_MEMORY)?), + ctx: ManuallyDrop::new(device.create_context(prio).ok_or(CL_OUT_OF_HOST_MEMORY)?), dev: device, }) } @@ -222,9 +231,21 @@ impl Queue { props: cl_command_queue_properties, props_v2: Properties, ) -> CLResult> { + // If CL_QUEUE_PRIORITY_KHR is not specified, the default priority CL_QUEUE_PRIORITY_MED_KHR + // is used. + let mut prio = *props_v2 + .get(&CL_QUEUE_PRIORITY_KHR.into()) + .unwrap_or(&CL_QUEUE_PRIORITY_MED_KHR.into()) + as cl_queue_priority_khr; + + // Fallback to the default priority if it's not supported by the device. + if device.context_priority_supported() & prio == 0 { + prio = CL_QUEUE_PRIORITY_MED_KHR; + } + // we assume that memory allocation is the only possible failure. Any other failure reason // should be detected earlier (e.g.: checking for CAPs). - let ctx = SendableQueueContext::new(device)?; + let ctx = SendableQueueContext::new(device, prio)?; let (tx_q, rx_t) = mpsc::channel::>>(); Ok(Arc::new(Self { base: CLObjectBase::new(RusticlTypes::Queue), diff --git a/src/gallium/frontends/rusticl/mesa/pipe/context.rs b/src/gallium/frontends/rusticl/mesa/pipe/context.rs index 027a04f5179..369101d079c 100644 --- a/src/gallium/frontends/rusticl/mesa/pipe/context.rs +++ b/src/gallium/frontends/rusticl/mesa/pipe/context.rs @@ -14,9 +14,24 @@ use std::ptr; use std::ptr::*; use std::sync::Arc; +#[repr(u32)] +#[derive(Clone, Copy, Debug, PartialEq)] +pub enum PipeContextPrio { + Low = PIPE_CONTEXT_LOW_PRIORITY, + Med = 0, + High = PIPE_CONTEXT_HIGH_PRIORITY, +} + +impl From for u32 { + fn from(value: PipeContextPrio) -> Self { + value as _ + } +} + pub struct PipeContext { pipe: NonNull, screen: Arc, + pub prio: PipeContextPrio, } unsafe impl Send for PipeContext {} @@ -36,10 +51,15 @@ impl From for pipe_map_flags { } impl PipeContext { - pub(super) fn new(context: *mut pipe_context, screen: &Arc) -> Option { + pub(super) fn new( + context: *mut pipe_context, + prio: PipeContextPrio, + screen: &Arc, + ) -> Option { let s = Self { pipe: NonNull::new(context)?, screen: Arc::clone(screen), + prio: prio, }; if !has_required_cbs(unsafe { s.pipe.as_ref() }) { diff --git a/src/gallium/frontends/rusticl/mesa/pipe/screen.rs b/src/gallium/frontends/rusticl/mesa/pipe/screen.rs index dc304c00796..b0d53f7b47a 100644 --- a/src/gallium/frontends/rusticl/mesa/pipe/screen.rs +++ b/src/gallium/frontends/rusticl/mesa/pipe/screen.rs @@ -83,15 +83,17 @@ impl PipeScreen { &self.screen().caps } - pub fn create_context(self: &Arc) -> Option { + pub fn create_context(self: &Arc, prio: PipeContextPrio) -> Option { + let flags: u32 = prio.into(); PipeContext::new( unsafe { self.screen().context_create.unwrap()( self.screen.as_ptr(), ptr::null_mut(), - PIPE_CONTEXT_COMPUTE_ONLY | PIPE_CONTEXT_NO_LOD_BIAS, + flags | PIPE_CONTEXT_COMPUTE_ONLY | PIPE_CONTEXT_NO_LOD_BIAS, ) }, + prio, self, ) }