rusticl: added

Initial code drop for Rusticl :)

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 2020-11-08 20:28:21 +01:00 committed by Marge Bot
parent d431a01d9a
commit 20c90fed5a
51 changed files with 6768 additions and 19 deletions

View file

@ -35,7 +35,10 @@ trim_trailing_whitespace = false
indent_style = space indent_style = space
indent_size = 2 indent_size = 2
[*.ps1] [*.ps1]
indent_style = space indent_style = space
indent_size = 2 indent_size = 2
[*.rs]
indent_style = space
indent_size = 4

View file

@ -118,27 +118,30 @@ if with_platform_haiku
) )
endif endif
opencl_headers = files(
'CL/cl.h',
'CL/cl.hpp',
'CL/cl2.hpp',
'CL/cl_d3d10.h',
'CL/cl_d3d11.h',
'CL/cl_dx9_media_sharing.h',
'CL/cl_dx9_media_sharing_intel.h',
'CL/cl_egl.h',
'CL/cl_ext.h',
'CL/cl_ext_intel.h',
'CL/cl_gl.h',
'CL/cl_gl_ext.h',
'CL/cl_icd.h',
'CL/cl_platform.h',
'CL/cl_va_api_media_sharing_intel.h',
'CL/cl_version.h',
'CL/opencl.h',
)
# Only install the headers if we are building a stand alone implementation and # Only install the headers if we are building a stand alone implementation and
# not an ICD enabled implementation # not an ICD enabled implementation
if with_gallium_opencl and not with_opencl_icd if with_gallium_opencl and not with_opencl_icd
install_headers( install_headers(
'CL/cl.h', opencl_headers,
'CL/cl.hpp',
'CL/cl2.hpp',
'CL/cl_d3d10.h',
'CL/cl_d3d11.h',
'CL/cl_dx9_media_sharing.h',
'CL/cl_dx9_media_sharing_intel.h',
'CL/cl_egl.h',
'CL/cl_ext.h',
'CL/cl_ext_intel.h',
'CL/cl_gl.h',
'CL/cl_gl_ext.h',
'CL/cl_icd.h',
'CL/cl_platform.h',
'CL/cl_va_api_media_sharing_intel.h',
'CL/cl_version.h',
'CL/opencl.h',
subdir: 'CL' subdir: 'CL'
) )
endif endif

View file

@ -27,7 +27,7 @@ project(
).stdout(), ).stdout(),
license : 'MIT', license : 'MIT',
meson_version : '>= 0.53', meson_version : '>= 0.53',
default_options : ['buildtype=debugoptimized', 'b_ndebug=if-release', 'c_std=c11', 'cpp_std=c++17'] default_options : ['buildtype=debugoptimized', 'b_ndebug=if-release', 'c_std=c11', 'cpp_std=c++17', 'rust_std=2021']
) )
# In recent versions, meson can inject some extra arguments to get richer # In recent versions, meson can inject some extra arguments to get richer
@ -953,6 +953,21 @@ else
with_opencl_icd = false with_opencl_icd = false
endif endif
with_gallium_rusticl = get_option('gallium-rusticl')
if with_gallium_rusticl
if not with_gallium
error('rusticl requires at least one gallium driver.')
endif
if meson.version().version_compare('< 0.61.4')
error('rusticl requires meson 0.61.4 or newer')
endif
add_languages('rust', required: true)
with_clc = true
endif
dep_clc = null_dep dep_clc = null_dep
if with_libclc if with_libclc
dep_clc = dependency('libclc') dep_clc = dependency('libclc')

View file

@ -163,6 +163,12 @@ option(
value : 'disabled', value : 'disabled',
description : 'build gallium "clover" OpenCL frontend.', description : 'build gallium "clover" OpenCL frontend.',
) )
option(
'gallium-rusticl',
type : 'boolean',
value : false,
description : 'build gallium "rusticl" OpenCL frontend.',
)
option( option(
'gallium-windows-dll-name', 'gallium-windows-dll-name',
type : 'string', type : 'string',

View file

@ -0,0 +1,117 @@
extern crate mesa_rust_util;
extern crate rusticl_opencl_gen;
use crate::api::device::get_devs_for_type;
use crate::api::icd::*;
use crate::api::platform::*;
use crate::api::types::*;
use crate::api::util::*;
use crate::core::context::*;
use self::mesa_rust_util::properties::Properties;
use self::rusticl_opencl_gen::*;
use std::collections::HashSet;
use std::iter::FromIterator;
use std::slice;
use std::sync::Arc;
impl CLInfo<cl_context_info> for cl_context {
fn query(&self, q: cl_context_info) -> CLResult<Vec<u8>> {
let ctx = self.get_ref()?;
Ok(match q {
CL_CONTEXT_DEVICES => {
cl_prop::<&Vec<cl_device_id>>(
&ctx.devs
.iter()
.map(|d| {
// Note we use as_ptr here which doesn't increase the reference count.
cl_device_id::from_ptr(Arc::as_ptr(d))
})
.collect(),
)
}
CL_CONTEXT_NUM_DEVICES => cl_prop::<cl_uint>(ctx.devs.len() as u32),
CL_CONTEXT_PROPERTIES => cl_prop::<&Vec<cl_context_properties>>(&ctx.properties),
CL_CONTEXT_REFERENCE_COUNT => cl_prop::<cl_uint>(self.refcnt()?),
// CL_INVALID_VALUE if param_name is not one of the supported values
_ => return Err(CL_INVALID_VALUE),
})
}
}
pub fn create_context(
properties: *const cl_context_properties,
num_devices: cl_uint,
devices: *const cl_device_id,
pfn_notify: Option<CreateContextCB>,
user_data: *mut ::std::os::raw::c_void,
) -> CLResult<cl_context> {
check_cb(&pfn_notify, user_data)?;
// CL_INVALID_VALUE if devices is NULL.
if devices.is_null() {
return Err(CL_INVALID_VALUE);
}
// CL_INVALID_VALUE if num_devices is equal to zero.
if num_devices == 0 {
return Err(CL_INVALID_VALUE);
}
// CL_INVALID_PROPERTY [...] if the same property name is specified more than once.
let props = Properties::from_ptr(properties).ok_or(CL_INVALID_PROPERTY)?;
for p in props.props {
match p.0 as u32 {
// CL_INVALID_PLATFORM [...] if platform value specified in properties is not a valid platform.
CL_CONTEXT_PLATFORM => {
(p.1 as cl_platform_id).get_ref()?;
}
CL_CONTEXT_INTEROP_USER_SYNC => {
check_cl_bool(p.1).ok_or(CL_INVALID_PROPERTY)?;
}
// CL_INVALID_PROPERTY if context property name in properties is not a supported property name
_ => return Err(CL_INVALID_PROPERTY),
}
}
// Duplicate devices specified in devices are ignored.
let set: HashSet<_> =
HashSet::from_iter(unsafe { slice::from_raw_parts(devices, num_devices as usize) }.iter());
let devs: Result<_, _> = set.into_iter().map(cl_device_id::get_arc).collect();
Ok(cl_context::from_arc(Context::new(
devs?,
Properties::from_ptr_raw(properties),
)))
}
pub fn create_context_from_type(
properties: *const cl_context_properties,
device_type: cl_device_type,
pfn_notify: Option<CreateContextCB>,
user_data: *mut ::std::os::raw::c_void,
) -> CLResult<cl_context> {
// CL_INVALID_DEVICE_TYPE if device_type is not a valid value.
check_cl_device_type(device_type)?;
let devs: Vec<_> = get_devs_for_type(device_type)
.iter()
.map(|d| cl_device_id::from_ptr(Arc::as_ptr(d)))
.collect();
// CL_DEVICE_NOT_FOUND if no devices that match device_type and property values specified in properties were found.
if devs.is_empty() {
return Err(CL_DEVICE_NOT_FOUND);
}
// errors are essentially the same and we will always pass in a valid
// device list, so that's fine as well.
create_context(
properties,
devs.len() as u32,
devs.as_ptr(),
pfn_notify,
user_data,
)
}

View file

@ -0,0 +1,230 @@
extern crate mesa_rust_util;
extern crate rusticl_opencl_gen;
use crate::api::icd::*;
use crate::api::platform::*;
use crate::api::util::*;
use crate::core::device::*;
use self::mesa_rust_util::ptr::*;
use self::rusticl_opencl_gen::*;
use std::cmp::min;
use std::mem::size_of;
use std::ptr;
use std::sync::Arc;
use std::sync::Once;
impl CLInfo<cl_device_info> for cl_device_id {
fn query(&self, q: cl_device_info) -> CLResult<Vec<u8>> {
let dev = self.get_ref()?;
Ok(match q {
CL_DEVICE_ADDRESS_BITS => cl_prop::<cl_uint>(dev.address_bits()),
CL_DEVICE_ATOMIC_FENCE_CAPABILITIES => cl_prop::<cl_device_atomic_capabilities>(0),
CL_DEVICE_ATOMIC_MEMORY_CAPABILITIES => cl_prop::<cl_device_atomic_capabilities>(0),
CL_DEVICE_AVAILABLE => cl_prop::<bool>(true),
CL_DEVICE_BUILT_IN_KERNELS => cl_prop::<&str>(""),
CL_DEVICE_BUILT_IN_KERNELS_WITH_VERSION => cl_prop::<Vec<cl_name_version>>(Vec::new()),
CL_DEVICE_COMPILER_AVAILABLE => cl_prop::<bool>(true),
CL_DEVICE_DEVICE_ENQUEUE_CAPABILITIES => {
cl_prop::<cl_device_device_enqueue_capabilities>(0)
}
CL_DEVICE_DOUBLE_FP_CONFIG => cl_prop::<cl_device_fp_config>(0),
CL_DEVICE_ENDIAN_LITTLE => cl_prop::<bool>(dev.little_endian()),
CL_DEVICE_ERROR_CORRECTION_SUPPORT => cl_prop::<bool>(false),
CL_DEVICE_EXECUTION_CAPABILITIES => {
cl_prop::<cl_device_exec_capabilities>(CL_EXEC_KERNEL.into())
}
CL_DEVICE_EXTENSIONS => cl_prop::<&str>(&dev.extension_string),
CL_DEVICE_EXTENSIONS_WITH_VERSION => cl_prop::<&Vec<cl_name_version>>(&dev.extensions),
CL_DEVICE_GENERIC_ADDRESS_SPACE_SUPPORT => cl_prop::<bool>(false),
CL_DEVICE_GLOBAL_MEM_CACHE_TYPE => cl_prop::<cl_device_mem_cache_type>(CL_NONE),
CL_DEVICE_GLOBAL_MEM_CACHE_SIZE => cl_prop::<cl_ulong>(0),
CL_DEVICE_GLOBAL_MEM_CACHELINE_SIZE => cl_prop::<cl_uint>(0),
CL_DEVICE_GLOBAL_MEM_SIZE => cl_prop::<cl_ulong>(dev.global_mem_size()),
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_IMAGE_BASE_ADDRESS_ALIGNMENT => {
cl_prop::<cl_uint>(dev.image_base_address_alignment())
}
CL_DEVICE_IMAGE_MAX_ARRAY_SIZE => cl_prop::<usize>(dev.image_array_size()),
CL_DEVICE_IMAGE_MAX_BUFFER_SIZE => cl_prop::<usize>(dev.image_buffer_size()),
CL_DEVICE_IMAGE_PITCH_ALIGNMENT => cl_prop::<cl_uint>(0),
CL_DEVICE_IMAGE_SUPPORT => cl_prop::<bool>(dev.image_supported()),
CL_DEVICE_IMAGE2D_MAX_HEIGHT => cl_prop::<usize>(dev.image_2d_size()),
CL_DEVICE_IMAGE2D_MAX_WIDTH => cl_prop::<usize>(dev.image_2d_size()),
CL_DEVICE_IMAGE3D_MAX_HEIGHT => cl_prop::<usize>(dev.image_3d_size()),
CL_DEVICE_IMAGE3D_MAX_WIDTH => cl_prop::<usize>(dev.image_3d_size()),
CL_DEVICE_IMAGE3D_MAX_DEPTH => cl_prop::<usize>(dev.image_3d_size()),
CL_DEVICE_LATEST_CONFORMANCE_VERSION_PASSED => cl_prop::<&str>("v0000-01-01-00"),
CL_DEVICE_LINKER_AVAILABLE => cl_prop::<bool>(true),
CL_DEVICE_LOCAL_MEM_SIZE => cl_prop::<cl_ulong>(dev.local_mem_size()),
// TODO add query for CL_LOCAL vs CL_GLOBAL
CL_DEVICE_LOCAL_MEM_TYPE => cl_prop::<cl_device_local_mem_type>(CL_GLOBAL),
CL_DEVICE_MAX_CLOCK_FREQUENCY => cl_prop::<cl_uint>(dev.max_clock_freq()),
CL_DEVICE_MAX_COMPUTE_UNITS => cl_prop::<cl_uint>(dev.max_compute_units()),
// TODO atm implemented as mem_const
CL_DEVICE_MAX_CONSTANT_ARGS => cl_prop::<cl_uint>(1024),
CL_DEVICE_MAX_CONSTANT_BUFFER_SIZE => cl_prop::<cl_ulong>(dev.const_max_size()),
CL_DEVICE_MAX_GLOBAL_VARIABLE_SIZE => cl_prop::<usize>(0),
CL_DEVICE_MAX_MEM_ALLOC_SIZE => cl_prop::<cl_ulong>(dev.max_mem_alloc()),
CL_DEVICE_MAX_NUM_SUB_GROUPS => cl_prop::<cl_uint>(0),
CL_DEVICE_MAX_ON_DEVICE_EVENTS => cl_prop::<cl_uint>(0),
CL_DEVICE_MAX_ON_DEVICE_QUEUES => cl_prop::<cl_uint>(0),
CL_DEVICE_MAX_PARAMETER_SIZE => cl_prop::<usize>(dev.param_max_size()),
CL_DEVICE_MAX_PIPE_ARGS => cl_prop::<cl_uint>(0),
CL_DEVICE_MAX_READ_IMAGE_ARGS => cl_prop::<cl_uint>(dev.image_read_count()),
CL_DEVICE_MAX_READ_WRITE_IMAGE_ARGS => cl_prop::<cl_uint>(0),
CL_DEVICE_MAX_SAMPLERS => cl_prop::<cl_uint>(dev.max_samplers()),
CL_DEVICE_MAX_WORK_GROUP_SIZE => cl_prop::<usize>(dev.max_threads_per_block()),
CL_DEVICE_MAX_WORK_ITEM_DIMENSIONS => cl_prop::<cl_uint>(dev.max_grid_dimensions()),
CL_DEVICE_MAX_WORK_ITEM_SIZES => cl_prop::<Vec<usize>>(dev.max_block_sizes()),
CL_DEVICE_MAX_WRITE_IMAGE_ARGS => cl_prop::<cl_uint>(dev.image_write_count()),
// TODO proper retrival from devices
CL_DEVICE_MEM_BASE_ADDR_ALIGN => cl_prop::<cl_uint>(0x1000),
CL_DEVICE_MIN_DATA_TYPE_ALIGN_SIZE => {
cl_prop::<cl_uint>(size_of::<cl_ulong16>() as cl_uint)
}
CL_DEVICE_NAME => cl_prop(dev.screen().name()),
CL_DEVICE_NATIVE_VECTOR_WIDTH_CHAR => cl_prop::<cl_uint>(1),
CL_DEVICE_NATIVE_VECTOR_WIDTH_DOUBLE => cl_prop::<cl_uint>(0),
CL_DEVICE_NATIVE_VECTOR_WIDTH_FLOAT => cl_prop::<cl_uint>(1),
CL_DEVICE_NATIVE_VECTOR_WIDTH_HALF => cl_prop::<cl_uint>(0),
CL_DEVICE_NATIVE_VECTOR_WIDTH_INT => cl_prop::<cl_uint>(1),
CL_DEVICE_NATIVE_VECTOR_WIDTH_LONG => cl_prop::<cl_uint>(1),
CL_DEVICE_NATIVE_VECTOR_WIDTH_SHORT => cl_prop::<cl_uint>(1),
CL_DEVICE_NON_UNIFORM_WORK_GROUP_SUPPORT => cl_prop::<bool>(false),
CL_DEVICE_NUMERIC_VERSION => cl_prop::<cl_version>(dev.cl_version as cl_version),
// TODO subdevice support
CL_DEVICE_PARENT_DEVICE => cl_prop::<cl_device_id>(cl_device_id::from_ptr(ptr::null())),
CL_DEVICE_PARTITION_AFFINITY_DOMAIN => cl_prop::<cl_device_affinity_domain>(0),
CL_DEVICE_PARTITION_MAX_SUB_DEVICES => cl_prop::<cl_uint>(0),
CL_DEVICE_PARTITION_PROPERTIES => cl_prop::<Vec<cl_device_partition_property>>(vec![0]),
CL_DEVICE_PARTITION_TYPE => cl_prop::<Vec<cl_device_partition_property>>(Vec::new()),
CL_DEVICE_PIPE_MAX_ACTIVE_RESERVATIONS => cl_prop::<cl_uint>(0),
CL_DEVICE_PIPE_MAX_PACKET_SIZE => cl_prop::<cl_uint>(0),
CL_DEVICE_PIPE_SUPPORT => cl_prop::<bool>(false),
CL_DEVICE_PLATFORM => cl_prop::<cl_platform_id>(get_platform()),
CL_DEVICE_PREFERRED_GLOBAL_ATOMIC_ALIGNMENT => cl_prop::<cl_uint>(0),
CL_DEVICE_PREFERRED_INTEROP_USER_SYNC => cl_prop::<bool>(true),
CL_DEVICE_PREFERRED_LOCAL_ATOMIC_ALIGNMENT => cl_prop::<cl_uint>(0),
CL_DEVICE_PREFERRED_PLATFORM_ATOMIC_ALIGNMENT => cl_prop::<cl_uint>(0),
CL_DEVICE_PREFERRED_VECTOR_WIDTH_CHAR => cl_prop::<cl_uint>(1),
CL_DEVICE_PREFERRED_VECTOR_WIDTH_DOUBLE => cl_prop::<cl_uint>(0),
CL_DEVICE_PREFERRED_VECTOR_WIDTH_FLOAT => cl_prop::<cl_uint>(1),
CL_DEVICE_PREFERRED_VECTOR_WIDTH_HALF => cl_prop::<cl_uint>(0),
CL_DEVICE_PREFERRED_VECTOR_WIDTH_INT => cl_prop::<cl_uint>(1),
CL_DEVICE_PREFERRED_VECTOR_WIDTH_LONG => cl_prop::<cl_uint>(1),
CL_DEVICE_PREFERRED_VECTOR_WIDTH_SHORT => cl_prop::<cl_uint>(1),
CL_DEVICE_PREFERRED_WORK_GROUP_SIZE_MULTIPLE => cl_prop::<usize>(1),
// TODO
CL_DEVICE_PRINTF_BUFFER_SIZE => cl_prop::<usize>(0),
// TODO
CL_DEVICE_PROFILING_TIMER_RESOLUTION => cl_prop::<usize>(0),
CL_DEVICE_OPENCL_C_FEATURES => cl_prop::<Vec<cl_name_version>>(Vec::new()),
CL_DEVICE_OPENCL_C_VERSION => {
cl_prop::<String>(format!("OpenCL C {} ", dev.clc_version.api_str()))
}
CL_DEVICE_OPENCL_C_ALL_VERSIONS => cl_prop::<&Vec<cl_name_version>>(&dev.clc_versions),
CL_DEVICE_PROFILE => cl_prop(if dev.embedded {
"EMBEDDED_PROFILE"
} else {
"FULL_PROFILE"
}),
CL_DEVICE_QUEUE_ON_DEVICE_MAX_SIZE => cl_prop::<cl_uint>(0),
CL_DEVICE_QUEUE_ON_DEVICE_PREFERRED_SIZE => cl_prop::<cl_uint>(0),
CL_DEVICE_QUEUE_ON_DEVICE_PROPERTIES => cl_prop::<cl_command_queue_properties>(0),
CL_DEVICE_QUEUE_ON_HOST_PROPERTIES => {
cl_prop::<cl_command_queue_properties>(CL_QUEUE_PROFILING_ENABLE.into())
}
CL_DEVICE_REFERENCE_COUNT => cl_prop::<cl_uint>(1),
CL_DEVICE_SINGLE_FP_CONFIG => cl_prop::<cl_device_fp_config>(
(CL_FP_ROUND_TO_NEAREST | CL_FP_INF_NAN) as cl_device_fp_config,
),
CL_DEVICE_SUB_GROUP_INDEPENDENT_FORWARD_PROGRESS => cl_prop::<bool>(false),
CL_DEVICE_SVM_CAPABILITIES => cl_prop::<cl_device_svm_capabilities>(0),
CL_DEVICE_TYPE => cl_prop::<cl_device_type>(dev.device_type()),
CL_DEVICE_VENDOR => cl_prop(dev.screen().device_vendor()),
CL_DEVICE_VENDOR_ID => cl_prop::<cl_uint>(dev.vendor_id()),
CL_DEVICE_VERSION => cl_prop::<String>(format!("OpenCL {}", dev.cl_version.api_str())),
CL_DRIVER_VERSION => cl_prop("0.1"),
CL_DEVICE_WORK_GROUP_COLLECTIVE_FUNCTIONS_SUPPORT => cl_prop::<bool>(false),
// CL_INVALID_VALUE if param_name is not one of the supported values
// CL_INVALID_VALUE [...] if param_name is a value that is available as an extension and the corresponding extension is not supported by the device.
_ => return Err(CL_INVALID_VALUE),
})
}
}
// TODO replace with const new container
static mut DEVICES: Vec<Arc<Device>> = Vec::new();
static INIT: Once = Once::new();
fn load_devices() {
Device::all()
.into_iter()
.for_each(|d| unsafe { DEVICES.push(d) });
}
fn devs() -> &'static Vec<Arc<Device>> {
INIT.call_once(load_devices);
unsafe { &DEVICES }
}
pub fn get_devs_for_type(device_type: cl_device_type) -> Vec<&'static Arc<Device>> {
devs()
.iter()
.filter(|d| device_type & d.device_type() != 0)
.collect()
}
pub fn get_device_ids(
platform: cl_platform_id,
device_type: cl_device_type,
num_entries: cl_uint,
devices: *mut cl_device_id,
num_devices: *mut cl_uint,
) -> CLResult<()> {
// CL_INVALID_PLATFORM if platform is not a valid platform.
platform.get_ref()?;
// CL_INVALID_DEVICE_TYPE if device_type is not a valid value.
check_cl_device_type(device_type)?;
// CL_INVALID_VALUE if num_entries is equal to zero and devices is not NULL
if num_entries == 0 && !devices.is_null() {
return Err(CL_INVALID_VALUE);
}
// CL_INVALID_VALUE [...] if both num_devices and devices are NULL.
if num_devices.is_null() && devices.is_null() {
return Err(CL_INVALID_VALUE);
}
let devs = get_devs_for_type(device_type);
// CL_DEVICE_NOT_FOUND if no OpenCL devices that matched device_type were found
if devs.is_empty() {
return Err(CL_DEVICE_NOT_FOUND);
}
// num_devices returns the number of OpenCL devices available that match device_type. If
// num_devices is NULL, this argument is ignored.
num_devices.write_checked(devs.len() as cl_uint);
if !devices.is_null() {
let n = min(num_entries as usize, devs.len());
#[allow(clippy::needless_range_loop)]
for i in 0..n {
unsafe {
// Note we use as_ptr here which doesn't increase the reference count.
*devices.add(i) = cl_device_id::from_ptr(Arc::as_ptr(devs[i]));
}
}
}
Ok(())
}

View file

@ -0,0 +1,58 @@
extern crate rusticl_opencl_gen;
use crate::api::icd::*;
use crate::api::util::*;
use crate::core::event::*;
use crate::core::queue::*;
use self::rusticl_opencl_gen::*;
use std::ptr;
use std::sync::Arc;
impl CLInfo<cl_event_info> for cl_event {
fn query(&self, q: cl_event_info) -> CLResult<Vec<u8>> {
let event = self.get_ref()?;
Ok(match q {
CL_EVENT_COMMAND_EXECUTION_STATUS => cl_prop::<cl_int>(event.status()),
CL_EVENT_CONTEXT => {
// Note we use as_ptr here which doesn't increase the reference count.
let ptr = Arc::as_ptr(&event.context);
cl_prop::<cl_context>(cl_context::from_ptr(ptr))
}
CL_EVENT_COMMAND_QUEUE => {
let ptr = match event.queue.as_ref() {
// Note we use as_ptr here which doesn't increase the reference count.
Some(queue) => Arc::as_ptr(queue),
None => ptr::null_mut(),
};
cl_prop::<cl_command_queue>(cl_command_queue::from_ptr(ptr))
}
CL_EVENT_REFERENCE_COUNT => cl_prop::<cl_uint>(self.refcnt()?),
CL_EVENT_COMMAND_TYPE => cl_prop::<cl_command_type>(event.cmd_type),
_ => return Err(CL_INVALID_VALUE),
})
}
}
pub fn create_user_event(context: cl_context) -> CLResult<cl_event> {
let c = context.get_arc()?;
Ok(cl_event::from_arc(Event::new_user(c)))
}
pub fn create_and_queue(
q: Arc<Queue>,
cmd_type: cl_command_type,
deps: Vec<Arc<Event>>,
event: *mut cl_event,
block: bool,
work: EventSig,
) -> CLResult<()> {
let e = Event::new(&q, cmd_type, deps, work);
cl_event::leak_ref(event, &e);
q.queue(&e);
if block {
q.flush(true)?;
}
Ok(())
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,24 @@
extern crate rusticl_opencl_gen;
use crate::api::icd::*;
use self::rusticl_opencl_gen::*;
pub fn create_kernel(
program: cl_program,
kernel_name: *const ::std::os::raw::c_char,
) -> CLResult<cl_kernel> {
let _p = program.get_ref()?;
// CL_INVALID_VALUE if kernel_name is NULL.
if kernel_name.is_null() {
return Err(CL_INVALID_VALUE);
}
println!("create_kernel not implemented");
Err(CL_OUT_OF_HOST_MEMORY)
//• CL_INVALID_PROGRAM_EXECUTABLE if there is no successfully built executable for program.
//• CL_INVALID_KERNEL_NAME if kernel_name is not found in program.
//• CL_INVALID_KERNEL_DEFINITION if the function definition for __kernel function given by kernel_name such as the number of arguments, the argument types are not the same for all devices for which the program executable has been built.
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,11 @@
mod context;
mod device;
mod event;
pub mod icd;
mod kernel;
mod memory;
mod platform;
mod program;
mod queue;
pub(super) mod types;
pub(super) mod util;

View file

@ -0,0 +1,121 @@
extern crate mesa_rust_util;
extern crate rusticl_opencl_gen;
use crate::api::icd::CLResult;
use crate::api::icd::DISPATCH;
use crate::api::util::*;
use crate::core::version::*;
use self::rusticl_opencl_gen::*;
use self::mesa_rust_util::ptr::*;
#[repr(C)]
#[allow(non_camel_case_types)]
pub struct _cl_platform_id {
dispatch: &'static cl_icd_dispatch,
extensions: [cl_name_version; 1],
}
impl CLInfo<cl_platform_info> for cl_platform_id {
fn query(&self, q: cl_platform_info) -> CLResult<Vec<u8>> {
let p = self.get_ref()?;
Ok(match q {
CL_PLATFORM_EXTENSIONS => cl_prop("cl_khr_icd"),
CL_PLATFORM_EXTENSIONS_WITH_VERSION => {
cl_prop::<Vec<cl_name_version>>(p.extensions.to_vec())
}
CL_PLATFORM_HOST_TIMER_RESOLUTION => cl_prop::<cl_ulong>(0),
CL_PLATFORM_ICD_SUFFIX_KHR => cl_prop("MESA"),
CL_PLATFORM_NAME => cl_prop("rusticl"),
CL_PLATFORM_NUMERIC_VERSION => cl_prop::<cl_version>(CLVersion::Cl3_0 as u32),
CL_PLATFORM_PROFILE => cl_prop("FULL_PROFILE"),
CL_PLATFORM_VENDOR => cl_prop("Mesa/X.org"),
// OpenCL<space><major_version.minor_version><space><platform-specific information>
CL_PLATFORM_VERSION => cl_prop("OpenCL 3.0 "),
// CL_INVALID_VALUE if param_name is not one of the supported values
_ => return Err(CL_INVALID_VALUE),
})
}
}
static PLATFORM: _cl_platform_id = _cl_platform_id {
dispatch: &DISPATCH,
extensions: [mk_cl_version_ext(1, 0, 0, "cl_khr_icd")],
};
pub fn get_platform() -> cl_platform_id {
&PLATFORM as *const crate::api::platform::_cl_platform_id
as *mut self::rusticl_opencl_gen::_cl_platform_id
}
pub trait GetPlatformRef {
fn get_ref(&self) -> CLResult<&'static _cl_platform_id>;
}
impl GetPlatformRef for cl_platform_id {
fn get_ref(&self) -> CLResult<&'static _cl_platform_id> {
if !self.is_null() && *self == get_platform() {
Ok(&PLATFORM)
} else {
Err(CL_INVALID_PLATFORM)
}
}
}
pub fn get_platform_ids(
num_entries: cl_uint,
platforms: *mut cl_platform_id,
num_platforms: *mut cl_uint,
) -> CLResult<()> {
// CL_INVALID_VALUE if num_entries is equal to zero and platforms is not NULL
if num_entries == 0 && !platforms.is_null() {
return Err(CL_INVALID_VALUE);
}
// or if both num_platforms and platforms are NULL."
if num_platforms.is_null() && platforms.is_null() {
return Err(CL_INVALID_VALUE);
}
// platforms returns a list of OpenCL platforms available for access through the Khronos ICD Loader.
// The cl_platform_id values returned in platforms are ICD compatible and can be used to identify a
// specific OpenCL platform. If the platforms argument is NULL, then this argument is ignored. The
// number of OpenCL platforms returned is the minimum of the value specified by num_entries or the
// number of OpenCL platforms available.
platforms.write_checked(get_platform());
// num_platforms returns the number of OpenCL platforms available. If num_platforms is NULL, then
// this argument is ignored.
num_platforms.write_checked(1);
Ok(())
}
#[test]
fn test_get_platform_info() {
let mut s: usize = 0;
let mut r = get_platform_info(
ptr::null(),
CL_PLATFORM_EXTENSIONS,
0,
ptr::null_mut(),
&mut s,
);
assert!(r.is_ok());
assert!(s > 0);
let mut v: Vec<u8> = vec![0; s];
r = get_platform_info(
ptr::null(),
CL_PLATFORM_EXTENSIONS,
s,
v.as_mut_ptr().cast(),
&mut s,
);
assert!(r.is_ok());
assert_eq!(s, v.len());
assert!(!v[0..s - 2].contains(&0));
assert_eq!(v[s - 1], 0);
}

View file

@ -0,0 +1,287 @@
extern crate mesa_rust;
extern crate mesa_rust_util;
extern crate rusticl_opencl_gen;
use crate::api::icd::*;
use crate::api::types::*;
use crate::api::util::*;
use crate::core::device::*;
use crate::core::program::*;
use self::mesa_rust::compiler::clc::*;
use self::mesa_rust_util::string::*;
use self::rusticl_opencl_gen::*;
use std::ffi::CStr;
use std::ffi::CString;
use std::os::raw::c_char;
use std::ptr;
use std::slice;
use std::sync::Arc;
impl CLInfo<cl_program_info> for cl_program {
fn query(&self, q: cl_program_info) -> CLResult<Vec<u8>> {
let prog = self.get_ref()?;
Ok(match q {
CL_PROGRAM_CONTEXT => {
// Note we use as_ptr here which doesn't increase the reference count.
let ptr = Arc::as_ptr(&prog.context);
cl_prop::<cl_context>(cl_context::from_ptr(ptr))
}
CL_PROGRAM_DEVICES => {
cl_prop::<&Vec<cl_device_id>>(
&prog
.devs
.iter()
.map(|d| {
// Note we use as_ptr here which doesn't increase the reference count.
cl_device_id::from_ptr(Arc::as_ptr(d))
})
.collect(),
)
}
CL_PROGRAM_NUM_DEVICES => cl_prop::<cl_uint>(prog.devs.len() as cl_uint),
CL_PROGRAM_NUM_KERNELS => cl_prop::<usize>(prog.kernels().len()),
CL_PROGRAM_REFERENCE_COUNT => cl_prop::<cl_uint>(self.refcnt()?),
CL_PROGRAM_SOURCE => cl_prop::<&CStr>(prog.src.as_c_str()),
// CL_INVALID_VALUE if param_name is not one of the supported values
_ => return Err(CL_INVALID_VALUE),
})
}
}
impl CLInfoObj<cl_program_build_info, cl_device_id> for cl_program {
fn query(&self, d: cl_device_id, q: cl_program_build_info) -> CLResult<Vec<u8>> {
let prog = self.get_ref()?;
let dev = d.get_arc()?;
Ok(match q {
CL_PROGRAM_BUILD_GLOBAL_VARIABLE_TOTAL_SIZE => cl_prop::<usize>(0),
CL_PROGRAM_BUILD_LOG => cl_prop::<String>(prog.log(&dev)),
CL_PROGRAM_BUILD_OPTIONS => cl_prop::<String>(prog.options(&dev)),
CL_PROGRAM_BUILD_STATUS => cl_prop::<cl_build_status>(prog.status(&dev)),
// CL_INVALID_VALUE if param_name is not one of the supported values
_ => return Err(CL_INVALID_VALUE),
})
}
}
fn validate_devices(
device_list: *const cl_device_id,
num_devices: cl_uint,
default: &[Arc<Device>],
) -> CLResult<Vec<Arc<Device>>> {
let mut devs = cl_device_id::get_arc_vec_from_arr(device_list, num_devices)?;
// If device_list is a NULL value, the compile is performed for all devices associated with
// program.
if devs.is_empty() {
devs = default.to_vec();
}
Ok(devs)
}
fn call_cb(
pfn_notify: Option<ProgramCB>,
program: cl_program,
user_data: *mut ::std::os::raw::c_void,
) {
if let Some(cb) = pfn_notify {
unsafe { cb(program, user_data) };
}
}
pub fn create_program_with_source(
context: cl_context,
count: cl_uint,
strings: *mut *const c_char,
lengths: *const usize,
) -> CLResult<cl_program> {
let c = context.get_arc()?;
// CL_INVALID_VALUE if count is zero or if strings ...
if count == 0 || strings.is_null() {
return Err(CL_INVALID_VALUE);
}
// ... or any entry in strings is NULL.
let srcs = unsafe { slice::from_raw_parts(strings, count as usize) };
if srcs.contains(&ptr::null()) {
return Err(CL_INVALID_VALUE);
}
let mut source = String::new();
// we don't want encoding or any other problems with the source to prevent compilations, so
// just use CString::from_vec_unchecked and to_string_lossy
for i in 0..count as usize {
unsafe {
if lengths.is_null() || *lengths.add(i) == 0 {
source.push_str(&CStr::from_ptr(*strings.add(i)).to_string_lossy());
} else {
let l = *lengths.add(i);
let arr = slice::from_raw_parts(*strings.add(i).cast(), l);
source.push_str(&CString::from_vec_unchecked(arr.to_vec()).to_string_lossy());
}
}
}
Ok(cl_program::from_arc(Program::new(
&c,
&c.devs,
CString::new(source).map_err(|_| CL_INVALID_VALUE)?,
)))
}
pub fn build_program(
program: cl_program,
num_devices: cl_uint,
device_list: *const cl_device_id,
options: *const c_char,
pfn_notify: Option<ProgramCB>,
user_data: *mut ::std::os::raw::c_void,
) -> CLResult<()> {
let mut res = true;
let p = program.get_ref()?;
let devs = validate_devices(device_list, num_devices, &p.devs)?;
check_cb(&pfn_notify, user_data)?;
// CL_BUILD_PROGRAM_FAILURE if there is a failure to build the program executable. This error
// will be returned if clBuildProgram does not return until the build has completed.
for dev in devs {
res &= p.compile(&dev, c_string_to_string(options), &Vec::new());
}
call_cb(pfn_notify, program, user_data);
// TODO link
//• CL_INVALID_BINARY if program is created with clCreateProgramWithBinary and devices listed in device_list do not have a valid program binary loaded.
//• CL_INVALID_BUILD_OPTIONS if the build options specified by options are invalid.
//• CL_INVALID_OPERATION if the build of a program executable for any of the devices listed in device_list by a previous call to clBuildProgram for program has not completed.
//• CL_INVALID_OPERATION if there are kernel objects attached to program.
//• CL_INVALID_OPERATION if program was not created with clCreateProgramWithSource, clCreateProgramWithIL or clCreateProgramWithBinary.
if res {
Ok(())
} else {
Err(CL_BUILD_PROGRAM_FAILURE)
}
}
pub fn compile_program(
program: cl_program,
num_devices: cl_uint,
device_list: *const cl_device_id,
options: *const c_char,
num_input_headers: cl_uint,
input_headers: *const cl_program,
header_include_names: *mut *const c_char,
pfn_notify: Option<ProgramCB>,
user_data: *mut ::std::os::raw::c_void,
) -> CLResult<()> {
let mut res = true;
let p = program.get_ref()?;
let devs = validate_devices(device_list, num_devices, &p.devs)?;
check_cb(&pfn_notify, user_data)?;
// CL_INVALID_VALUE if num_input_headers is zero and header_include_names or input_headers are
// not NULL or if num_input_headers is not zero and header_include_names or input_headers are
// NULL.
if num_input_headers == 0 && (!header_include_names.is_null() || !input_headers.is_null())
|| num_input_headers != 0 && (header_include_names.is_null() || input_headers.is_null())
{
return Err(CL_INVALID_VALUE);
}
let mut headers = Vec::new();
for h in 0..num_input_headers as usize {
unsafe {
headers.push(spirv::CLCHeader {
name: CStr::from_ptr(*header_include_names.add(h)).to_owned(),
source: &(*input_headers.add(h)).get_ref()?.src,
});
}
}
// CL_COMPILE_PROGRAM_FAILURE if there is a failure to compile the program source. This error
// will be returned if clCompileProgram does not return until the compile has completed.
for dev in devs {
res &= p.compile(&dev, c_string_to_string(options), &headers);
}
call_cb(pfn_notify, program, user_data);
// CL_INVALID_OPERATION if program has no source or IL available, i.e. it has not been created with clCreateProgramWithSource or clCreateProgramWithIL.
// • CL_INVALID_COMPILER_OPTIONS if the compiler options specified by options are invalid.
// • CL_INVALID_OPERATION if the compilation or build of a program executable for any of the devices listed in device_list by a previous call to clCompileProgram or clBuildProgram for program has not completed.
// • CL_INVALID_OPERATION if there are kernel objects attached to program.
if res {
Ok(())
} else {
Err(CL_COMPILE_PROGRAM_FAILURE)
}
}
pub fn link_program(
context: cl_context,
num_devices: cl_uint,
device_list: *const cl_device_id,
_options: *const ::std::os::raw::c_char,
num_input_programs: cl_uint,
input_programs: *const cl_program,
pfn_notify: Option<ProgramCB>,
user_data: *mut ::std::os::raw::c_void,
) -> CLResult<(cl_program, cl_int)> {
let c = context.get_arc()?;
let devs = validate_devices(device_list, num_devices, &c.devs)?;
let progs = cl_program::get_arc_vec_from_arr(input_programs, num_input_programs)?;
check_cb(&pfn_notify, user_data)?;
// CL_INVALID_VALUE if num_input_programs is zero and input_programs is NULL
if progs.is_empty() {
return Err(CL_INVALID_VALUE);
}
// CL_INVALID_DEVICE if any device in device_list is not in the list of devices associated with
// context.
if !devs.iter().all(|d| c.devs.contains(d)) {
return Err(CL_INVALID_DEVICE);
}
// CL_INVALID_OPERATION if the compilation or build of a program executable for any of the
// devices listed in device_list by a previous call to clCompileProgram or clBuildProgram for
// program has not completed.
for d in &devs {
if progs
.iter()
.map(|p| p.status(d))
.any(|s| s != CL_BUILD_SUCCESS as cl_build_status)
{
return Err(CL_INVALID_OPERATION);
}
}
// CL_LINK_PROGRAM_FAILURE if there is a failure to link the compiled binaries and/or libraries.
let res = Program::link(c, &devs, &progs);
let code = if devs
.iter()
.map(|d| res.status(d))
.all(|s| s == CL_BUILD_SUCCESS as cl_build_status)
{
CL_SUCCESS as cl_int
} else {
CL_LINK_PROGRAM_FAILURE
};
let res = cl_program::from_arc(res);
call_cb(pfn_notify, res, user_data);
Ok((res, code))
//• CL_INVALID_LINKER_OPTIONS if the linker options specified by options are invalid.
//• CL_INVALID_OPERATION if the rules for devices containing compiled binaries or libraries as described in input_programs argument above are not followed.
}

View file

@ -0,0 +1,77 @@
extern crate rusticl_opencl_gen;
use crate::api::icd::*;
use crate::api::util::*;
use crate::core::queue::*;
use self::rusticl_opencl_gen::*;
use std::sync::Arc;
impl CLInfo<cl_command_queue_info> for cl_command_queue {
fn query(&self, q: cl_command_queue_info) -> CLResult<Vec<u8>> {
let queue = self.get_ref()?;
Ok(match q {
CL_QUEUE_CONTEXT => {
// Note we use as_ptr here which doesn't increase the reference count.
let ptr = Arc::as_ptr(&queue.context);
cl_prop::<cl_context>(cl_context::from_ptr(ptr))
}
CL_QUEUE_DEVICE => {
// Note we use as_ptr here which doesn't increase the reference count.
let ptr = Arc::as_ptr(&queue.device);
cl_prop::<cl_device_id>(cl_device_id::from_ptr(ptr))
}
CL_QUEUE_PROPERTIES => cl_prop::<cl_command_queue_properties>(queue.props),
CL_QUEUE_REFERENCE_COUNT => cl_prop::<cl_uint>(self.refcnt()?),
// CL_INVALID_VALUE if param_name is not one of the supported values
_ => return Err(CL_INVALID_VALUE),
})
}
}
fn valid_command_queue_properties(properties: cl_command_queue_properties) -> bool {
let valid_flags =
cl_bitfield::from(CL_QUEUE_OUT_OF_ORDER_EXEC_MODE_ENABLE | CL_QUEUE_PROFILING_ENABLE);
properties & !valid_flags == 0
}
fn supported_command_queue_properties(properties: cl_command_queue_properties) -> bool {
let valid_flags = cl_bitfield::from(CL_QUEUE_PROFILING_ENABLE);
properties & !valid_flags == 0
}
pub fn create_command_queue(
context: cl_context,
device: cl_device_id,
properties: cl_command_queue_properties,
) -> CLResult<cl_command_queue> {
// CL_INVALID_CONTEXT if context is not a valid context.
let c = context.get_arc()?;
// CL_INVALID_DEVICE if device is not a valid device
let d = device.get_arc()?;
// ... or is not associated with context.
if !c.devs.contains(&d) {
return Err(CL_INVALID_DEVICE);
}
// CL_INVALID_VALUE if values specified in properties are not valid.
if !valid_command_queue_properties(properties) {
return Err(CL_INVALID_VALUE);
}
// CL_INVALID_QUEUE_PROPERTIES if values specified in properties are valid but are not supported by the device.
if !supported_command_queue_properties(properties) {
return Err(CL_INVALID_QUEUE_PROPERTIES);
}
Ok(cl_command_queue::from_arc(Queue::new(c, d, properties)?))
}
pub fn finish_queue(command_queue: cl_command_queue) -> CLResult<()> {
// CL_INVALID_COMMAND_QUEUE if command_queue is not a valid host command-queue.
command_queue.get_ref()?;
Ok(())
}

View file

@ -0,0 +1,147 @@
extern crate rusticl_opencl_gen;
use self::rusticl_opencl_gen::*;
#[macro_export]
macro_rules! cl_closure {
(|$obj:ident| $cb:ident($($arg:ident$(,)?)*)) => {
Box::new(
unsafe {
move|$obj| $cb.unwrap()($($arg,)*)
}
)
}
}
macro_rules! cl_callback {
($cb:ident {
$($p:ident : $ty:ty,)*
}) => {
#[allow(dead_code)]
pub type $cb = unsafe extern "C" fn(
$($p: $ty,)*
);
}
}
cl_callback!(
CreateContextCB {
errinfo: *const ::std::os::raw::c_char,
private_info: *const ::std::ffi::c_void,
cb: usize,
user_data: *mut ::std::ffi::c_void,
}
);
cl_callback!(
EventCB {
event: cl_event,
event_command_status: cl_int,
user_data: *mut ::std::os::raw::c_void,
}
);
cl_callback!(
MemCB {
memobj: cl_mem,
user_data: *mut ::std::os::raw::c_void,
}
);
cl_callback!(
ProgramCB {
program: cl_program,
user_data: *mut ::std::os::raw::c_void,
}
);
// a lot of APIs use 3 component vectors passed as C arrays
#[derive(Clone, Copy, PartialEq, Eq)]
pub struct CLVec<T> {
vals: [T; 3],
}
impl<T: Copy> CLVec<T> {
/// # Safety
///
/// This function is intended for use around OpenCL vectors of size 3.
/// Most commonly for `origin` and `region` API arguments.
///
/// Using it for anything else is undefined.
pub unsafe fn from_raw(v: *const T) -> Self {
Self { vals: *v.cast() }
}
}
impl CLVec<usize> {
pub fn is_in_bound(base: Self, offset: Self, pitch: [usize; 3], size: usize) -> bool {
(base + offset - [1, 1, 1]) * pitch < size
}
}
impl<T: Default + Copy> Default for CLVec<T> {
fn default() -> Self {
Self {
vals: [T::default(); 3],
}
}
}
// provides a ton of functions
impl<T> std::ops::Deref for CLVec<T> {
type Target = [T; 3];
fn deref(&self) -> &Self::Target {
&self.vals
}
}
impl<T: Copy + std::ops::Add<Output = T>> std::ops::Add for CLVec<T> {
type Output = Self;
fn add(self, other: Self) -> Self {
self + other.vals
}
}
impl<T: Copy + std::ops::Add<Output = T>> std::ops::Add<[T; 3]> for CLVec<T> {
type Output = Self;
fn add(self, other: [T; 3]) -> Self {
Self {
vals: [self[0] + other[0], self[1] + other[1], self[2] + other[2]],
}
}
}
impl<T: Copy + std::ops::Sub<Output = T>> std::ops::Sub<[T; 3]> for CLVec<T> {
type Output = Self;
fn sub(self, other: [T; 3]) -> Self {
Self {
vals: [self[0] - other[0], self[1] - other[1], self[2] - other[2]],
}
}
}
impl<T> std::ops::Mul for CLVec<T>
where
T: Copy + std::ops::Mul<Output = T> + std::ops::Add<Output = T>,
{
type Output = T;
fn mul(self, other: Self) -> T {
self * other.vals
}
}
impl<T> std::ops::Mul<[T; 3]> for CLVec<T>
where
T: Copy + std::ops::Mul<Output = T> + std::ops::Add<Output = T>,
{
type Output = T;
fn mul(self, other: [T; 3]) -> T {
self[0] * other[0] + self[1] * other[1] + self[2] * other[2]
}
}

View file

@ -0,0 +1,341 @@
extern crate mesa_rust_util;
extern crate rusticl_opencl_gen;
use crate::api::icd::CLResult;
use crate::api::types::*;
use crate::core::event::*;
use crate::core::queue::*;
use self::mesa_rust_util::ptr::CheckedPtr;
use self::rusticl_opencl_gen::*;
use std::cmp;
use std::convert::TryInto;
use std::ffi::CStr;
use std::ffi::CString;
use std::mem::size_of;
use std::ops::BitAnd;
use std::os::raw::c_void;
use std::slice;
use std::sync::Arc;
pub trait CLInfo<I> {
fn query(&self, q: I) -> CLResult<Vec<u8>>;
fn get_info(
&self,
param_name: I,
param_value_size: usize,
param_value: *mut ::std::os::raw::c_void,
param_value_size_ret: *mut usize,
) -> CLResult<()> {
let d = self.query(param_name)?;
let size: usize = d.len();
// CL_INVALID_VALUE [...] if size in bytes specified by param_value_size is < size of return
// type as specified in the Context Attributes table and param_value is not a NULL value.
if param_value_size < size && !param_value.is_null() {
return Err(CL_INVALID_VALUE);
}
// param_value_size_ret returns the actual size in bytes of data being queried by param_name.
// If param_value_size_ret is NULL, it is ignored.
param_value_size_ret.write_checked(size);
// param_value is a pointer to memory where the appropriate result being queried is returned.
// If param_value is NULL, it is ignored.
unsafe {
param_value.copy_checked(d.as_ptr().cast(), size);
}
Ok(())
}
}
pub trait CLInfoObj<I, O> {
fn query(&self, o: O, q: I) -> CLResult<Vec<u8>>;
fn get_info_obj(
&self,
obj: O,
param_name: I,
param_value_size: usize,
param_value: *mut ::std::os::raw::c_void,
param_value_size_ret: *mut usize,
) -> CLResult<()> {
let d = self.query(obj, param_name)?;
let size: usize = d.len();
// CL_INVALID_VALUE [...] if size in bytes specified by param_value_size is < size of return
// type as specified in the Context Attributes table and param_value is not a NULL value.
if param_value_size < size && !param_value.is_null() {
return Err(CL_INVALID_VALUE);
}
// param_value_size_ret returns the actual size in bytes of data being queried by param_name.
// If param_value_size_ret is NULL, it is ignored.
param_value_size_ret.write_checked(size);
// param_value is a pointer to memory where the appropriate result being queried is returned.
// If param_value is NULL, it is ignored.
unsafe {
param_value.copy_checked(d.as_ptr().cast(), size);
}
Ok(())
}
}
pub trait CLProp {
fn cl_vec(&self) -> Vec<u8>;
}
macro_rules! cl_prop_for_type {
($ty: ty) => {
impl CLProp for $ty {
fn cl_vec(&self) -> Vec<u8> {
self.to_ne_bytes().to_vec()
}
}
};
}
macro_rules! cl_prop_for_struct {
($ty: ty) => {
impl CLProp for $ty {
fn cl_vec(&self) -> Vec<u8> {
unsafe { slice::from_raw_parts((self as *const Self).cast(), size_of::<Self>()) }
.to_vec()
}
}
};
}
cl_prop_for_type!(cl_char);
cl_prop_for_type!(cl_int);
cl_prop_for_type!(cl_uint);
cl_prop_for_type!(cl_ulong);
cl_prop_for_type!(isize);
cl_prop_for_type!(usize);
cl_prop_for_struct!(cl_image_format);
cl_prop_for_struct!(cl_name_version);
impl CLProp for bool {
fn cl_vec(&self) -> Vec<u8> {
cl_prop::<cl_bool>(if *self { CL_TRUE } else { CL_FALSE })
}
}
impl CLProp for String {
fn cl_vec(&self) -> Vec<u8> {
let mut c = self.clone();
c.push('\0');
c.into_bytes()
}
}
impl CLProp for &str {
fn cl_vec(&self) -> Vec<u8> {
CString::new(*self)
.unwrap_or_default()
.into_bytes_with_nul()
}
}
impl CLProp for &CStr {
fn cl_vec(&self) -> Vec<u8> {
self.to_bytes_with_nul().to_vec()
}
}
impl<T> CLProp for Vec<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> CLProp for &Vec<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> CLProp for *const T {
fn cl_vec(&self) -> Vec<u8> {
(*self as usize).cl_vec()
}
}
impl<T> CLProp for *mut T {
fn cl_vec(&self) -> Vec<u8> {
(*self as usize).cl_vec()
}
}
pub fn cl_prop<T: CLProp>(v: T) -> Vec<u8> {
v.cl_vec()
}
const CL_DEVICE_TYPES: u32 = CL_DEVICE_TYPE_ACCELERATOR
| CL_DEVICE_TYPE_CPU
| CL_DEVICE_TYPE_GPU
| CL_DEVICE_TYPE_CUSTOM
| CL_DEVICE_TYPE_DEFAULT;
pub fn check_cl_device_type(val: cl_device_type) -> CLResult<()> {
let v: u32 = val.try_into().or(Err(CL_INVALID_DEVICE_TYPE))?;
if v == CL_DEVICE_TYPE_ALL || v & CL_DEVICE_TYPES == v {
return Ok(());
}
Err(CL_INVALID_DEVICE_TYPE)
}
pub const CL_IMAGE_TYPES: [cl_mem_object_type; 6] = [
CL_MEM_OBJECT_IMAGE1D,
CL_MEM_OBJECT_IMAGE2D,
CL_MEM_OBJECT_IMAGE3D,
CL_MEM_OBJECT_IMAGE1D_ARRAY,
CL_MEM_OBJECT_IMAGE2D_ARRAY,
CL_MEM_OBJECT_IMAGE1D_BUFFER,
];
pub const fn cl_image_format(
order: cl_channel_order,
data_type: cl_channel_type,
) -> cl_image_format {
cl_image_format {
image_channel_order: order,
image_channel_data_type: data_type,
}
}
pub fn check_cl_bool<T: PartialEq + TryInto<cl_uint>>(val: T) -> Option<bool> {
let c: u32 = val.try_into().ok()?;
if c != CL_TRUE && c != CL_FALSE {
return None;
}
Some(c == CL_TRUE)
}
pub fn event_list_from_cl(
q: &Arc<Queue>,
num_events_in_wait_list: cl_uint,
event_wait_list: *const cl_event,
) -> CLResult<Vec<Arc<Event>>> {
// CL_INVALID_EVENT_WAIT_LIST if event_wait_list is NULL and num_events_in_wait_list > 0, or
// event_wait_list is not NULL and num_events_in_wait_list is 0, or if event objects in
// event_wait_list are not valid events.
if event_wait_list.is_null() && num_events_in_wait_list > 0
|| !event_wait_list.is_null() && num_events_in_wait_list == 0
{
return Err(CL_INVALID_EVENT_WAIT_LIST);
}
let res = Event::from_cl_arr(event_wait_list, num_events_in_wait_list)
.map_err(|_| CL_INVALID_EVENT_WAIT_LIST)?;
// CL_INVALID_CONTEXT if context associated with command_queue and events in event_list are not
// the same.
if res.iter().any(|e| e.context != q.context) {
return Err(CL_INVALID_CONTEXT);
}
Ok(res)
}
pub fn check_cb<T>(cb: &Option<T>, user_data: *mut c_void) -> CLResult<()> {
// CL_INVALID_VALUE if pfn_notify is NULL but user_data is not NULL.
if cb.is_none() && !user_data.is_null() {
return Err(CL_INVALID_VALUE);
}
Ok(())
}
pub fn checked_compare(a: usize, o: cmp::Ordering, b: u64) -> bool {
if usize::BITS > u64::BITS {
a.cmp(&(b as usize)) == o
} else {
(a as u64).cmp(&b) == o
}
}
pub fn is_alligned<T>(ptr: *const T, alignment: usize) -> bool {
ptr as usize & (alignment - 1) == 0
}
pub fn bit_check<A: BitAnd<Output = A> + PartialEq + Default, B: Into<A>>(a: A, b: B) -> bool {
a & b.into() != A::default()
}
// Taken from "Appendix D: Checking for Memory Copy Overlap"
// src_offset and dst_offset are additions to support sub-buffers
pub fn check_copy_overlap(
src_origin: &CLVec<usize>,
src_offset: usize,
dst_origin: &CLVec<usize>,
dst_offset: usize,
region: &CLVec<usize>,
row_pitch: usize,
slice_pitch: usize,
) -> bool {
let slice_size = (region[1] - 1) * row_pitch + region[0];
let block_size = (region[2] - 1) * slice_pitch + slice_size;
let src_start =
src_origin[2] * slice_pitch + src_origin[1] * row_pitch + src_origin[0] + src_offset;
let src_end = src_start + block_size;
let dst_start =
dst_origin[2] * slice_pitch + dst_origin[1] * row_pitch + dst_origin[0] + dst_offset;
let dst_end = dst_start + block_size;
/* No overlap if dst ends before src starts or if src ends
* before dst starts.
*/
if (dst_end <= src_start) || (src_end <= dst_start) {
return false;
}
/* No overlap if region[0] for dst or src fits in the gap
* between region[0] and row_pitch.
*/
{
let src_dx = (src_origin[0] + src_offset) % row_pitch;
let dst_dx = (dst_origin[0] + dst_offset) % row_pitch;
if ((dst_dx >= src_dx + region[0]) && (dst_dx + region[0] <= src_dx + row_pitch))
|| ((src_dx >= dst_dx + region[0]) && (src_dx + region[0] <= dst_dx + row_pitch))
{
return false;
}
}
/* No overlap if region[1] for dst or src fits in the gap
* between region[1] and slice_pitch.
*/
{
let src_dy = (src_origin[1] * row_pitch + src_origin[0] + src_offset) % slice_pitch;
let dst_dy = (dst_origin[1] * row_pitch + dst_origin[0] + dst_offset) % slice_pitch;
if ((dst_dy >= src_dy + slice_size) && (dst_dy + slice_size <= src_dy + slice_pitch))
|| ((src_dy >= dst_dy + slice_size) && (src_dy + slice_size <= dst_dy + slice_pitch))
{
return false;
}
}
/* Otherwise src and dst overlap. */
true
}

View file

@ -0,0 +1,62 @@
extern crate mesa_rust;
extern crate rusticl_opencl_gen;
use crate::api::icd::*;
use crate::core::device::*;
use crate::impl_cl_type_trait;
use self::mesa_rust::pipe::resource::*;
use self::rusticl_opencl_gen::*;
use std::collections::HashMap;
use std::convert::TryInto;
use std::os::raw::c_void;
use std::sync::Arc;
pub struct Context {
pub base: CLObjectBase<CL_INVALID_CONTEXT>,
pub devs: Vec<Arc<Device>>,
pub properties: Vec<cl_context_properties>,
}
impl_cl_type_trait!(cl_context, Context, CL_INVALID_CONTEXT);
impl Context {
pub fn new(devs: Vec<Arc<Device>>, properties: Vec<cl_context_properties>) -> Arc<Context> {
Arc::new(Self {
base: CLObjectBase::new(),
devs: devs,
properties: properties,
})
}
pub fn create_buffer(&self, size: usize) -> CLResult<HashMap<Arc<Device>, PipeResource>> {
let adj_size: u32 = size.try_into().map_err(|_| CL_OUT_OF_HOST_MEMORY)?;
let mut res = HashMap::new();
for dev in &self.devs {
let resource = dev
.screen()
.resource_create_buffer(adj_size)
.ok_or(CL_OUT_OF_RESOURCES);
res.insert(Arc::clone(dev), resource?);
}
Ok(res)
}
pub fn create_buffer_from_user(
&self,
size: usize,
user_ptr: *mut c_void,
) -> CLResult<HashMap<Arc<Device>, PipeResource>> {
let adj_size: u32 = size.try_into().map_err(|_| CL_OUT_OF_HOST_MEMORY)?;
let mut res = HashMap::new();
for dev in &self.devs {
let resource = dev
.screen()
.resource_create_buffer_from_user(adj_size, user_ptr)
.ok_or(CL_OUT_OF_RESOURCES);
res.insert(Arc::clone(dev), resource?);
}
Ok(res)
}
}

View file

@ -0,0 +1,489 @@
extern crate mesa_rust;
extern crate mesa_rust_gen;
extern crate mesa_rust_util;
extern crate rusticl_opencl_gen;
use crate::api::icd::*;
use crate::api::util::*;
use crate::core::format::*;
use crate::core::util::*;
use crate::core::version::*;
use crate::impl_cl_type_trait;
use self::mesa_rust::pipe::context::*;
use self::mesa_rust::pipe::device::load_screens;
use self::mesa_rust::pipe::screen::*;
use self::mesa_rust_gen::*;
use self::rusticl_opencl_gen::*;
use std::cmp::max;
use std::cmp::min;
use std::collections::HashMap;
use std::convert::TryInto;
use std::env;
use std::sync::Arc;
use std::sync::Mutex;
use std::sync::MutexGuard;
pub struct Device {
pub base: CLObjectBase<CL_INVALID_DEVICE>,
screen: Arc<PipeScreen>,
pub cl_version: CLVersion,
pub clc_version: CLVersion,
pub clc_versions: Vec<cl_name_version>,
pub custom: bool,
pub embedded: bool,
pub extension_string: String,
pub extensions: Vec<cl_name_version>,
pub formats: HashMap<cl_image_format, HashMap<cl_mem_object_type, cl_mem_flags>>,
helper_ctx: Mutex<Arc<PipeContext>>,
}
impl_cl_type_trait!(cl_device_id, Device, CL_INVALID_DEVICE);
impl Device {
fn new(screen: Arc<PipeScreen>) -> Option<Arc<Device>> {
let mut d = Self {
base: CLObjectBase::new(),
helper_ctx: Mutex::new(screen.create_context().unwrap()),
screen: screen,
cl_version: CLVersion::Cl3_0,
clc_version: CLVersion::Cl3_0,
clc_versions: Vec::new(),
custom: false,
embedded: false,
extension_string: String::from(""),
extensions: Vec::new(),
formats: HashMap::new(),
};
if !d.check_valid() {
return None;
}
d.fill_format_tables();
// check if we are embedded or full profile first
d.embedded = d.check_embedded_profile();
// check if we have to report it as a custom device
d.custom = d.check_custom();
// query supported extensions
d.fill_extensions();
// now figure out what version we are
d.check_version();
Some(Arc::new(d))
}
fn fill_format_tables(&mut self) {
for f in FORMATS {
let mut fs = HashMap::new();
for t in CL_IMAGE_TYPES {
let mut flags: cl_uint = 0;
if self.screen.is_format_supported(
f.pipe,
cl_mem_type_to_texture_target(t),
PIPE_BIND_SAMPLER_VIEW,
) {
flags |= CL_MEM_READ_ONLY;
}
if self.screen.is_format_supported(
f.pipe,
cl_mem_type_to_texture_target(t),
PIPE_BIND_SHADER_IMAGE,
) {
flags |= CL_MEM_WRITE_ONLY;
// TODO: enable once we support it
// flags |= CL_MEM_KERNEL_READ_AND_WRITE;
}
if self.screen.is_format_supported(
f.pipe,
cl_mem_type_to_texture_target(t),
PIPE_BIND_SAMPLER_VIEW | PIPE_BIND_SHADER_IMAGE,
) {
flags |= CL_MEM_READ_WRITE;
}
fs.insert(t, flags as cl_mem_flags);
}
self.formats.insert(f.cl_image_format, fs);
}
}
fn check_valid(&self) -> bool {
if self.screen.param(pipe_cap::PIPE_CAP_COMPUTE) == 0 ||
// even though we use PIPE_SHADER_IR_NIR, PIPE_SHADER_IR_NIR_SERIALIZED marks CL support by the driver
self.shader_param(pipe_shader_cap::PIPE_SHADER_CAP_SUPPORTED_IRS) & (1 << (pipe_shader_ir::PIPE_SHADER_IR_NIR_SERIALIZED as i32)) == 0
{
return false;
}
// CL_DEVICE_MAX_PARAMETER_SIZE
// For this minimum value, only a maximum of 128 arguments can be passed to a kernel
if self.param_max_size() < 128 {
return false;
}
true
}
fn check_custom(&self) -> bool {
// Max size of memory object allocation in bytes. The minimum value is
// max(min(1024 × 1024 × 1024, 1/4th of CL_DEVICE_GLOBAL_MEM_SIZE), 32 × 1024 × 1024)
// for devices that are not of type CL_DEVICE_TYPE_CUSTOM.
let mut limit = min(1024 * 1024 * 1024, self.global_mem_size());
limit = max(limit, 32 * 1024 * 1024);
if self.max_mem_alloc() < limit {
return true;
}
// CL_DEVICE_MAX_WORK_ITEM_DIMENSIONS
// The minimum value is 3 for devices that are not of type CL_DEVICE_TYPE_CUSTOM.
if self.max_grid_dimensions() < 3 {
return true;
}
if self.embedded {
// CL_DEVICE_MAX_PARAMETER_SIZE
// The minimum value is 256 bytes for devices that are not of type CL_DEVICE_TYPE_CUSTOM.
if self.param_max_size() < 256 {
return true;
}
// CL_DEVICE_MAX_CONSTANT_BUFFER_SIZE
// The minimum value is 1 KB for devices that are not of type CL_DEVICE_TYPE_CUSTOM.
if self.const_max_size() < 1024 {
return true;
}
// TODO
// CL_DEVICE_MAX_CONSTANT_ARGS
// The minimum value is 4 for devices that are not of type CL_DEVICE_TYPE_CUSTOM.
// CL_DEVICE_LOCAL_MEM_SIZE
// The minimum value is 1 KB for devices that are not of type CL_DEVICE_TYPE_CUSTOM.
if self.local_mem_size() < 1024 {
return true;
}
} else {
// CL 1.0 spec:
// CL_DEVICE_MAX_PARAMETER_SIZE
// The minimum value is 256 for devices that are not of type CL_DEVICE_TYPE_CUSTOM.
if self.param_max_size() < 256 {
return true;
}
// CL_DEVICE_MAX_CONSTANT_BUFFER_SIZE
// The minimum value is 64 KB for devices that are not of type CL_DEVICE_TYPE_CUSTOM.
if self.const_max_size() < 64 * 1024 {
return true;
}
// TODO
// CL_DEVICE_MAX_CONSTANT_ARGS
// The minimum value is 8 for devices that are not of type CL_DEVICE_TYPE_CUSTOM.
// CL 1.0 spec:
// CL_DEVICE_LOCAL_MEM_SIZE
// The minimum value is 16 KB for devices that are not of type CL_DEVICE_TYPE_CUSTOM.
if self.local_mem_size() < 16 * 1024 {
return true;
}
}
false
}
fn check_embedded_profile(&self) -> bool {
if self.image_supported() {
// The minimum value is 16 if CL_DEVICE_IMAGE_SUPPORT is CL_TRUE
if self.max_samplers() < 16 ||
// The minimum value is 128 if CL_DEVICE_IMAGE_SUPPORT is CL_TRUE
self.image_read_count() < 128 ||
// The minimum value is 64 if CL_DEVICE_IMAGE_SUPPORT is CL_TRUE
self.image_write_count() < 64 ||
// The minimum value is 16384 if CL_DEVICE_IMAGE_SUPPORT is CL_TRUE
self.image_2d_size() < 16384 ||
// The minimum value is 2048 if CL_DEVICE_IMAGE_SUPPORT is CL_TRUE
self.image_array_size() < 2048 ||
// The minimum value is 65536 if CL_DEVICE_IMAGE_SUPPORT is CL_TRUE
self.image_buffer_size() < 65536
{
return true;
}
// TODO check req formats
}
false
}
fn parse_env_version() -> Option<CLVersion> {
let val = env::var("RUSTICL_CL_VERSION").ok()?;
let (major, minor) = val.split_once('.')?;
let major = major.parse().ok()?;
let minor = minor.parse().ok()?;
mk_cl_version(major, minor, 0).try_into().ok()
}
// TODO add CLC checks
fn check_version(&mut self) {
let exts: Vec<&str> = self.extension_string.split(' ').collect();
let mut res = CLVersion::Cl3_0;
if self.embedded {
if self.image_supported() {
let supports_array_writes = !FORMATS
.iter()
.filter(|f| f.req_for_embeded_read_or_write)
.map(|f| self.formats.get(&f.cl_image_format).unwrap())
.map(|f| f.get(&CL_MEM_OBJECT_IMAGE2D_ARRAY).unwrap())
.any(|f| *f & cl_mem_flags::from(CL_MEM_WRITE_ONLY) == 0);
if self.image_3d_size() < 2048 || !supports_array_writes {
res = CLVersion::Cl1_2;
}
}
}
// TODO: check image 1D, 1Dbuffer, 1Darray and 2Darray support explicitly
if self.image_supported() {
// The minimum value is 256 if CL_DEVICE_IMAGE_SUPPORT is CL_TRUE
if self.image_array_size() < 256 ||
// The minimum value is 2048 if CL_DEVICE_IMAGE_SUPPORT is CL_TRUE
self.image_buffer_size() < 2048
{
res = CLVersion::Cl1_1;
}
}
if !exts.contains(&"cl_khr_byte_addressable_store")
|| !exts.contains(&"cl_khr_global_int32_base_atomics")
|| !exts.contains(&"cl_khr_global_int32_extended_atomics")
|| !exts.contains(&"cl_khr_local_int32_base_atomics")
|| !exts.contains(&"cl_khr_local_int32_extended_atomics")
// The following modifications are made to the OpenCL 1.1 platform layer and runtime (sections 4 and 5):
// The minimum FULL_PROFILE value for CL_DEVICE_MAX_PARAMETER_SIZE increased from 256 to 1024 bytes
|| self.param_max_size() < 1024
// The minimum FULL_PROFILE value for CL_DEVICE_LOCAL_MEM_SIZE increased from 16 KB to 32 KB.
|| self.local_mem_size() < 32 * 1024
{
res = CLVersion::Cl1_0;
}
if let Some(val) = Self::parse_env_version() {
res = val;
}
if res >= CLVersion::Cl3_0 {
self.clc_versions
.push(mk_cl_version_ext(3, 0, 0, "OpenCL C"));
}
if res >= CLVersion::Cl1_2 {
self.clc_versions
.push(mk_cl_version_ext(1, 2, 0, "OpenCL C"));
}
if res >= CLVersion::Cl1_1 {
self.clc_versions
.push(mk_cl_version_ext(1, 1, 0, "OpenCL C"));
}
if res >= CLVersion::Cl1_0 {
self.clc_versions
.push(mk_cl_version_ext(1, 0, 0, "OpenCL C"));
}
self.cl_version = res;
self.clc_version = min(CLVersion::Cl1_2, res);
}
fn fill_extensions(&mut self) {
let mut exts_str: Vec<String> = Vec::new();
let mut exts = Vec::new();
let mut add_ext = |major, minor, patch, ext| {
exts.push(mk_cl_version_ext(major, minor, patch, ext));
exts_str.push(ext.to_owned());
};
add_ext(1, 0, 0, "cl_khr_byte_addressable_store");
self.extensions = exts;
self.extension_string = exts_str.join(" ");
}
fn shader_param(&self, cap: pipe_shader_cap) -> i32 {
self.screen
.shader_param(pipe_shader_type::PIPE_SHADER_COMPUTE, cap)
}
pub fn all() -> Vec<Arc<Device>> {
load_screens().into_iter().filter_map(Device::new).collect()
}
pub fn address_bits(&self) -> cl_uint {
self.screen
.compute_param(pipe_compute_cap::PIPE_COMPUTE_CAP_ADDRESS_BITS)
}
pub fn const_max_size(&self) -> cl_ulong {
self.screen
.param(pipe_cap::PIPE_CAP_MAX_SHADER_BUFFER_SIZE_UINT) as u64
}
pub fn device_type(&self) -> cl_device_type {
if self.custom {
return CL_DEVICE_TYPE_CUSTOM as cl_device_type;
}
(match self.screen.device_type() {
pipe_loader_device_type::PIPE_LOADER_DEVICE_SOFTWARE => CL_DEVICE_TYPE_CPU,
pipe_loader_device_type::PIPE_LOADER_DEVICE_PCI => {
CL_DEVICE_TYPE_GPU | CL_DEVICE_TYPE_DEFAULT
}
pipe_loader_device_type::PIPE_LOADER_DEVICE_PLATFORM => {
CL_DEVICE_TYPE_GPU | CL_DEVICE_TYPE_DEFAULT
}
pipe_loader_device_type::NUM_PIPE_LOADER_DEVICE_TYPES => CL_DEVICE_TYPE_CUSTOM,
}) as cl_device_type
}
pub fn global_mem_size(&self) -> cl_ulong {
self.screen
.compute_param(pipe_compute_cap::PIPE_COMPUTE_CAP_MAX_GLOBAL_SIZE)
}
pub fn image_2d_size(&self) -> usize {
self.screen.param(pipe_cap::PIPE_CAP_MAX_TEXTURE_2D_SIZE) as usize
}
pub fn image_3d_size(&self) -> usize {
1 << (self.screen.param(pipe_cap::PIPE_CAP_MAX_TEXTURE_3D_LEVELS) - 1)
}
pub fn image_3d_supported(&self) -> bool {
self.screen.param(pipe_cap::PIPE_CAP_MAX_TEXTURE_3D_LEVELS) != 0
}
pub fn image_array_size(&self) -> usize {
self.screen
.param(pipe_cap::PIPE_CAP_MAX_TEXTURE_ARRAY_LAYERS) as usize
}
pub fn image_base_address_alignment(&self) -> cl_uint {
0
}
pub fn image_buffer_size(&self) -> usize {
self.screen
.param(pipe_cap::PIPE_CAP_MAX_TEXEL_BUFFER_ELEMENTS_UINT) as usize
}
pub fn image_read_count(&self) -> cl_uint {
self.shader_param(pipe_shader_cap::PIPE_SHADER_CAP_MAX_SAMPLER_VIEWS) as cl_uint
}
pub fn image_supported(&self) -> bool {
// TODO check CL_DEVICE_IMAGE_SUPPORT reqs
self.shader_param(pipe_shader_cap::PIPE_SHADER_CAP_MAX_SHADER_IMAGES) != 0 &&
// The minimum value is 8 if CL_DEVICE_IMAGE_SUPPORT is CL_TRUE
self.image_read_count() >= 8 &&
// The minimum value is 8 if CL_DEVICE_IMAGE_SUPPORT is CL_TRUE
self.image_write_count() >= 8 &&
// The minimum value is 2048 if CL_DEVICE_IMAGE_SUPPORT is CL_TRUE
self.image_2d_size() >= 2048
}
pub fn image_write_count(&self) -> cl_uint {
self.shader_param(pipe_shader_cap::PIPE_SHADER_CAP_MAX_SHADER_IMAGES) as cl_uint
}
pub fn little_endian(&self) -> bool {
let endianness = self.screen.param(pipe_cap::PIPE_CAP_ENDIANNESS);
endianness == (pipe_endian::PIPE_ENDIAN_LITTLE as i32)
}
pub fn local_mem_size(&self) -> cl_ulong {
self.screen
.compute_param(pipe_compute_cap::PIPE_COMPUTE_CAP_MAX_LOCAL_SIZE)
}
pub fn max_block_sizes(&self) -> Vec<usize> {
let v: Vec<u64> = self
.screen
.compute_param(pipe_compute_cap::PIPE_COMPUTE_CAP_MAX_BLOCK_SIZE);
v.into_iter().map(|v| v as usize).collect()
}
pub fn max_clock_freq(&self) -> cl_uint {
self.screen
.compute_param(pipe_compute_cap::PIPE_COMPUTE_CAP_MAX_CLOCK_FREQUENCY)
}
pub fn max_compute_units(&self) -> cl_uint {
self.screen
.compute_param(pipe_compute_cap::PIPE_COMPUTE_CAP_MAX_COMPUTE_UNITS)
}
pub fn max_grid_dimensions(&self) -> cl_uint {
ComputeParam::<u64>::compute_param(
self.screen.as_ref(),
pipe_compute_cap::PIPE_COMPUTE_CAP_GRID_DIMENSION,
) as cl_uint
}
pub fn max_mem_alloc(&self) -> cl_ulong {
self.screen
.compute_param(pipe_compute_cap::PIPE_COMPUTE_CAP_MAX_MEM_ALLOC_SIZE)
}
pub fn max_samplers(&self) -> cl_uint {
self.shader_param(pipe_shader_cap::PIPE_SHADER_CAP_MAX_TEXTURE_SAMPLERS) as cl_uint
}
pub fn max_threads_per_block(&self) -> usize {
ComputeParam::<u64>::compute_param(
self.screen.as_ref(),
pipe_compute_cap::PIPE_COMPUTE_CAP_MAX_THREADS_PER_BLOCK,
) as usize
}
pub fn param_max_size(&self) -> usize {
ComputeParam::<u64>::compute_param(
self.screen.as_ref(),
pipe_compute_cap::PIPE_COMPUTE_CAP_MAX_INPUT_SIZE,
) as usize
}
pub fn screen(&self) -> &Arc<PipeScreen> {
&self.screen
}
pub fn unified_memory(&self) -> bool {
self.screen.param(pipe_cap::PIPE_CAP_UMA) == 1
}
pub fn vendor_id(&self) -> cl_uint {
let id = self.screen.param(pipe_cap::PIPE_CAP_VENDOR_ID);
if id == -1 {
return 0;
}
id as u32
}
pub fn helper_ctx(&self) -> MutexGuard<Arc<PipeContext>> {
self.helper_ctx.lock().unwrap()
}
pub fn cl_features(&self) -> clc_optional_features {
clc_optional_features {
fp16: false,
fp64: false,
int64: false,
images: self.image_supported(),
images_read_write: false,
images_write_3d: false,
intel_subgroups: false,
subgroups: false,
}
}
}

View file

@ -0,0 +1,110 @@
extern crate mesa_rust;
extern crate rusticl_opencl_gen;
use crate::api::icd::*;
use crate::core::context::*;
use crate::core::queue::*;
use crate::impl_cl_type_trait;
use self::mesa_rust::pipe::context::*;
use self::rusticl_opencl_gen::*;
use std::slice;
use std::sync::atomic::AtomicI32;
use std::sync::atomic::Ordering;
use std::sync::Arc;
pub type EventSig = Box<dyn Fn(&Arc<Queue>, &Arc<PipeContext>) -> CLResult<()>>;
#[repr(C)]
pub struct Event {
pub base: CLObjectBase<CL_INVALID_EVENT>,
pub context: Arc<Context>,
pub queue: Option<Arc<Queue>>,
pub cmd_type: cl_command_type,
pub deps: Vec<Arc<Event>>,
// use AtomicI32 instead of cl_int so we can change it without a &mut reference
status: AtomicI32,
work: Option<EventSig>,
}
impl_cl_type_trait!(cl_event, Event, CL_INVALID_EVENT);
// TODO shouldn't be needed, but... uff C pointers are annoying
unsafe impl Send for Event {}
unsafe impl Sync for Event {}
impl Event {
pub fn new(
queue: &Arc<Queue>,
cmd_type: cl_command_type,
deps: Vec<Arc<Event>>,
work: EventSig,
) -> Arc<Event> {
Arc::new(Self {
base: CLObjectBase::new(),
context: queue.context.clone(),
queue: Some(queue.clone()),
cmd_type: cmd_type,
deps: deps,
status: AtomicI32::new(CL_QUEUED as cl_int),
work: Some(work),
})
}
pub fn new_user(context: Arc<Context>) -> Arc<Event> {
Arc::new(Self {
base: CLObjectBase::new(),
context: context,
queue: None,
cmd_type: CL_COMMAND_USER,
deps: Vec::new(),
status: AtomicI32::new(CL_SUBMITTED as cl_int),
work: None,
})
}
pub fn from_cl_arr(events: *const cl_event, num_events: u32) -> CLResult<Vec<Arc<Event>>> {
let s = unsafe { slice::from_raw_parts(events, num_events as usize) };
s.iter().map(|e| e.get_arc()).collect()
}
pub fn is_error(&self) -> bool {
self.status.load(Ordering::Relaxed) < 0
}
pub fn status(&self) -> cl_int {
self.status.load(Ordering::Relaxed)
}
// We always assume that work here simply submits stuff to the hardware even if it's just doing
// sw emulation or nothing at all.
// If anything requets waiting, we will update the status through fencing later.
pub fn call(&self, ctx: &Arc<PipeContext>) -> cl_int {
let status = self.status();
if status == CL_QUEUED as cl_int {
let new = self.work.as_ref().map_or(
// if there is no work
CL_SUBMITTED as cl_int,
|w| {
w(self.queue.as_ref().unwrap(), ctx).err().map_or(
// if there is an error, negate it
CL_SUBMITTED as cl_int,
|e| e,
)
},
);
self.status.store(new, Ordering::Relaxed);
new
} else {
status
}
}
}
// TODO worker thread per device
// Condvar to wait on new events to work on
// notify condvar when flushing queue events to worker
// attach fence to flushed events on context->flush
// store "newest" event for in-order queues per queue
// reordering/graph building done in worker

View file

@ -0,0 +1,181 @@
extern crate mesa_rust_gen;
extern crate rusticl_opencl_gen;
use crate::api::util::*;
use self::mesa_rust_gen::pipe_format;
use self::rusticl_opencl_gen::*;
pub struct RusticlImageFormat {
pub cl_image_format: cl_image_format,
pub req_for_full_read_or_write: bool,
pub req_for_embeded_read_or_write: bool,
pub req_for_full_read_and_write: bool,
pub pipe: pipe_format,
}
pub const fn rusticl_image_format(
cl_image_format: cl_image_format,
req_for_full_read_or_write: bool,
req_for_embeded_read_or_write: bool,
req_for_full_read_and_write: bool,
pipe: pipe_format,
) -> RusticlImageFormat {
RusticlImageFormat {
cl_image_format: cl_image_format,
req_for_full_read_or_write: req_for_full_read_or_write,
req_for_embeded_read_or_write: req_for_embeded_read_or_write,
req_for_full_read_and_write: req_for_full_read_and_write,
pipe: pipe,
}
}
pub const FORMATS: &[RusticlImageFormat] = &[
rusticl_image_format(
cl_image_format(CL_R, CL_HALF_FLOAT),
false,
false,
true,
pipe_format::PIPE_FORMAT_R16_FLOAT,
),
rusticl_image_format(
cl_image_format(CL_R, CL_FLOAT),
false,
false,
true,
pipe_format::PIPE_FORMAT_R32_FLOAT,
),
rusticl_image_format(
cl_image_format(CL_R, CL_SIGNED_INT8),
false,
false,
true,
pipe_format::PIPE_FORMAT_R8_SINT,
),
rusticl_image_format(
cl_image_format(CL_R, CL_SIGNED_INT16),
false,
false,
true,
pipe_format::PIPE_FORMAT_R16_SINT,
),
rusticl_image_format(
cl_image_format(CL_R, CL_SIGNED_INT32),
false,
false,
true,
pipe_format::PIPE_FORMAT_R32_SINT,
),
rusticl_image_format(
cl_image_format(CL_R, CL_UNORM_INT8),
false,
false,
true,
pipe_format::PIPE_FORMAT_R8_UNORM,
),
rusticl_image_format(
cl_image_format(CL_R, CL_UNORM_INT16),
false,
false,
false,
pipe_format::PIPE_FORMAT_R16_UNORM,
),
rusticl_image_format(
cl_image_format(CL_R, CL_UNSIGNED_INT8),
false,
false,
true,
pipe_format::PIPE_FORMAT_R8_UINT,
),
rusticl_image_format(
cl_image_format(CL_R, CL_UNSIGNED_INT16),
false,
false,
true,
pipe_format::PIPE_FORMAT_R16_UINT,
),
rusticl_image_format(
cl_image_format(CL_R, CL_UNSIGNED_INT32),
false,
false,
true,
pipe_format::PIPE_FORMAT_R32_UINT,
),
rusticl_image_format(
cl_image_format(CL_RGBA, CL_HALF_FLOAT),
true,
true,
true,
pipe_format::PIPE_FORMAT_R16G16B16A16_FLOAT,
),
rusticl_image_format(
cl_image_format(CL_RGBA, CL_FLOAT),
true,
true,
true,
pipe_format::PIPE_FORMAT_R32G32B32A32_FLOAT,
),
rusticl_image_format(
cl_image_format(CL_RGBA, CL_SIGNED_INT8),
true,
true,
true,
pipe_format::PIPE_FORMAT_R8G8B8A8_SINT,
),
rusticl_image_format(
cl_image_format(CL_RGBA, CL_SIGNED_INT16),
true,
true,
true,
pipe_format::PIPE_FORMAT_R16G16B16A16_SINT,
),
rusticl_image_format(
cl_image_format(CL_RGBA, CL_SIGNED_INT32),
true,
true,
true,
pipe_format::PIPE_FORMAT_R32G32B32A32_SINT,
),
rusticl_image_format(
cl_image_format(CL_RGBA, CL_UNORM_INT8),
true,
true,
true,
pipe_format::PIPE_FORMAT_R8G8B8A8_UNORM,
),
rusticl_image_format(
cl_image_format(CL_RGBA, CL_UNORM_INT16),
true,
true,
false,
pipe_format::PIPE_FORMAT_R16G16B16A16_UNORM,
),
rusticl_image_format(
cl_image_format(CL_RGBA, CL_UNSIGNED_INT8),
true,
true,
true,
pipe_format::PIPE_FORMAT_R8G8B8A8_UINT,
),
rusticl_image_format(
cl_image_format(CL_RGBA, CL_UNSIGNED_INT16),
true,
true,
true,
pipe_format::PIPE_FORMAT_R16G16B16A16_UINT,
),
rusticl_image_format(
cl_image_format(CL_RGBA, CL_UNSIGNED_INT32),
true,
true,
true,
pipe_format::PIPE_FORMAT_R32G32B32A32_UINT,
),
rusticl_image_format(
cl_image_format(CL_BGRA, CL_UNORM_INT8),
true,
false,
false,
pipe_format::PIPE_FORMAT_B8G8R8A8_UNORM,
),
];

View file

@ -0,0 +1,24 @@
extern crate mesa_rust;
extern crate rusticl_opencl_gen;
use crate::api::icd::*;
use crate::impl_cl_type_trait;
use self::rusticl_opencl_gen::*;
use std::sync::Arc;
#[repr(C)]
pub struct Kernel {
pub base: CLObjectBase<CL_INVALID_KERNEL>,
}
impl_cl_type_trait!(cl_kernel, Kernel, CL_INVALID_KERNEL);
impl Kernel {
pub fn new() -> Arc<Kernel> {
Arc::new(Self {
base: CLObjectBase::new(),
})
}
}

View file

@ -0,0 +1,398 @@
extern crate mesa_rust;
extern crate rusticl_opencl_gen;
use crate::api::icd::*;
use crate::api::types::*;
use crate::api::util::*;
use crate::core::context::*;
use crate::core::device::*;
use crate::core::queue::*;
use crate::impl_cl_type_trait;
use self::mesa_rust::pipe::context::*;
use self::mesa_rust::pipe::resource::*;
use self::mesa_rust::pipe::transfer::*;
use self::rusticl_opencl_gen::*;
use std::collections::HashMap;
use std::convert::TryInto;
use std::os::raw::c_void;
use std::ptr;
use std::sync::Arc;
use std::sync::Mutex;
#[repr(C)]
pub struct Mem {
pub base: CLObjectBase<CL_INVALID_MEM_OBJECT>,
pub context: Arc<Context>,
pub parent: Option<Arc<Mem>>,
pub mem_type: cl_mem_object_type,
pub flags: cl_mem_flags,
pub size: usize,
pub offset: usize,
pub host_ptr: *mut c_void,
pub image_format: cl_image_format,
pub image_desc: cl_image_desc,
pub image_elem_size: u8,
pub cbs: Mutex<Vec<Box<dyn Fn(cl_mem)>>>,
res: Option<HashMap<Arc<Device>, PipeResource>>,
maps: Mutex<HashMap<*mut c_void, (u32, PipeTransfer)>>,
}
impl_cl_type_trait!(cl_mem, Mem, CL_INVALID_MEM_OBJECT);
fn sw_copy(
src: *const c_void,
dst: *mut c_void,
region: &CLVec<usize>,
src_origin: &CLVec<usize>,
src_row_pitch: usize,
src_slice_pitch: usize,
dst_origin: &CLVec<usize>,
dst_row_pitch: usize,
dst_slice_pitch: usize,
) {
for z in 0..region[2] {
for y in 0..region[1] {
unsafe {
ptr::copy_nonoverlapping(
src.add((*src_origin + [0, y, z]) * [1, src_row_pitch, src_slice_pitch]),
dst.add((*dst_origin + [0, y, z]) * [1, dst_row_pitch, dst_slice_pitch]),
region[0],
)
};
}
}
}
impl Mem {
pub fn new_buffer(
context: Arc<Context>,
flags: cl_mem_flags,
size: usize,
host_ptr: *mut c_void,
) -> CLResult<Arc<Mem>> {
if bit_check(flags, CL_MEM_COPY_HOST_PTR | CL_MEM_ALLOC_HOST_PTR) {
println!("host ptr semantics not implemented!");
}
let buffer = if bit_check(flags, CL_MEM_USE_HOST_PTR) {
context.create_buffer_from_user(size, host_ptr)
} else {
context.create_buffer(size)
}?;
let host_ptr = if bit_check(flags, CL_MEM_USE_HOST_PTR) {
host_ptr
} else {
ptr::null_mut()
};
Ok(Arc::new(Self {
base: CLObjectBase::new(),
context: context,
parent: None,
mem_type: CL_MEM_OBJECT_BUFFER,
flags: flags,
size: size,
offset: 0,
host_ptr: host_ptr,
image_format: cl_image_format::default(),
image_desc: cl_image_desc::default(),
image_elem_size: 0,
cbs: Mutex::new(Vec::new()),
res: Some(buffer),
maps: Mutex::new(HashMap::new()),
}))
}
pub fn new_sub_buffer(
parent: Arc<Mem>,
flags: cl_mem_flags,
offset: usize,
size: usize,
) -> Arc<Mem> {
let host_ptr = if parent.host_ptr.is_null() {
ptr::null_mut()
} else {
unsafe { parent.host_ptr.add(offset) }
};
Arc::new(Self {
base: CLObjectBase::new(),
context: parent.context.clone(),
parent: Some(parent),
mem_type: CL_MEM_OBJECT_BUFFER,
flags: flags,
size: size,
offset: offset,
host_ptr: host_ptr,
image_format: cl_image_format::default(),
image_desc: cl_image_desc::default(),
image_elem_size: 0,
cbs: Mutex::new(Vec::new()),
res: None,
maps: Mutex::new(HashMap::new()),
})
}
pub fn new_image(
context: Arc<Context>,
mem_type: cl_mem_object_type,
flags: cl_mem_flags,
image_format: &cl_image_format,
image_desc: cl_image_desc,
image_elem_size: u8,
host_ptr: *mut c_void,
) -> Arc<Mem> {
if bit_check(
flags,
CL_MEM_USE_HOST_PTR | CL_MEM_COPY_HOST_PTR | CL_MEM_ALLOC_HOST_PTR,
) {
println!("host ptr semantics not implemented!");
}
let host_ptr = if bit_check(flags, CL_MEM_USE_HOST_PTR) {
host_ptr
} else {
ptr::null_mut()
};
Arc::new(Self {
base: CLObjectBase::new(),
context: context,
parent: None,
mem_type: mem_type,
flags: flags,
size: 0,
offset: 0,
host_ptr: host_ptr,
image_format: *image_format,
image_desc: image_desc,
image_elem_size: image_elem_size,
cbs: Mutex::new(Vec::new()),
res: None,
maps: Mutex::new(HashMap::new()),
})
}
pub fn is_buffer(&self) -> bool {
self.mem_type == CL_MEM_OBJECT_BUFFER
}
pub fn has_same_parent(&self, other: &Self) -> bool {
let a = self.parent.as_ref().map_or(self, |p| p);
let b = other.parent.as_ref().map_or(other, |p| p);
ptr::eq(a, b)
}
fn get_res(&self) -> &HashMap<Arc<Device>, PipeResource> {
self.parent
.as_ref()
.map_or(self, |p| p.as_ref())
.res
.as_ref()
.unwrap()
}
pub fn write_from_user(
&self,
q: &Arc<Queue>,
ctx: &PipeContext,
offset: usize,
ptr: *const c_void,
size: usize,
) -> CLResult<()> {
// TODO support sub buffers
let r = self.get_res().get(&q.device).unwrap();
ctx.buffer_subdata(
r,
offset.try_into().map_err(|_| CL_OUT_OF_HOST_MEMORY)?,
ptr,
size.try_into().map_err(|_| CL_OUT_OF_HOST_MEMORY)?,
);
Ok(())
}
pub fn write_from_user_rect(
&self,
src: *const c_void,
q: &Arc<Queue>,
ctx: &Arc<PipeContext>,
region: &CLVec<usize>,
src_origin: &CLVec<usize>,
src_row_pitch: usize,
src_slice_pitch: usize,
dst_origin: &CLVec<usize>,
dst_row_pitch: usize,
dst_slice_pitch: usize,
) -> CLResult<()> {
let r = self.res.as_ref().unwrap().get(&q.device).unwrap();
let tx = ctx.buffer_map(r, 0, self.size.try_into().unwrap(), true);
sw_copy(
src,
tx.ptr(),
region,
src_origin,
src_row_pitch,
src_slice_pitch,
dst_origin,
dst_row_pitch,
dst_slice_pitch,
);
drop(tx);
Ok(())
}
pub fn read_to_user_rect(
&self,
dst: *mut c_void,
q: &Arc<Queue>,
ctx: &Arc<PipeContext>,
region: &CLVec<usize>,
src_origin: &CLVec<usize>,
src_row_pitch: usize,
src_slice_pitch: usize,
dst_origin: &CLVec<usize>,
dst_row_pitch: usize,
dst_slice_pitch: usize,
) -> CLResult<()> {
let r = self.res.as_ref().unwrap().get(&q.device).unwrap();
let tx = ctx.buffer_map(r, 0, self.size.try_into().unwrap(), true);
sw_copy(
tx.ptr(),
dst,
region,
src_origin,
src_row_pitch,
src_slice_pitch,
dst_origin,
dst_row_pitch,
dst_slice_pitch,
);
drop(tx);
Ok(())
}
pub fn copy_to(
&self,
dst: &Self,
q: &Arc<Queue>,
ctx: &Arc<PipeContext>,
region: &CLVec<usize>,
src_origin: &CLVec<usize>,
src_row_pitch: usize,
src_slice_pitch: usize,
dst_origin: &CLVec<usize>,
dst_row_pitch: usize,
dst_slice_pitch: usize,
) -> CLResult<()> {
let res_src = self.res.as_ref().unwrap().get(&q.device).unwrap();
let res_dst = dst.res.as_ref().unwrap().get(&q.device).unwrap();
let tx_src = ctx.buffer_map(res_src, 0, self.size.try_into().unwrap(), true);
let tx_dst = ctx.buffer_map(res_dst, 0, dst.size.try_into().unwrap(), true);
// TODO check to use hw accelerated paths (e.g. resource_copy_region or blits)
sw_copy(
tx_src.ptr(),
tx_dst.ptr(),
region,
src_origin,
src_row_pitch,
src_slice_pitch,
dst_origin,
dst_row_pitch,
dst_slice_pitch,
);
drop(tx_src);
drop(tx_dst);
Ok(())
}
// TODO use PIPE_MAP_UNSYNCHRONIZED for non blocking
pub fn map(&self, q: &Arc<Queue>, offset: usize, size: usize, block: bool) -> *mut c_void {
let res = self.res.as_ref().unwrap().get(&q.device).unwrap();
let tx = q.device.helper_ctx().buffer_map(
res,
offset.try_into().unwrap(),
size.try_into().unwrap(),
block,
);
let ptr = tx.ptr();
let mut lock = self.maps.lock().unwrap();
let e = lock.get_mut(&ptr);
// if we already have a mapping, reuse that and increase the refcount
if let Some(e) = e {
e.0 += 1;
} else {
lock.insert(tx.ptr(), (1, tx));
}
ptr
}
pub fn is_mapped_ptr(&self, ptr: *mut c_void) -> bool {
self.maps.lock().unwrap().contains_key(&ptr)
}
pub fn unmap(&self, q: &Arc<Queue>, ptr: *mut c_void) {
let mut lock = self.maps.lock().unwrap();
let e = lock.get_mut(&ptr).unwrap();
e.0 -= 1;
if e.0 == 0 {
lock.remove(&ptr)
.unwrap()
.1
.with_ctx(&q.device.helper_ctx());
}
}
}
impl Drop for Mem {
fn drop(&mut self) {
let cl = cl_mem::from_ptr(self);
self.cbs
.get_mut()
.unwrap()
.iter()
.rev()
.for_each(|cb| cb(cl));
}
}
#[repr(C)]
pub struct Sampler {
pub base: CLObjectBase<CL_INVALID_SAMPLER>,
pub context: Arc<Context>,
pub normalized_coords: bool,
pub addressing_mode: cl_addressing_mode,
pub filter_mode: cl_filter_mode,
}
impl_cl_type_trait!(cl_sampler, Sampler, CL_INVALID_SAMPLER);
impl Sampler {
pub fn new(
context: Arc<Context>,
normalized_coords: bool,
addressing_mode: cl_addressing_mode,
filter_mode: cl_filter_mode,
) -> Arc<Sampler> {
Arc::new(Self {
base: CLObjectBase::new(),
context: context,
normalized_coords: normalized_coords,
addressing_mode: addressing_mode,
filter_mode: filter_mode,
})
}
}

View file

@ -0,0 +1,10 @@
pub mod context;
pub mod device;
pub mod event;
pub mod format;
pub mod kernel;
pub mod memory;
pub mod program;
pub mod queue;
pub mod util;
pub mod version;

View file

@ -0,0 +1,187 @@
extern crate mesa_rust;
extern crate rusticl_opencl_gen;
use crate::api::icd::*;
use crate::core::context::*;
use crate::core::device::*;
use crate::impl_cl_type_trait;
use self::mesa_rust::compiler::clc::*;
use self::rusticl_opencl_gen::*;
use std::collections::HashMap;
use std::collections::HashSet;
use std::ffi::CString;
use std::sync::Arc;
use std::sync::Mutex;
use std::sync::MutexGuard;
#[repr(C)]
pub struct Program {
pub base: CLObjectBase<CL_INVALID_PROGRAM>,
pub context: Arc<Context>,
pub devs: Vec<Arc<Device>>,
pub src: CString,
build: Mutex<ProgramBuild>,
}
impl_cl_type_trait!(cl_program, Program, CL_INVALID_PROGRAM);
struct ProgramBuild {
builds: HashMap<Arc<Device>, ProgramDevBuild>,
kernels: Vec<String>,
}
struct ProgramDevBuild {
spirv: Option<spirv::SPIRVBin>,
status: cl_build_status,
options: String,
log: String,
}
fn prepare_options(options: &str) -> Vec<CString> {
options
.split_whitespace()
.map(|a| match a {
"-cl-denorms-are-zero" => "-fdenormal-fp-math=positive-zero",
_ => a,
})
.map(CString::new)
.map(Result::unwrap)
.collect()
}
impl Program {
pub fn new(context: &Arc<Context>, devs: &[Arc<Device>], src: CString) -> Arc<Program> {
let builds = devs
.iter()
.map(|d| {
(
d.clone(),
ProgramDevBuild {
spirv: None,
status: CL_BUILD_NONE,
log: String::from(""),
options: String::from(""),
},
)
})
.collect();
Arc::new(Self {
base: CLObjectBase::new(),
context: context.clone(),
devs: devs.to_vec(),
src: src,
build: Mutex::new(ProgramBuild {
builds: builds,
kernels: Vec::new(),
}),
})
}
fn build_info(&self) -> MutexGuard<ProgramBuild> {
self.build.lock().unwrap()
}
fn dev_build_info<'a>(
l: &'a mut MutexGuard<ProgramBuild>,
dev: &Arc<Device>,
) -> &'a mut ProgramDevBuild {
l.builds.get_mut(dev).unwrap()
}
pub fn status(&self, dev: &Arc<Device>) -> cl_build_status {
Self::dev_build_info(&mut self.build_info(), dev).status
}
pub fn log(&self, dev: &Arc<Device>) -> String {
Self::dev_build_info(&mut self.build_info(), dev)
.log
.clone()
}
pub fn options(&self, dev: &Arc<Device>) -> String {
Self::dev_build_info(&mut self.build_info(), dev)
.options
.clone()
}
pub fn kernels(&self) -> Vec<String> {
self.build_info().kernels.clone()
}
pub fn compile(
&self,
dev: &Arc<Device>,
options: String,
headers: &[spirv::CLCHeader],
) -> bool {
let mut info = self.build_info();
let d = Self::dev_build_info(&mut info, dev);
let args = prepare_options(&options);
let (spirv, log) = spirv::SPIRVBin::from_clc(&self.src, &args, headers, dev.cl_features());
d.spirv = spirv;
d.log = log;
d.options = options;
if d.spirv.is_some() {
d.status = CL_BUILD_SUCCESS as cl_build_status;
true
} else {
d.status = CL_BUILD_ERROR;
false
}
}
pub fn link(
context: Arc<Context>,
devs: &[Arc<Device>],
progs: &[Arc<Program>],
) -> Arc<Program> {
let devs: Vec<Arc<Device>> = devs.iter().map(|d| (*d).clone()).collect();
let mut builds = HashMap::new();
let mut kernels = HashSet::new();
let mut locks: Vec<_> = progs.iter().map(|p| p.build_info()).collect();
for d in &devs {
let bins: Vec<_> = locks
.iter_mut()
.map(|l| Self::dev_build_info(l, d).spirv.as_ref().unwrap())
.collect();
let (spirv, log) = spirv::SPIRVBin::link(&bins, false);
let status = if let Some(spirv) = &spirv {
for k in spirv.kernels() {
kernels.insert(k);
}
CL_BUILD_SUCCESS as cl_build_status
} else {
CL_BUILD_ERROR
};
builds.insert(
d.clone(),
ProgramDevBuild {
spirv: spirv,
status: status,
log: log,
options: String::from(""),
},
);
}
Arc::new(Self {
base: CLObjectBase::new(),
context: context,
devs: devs,
src: CString::new("").unwrap(),
build: Mutex::new(ProgramBuild {
builds: builds,
kernels: kernels.into_iter().collect(),
}),
})
}
}

View file

@ -0,0 +1,96 @@
extern crate mesa_rust;
extern crate rusticl_opencl_gen;
use crate::api::icd::*;
use crate::core::context::*;
use crate::core::device::*;
use crate::core::event::*;
use crate::impl_cl_type_trait;
use self::rusticl_opencl_gen::*;
use std::sync::mpsc;
use std::sync::Arc;
use std::sync::Mutex;
use std::thread;
use std::thread::JoinHandle;
#[repr(C)]
pub struct Queue {
pub base: CLObjectBase<CL_INVALID_COMMAND_QUEUE>,
pub context: Arc<Context>,
pub device: Arc<Device>,
pub props: cl_command_queue_properties,
pending: Mutex<Vec<Arc<Event>>>,
_thrd: Option<JoinHandle<()>>,
chan_in: mpsc::Sender<Vec<Arc<Event>>>,
chan_out: mpsc::Receiver<bool>,
}
impl_cl_type_trait!(cl_command_queue, Queue, CL_INVALID_COMMAND_QUEUE);
impl Queue {
pub fn new(
context: Arc<Context>,
device: Arc<Device>,
props: cl_command_queue_properties,
) -> CLResult<Arc<Queue>> {
// we assume that memory allocation is the only possible failure. Any other failure reason
// should be detected earlier (e.g.: checking for CAPs).
let pipe = device.screen().create_context().unwrap();
let (tx_q, rx_t) = mpsc::channel::<Vec<Arc<Event>>>();
let (tx_t, rx_q) = mpsc::channel::<bool>();
Ok(Arc::new(Self {
base: CLObjectBase::new(),
context: context,
device: device,
props: props,
pending: Mutex::new(Vec::new()),
_thrd: Some(
thread::Builder::new()
.name("rusticl queue thread".into())
.spawn(move || loop {
let r = rx_t.recv();
if r.is_err() {
break;
}
for e in r.unwrap() {
e.call(&pipe);
}
if tx_t.send(true).is_err() {
break;
}
})
.unwrap(),
),
chan_in: tx_q,
chan_out: rx_q,
}))
}
pub fn queue(&self, e: &Arc<Event>) {
self.pending.lock().unwrap().push(e.clone());
}
// TODO: implement non blocking flush
pub fn flush(&self, _wait: bool) -> CLResult<()> {
let mut p = self.pending.lock().unwrap();
// This should never ever error, but if it does return an error
self.chan_in
.send((*p).drain(0..).collect())
.map_err(|_| CL_OUT_OF_HOST_MEMORY)?;
self.chan_out.recv().unwrap();
Ok(())
}
}
impl Drop for Queue {
fn drop(&mut self) {
// when deleting the application side object, we have to flush
// From the OpenCL spec:
// clReleaseCommandQueue performs an implicit flush to issue any previously queued OpenCL
// commands in command_queue.
// TODO: maybe we have to do it on every release?
let _ = self.flush(true);
}
}

View file

@ -0,0 +1,17 @@
extern crate mesa_rust_gen;
extern crate rusticl_opencl_gen;
use self::mesa_rust_gen::*;
use self::rusticl_opencl_gen::*;
pub fn cl_mem_type_to_texture_target(mem_type: cl_mem_object_type) -> pipe_texture_target {
match mem_type {
CL_MEM_OBJECT_IMAGE1D => pipe_texture_target::PIPE_TEXTURE_1D,
CL_MEM_OBJECT_IMAGE2D => pipe_texture_target::PIPE_TEXTURE_2D,
CL_MEM_OBJECT_IMAGE3D => pipe_texture_target::PIPE_TEXTURE_3D,
CL_MEM_OBJECT_IMAGE1D_ARRAY => pipe_texture_target::PIPE_TEXTURE_1D_ARRAY,
CL_MEM_OBJECT_IMAGE2D_ARRAY => pipe_texture_target::PIPE_TEXTURE_2D_ARRAY,
CL_MEM_OBJECT_IMAGE1D_BUFFER => pipe_texture_target::PIPE_BUFFER,
_ => pipe_texture_target::PIPE_TEXTURE_2D,
}
}

View file

@ -0,0 +1,80 @@
extern crate rusticl_opencl_gen;
use self::rusticl_opencl_gen::*;
use std::convert::TryFrom;
use std::os::raw::c_char;
pub const CL1_0_VER: u32 = mk_cl_version(1, 0, 0);
pub const CL1_1_VER: u32 = mk_cl_version(1, 1, 0);
pub const CL1_2_VER: u32 = mk_cl_version(1, 2, 0);
pub const CL2_0_VER: u32 = mk_cl_version(2, 0, 0);
pub const CL2_1_VER: u32 = mk_cl_version(2, 1, 0);
pub const CL2_2_VER: u32 = mk_cl_version(2, 2, 0);
pub const CL3_0_VER: u32 = mk_cl_version(3, 0, 0);
#[allow(dead_code)]
#[repr(u32)]
#[derive(Copy, Clone, Eq, Hash, PartialEq, PartialOrd, Ord)]
pub enum CLVersion {
Cl1_0 = CL1_0_VER,
Cl1_1 = CL1_1_VER,
Cl1_2 = CL1_2_VER,
Cl2_0 = CL2_0_VER,
Cl2_1 = CL2_1_VER,
Cl2_2 = CL2_2_VER,
Cl3_0 = CL3_0_VER,
}
pub const fn mk_cl_version_ext(major: u32, minor: u32, patch: u32, ext: &str) -> cl_name_version {
let mut name: [c_char; 64] = [0; 64];
let ext = ext.as_bytes();
let mut i = 0;
while i < ext.len() {
name[i] = ext[i] as c_char;
i += 1;
}
cl_name_version {
version: mk_cl_version(major, minor, patch),
name,
}
}
pub const fn mk_cl_version(major: u32, minor: u32, patch: u32) -> u32 {
((major & CL_VERSION_MAJOR_MASK) << (CL_VERSION_MINOR_BITS + CL_VERSION_PATCH_BITS))
| ((minor & CL_VERSION_MINOR_MASK) << CL_VERSION_PATCH_BITS)
| (patch & CL_VERSION_PATCH_MASK)
}
impl CLVersion {
pub fn api_str(&self) -> &'static str {
match self {
CLVersion::Cl1_0 => "1.0",
CLVersion::Cl1_1 => "1.1",
CLVersion::Cl1_2 => "1.2",
CLVersion::Cl2_0 => "2.0",
CLVersion::Cl2_1 => "2.1",
CLVersion::Cl2_2 => "2.2",
CLVersion::Cl3_0 => "3.0",
}
}
}
impl TryFrom<u32> for CLVersion {
type Error = cl_int;
fn try_from(value: u32) -> Result<Self, Self::Error> {
Ok(match value {
CL1_0_VER => CLVersion::Cl1_0,
CL1_1_VER => CLVersion::Cl1_1,
CL1_2_VER => CLVersion::Cl1_2,
CL2_0_VER => CLVersion::Cl2_0,
CL2_1_VER => CLVersion::Cl2_1,
CL2_2_VER => CLVersion::Cl2_2,
CL3_0_VER => CLVersion::Cl3_0,
_ => return Err(CL_INVALID_VALUE),
})
}
}

View file

@ -0,0 +1,5 @@
#![allow(clippy::collapsible_if)]
#![allow(clippy::needless_range_loop)]
mod api;
pub mod core;

View file

@ -0,0 +1 @@
pub mod spirv;

View file

@ -0,0 +1,148 @@
extern crate mesa_rust_gen;
extern crate mesa_rust_util;
use self::mesa_rust_gen::*;
use self::mesa_rust_util::string::*;
use std::ffi::CString;
use std::os::raw::c_char;
use std::os::raw::c_void;
use std::ptr;
use std::slice;
const INPUT_STR: *const c_char = b"input.cl\0" as *const u8 as *const c_char;
pub struct SPIRVBin {
spirv: clc_binary,
info: Option<clc_parsed_spirv>,
}
pub struct CLCHeader<'a> {
pub name: CString,
pub source: &'a CString,
}
unsafe extern "C" fn msg_callback(data: *mut std::ffi::c_void, msg: *const c_char) {
let msgs = (data as *mut Vec<String>).as_mut().expect("");
msgs.push(c_string_to_string(msg));
}
impl SPIRVBin {
pub fn from_clc(
source: &CString,
args: &[CString],
headers: &[CLCHeader],
features: clc_optional_features,
) -> (Option<Self>, String) {
let c_headers: Vec<_> = headers
.iter()
.map(|h| clc_named_value {
name: h.name.as_ptr(),
value: h.source.as_ptr(),
})
.collect();
let c_args: Vec<_> = args.iter().map(|a| a.as_ptr()).collect();
let args = clc_compile_args {
headers: c_headers.as_ptr(),
num_headers: c_headers.len() as u32,
source: clc_named_value {
name: INPUT_STR,
value: source.as_ptr(),
},
args: c_args.as_ptr(),
num_args: c_args.len() as u32,
spirv_version: clc_spirv_version::CLC_SPIRV_VERSION_MAX,
features: features,
allowed_spirv_extensions: ptr::null(),
};
let mut msgs: Vec<String> = Vec::new();
let logger = clc_logger {
priv_: &mut msgs as *mut Vec<String> as *mut c_void,
error: Some(msg_callback),
warning: Some(msg_callback),
};
let mut out = clc_binary::default();
let res = unsafe { clc_compile_c_to_spirv(&args, &logger, &mut out) };
let res = if res {
Some(SPIRVBin {
spirv: out,
info: None,
})
} else {
None
};
(res, msgs.join("\n"))
}
pub fn link(spirvs: &[&SPIRVBin], library: bool) -> (Option<Self>, String) {
let bins: Vec<_> = spirvs.iter().map(|s| &s.spirv as *const _).collect();
let linker_args = clc_linker_args {
in_objs: bins.as_ptr(),
num_in_objs: bins.len() as u32,
create_library: library as u32,
};
let mut msgs: Vec<String> = Vec::new();
let logger = clc_logger {
priv_: &mut msgs as *mut Vec<String> as *mut c_void,
error: Some(msg_callback),
warning: Some(msg_callback),
};
let mut out = clc_binary::default();
let res = unsafe { clc_link_spirv(&linker_args, &logger, &mut out) };
let info;
if !library {
let mut pspirv = clc_parsed_spirv::default();
let res = unsafe { clc_parse_spirv(&out, &logger, &mut pspirv) };
if res {
info = Some(pspirv);
} else {
info = None;
}
} else {
info = None;
}
let res = if res {
Some(SPIRVBin {
spirv: out,
info: info,
})
} else {
None
};
(res, msgs.join("\n"))
}
pub fn kernels(&self) -> Vec<String> {
unsafe {
match self.info {
None => Vec::new(),
Some(info) => slice::from_raw_parts(info.kernels, info.num_kernels as usize)
.iter()
.map(|i| i.name)
.map(c_string_to_string)
.collect(),
}
}
}
}
impl Drop for SPIRVBin {
fn drop(&mut self) {
unsafe {
clc_free_spirv(&mut self.spirv);
if let Some(info) = &mut self.info {
clc_free_parsed_spirv(info);
}
}
}
}

View file

@ -0,0 +1 @@
pub mod clc;

View file

@ -0,0 +1,2 @@
pub mod compiler;
pub mod pipe;

View file

@ -0,0 +1,116 @@
extern crate mesa_rust_gen;
use crate::pipe::resource::*;
use crate::pipe::transfer::*;
use self::mesa_rust_gen::*;
use std::os::raw::*;
use std::ptr;
use std::ptr::*;
use std::sync::Arc;
pub struct PipeContext {
pipe: NonNull<pipe_context>,
}
unsafe impl Send for PipeContext {}
unsafe impl Sync for PipeContext {}
impl PipeContext {
pub(super) fn new(context: *mut pipe_context) -> Option<Arc<Self>> {
let s = Self {
pipe: NonNull::new(context)?,
};
if !has_required_cbs(unsafe { s.pipe.as_ref() }) {
assert!(false, "Context missing features. This should never happen!");
return None;
}
Some(Arc::new(s))
}
pub fn buffer_subdata(
&self,
res: &PipeResource,
offset: c_uint,
data: *const c_void,
size: c_uint,
) {
unsafe {
self.pipe.as_ref().buffer_subdata.unwrap()(
self.pipe.as_ptr(),
res.pipe(),
pipe_map_flags::PIPE_MAP_WRITE.0, // TODO PIPE_MAP_x
offset,
size,
data,
)
}
}
pub fn buffer_map(
&self,
res: &PipeResource,
offset: i32,
size: i32,
block: bool,
) -> PipeTransfer {
let mut b = pipe_box::default();
let mut out: *mut pipe_transfer = ptr::null_mut();
b.x = offset;
b.width = size;
b.height = 1;
b.depth = 1;
let flags = match block {
false => pipe_map_flags::PIPE_MAP_UNSYNCHRONIZED,
true => pipe_map_flags(0),
} | pipe_map_flags::PIPE_MAP_READ_WRITE;
let ptr = unsafe {
self.pipe.as_ref().buffer_map.unwrap()(
self.pipe.as_ptr(),
res.pipe(),
0,
flags.0,
&b,
&mut out,
)
};
PipeTransfer::new(out, ptr)
}
pub(super) fn buffer_unmap(&self, tx: *mut pipe_transfer) {
unsafe { self.pipe.as_ref().buffer_unmap.unwrap()(self.pipe.as_ptr(), tx) };
}
pub fn blit(&self, src: &PipeResource, dst: &PipeResource) {
let mut blit_info = pipe_blit_info::default();
blit_info.src.resource = src.pipe();
blit_info.dst.resource = dst.pipe();
println!("blit not implemented!");
unsafe { self.pipe.as_ref().blit.unwrap()(self.pipe.as_ptr(), &blit_info) }
}
}
impl Drop for PipeContext {
fn drop(&mut self) {
unsafe {
self.pipe.as_ref().destroy.unwrap()(self.pipe.as_ptr());
}
}
}
fn has_required_cbs(c: &pipe_context) -> bool {
c.destroy.is_some()
&& c.blit.is_some()
&& c.buffer_map.is_some()
&& c.buffer_subdata.is_some()
&& c.buffer_unmap.is_some()
}

View file

@ -0,0 +1,55 @@
extern crate mesa_rust_gen;
use crate::pipe::screen::*;
use self::mesa_rust_gen::*;
use std::ptr;
use std::sync::Arc;
#[derive(PartialEq)]
pub(super) struct PipeLoaderDevice {
pub(super) ldev: *mut pipe_loader_device,
}
impl PipeLoaderDevice {
fn new(ldev: *mut pipe_loader_device) -> Option<Self> {
if ldev.is_null() {
return None;
}
Some(Self { ldev })
}
fn load_screen(self) -> Option<Arc<PipeScreen>> {
let s = unsafe { pipe_loader_create_screen(self.ldev) };
PipeScreen::new(self, s)
}
}
impl Drop for PipeLoaderDevice {
fn drop(&mut self) {
unsafe {
pipe_loader_release(&mut self.ldev, 1);
}
}
}
fn load_devs() -> Vec<PipeLoaderDevice> {
let n = unsafe { pipe_loader_probe(ptr::null_mut(), 0) };
let mut devices: Vec<*mut pipe_loader_device> = vec![ptr::null_mut(); n as usize];
unsafe {
pipe_loader_probe(devices.as_mut_ptr(), n);
}
devices
.into_iter()
.filter_map(PipeLoaderDevice::new)
.collect()
}
pub fn load_screens() -> Vec<Arc<PipeScreen>> {
load_devs()
.into_iter()
.filter_map(PipeLoaderDevice::load_screen)
.collect()
}

View file

@ -0,0 +1,5 @@
pub mod context;
pub mod device;
pub mod resource;
pub mod screen;
pub mod transfer;

View file

@ -0,0 +1,29 @@
extern crate mesa_rust_gen;
use self::mesa_rust_gen::*;
use std::ptr;
pub struct PipeResource {
pipe: *mut pipe_resource,
}
impl PipeResource {
pub fn new(res: *mut pipe_resource) -> Option<Self> {
if res.is_null() {
return None;
}
Some(Self { pipe: res })
}
pub(super) fn pipe(&self) -> *mut pipe_resource {
self.pipe
}
}
impl Drop for PipeResource {
fn drop(&mut self) {
unsafe { pipe_resource_reference(&mut self.pipe, ptr::null_mut()) }
}
}

View file

@ -0,0 +1,175 @@
extern crate mesa_rust_gen;
extern crate mesa_rust_util;
use crate::pipe::context::*;
use crate::pipe::device::*;
use crate::pipe::resource::*;
use self::mesa_rust_gen::*;
use self::mesa_rust_util::string::*;
use std::convert::TryInto;
use std::mem::size_of;
use std::os::raw::c_void;
use std::ptr;
use std::sync::Arc;
#[derive(PartialEq)]
pub struct PipeScreen {
ldev: PipeLoaderDevice,
screen: *mut pipe_screen,
}
// until we have a better solution
pub trait ComputeParam<T> {
fn compute_param(&self, cap: pipe_compute_cap) -> T;
}
macro_rules! compute_param_impl {
($ty:ty) => {
impl ComputeParam<$ty> for PipeScreen {
fn compute_param(&self, cap: pipe_compute_cap) -> $ty {
let size = self.compute_param_wrapped(cap, ptr::null_mut());
let mut d = [0; size_of::<$ty>()];
assert_eq!(size as usize, d.len());
self.compute_param_wrapped(cap, d.as_mut_ptr().cast());
<$ty>::from_ne_bytes(d)
}
}
};
}
compute_param_impl!(u32);
compute_param_impl!(u64);
impl ComputeParam<Vec<u64>> for PipeScreen {
fn compute_param(&self, cap: pipe_compute_cap) -> Vec<u64> {
let size = self.compute_param_wrapped(cap, ptr::null_mut());
let elems = (size / 8) as usize;
let mut res: Vec<u64> = Vec::new();
let mut d: Vec<u8> = vec![0; size as usize];
self.compute_param_wrapped(cap, d.as_mut_ptr().cast());
for i in 0..elems {
let offset = i * 8;
let slice = &d[offset..offset + 8];
res.push(u64::from_ne_bytes(slice.try_into().expect("")));
}
res
}
}
impl PipeScreen {
pub(super) fn new(ldev: PipeLoaderDevice, screen: *mut pipe_screen) -> Option<Arc<Self>> {
if screen.is_null() || !has_required_cbs(screen) {
return None;
}
Some(Arc::new(Self { ldev, screen }))
}
pub fn create_context(self: &Arc<Self>) -> Option<Arc<PipeContext>> {
PipeContext::new(unsafe {
(*self.screen).context_create.unwrap()(
self.screen,
ptr::null_mut(),
PIPE_CONTEXT_COMPUTE_ONLY,
)
})
}
pub fn resource_create_buffer(&self, size: u32) -> Option<PipeResource> {
let mut tmpl = pipe_resource::default();
tmpl.set_target(pipe_texture_target::PIPE_BUFFER);
tmpl.width0 = size;
tmpl.height0 = 1;
tmpl.depth0 = 1;
tmpl.array_size = 1;
PipeResource::new(unsafe { (*self.screen).resource_create.unwrap()(self.screen, &tmpl) })
}
pub fn resource_create_buffer_from_user(
&self,
size: u32,
mem: *mut c_void,
) -> Option<PipeResource> {
let mut tmpl = pipe_resource::default();
tmpl.set_target(pipe_texture_target::PIPE_BUFFER);
tmpl.width0 = size;
tmpl.height0 = 1;
tmpl.depth0 = 1;
tmpl.array_size = 1;
PipeResource::new(unsafe {
(*self.screen).resource_from_user_memory.unwrap()(self.screen, &tmpl, mem)
})
}
pub fn param(&self, cap: pipe_cap) -> i32 {
unsafe { (*self.screen).get_param.unwrap()(self.screen, cap) }
}
pub fn shader_param(&self, t: pipe_shader_type, cap: pipe_shader_cap) -> i32 {
unsafe { (*self.screen).get_shader_param.unwrap()(self.screen, t, cap) }
}
fn compute_param_wrapped(&self, cap: pipe_compute_cap, ptr: *mut c_void) -> i32 {
let s = &mut unsafe { *self.screen };
unsafe {
s.get_compute_param.unwrap()(self.screen, pipe_shader_ir::PIPE_SHADER_IR_NIR, cap, ptr)
}
}
pub fn name(&self) -> String {
unsafe {
let s = *self.screen;
c_string_to_string(s.get_name.unwrap()(self.screen))
}
}
pub fn device_vendor(&self) -> String {
unsafe {
let s = *self.screen;
c_string_to_string(s.get_device_vendor.unwrap()(self.screen))
}
}
pub fn device_type(&self) -> pipe_loader_device_type {
unsafe { *self.ldev.ldev }.type_
}
pub fn is_format_supported(
&self,
format: pipe_format,
target: pipe_texture_target,
bindings: u32,
) -> bool {
let s = &mut unsafe { *self.screen };
unsafe { s.is_format_supported.unwrap()(self.screen, format, target, 0, 0, bindings) }
}
}
impl Drop for PipeScreen {
fn drop(&mut self) {
unsafe {
(*self.screen).destroy.unwrap()(self.screen);
}
}
}
fn has_required_cbs(screen: *mut pipe_screen) -> bool {
let s = unsafe { *screen };
s.context_create.is_some()
&& s.destroy.is_some()
&& s.get_compute_param.is_some()
&& s.get_name.is_some()
&& s.get_param.is_some()
&& s.get_shader_param.is_some()
&& s.is_format_supported.is_some()
&& s.resource_create.is_some()
&& s.resource_from_user_memory.is_some()
}

View file

@ -0,0 +1,66 @@
extern crate mesa_rust_gen;
use crate::pipe::context::*;
use self::mesa_rust_gen::*;
use std::ops::Deref;
use std::os::raw::c_void;
use std::ptr;
pub struct PipeTransfer {
pipe: *mut pipe_transfer,
res: *mut pipe_resource,
ptr: *mut c_void,
}
pub struct GuardedPipeTransfer<'a> {
inner: PipeTransfer,
ctx: &'a PipeContext,
}
impl<'a> Deref for GuardedPipeTransfer<'a> {
type Target = PipeTransfer;
fn deref(&self) -> &Self::Target {
&self.inner
}
}
impl<'a> Drop for GuardedPipeTransfer<'a> {
fn drop(&mut self) {
self.ctx.buffer_unmap(self.inner.pipe);
unsafe { pipe_resource_reference(&mut self.inner.res, ptr::null_mut()) };
}
}
impl PipeTransfer {
pub(super) fn new(pipe: *mut pipe_transfer, ptr: *mut c_void) -> Self {
let mut res: *mut pipe_resource = ptr::null_mut();
unsafe { pipe_resource_reference(&mut res, (*pipe).resource) }
Self {
pipe: pipe,
res: res,
ptr: ptr,
}
}
pub fn ptr(&self) -> *mut c_void {
self.ptr
}
pub fn with_ctx(self, ctx: &PipeContext) -> GuardedPipeTransfer {
GuardedPipeTransfer {
inner: self,
ctx: ctx,
}
}
}
// use set_ctx before operating on the PipeTransfer inside a block where it gets droped
impl Drop for PipeTransfer {
fn drop(&mut self) {
assert_eq!(ptr::null_mut(), self.res);
}
}

View file

@ -0,0 +1,250 @@
# Copyright ©
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
rust = import('unstable-rust')
libmesa_rust_util_files = files(
'util/lib.rs',
'util/properties.rs',
'util/ptr.rs',
'util/string.rs',
)
libmesa_rust_files = files(
'mesa/lib.rs',
'mesa/compiler/mod.rs',
'mesa/compiler/clc/mod.rs',
'mesa/compiler/clc/spirv.rs',
'mesa/pipe/context.rs',
'mesa/pipe/device.rs',
'mesa/pipe/mod.rs',
'mesa/pipe/screen.rs',
'mesa/pipe/transfer.rs',
)
rusticl_files = files(
'lib.rs',
'api/context.rs',
'api/device.rs',
'api/event.rs',
'api/icd.rs',
'api/kernel.rs',
'api/memory.rs',
'api/mod.rs',
'api/platform.rs',
'api/program.rs',
'api/queue.rs',
'api/types.rs',
'api/util.rs',
'core/context.rs',
'core/device.rs',
'core/format.rs',
'core/kernel.rs',
'core/memory.rs',
'core/mod.rs',
'core/program.rs',
'core/queue.rs',
'core/util.rs',
'core/version.rs',
)
rusticl_args = [
# we error on all clippy warnings unless they are disabled
'-Dclippy::all',
# we want to add asserts in control flow
'-Aclippy::assertions_on_constants',
# warns on Arc<_> as keys
'-Aclippy::mutable_key_type',
'-Aclippy::not_unsafe_ptr_arg_deref',
# dunno, kind of looks nicier being explicit
'-Aclippy::redundant_field_names',
'-Aclippy::too_many_arguments',
'-Aclippy::type_complexity',
]
rusticl_gen_args = [
# can't do anything about it anyway
'-Aclippy::all',
]
rusticl_bindgen_args = [
'--no-convert-floats',
'--use-array-pointers-in-arguments',
'--size_t-is-usize',
'--default-enum-style', 'rust',
'--with-derive-partialeq',
'--with-derive-eq',
'--with-derive-partialord',
'--with-derive-ord',
'--with-derive-hash',
'--with-derive-default',
'--anon-fields-prefix', 'anon_',
]
rusticl_opencl_bindings_rs = rust.bindgen(
input : [
'rusticl_opencl_bindings.h',
opencl_headers,
],
output : 'rusticl_opencl_bindings.rs',
include_directories : [
inc_include,
],
c_args : [
'-DCL_USE_DEPRECATED_OPENCL_1_0_APIS',
'-DCL_USE_DEPRECATED_OPENCL_1_1_APIS',
'-DCL_USE_DEPRECATED_OPENCL_1_2_APIS',
'-DCL_USE_DEPRECATED_OPENCL_2_0_APIS',
'-DCL_USE_DEPRECATED_OPENCL_2_1_APIS',
'-DCL_USE_DEPRECATED_OPENCL_2_2_APIS',
'-DCL_TARGET_OPENCL_VERSION=300',
],
args : [
rusticl_bindgen_args,
'--disable-header-comment',
'--ignore-functions',
# needed because bindgen adds *mut void fields...
'--raw-line', 'unsafe impl std::marker::Sync for _cl_icd_dispatch {}',
'--whitelist-type', 'cl_.*',
'--whitelist-var', 'CL_.*',
# some info types need to be strongly typed so we can implement various get_infos
'--new-type-alias-deref', 'cl_(mem|image)_info',
],
)
rusticl_opencl_gen = static_library(
'rusticl_opencl_gen',
rusticl_opencl_bindings_rs,
gnu_symbol_visibility : 'hidden',
rust_crate_type : 'rlib',
rust_args : [
rusticl_gen_args,
'-Anon_snake_case',
'-Anon_camel_case_types',
'-Anon_upper_case_globals',
],
)
rusticl_mesa_bindings_inline_wrapper = static_library(
'mesa_bindings_inline_wrapper',
[
'rusticl_mesa_inline_bindings_wrapper.c',
'rusticl_mesa_inline_bindings_wrapper.h'
],
gnu_symbol_visibility : 'hidden',
include_directories : [
inc_gallium,
inc_gallium_aux,
inc_include,
inc_src,
],
c_args : pre_args,
dependencies: [
idep_nir_headers,
],
)
rusticl_mesa_bindings_rs = rust.bindgen(
input : 'rusticl_mesa_bindings.h',
output : 'rusticl_mesa_bindings.rs',
include_directories : [
inc_gallium,
inc_gallium_aux,
inc_include,
inc_src,
],
c_args : pre_args,
args : [
rusticl_bindgen_args,
'--whitelist-function', 'clc_.*',
'--whitelist-function', 'pipe_.*',
'--whitelist-type', 'pipe_endian',
'--whitelist-var', 'PIPE_.*',
'--bitfield-enum', 'pipe_map_flags',
],
)
idep_rusticl_gen = declare_dependency(
sources: [
rusticl_opencl_bindings_rs,
],
)
libmesa_rust_gen = static_library(
'mesa_rust_gen',
rusticl_mesa_bindings_rs,
gnu_symbol_visibility : 'hidden',
link_with: [
libgallium,
],
dependencies: [
idep_clc,
],
rust_crate_type : 'rlib',
rust_args : [
rusticl_gen_args,
'-Anon_snake_case',
'-Anon_camel_case_types',
'-Anon_upper_case_globals',
],
)
libmesa_rust_util = static_library(
'mesa_rust_util',
[libmesa_rust_util_files],
gnu_symbol_visibility : 'hidden',
rust_crate_type : 'rlib',
rust_args : [
rusticl_args,
],
)
libmesa_rust = static_library(
'mesa_rust',
[libmesa_rust_files],
gnu_symbol_visibility : 'hidden',
rust_crate_type : 'rlib',
rust_args : [
rusticl_args,
],
link_with : [
libmesa_rust_gen,
libmesa_rust_util,
rusticl_mesa_bindings_inline_wrapper,
]
)
librusticl = static_library(
'rusticl',
[rusticl_files],
gnu_symbol_visibility : 'hidden',
rust_crate_type : 'staticlib',
rust_args : [
rusticl_args,
],
link_with : [
libmesa_rust,
libmesa_rust_util,
rusticl_opencl_gen,
],
dependencies : [
idep_rusticl_gen,
],
)

View file

@ -0,0 +1,9 @@
#include "rusticl_mesa_inline_bindings_wrapper.h"
#include "compiler/clc/clc.h"
#include "pipe/p_context.h"
#include "pipe/p_defines.h"
#include "pipe/p_screen.h"
#include "pipe/p_state.h"
#include "pipe-loader/pipe_loader.h"

View file

@ -0,0 +1,7 @@
#include "rusticl_mesa_inline_bindings_wrapper.h"
void
pipe_resource_reference(struct pipe_resource **dst, struct pipe_resource *src)
{
__pipe_resource_reference_wraped(dst, src);
}

View file

@ -0,0 +1,5 @@
#define pipe_resource_reference __pipe_resource_reference_wraped
#include "util/u_inlines.h"
#undef pipe_resource_reference
void pipe_resource_reference(struct pipe_resource **dst, struct pipe_resource *src);

View file

@ -0,0 +1,13 @@
#include <CL/cl_icd.h>
#define DECL_CL_STRUCT(name) struct name { const cl_icd_dispatch *dispatch; }
DECL_CL_STRUCT(_cl_command_queue);
DECL_CL_STRUCT(_cl_context);
DECL_CL_STRUCT(_cl_device_id);
DECL_CL_STRUCT(_cl_event);
DECL_CL_STRUCT(_cl_kernel);
DECL_CL_STRUCT(_cl_mem);
DECL_CL_STRUCT(_cl_platform_id);
DECL_CL_STRUCT(_cl_program);
DECL_CL_STRUCT(_cl_sampler);
#undef DECL_CL_STRUCT

View file

@ -0,0 +1,3 @@
pub mod properties;
pub mod ptr;
pub mod string;

View file

@ -0,0 +1,54 @@
pub struct Properties<T> {
pub props: Vec<(T, T)>,
}
impl<T: Copy + PartialEq + Default> Properties<T> {
#[allow(clippy::not_unsafe_ptr_arg_deref)]
pub fn from_ptr_raw(mut p: *const T) -> Vec<T> {
let mut res: Vec<T> = Vec::new();
if !p.is_null() {
unsafe {
while *p != T::default() {
res.push(*p);
res.push(*p.add(1));
p = p.add(2);
}
}
res.push(T::default());
}
res
}
#[allow(clippy::not_unsafe_ptr_arg_deref)]
pub fn from_ptr(mut p: *const T) -> Option<Self> {
let mut res = Self::default();
if !p.is_null() {
let mut k: Vec<T> = Vec::new();
let mut v: Vec<T> = Vec::new();
unsafe {
while *p != T::default() {
if k.contains(&*p) {
return None;
}
k.push(*p);
v.push(*p.add(1));
p = p.add(2);
}
}
res.props = k.iter().cloned().zip(v).collect();
}
Some(res)
}
}
impl<T> Default for Properties<T> {
fn default() -> Self {
Self { props: Vec::new() }
}
}

View file

@ -0,0 +1,26 @@
use std::ptr;
pub trait CheckedPtr<T> {
/// # Safety
///
/// besides a null check the function can't make sure the pointer is valid
/// for the entire size
unsafe fn copy_checked(self, val: *const T, size: usize);
fn write_checked(self, val: T);
}
impl<T> CheckedPtr<T> for *mut T {
unsafe fn copy_checked(self, val: *const T, size: usize) {
if !self.is_null() {
ptr::copy(val, self, size);
}
}
fn write_checked(self, val: T) {
if !self.is_null() {
unsafe {
*self = val;
}
}
}
}

View file

@ -0,0 +1,13 @@
use std::ffi::CStr;
use std::os::raw::c_char;
#[allow(clippy::not_unsafe_ptr_arg_deref)]
pub fn c_string_to_string(cstr: *const c_char) -> String {
if cstr.is_null() {
return String::from("");
}
let res = unsafe { CStr::from_ptr(cstr).to_str() };
assert!(res.is_ok());
String::from(res.unwrap_or(""))
}

View file

@ -182,6 +182,10 @@ if with_gallium_opencl
subdir('frontends/clover') subdir('frontends/clover')
subdir('targets/opencl') subdir('targets/opencl')
endif endif
if with_gallium_rusticl
subdir('frontends/rusticl')
subdir('targets/rusticl')
endif
if with_dri if with_dri
subdir('frontends/dri') subdir('frontends/dri')
subdir('targets/dri') subdir('targets/dri')

View file

@ -0,0 +1,62 @@
# Copyright © 2017 Intel Corporation
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
# SOFTWARE.
opencl_version = '1'
librusticl = shared_library(
'RusticlOpenCL',
'target.c',
gnu_symbol_visibility : 'hidden',
include_directories : [
inc_include,
inc_src,
inc_gallium,
inc_gallium_aux,
inc_gallium_drivers,
inc_gallium_winsys,
],
link_whole : librusticl,
link_with : [
libgalliumvl,
libpipe_loader_static,
libswdri,
libswkmsdri,
libwsw,
libws_null,
],
dependencies : [
driver_iris,
driver_nouveau,
driver_swrast,
idep_nir,
],
version : '@0@.0.0'.format(opencl_version),
install : true,
)
_config = configuration_data()
_config.set('OPENCL_VERSION', opencl_version)
configure_file(
configuration : _config,
input : 'rusticl.icd.in',
output : 'rusticl.icd',
install : true,
install_dir : join_paths(get_option('sysconfdir'), 'OpenCL', 'vendors'),
)

View file

@ -0,0 +1 @@
libRusticlOpenCL.so.@OPENCL_VERSION@

View file

@ -0,0 +1,2 @@
#include "target-helpers/drm_helper.h"
#include "target-helpers/sw_helper.h"