rusticl/program: some boilerplate code for SPIR-V support

Signed-off-by: Karol Herbst <kherbst@redhat.com>
Acked-by: Alyssa Rosenzweig <alyssa.rosenzweig@collabora.com>
Part-of: <https://gitlab.freedesktop.org/mesa/mesa/-/merge_requests/15439>
This commit is contained in:
Karol Herbst 2022-04-23 03:05:09 +02:00 committed by Marge Bot
parent 1b00d4f22e
commit 734352ddfb
8 changed files with 163 additions and 45 deletions

View file

@ -12,6 +12,18 @@ use std::ptr;
use std::sync::Arc;
use std::sync::Once;
// TODO spec constants need to be implemented
const SPIRV_SUPPORT_STRING: &str = "";
// "SPIR-V_1.0 SPIR-V_1.1 SPIR-V_1.2 SPIR-V_1.3 SPIR-V_1.4 SPIR-V_1.5";
const SPIRV_SUPPORT: [cl_name_version; 0] = [
/* mk_cl_version_ext(1, 0, 0, b"SPIR-V"),
mk_cl_version_ext(1, 1, 0, b"SPIR-V"),
mk_cl_version_ext(1, 2, 0, b"SPIR-V"),
mk_cl_version_ext(1, 3, 0, b"SPIR-V"),
mk_cl_version_ext(1, 4, 0, b"SPIR-V"),
mk_cl_version_ext(1, 5, 0, b"SPIR-V"),*/
];
impl CLInfo<cl_device_info> for cl_device_id {
fn query(&self, q: cl_device_info, _: &[u8]) -> CLResult<Vec<u8>> {
let dev = self.get_ref()?;
@ -61,8 +73,8 @@ impl CLInfo<cl_device_info> for cl_device_id {
CL_DEVICE_GLOBAL_VARIABLE_PREFERRED_TOTAL_SIZE => cl_prop::<usize>(0),
CL_DEVICE_HALF_FP_CONFIG => cl_prop::<cl_device_fp_config>(0),
CL_DEVICE_HOST_UNIFIED_MEMORY => cl_prop::<bool>(dev.unified_memory()),
CL_DEVICE_IL_VERSION => cl_prop::<&str>(""),
CL_DEVICE_ILS_WITH_VERSION => cl_prop::<Vec<cl_name_version>>(Vec::new()),
CL_DEVICE_IL_VERSION => cl_prop::<&str>(SPIRV_SUPPORT_STRING),
CL_DEVICE_ILS_WITH_VERSION => cl_prop::<Vec<cl_name_version>>(SPIRV_SUPPORT.to_vec()),
CL_DEVICE_IMAGE_BASE_ADDRESS_ALIGNMENT => {
cl_prop::<cl_uint>(dev.image_base_address_alignment())
}

View file

@ -1258,8 +1258,9 @@ extern "C" fn cl_get_extension_function_address(
return ptr::null_mut();
}
match unsafe { CStr::from_ptr(function_name) }.to_str().unwrap() {
"clGetPlatformInfo" => cl_get_platform_info as *mut std::ffi::c_void,
"clIcdGetPlatformIDsKHR" => cl_icd_get_platform_ids_khr as *mut std::ffi::c_void,
"clCreateProgramWithILKHR" => cl_create_program_with_il as *mut ::std::ffi::c_void,
"clGetPlatformInfo" => cl_get_platform_info as *mut ::std::ffi::c_void,
"clIcdGetPlatformIDsKHR" => cl_icd_get_platform_ids_khr as *mut ::std::ffi::c_void,
_ => ptr::null_mut(),
}
}

View file

@ -17,7 +17,9 @@ impl CLInfo<cl_platform_info> for cl_platform_id {
fn query(&self, q: cl_platform_info, _: &[u8]) -> CLResult<Vec<u8>> {
let p = self.get_ref()?;
Ok(match q {
// TODO spirv
CL_PLATFORM_EXTENSIONS => cl_prop("cl_khr_icd"),
// CL_PLATFORM_EXTENSIONS => cl_prop("cl_khr_icd cl_khr_il_program"),
CL_PLATFORM_EXTENSIONS_WITH_VERSION => {
cl_prop::<Vec<cl_name_version>>(p.extensions.to_vec())
}
@ -37,7 +39,11 @@ impl CLInfo<cl_platform_info> for cl_platform_id {
static PLATFORM: _cl_platform_id = _cl_platform_id {
dispatch: &DISPATCH,
extensions: [mk_cl_version_ext(1, 0, 0, "cl_khr_icd")],
extensions: [
mk_cl_version_ext(1, 0, 0, "cl_khr_icd"),
// TODO spirv
// mk_cl_version_ext(1, 0, 0, "cl_khr_il_program"),
],
};
pub fn get_platform() -> cl_platform_id {

View file

@ -38,7 +38,7 @@ impl CLInfo<cl_program_info> for cl_program {
.collect(),
)
}
CL_PROGRAM_IL => Vec::new(),
CL_PROGRAM_IL => prog.il.clone(),
CL_PROGRAM_KERNEL_NAMES => cl_prop::<String>(prog.kernels().join(";")),
CL_PROGRAM_NUM_DEVICES => cl_prop::<cl_uint>(prog.devs.len() as cl_uint),
CL_PROGRAM_NUM_KERNELS => cl_prop::<usize>(prog.kernels().len()),
@ -195,17 +195,20 @@ pub fn create_program_with_binary(
pub fn create_program_with_il(
context: cl_context,
_il: *const ::std::os::raw::c_void,
_length: usize,
il: *const ::std::os::raw::c_void,
length: usize,
) -> CLResult<cl_program> {
let _c = context.get_ref()?;
let _c = context.get_arc()?;
println!("create_program_with_il not implemented");
// CL_INVALID_VALUE if il is NULL or if length is zero.
if il.is_null() || length == 0 {
return Err(CL_INVALID_VALUE);
}
// let spirv = unsafe { slice::from_raw_parts(il.cast(), length) };
// TODO SPIR-V
// Ok(cl_program::from_arc(Program::from_spirv(c, spirv)))
Err(CL_INVALID_OPERATION)
//• CL_INVALID_CONTEXT if context is not a valid context.
//• CL_INVALID_OPERATION if no devices in context support intermediate language programs.
//• CL_INVALID_VALUE if il is NULL or if length is zero.
//• CL_INVALID_VALUE if the length-byte memory pointed to by il does not contain well-formed intermediate language input that can be consumed by the OpenCL runtime.
}
pub fn build_program(
@ -361,13 +364,30 @@ pub fn link_program(
}
pub fn set_program_specialization_constant(
_program: cl_program,
program: cl_program,
_spec_id: cl_uint,
_spec_size: usize,
_spec_value: *const ::std::os::raw::c_void,
spec_value: *const ::std::os::raw::c_void,
) -> CLResult<()> {
println!("set_program_specialization_constantnot implemented");
let _program = program.get_ref()?;
// CL_INVALID_PROGRAM if program is not a valid program object created from an intermediate
// language (e.g. SPIR-V)
// TODO: or if the intermediate language does not support specialization constants.
// if program.il.is_empty() {
// Err(CL_INVALID_PROGRAM)?
// }
// TODO: CL_INVALID_VALUE if spec_size does not match the size of the specialization constant in the module,
// or if spec_value is NULL.
if spec_value.is_null() {
return Err(CL_INVALID_VALUE);
}
Err(CL_INVALID_OPERATION)
//• CL_INVALID_SPEC_ID if spec_id is not a valid specialization constant identifier.
}
pub fn set_program_release_callback(

View file

@ -116,6 +116,7 @@ macro_rules! cl_prop_for_struct {
}
cl_prop_for_type!(cl_char);
cl_prop_for_type!(cl_uchar);
cl_prop_for_type!(cl_ushort);
cl_prop_for_type!(cl_int);
cl_prop_for_type!(cl_uint);
@ -176,6 +177,19 @@ where
}
}
impl<T> CLProp for [T]
where
T: CLProp,
{
fn cl_vec(&self) -> Vec<u8> {
let mut res: Vec<u8> = Vec::new();
for i in self {
res.append(&mut i.cl_vec())
}
res
}
}
impl<T, const I: usize> CLProp for [T; I]
where
T: CLProp,
@ -225,7 +239,10 @@ where
}
}
pub fn cl_prop<T: CLProp>(v: T) -> Vec<u8> {
pub fn cl_prop<T: CLProp>(v: T) -> Vec<u8>
where
T: Sized,
{
v.cl_vec()
}

View file

@ -400,6 +400,8 @@ impl Device {
add_ext(1, 0, 0, "cl_khr_byte_addressable_store", "");
add_ext(1, 0, 0, "cl_khr_global_int32_base_atomics", "");
add_ext(1, 0, 0, "cl_khr_global_int32_extended_atomics", "");
// TODO spirv
// add_ext(1, 0, 0, "cl_khr_il_program", "");
add_ext(1, 0, 0, "cl_khr_local_int32_base_atomics", "");
add_ext(1, 0, 0, "cl_khr_local_int32_extended_atomics", "");

View file

@ -49,6 +49,8 @@ pub struct Program {
pub context: Arc<Context>,
pub devs: Vec<Arc<Device>>,
pub src: CString,
pub il: Vec<u8>,
spec_constants: Mutex<Vec<spirv::SpecConstant>>,
build: Mutex<ProgramBuild>,
}
@ -112,6 +114,8 @@ impl Program {
context: context.clone(),
devs: devs.to_vec(),
src: src,
il: Vec::new(),
spec_constants: Mutex::new(Vec::new()),
build: Mutex::new(ProgramBuild {
builds: builds,
kernels: Vec::new(),
@ -150,10 +154,10 @@ impl Program {
// 4. the spirv
assert!(b.as_ptr().add(BIN_HEADER_SIZE_V1) == ptr);
assert!(b.len() == BIN_HEADER_SIZE_V1 + spirv_size as usize);
spirv = Some(spirv::SPIRVBin::from_bin(
slice::from_raw_parts(ptr, spirv_size as usize),
bin_type == CL_PROGRAM_BINARY_TYPE_EXECUTABLE,
));
spirv = Some(spirv::SPIRVBin::from_bin(slice::from_raw_parts(
ptr,
spirv_size as usize,
)));
}
_ => panic!("unknown version"),
}
@ -182,6 +186,8 @@ impl Program {
context: context,
devs: devs,
src: CString::new("").unwrap(),
il: Vec::new(),
spec_constants: Mutex::new(Vec::new()),
build: Mutex::new(ProgramBuild {
builds: builds,
kernels: kernels.into_iter().collect(),
@ -189,6 +195,38 @@ impl Program {
})
}
pub fn from_spirv(context: Arc<Context>, spirv: &[u8]) -> Arc<Program> {
let mut builds = HashMap::new();
for d in &context.devs {
let spirv = Some(spirv::SPIRVBin::from_bin(spirv));
builds.insert(
d.clone(),
ProgramDevBuild {
spirv: spirv,
status: CL_BUILD_SUCCESS as cl_build_status,
log: String::from(""),
options: String::from(""),
bin_type: CL_PROGRAM_BINARY_TYPE_INTERMEDIATE,
},
);
}
Arc::new(Self {
base: CLObjectBase::new(),
devs: context.devs.clone(),
context: context,
src: CString::new("").unwrap(),
il: spirv.to_vec(),
spec_constants: Mutex::new(Vec::new()),
build: Mutex::new(ProgramBuild {
builds: builds,
kernels: Vec::new(),
}),
})
}
fn build_info(&self) -> MutexGuard<ProgramBuild> {
self.build.lock().unwrap()
}
@ -293,31 +331,36 @@ impl Program {
pub fn build(&self, dev: &Arc<Device>, options: String) -> bool {
// program binary
if self.src.as_bytes().is_empty() {
let is_il = !self.il.is_empty();
if self.src.as_bytes().is_empty() && !is_il {
return true;
}
let mut info = self.build_info();
let d = Self::dev_build_info(&mut info, dev);
let lib = options.contains("-create-library");
let args = prepare_options(&options, dev);
let (spirv, log) = spirv::SPIRVBin::from_clc(
&self.src,
&args,
&Vec::new(),
get_disk_cache(),
dev.cl_features(),
);
d.log = log;
d.options = options;
if spirv.is_none() {
d.status = CL_BUILD_ERROR;
return false;
if !is_il {
let (spirv, log) = spirv::SPIRVBin::from_clc(
&self.src,
&args,
&Vec::new(),
get_disk_cache(),
dev.cl_features(),
);
d.log = log;
if spirv.is_none() {
d.status = CL_BUILD_ERROR;
return false;
}
d.spirv = spirv;
}
let spirvs = vec![spirv.as_ref().unwrap()];
d.options = options;
let spirvs = [d.spirv.as_ref().unwrap()];
let (spirv, log) = spirv::SPIRVBin::link(&spirvs, lib);
d.log.push_str(&log);
@ -345,12 +388,19 @@ impl Program {
headers: &[spirv::CLCHeader],
) -> bool {
// program binary
if self.src.as_bytes().is_empty() {
let is_il = !self.il.is_empty();
if self.src.as_bytes().is_empty() && !is_il {
return true;
}
let mut info = self.build_info();
let d = Self::dev_build_info(&mut info, dev);
if is_il {
d.bin_type = CL_PROGRAM_BINARY_TYPE_COMPILED_OBJECT;
return true;
}
let args = prepare_options(&options, dev);
let (spirv, log) = spirv::SPIRVBin::from_clc(
@ -429,6 +479,8 @@ impl Program {
context: context,
devs: devs,
src: CString::new("").unwrap(),
il: Vec::new(),
spec_constants: Mutex::new(Vec::new()),
build: Mutex::new(ProgramBuild {
builds: builds,
kernels: kernels.into_iter().collect(),
@ -492,6 +544,7 @@ impl Program {
d.screen
.nir_shader_compiler_options(pipe_shader_type::PIPE_SHADER_COMPUTE),
&d.lib_clc,
&mut [],
)
.unwrap()
}

View file

@ -14,6 +14,10 @@ use std::slice;
const INPUT_STR: *const c_char = b"input.cl\0" as *const u8 as *const c_char;
pub enum SpecConstant {
None,
}
pub struct SPIRVBin {
spirv: clc_binary,
info: Option<clc_parsed_spirv>,
@ -63,7 +67,7 @@ impl SPIRVBin {
let mut key = cache.gen_key(&key);
if let Some(data) = cache.get(&mut key) {
return (Some(Self::from_bin(&data, false)), String::from(""));
return (Some(Self::from_bin(&data)), String::from(""));
}
hash_key = Some(key);
@ -285,15 +289,17 @@ impl SPIRVBin {
entry_point: &str,
nir_options: *const nir_shader_compiler_options,
libclc: &NirShader,
spec_constants: &mut [nir_spirv_specialization],
) -> Option<NirShader> {
let c_entry = CString::new(entry_point.as_bytes()).unwrap();
let spirv_options = Self::get_spirv_options(false, libclc.get_nir());
let nir = unsafe {
spirv_to_nir(
self.spirv.data.cast(),
self.spirv.size / 4,
ptr::null_mut(), // spec
0, // spec count
spec_constants.as_mut_ptr(),
spec_constants.len() as u32,
gl_shader_stage::MESA_SHADER_KERNEL,
c_entry.as_ptr(),
&spirv_options,
@ -317,7 +323,7 @@ impl SPIRVBin {
unsafe { slice::from_raw_parts(self.spirv.data.cast(), self.spirv.size) }
}
pub fn from_bin(bin: &[u8], executable: bool) -> Self {
pub fn from_bin(bin: &[u8]) -> Self {
unsafe {
let ptr = malloc(bin.len());
ptr::copy_nonoverlapping(bin.as_ptr(), ptr.cast(), bin.len());
@ -325,9 +331,10 @@ impl SPIRVBin {
data: ptr,
size: bin.len(),
};
let info = if executable {
let mut pspirv = clc_parsed_spirv::default();
clc_parse_spirv(&spirv, ptr::null(), &mut pspirv);
let mut pspirv = clc_parsed_spirv::default();
let info = if clc_parse_spirv(&spirv, ptr::null(), &mut pspirv) {
Some(pspirv)
} else {
None