rusticl/queue: cache samplers

OpenCL doesn't really allow a wide range of different samplers, so the
cache hit rate is pretty high across all applications.

This also allows us to stop unbinding samplers after each kernel launch.

Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/36243>
This commit is contained in:
Karol Herbst
2025-07-19 19:03:16 +02:00
committed by Marge Bot
parent eb904cd51c
commit aa5f8b9d35
3 changed files with 61 additions and 9 deletions
+1 -9
View File
@@ -1641,11 +1641,6 @@ impl Kernel {
// subtract the shader local_size as we only request something on top of that.
variable_local_size -= static_local_size;
let samplers: Vec<_> = samplers
.iter()
.map(|s| ctx.create_sampler_state(s))
.collect();
let mut resources = Vec::with_capacity(resource_info.len());
let mut globals: Vec<*mut u32> = Vec::with_capacity(resource_info.len());
for (res, offset) in resource_info {
@@ -1654,7 +1649,7 @@ impl Kernel {
}
ctx.bind_kernel(&nir_kernel_builds, variant)?;
ctx.bind_sampler_states(&samplers);
ctx.bind_sampler_states(samplers);
ctx.bind_sampler_views(sviews);
ctx.set_shader_images(&iviews);
ctx.set_global_binding(resources.as_slice(), &mut globals);
@@ -1700,12 +1695,9 @@ impl Kernel {
}
ctx.clear_global_binding(globals.len() as u32);
ctx.clear_sampler_states(samplers.len() as u32);
ctx.memory_barrier(PIPE_BARRIER_GLOBAL_BUFFER);
samplers.iter().for_each(|s| ctx.delete_sampler_state(*s));
if let Some(printf_buf) = &printf_buf {
let tx = ctx
.buffer_map(printf_buf, 0, printf_size as i32, RWFlags::RD)
@@ -30,6 +30,7 @@ use std::collections::btree_map::Entry;
use std::collections::HashMap;
use std::convert::TryInto;
use std::fmt::Debug;
use std::hash::Hash;
use std::mem;
use std::mem::size_of;
use std::num::NonZeroU64;
@@ -2296,3 +2297,38 @@ impl Sampler {
))
}
}
/// A custom wrapper around pipe_sampler_state that implements certain Traits (e.g. Hash and
/// PartialEq) only looking at fields we actually care about. All other fields will be ignored!
#[repr(transparent)]
pub struct PipeSamplerState(pipe_sampler_state);
impl From<pipe_sampler_state> for PipeSamplerState {
fn from(value: pipe_sampler_state) -> Self {
Self(value)
}
}
impl Hash for PipeSamplerState {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
state.write_u32(self.0.wrap_r());
state.write_u32(self.0.min_img_filter());
state.write_u32(self.0.unnormalized_coords());
}
}
impl PartialEq for PipeSamplerState {
fn eq(&self, other: &Self) -> bool {
self.0.wrap_r() == other.0.wrap_r()
&& self.0.min_img_filter() == other.0.min_img_filter()
&& self.0.unnormalized_coords() == other.0.unnormalized_coords()
}
}
impl Eq for PipeSamplerState {}
impl PipeSamplerState {
pub fn pipe(&self) -> &pipe_sampler_state {
&self.0
}
}
@@ -3,6 +3,7 @@ use crate::core::context::*;
use crate::core::device::*;
use crate::core::event::*;
use crate::core::kernel::*;
use crate::core::memory::PipeSamplerState;
use crate::core::platform::*;
use crate::impl_cl_type_trait;
@@ -16,6 +17,7 @@ use mesa_rust_util::properties::*;
use rusticl_opencl_gen::*;
use std::cmp;
use std::collections::HashMap;
use std::ffi::c_void;
use std::mem;
use std::mem::ManuallyDrop;
@@ -72,6 +74,7 @@ impl<'a> QueueContext<'a> {
cso: None,
use_stream: self.dev.prefers_real_buffer_in_cb0(),
bound_sampler_views: 0,
samplers: HashMap::new(),
}
}
}
@@ -86,6 +89,7 @@ pub struct QueueContextWithState<'a> {
variant: NirKernelVariant,
cso: Option<CSOWrapper<'a>>,
bound_sampler_views: u32,
samplers: HashMap<PipeSamplerState, *mut c_void>,
}
impl QueueContextWithState<'_> {
@@ -126,6 +130,21 @@ impl QueueContextWithState<'_> {
Ok(())
}
pub fn bind_sampler_states(&mut self, samplers: Vec<pipe_sampler_state>) {
let samplers = samplers
.into_iter()
.map(PipeSamplerState::from)
.map(|sampler| {
*self
.samplers
.entry(sampler)
.or_insert_with_key(|sampler| self.ctx.create_sampler_state(sampler.pipe()))
})
.collect::<Vec<_>>();
self.ctx.bind_sampler_states(&samplers);
}
pub fn bind_sampler_views(&mut self, views: Vec<PipeSamplerView>) {
let cnt = views.len() as u32;
let unbind_cnt = self.bound_sampler_views.saturating_sub(cnt);
@@ -160,6 +179,11 @@ impl Drop for QueueContextWithState<'_> {
fn drop(&mut self) {
self.set_constant_buffer(0, &[]);
self.ctx.clear_sampler_views(self.bound_sampler_views);
self.ctx.clear_sampler_states(self.dev.max_samplers());
self.samplers
.values()
.for_each(|&sampler| self.ctx.delete_sampler_state(sampler));
self.ctx.clear_shader_images(self.dev.caps.max_write_images);
if self.builds.is_some() {
// SAFETY: We simply unbind here. The bound cso will only be dropped at the end of this