rusticl: implement cl_khr_priority_hints
Some checks are pending
macOS-CI / macOS-CI (dri) (push) Waiting to run
macOS-CI / macOS-CI (xlib) (push) Waiting to run

Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/35456>
This commit is contained in:
Karol Herbst 2025-06-11 03:04:57 +02:00
parent 1c3f238ce9
commit 79656dbcd3
7 changed files with 97 additions and 13 deletions

View file

@ -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

View file

@ -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

View file

@ -129,12 +129,23 @@ fn create_command_queue_with_properties(
properties: *const cl_queue_properties,
) -> CLResult<cl_command_queue> {
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),

View file

@ -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<PipeContext> {
self.reusable_ctx()
.pop()
.or_else(|| self.screen.create_context())
pub fn create_context(&self, prio: PipeContextPrio) -> Option<PipeContext> {
// 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),

View file

@ -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<Self> {
fn new(device: &'static Device, prio: cl_queue_priority_khr) -> CLResult<Self> {
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<cl_queue_properties>,
) -> CLResult<Arc<Queue>> {
// 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::<Vec<Arc<Event>>>();
Ok(Arc::new(Self {
base: CLObjectBase::new(RusticlTypes::Queue),

View file

@ -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<PipeContextPrio> for u32 {
fn from(value: PipeContextPrio) -> Self {
value as _
}
}
pub struct PipeContext {
pipe: NonNull<pipe_context>,
screen: Arc<PipeScreen>,
pub prio: PipeContextPrio,
}
unsafe impl Send for PipeContext {}
@ -36,10 +51,15 @@ impl From<RWFlags> for pipe_map_flags {
}
impl PipeContext {
pub(super) fn new(context: *mut pipe_context, screen: &Arc<PipeScreen>) -> Option<Self> {
pub(super) fn new(
context: *mut pipe_context,
prio: PipeContextPrio,
screen: &Arc<PipeScreen>,
) -> Option<Self> {
let s = Self {
pipe: NonNull::new(context)?,
screen: Arc::clone(screen),
prio: prio,
};
if !has_required_cbs(unsafe { s.pipe.as_ref() }) {

View file

@ -83,15 +83,17 @@ impl PipeScreen {
&self.screen().caps
}
pub fn create_context(self: &Arc<Self>) -> Option<PipeContext> {
pub fn create_context(self: &Arc<Self>, prio: PipeContextPrio) -> Option<PipeContext> {
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,
)
}