mirror of
https://gitlab.freedesktop.org/mesa/mesa.git
synced 2025-12-20 05:10:11 +01:00
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:
parent
d431a01d9a
commit
20c90fed5a
51 changed files with 6768 additions and 19 deletions
|
|
@ -35,7 +35,10 @@ trim_trailing_whitespace = false
|
|||
indent_style = space
|
||||
indent_size = 2
|
||||
|
||||
|
||||
[*.ps1]
|
||||
indent_style = space
|
||||
indent_size = 2
|
||||
|
||||
[*.rs]
|
||||
indent_style = space
|
||||
indent_size = 4
|
||||
|
|
|
|||
|
|
@ -118,10 +118,7 @@ if with_platform_haiku
|
|||
)
|
||||
endif
|
||||
|
||||
# Only install the headers if we are building a stand alone implementation and
|
||||
# not an ICD enabled implementation
|
||||
if with_gallium_opencl and not with_opencl_icd
|
||||
install_headers(
|
||||
opencl_headers = files(
|
||||
'CL/cl.h',
|
||||
'CL/cl.hpp',
|
||||
'CL/cl2.hpp',
|
||||
|
|
@ -139,6 +136,12 @@ if with_gallium_opencl and not with_opencl_icd
|
|||
'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
|
||||
# not an ICD enabled implementation
|
||||
if with_gallium_opencl and not with_opencl_icd
|
||||
install_headers(
|
||||
opencl_headers,
|
||||
subdir: 'CL'
|
||||
)
|
||||
endif
|
||||
|
|
|
|||
17
meson.build
17
meson.build
|
|
@ -27,7 +27,7 @@ project(
|
|||
).stdout(),
|
||||
license : 'MIT',
|
||||
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
|
||||
|
|
@ -953,6 +953,21 @@ else
|
|||
with_opencl_icd = false
|
||||
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
|
||||
if with_libclc
|
||||
dep_clc = dependency('libclc')
|
||||
|
|
|
|||
|
|
@ -163,6 +163,12 @@ option(
|
|||
value : 'disabled',
|
||||
description : 'build gallium "clover" OpenCL frontend.',
|
||||
)
|
||||
option(
|
||||
'gallium-rusticl',
|
||||
type : 'boolean',
|
||||
value : false,
|
||||
description : 'build gallium "rusticl" OpenCL frontend.',
|
||||
)
|
||||
option(
|
||||
'gallium-windows-dll-name',
|
||||
type : 'string',
|
||||
|
|
|
|||
117
src/gallium/frontends/rusticl/api/context.rs
Normal file
117
src/gallium/frontends/rusticl/api/context.rs
Normal 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,
|
||||
)
|
||||
}
|
||||
230
src/gallium/frontends/rusticl/api/device.rs
Normal file
230
src/gallium/frontends/rusticl/api/device.rs
Normal 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(())
|
||||
}
|
||||
58
src/gallium/frontends/rusticl/api/event.rs
Normal file
58
src/gallium/frontends/rusticl/api/event.rs
Normal 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(())
|
||||
}
|
||||
1260
src/gallium/frontends/rusticl/api/icd.rs
Normal file
1260
src/gallium/frontends/rusticl/api/icd.rs
Normal file
File diff suppressed because it is too large
Load diff
24
src/gallium/frontends/rusticl/api/kernel.rs
Normal file
24
src/gallium/frontends/rusticl/api/kernel.rs
Normal 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.
|
||||
}
|
||||
1343
src/gallium/frontends/rusticl/api/memory.rs
Normal file
1343
src/gallium/frontends/rusticl/api/memory.rs
Normal file
File diff suppressed because it is too large
Load diff
11
src/gallium/frontends/rusticl/api/mod.rs
Normal file
11
src/gallium/frontends/rusticl/api/mod.rs
Normal 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;
|
||||
121
src/gallium/frontends/rusticl/api/platform.rs
Normal file
121
src/gallium/frontends/rusticl/api/platform.rs
Normal 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);
|
||||
}
|
||||
287
src/gallium/frontends/rusticl/api/program.rs
Normal file
287
src/gallium/frontends/rusticl/api/program.rs
Normal 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.
|
||||
}
|
||||
77
src/gallium/frontends/rusticl/api/queue.rs
Normal file
77
src/gallium/frontends/rusticl/api/queue.rs
Normal 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(())
|
||||
}
|
||||
147
src/gallium/frontends/rusticl/api/types.rs
Normal file
147
src/gallium/frontends/rusticl/api/types.rs
Normal 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]
|
||||
}
|
||||
}
|
||||
341
src/gallium/frontends/rusticl/api/util.rs
Normal file
341
src/gallium/frontends/rusticl/api/util.rs
Normal 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
|
||||
}
|
||||
62
src/gallium/frontends/rusticl/core/context.rs
Normal file
62
src/gallium/frontends/rusticl/core/context.rs
Normal 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)
|
||||
}
|
||||
}
|
||||
489
src/gallium/frontends/rusticl/core/device.rs
Normal file
489
src/gallium/frontends/rusticl/core/device.rs
Normal 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,
|
||||
}
|
||||
}
|
||||
}
|
||||
110
src/gallium/frontends/rusticl/core/event.rs
Normal file
110
src/gallium/frontends/rusticl/core/event.rs
Normal 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
|
||||
181
src/gallium/frontends/rusticl/core/format.rs
Normal file
181
src/gallium/frontends/rusticl/core/format.rs
Normal 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,
|
||||
),
|
||||
];
|
||||
24
src/gallium/frontends/rusticl/core/kernel.rs
Normal file
24
src/gallium/frontends/rusticl/core/kernel.rs
Normal 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(),
|
||||
})
|
||||
}
|
||||
}
|
||||
398
src/gallium/frontends/rusticl/core/memory.rs
Normal file
398
src/gallium/frontends/rusticl/core/memory.rs
Normal 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,
|
||||
})
|
||||
}
|
||||
}
|
||||
10
src/gallium/frontends/rusticl/core/mod.rs
Normal file
10
src/gallium/frontends/rusticl/core/mod.rs
Normal 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;
|
||||
187
src/gallium/frontends/rusticl/core/program.rs
Normal file
187
src/gallium/frontends/rusticl/core/program.rs
Normal 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(),
|
||||
}),
|
||||
})
|
||||
}
|
||||
}
|
||||
96
src/gallium/frontends/rusticl/core/queue.rs
Normal file
96
src/gallium/frontends/rusticl/core/queue.rs
Normal 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);
|
||||
}
|
||||
}
|
||||
17
src/gallium/frontends/rusticl/core/util.rs
Normal file
17
src/gallium/frontends/rusticl/core/util.rs
Normal 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,
|
||||
}
|
||||
}
|
||||
80
src/gallium/frontends/rusticl/core/version.rs
Normal file
80
src/gallium/frontends/rusticl/core/version.rs
Normal 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),
|
||||
})
|
||||
}
|
||||
}
|
||||
5
src/gallium/frontends/rusticl/lib.rs
Normal file
5
src/gallium/frontends/rusticl/lib.rs
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
#![allow(clippy::collapsible_if)]
|
||||
#![allow(clippy::needless_range_loop)]
|
||||
|
||||
mod api;
|
||||
pub mod core;
|
||||
1
src/gallium/frontends/rusticl/mesa/compiler/clc/mod.rs
Normal file
1
src/gallium/frontends/rusticl/mesa/compiler/clc/mod.rs
Normal file
|
|
@ -0,0 +1 @@
|
|||
pub mod spirv;
|
||||
148
src/gallium/frontends/rusticl/mesa/compiler/clc/spirv.rs
Normal file
148
src/gallium/frontends/rusticl/mesa/compiler/clc/spirv.rs
Normal 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
1
src/gallium/frontends/rusticl/mesa/compiler/mod.rs
Normal file
1
src/gallium/frontends/rusticl/mesa/compiler/mod.rs
Normal file
|
|
@ -0,0 +1 @@
|
|||
pub mod clc;
|
||||
2
src/gallium/frontends/rusticl/mesa/lib.rs
Normal file
2
src/gallium/frontends/rusticl/mesa/lib.rs
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
pub mod compiler;
|
||||
pub mod pipe;
|
||||
116
src/gallium/frontends/rusticl/mesa/pipe/context.rs
Normal file
116
src/gallium/frontends/rusticl/mesa/pipe/context.rs
Normal 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()
|
||||
}
|
||||
55
src/gallium/frontends/rusticl/mesa/pipe/device.rs
Normal file
55
src/gallium/frontends/rusticl/mesa/pipe/device.rs
Normal 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()
|
||||
}
|
||||
5
src/gallium/frontends/rusticl/mesa/pipe/mod.rs
Normal file
5
src/gallium/frontends/rusticl/mesa/pipe/mod.rs
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
pub mod context;
|
||||
pub mod device;
|
||||
pub mod resource;
|
||||
pub mod screen;
|
||||
pub mod transfer;
|
||||
29
src/gallium/frontends/rusticl/mesa/pipe/resource.rs
Normal file
29
src/gallium/frontends/rusticl/mesa/pipe/resource.rs
Normal 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()) }
|
||||
}
|
||||
}
|
||||
175
src/gallium/frontends/rusticl/mesa/pipe/screen.rs
Normal file
175
src/gallium/frontends/rusticl/mesa/pipe/screen.rs
Normal 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()
|
||||
}
|
||||
66
src/gallium/frontends/rusticl/mesa/pipe/transfer.rs
Normal file
66
src/gallium/frontends/rusticl/mesa/pipe/transfer.rs
Normal 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);
|
||||
}
|
||||
}
|
||||
250
src/gallium/frontends/rusticl/meson.build
Normal file
250
src/gallium/frontends/rusticl/meson.build
Normal 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,
|
||||
],
|
||||
)
|
||||
9
src/gallium/frontends/rusticl/rusticl_mesa_bindings.h
Normal file
9
src/gallium/frontends/rusticl/rusticl_mesa_bindings.h
Normal 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"
|
||||
|
|
@ -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);
|
||||
}
|
||||
|
|
@ -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);
|
||||
13
src/gallium/frontends/rusticl/rusticl_opencl_bindings.h
Normal file
13
src/gallium/frontends/rusticl/rusticl_opencl_bindings.h
Normal 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
|
||||
3
src/gallium/frontends/rusticl/util/lib.rs
Normal file
3
src/gallium/frontends/rusticl/util/lib.rs
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
pub mod properties;
|
||||
pub mod ptr;
|
||||
pub mod string;
|
||||
54
src/gallium/frontends/rusticl/util/properties.rs
Normal file
54
src/gallium/frontends/rusticl/util/properties.rs
Normal 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() }
|
||||
}
|
||||
}
|
||||
26
src/gallium/frontends/rusticl/util/ptr.rs
Normal file
26
src/gallium/frontends/rusticl/util/ptr.rs
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
13
src/gallium/frontends/rusticl/util/string.rs
Normal file
13
src/gallium/frontends/rusticl/util/string.rs
Normal 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(""))
|
||||
}
|
||||
|
|
@ -182,6 +182,10 @@ if with_gallium_opencl
|
|||
subdir('frontends/clover')
|
||||
subdir('targets/opencl')
|
||||
endif
|
||||
if with_gallium_rusticl
|
||||
subdir('frontends/rusticl')
|
||||
subdir('targets/rusticl')
|
||||
endif
|
||||
if with_dri
|
||||
subdir('frontends/dri')
|
||||
subdir('targets/dri')
|
||||
|
|
|
|||
62
src/gallium/targets/rusticl/meson.build
Normal file
62
src/gallium/targets/rusticl/meson.build
Normal 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'),
|
||||
)
|
||||
1
src/gallium/targets/rusticl/rusticl.icd.in
Normal file
1
src/gallium/targets/rusticl/rusticl.icd.in
Normal file
|
|
@ -0,0 +1 @@
|
|||
libRusticlOpenCL.so.@OPENCL_VERSION@
|
||||
2
src/gallium/targets/rusticl/target.c
Normal file
2
src/gallium/targets/rusticl/target.c
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
#include "target-helpers/drm_helper.h"
|
||||
#include "target-helpers/sw_helper.h"
|
||||
Loading…
Add table
Reference in a new issue