Vendor things
This commit is contained in:
parent
5deceec006
commit
977e3c17e5
19434 changed files with 10682014 additions and 0 deletions
512
third-party/vendor/metal/examples/raytracing/renderer.rs
vendored
Normal file
512
third-party/vendor/metal/examples/raytracing/renderer.rs
vendored
Normal file
|
|
@ -0,0 +1,512 @@
|
|||
use core_graphics_types::{base::CGFloat, geometry::CGSize};
|
||||
use std::{
|
||||
collections::BTreeMap,
|
||||
ffi::c_void,
|
||||
mem::{size_of, transmute},
|
||||
ops::Index,
|
||||
sync::{Arc, Condvar, Mutex},
|
||||
};
|
||||
|
||||
use glam::{Vec3, Vec4, Vec4Swizzles};
|
||||
use rand::{thread_rng, RngCore};
|
||||
|
||||
use metal::{foreign_types::ForeignType, *};
|
||||
|
||||
use crate::{camera::Camera, geometry::get_managed_buffer_storage_mode, scene::Scene};
|
||||
|
||||
#[repr(C)]
|
||||
struct Uniforms {
|
||||
pub width: u32,
|
||||
pub height: u32,
|
||||
pub frame_index: u32,
|
||||
pub light_count: u32,
|
||||
pub camera: Camera,
|
||||
}
|
||||
|
||||
pub const MAX_FRAMES_IN_FLIGHT: NSUInteger = 3;
|
||||
pub const ALIGNED_UNIFORMS_SIZE: NSUInteger = (size_of::<Uniforms>() as NSUInteger + 255) & !255;
|
||||
pub const UNIFORM_BUFFER_SIZE: NSUInteger = MAX_FRAMES_IN_FLIGHT * ALIGNED_UNIFORMS_SIZE;
|
||||
|
||||
#[derive(Clone)]
|
||||
struct Semaphore {
|
||||
data: Arc<(Mutex<usize>, Condvar)>,
|
||||
}
|
||||
|
||||
impl Semaphore {
|
||||
fn new(capacity: usize) -> Self {
|
||||
Self {
|
||||
data: Arc::new((Mutex::new(capacity), Condvar::new())),
|
||||
}
|
||||
}
|
||||
|
||||
fn acquire(&self) {
|
||||
let mut value = self.data.0.lock().unwrap();
|
||||
while *value == 0 {
|
||||
value = self.data.1.wait(value).unwrap();
|
||||
}
|
||||
*value -= 1;
|
||||
}
|
||||
|
||||
fn release(&self) {
|
||||
let mut value = self.data.0.lock().unwrap();
|
||||
*value += 1;
|
||||
self.data.1.notify_one();
|
||||
}
|
||||
}
|
||||
|
||||
pub struct Renderer {
|
||||
pub device: Device,
|
||||
pub scene: Scene,
|
||||
pub uniform_buffer: Buffer,
|
||||
pub resource_buffer: Buffer,
|
||||
pub instance_acceleration_structure: AccelerationStructure,
|
||||
pub accumulation_targets: [Texture; 2],
|
||||
pub random_texture: Texture,
|
||||
pub frame_index: NSUInteger,
|
||||
pub uniform_buffer_index: NSUInteger,
|
||||
pub uniform_buffer_offset: NSUInteger,
|
||||
pub size: CGSize,
|
||||
semaphore: Semaphore,
|
||||
pub queue: CommandQueue,
|
||||
instance_buffer: Buffer,
|
||||
intersection_function_table: IntersectionFunctionTable,
|
||||
primitive_acceleration_structures: Vec<AccelerationStructure>,
|
||||
raytracing_pipeline: ComputePipelineState,
|
||||
copy_pipeline: RenderPipelineState,
|
||||
}
|
||||
|
||||
impl Renderer {
|
||||
pub fn new(device: Device) -> Self {
|
||||
let scene = Scene::new(device.clone());
|
||||
|
||||
let library_path = std::path::PathBuf::from(env!("CARGO_MANIFEST_DIR"))
|
||||
.join("examples/raytracing/shaders.metallib");
|
||||
let library = device.new_library_with_file(library_path).unwrap();
|
||||
let queue = device.new_command_queue();
|
||||
|
||||
let buffer_data = [0u8; UNIFORM_BUFFER_SIZE as usize];
|
||||
let uniform_buffer = device.new_buffer_with_data(
|
||||
buffer_data.as_ptr() as *const c_void,
|
||||
UNIFORM_BUFFER_SIZE,
|
||||
get_managed_buffer_storage_mode(),
|
||||
);
|
||||
uniform_buffer.set_label("uniform buffer");
|
||||
let resources_stride = {
|
||||
let mut max = 0;
|
||||
for geometry in &scene.geometries {
|
||||
let s = geometry.get_resources().len();
|
||||
if s > max {
|
||||
max = s;
|
||||
}
|
||||
}
|
||||
max
|
||||
};
|
||||
let mut resource_buffer_data = vec![0u64; resources_stride * scene.geometries.len()];
|
||||
for geometry_index in 0..scene.geometries.len() {
|
||||
let geometry = scene.geometries[geometry_index].as_ref();
|
||||
let resource_buffer_begin_index = resources_stride * geometry_index;
|
||||
let resources = geometry.get_resources();
|
||||
|
||||
for argument_index in 0..resources.len() {
|
||||
let resource_buffer_index = resource_buffer_begin_index + argument_index;
|
||||
let resource = resources[argument_index].clone();
|
||||
resource_buffer_data[resource_buffer_index] =
|
||||
if resource.conforms_to_protocol::<MTLBuffer>().unwrap() {
|
||||
let buffer = unsafe { Buffer::from_ptr(transmute(resource.into_ptr())) };
|
||||
buffer.gpu_address()
|
||||
} else if resource.conforms_to_protocol::<MTLTexture>().unwrap() {
|
||||
let texture = unsafe { Texture::from_ptr(transmute(resource.into_ptr())) };
|
||||
texture.gpu_resource_id()._impl
|
||||
} else {
|
||||
panic!("Unexpected resource!")
|
||||
}
|
||||
}
|
||||
}
|
||||
let resource_buffer = device.new_buffer_with_data(
|
||||
resource_buffer_data.as_ptr() as *const c_void,
|
||||
(resource_buffer_data.len() * size_of::<u64>()) as NSUInteger,
|
||||
get_managed_buffer_storage_mode(),
|
||||
);
|
||||
resource_buffer.set_label("resource buffer");
|
||||
resource_buffer.did_modify_range(NSRange::new(0, resource_buffer.length()));
|
||||
|
||||
let mut primitive_acceleration_structures = Vec::new();
|
||||
for i in 0..scene.geometries.len() {
|
||||
let mesh = scene.geometries[i].as_ref();
|
||||
let geometry_descriptor = mesh.get_geometry_descriptor();
|
||||
geometry_descriptor.set_intersection_function_table_offset(i as NSUInteger);
|
||||
let geometry_descriptors = Array::from_owned_slice(&[geometry_descriptor]);
|
||||
let accel_descriptor = PrimitiveAccelerationStructureDescriptor::descriptor();
|
||||
accel_descriptor.set_geometry_descriptors(&geometry_descriptors);
|
||||
let accel_descriptor: AccelerationStructureDescriptor = From::from(accel_descriptor);
|
||||
primitive_acceleration_structures.push(
|
||||
Self::new_acceleration_structure_with_descriptor(
|
||||
&device,
|
||||
&queue,
|
||||
&accel_descriptor,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
let mut instance_descriptors = vec![
|
||||
MTLAccelerationStructureInstanceDescriptor::default();
|
||||
scene.geometry_instances.len()
|
||||
];
|
||||
for instance_index in 0..scene.geometry_instances.len() {
|
||||
let instance = scene.geometry_instances[instance_index].as_ref();
|
||||
let geometry_index = instance.index_in_scene;
|
||||
instance_descriptors[instance_index].acceleration_structure_index =
|
||||
geometry_index as u32;
|
||||
instance_descriptors[instance_index].options =
|
||||
if instance.geometry.get_intersection_function_name().is_none() {
|
||||
MTLAccelerationStructureInstanceOptions::Opaque
|
||||
} else {
|
||||
MTLAccelerationStructureInstanceOptions::None
|
||||
};
|
||||
instance_descriptors[instance_index].intersection_function_table_offset = 0;
|
||||
instance_descriptors[instance_index].mask = instance.mask as u32;
|
||||
for column in 0..4 {
|
||||
for row in 0..3 {
|
||||
instance_descriptors[instance_index].transformation_matrix[column][row] =
|
||||
*instance.transform.col(column).index(row);
|
||||
}
|
||||
}
|
||||
}
|
||||
let instance_buffer = device.new_buffer_with_data(
|
||||
instance_descriptors.as_ptr() as *const c_void,
|
||||
(size_of::<MTLAccelerationStructureInstanceDescriptor>()
|
||||
* scene.geometry_instances.len()) as NSUInteger,
|
||||
get_managed_buffer_storage_mode(),
|
||||
);
|
||||
instance_buffer.set_label("instance buffer");
|
||||
instance_buffer.did_modify_range(NSRange::new(0, instance_buffer.length()));
|
||||
|
||||
let accel_descriptor = InstanceAccelerationStructureDescriptor::descriptor();
|
||||
accel_descriptor.set_instanced_acceleration_structures(&Array::from_owned_slice(
|
||||
&primitive_acceleration_structures,
|
||||
));
|
||||
accel_descriptor.set_instance_count(scene.geometry_instances.len() as NSUInteger);
|
||||
accel_descriptor.set_instance_descriptor_buffer(&instance_buffer);
|
||||
let accel_descriptor: AccelerationStructureDescriptor = From::from(accel_descriptor);
|
||||
let instance_acceleration_structure =
|
||||
Self::new_acceleration_structure_with_descriptor(&device, &queue, &accel_descriptor);
|
||||
|
||||
let mut intersection_functions = BTreeMap::<String, Function>::new();
|
||||
for geometry in &scene.geometries {
|
||||
if let Some(name) = geometry.get_intersection_function_name() {
|
||||
if !intersection_functions.contains_key(name) {
|
||||
let intersection_function = Self::new_specialised_function_with_name(
|
||||
&library,
|
||||
resources_stride as u32,
|
||||
name,
|
||||
);
|
||||
intersection_functions.insert(name.to_string(), intersection_function);
|
||||
}
|
||||
}
|
||||
}
|
||||
let raytracing_function = Self::new_specialised_function_with_name(
|
||||
&library,
|
||||
resources_stride as u32,
|
||||
"raytracingKernel",
|
||||
);
|
||||
let intersection_function_array: Vec<&FunctionRef> = intersection_functions
|
||||
.values()
|
||||
.map(|f| -> &FunctionRef { f })
|
||||
.collect();
|
||||
let raytracing_pipeline = Self::new_compute_pipeline_state_with_function(
|
||||
&device,
|
||||
&raytracing_function,
|
||||
&intersection_function_array,
|
||||
);
|
||||
let intersection_function_table_descriptor = IntersectionFunctionTableDescriptor::new();
|
||||
intersection_function_table_descriptor
|
||||
.set_function_count(scene.geometries.len() as NSUInteger);
|
||||
let intersection_function_table = raytracing_pipeline
|
||||
.new_intersection_function_table_with_descriptor(
|
||||
&intersection_function_table_descriptor,
|
||||
);
|
||||
for geometry_index in 0..scene.geometries.len() {
|
||||
let geometry = scene.geometries[geometry_index].as_ref();
|
||||
if let Some(intersection_function_name) = geometry.get_intersection_function_name() {
|
||||
let intersection_function = &intersection_functions[intersection_function_name];
|
||||
let handle = raytracing_pipeline
|
||||
.function_handle_with_function(intersection_function)
|
||||
.unwrap();
|
||||
intersection_function_table.set_function(handle, geometry_index as NSUInteger);
|
||||
}
|
||||
}
|
||||
let render_descriptor = RenderPipelineDescriptor::new();
|
||||
render_descriptor
|
||||
.set_vertex_function(Some(&library.get_function("copyVertex", None).unwrap()));
|
||||
render_descriptor
|
||||
.set_fragment_function(Some(&library.get_function("copyFragment", None).unwrap()));
|
||||
render_descriptor
|
||||
.color_attachments()
|
||||
.object_at(0)
|
||||
.unwrap()
|
||||
.set_pixel_format(MTLPixelFormat::RGBA16Float);
|
||||
let copy_pipeline = device
|
||||
.new_render_pipeline_state(&render_descriptor)
|
||||
.unwrap();
|
||||
|
||||
let texture_descriptor = Self::create_target_descriptor(1024, 1024);
|
||||
let accumulation_targets = [
|
||||
device.new_texture(&texture_descriptor),
|
||||
device.new_texture(&texture_descriptor),
|
||||
];
|
||||
let random_texture = device.new_texture(&texture_descriptor);
|
||||
|
||||
Self {
|
||||
device,
|
||||
scene,
|
||||
uniform_buffer,
|
||||
resource_buffer,
|
||||
instance_acceleration_structure,
|
||||
accumulation_targets,
|
||||
random_texture,
|
||||
frame_index: 0,
|
||||
uniform_buffer_index: 0,
|
||||
uniform_buffer_offset: 0,
|
||||
size: CGSize::new(1024 as CGFloat, 1024 as CGFloat),
|
||||
semaphore: Semaphore::new((MAX_FRAMES_IN_FLIGHT - 2) as usize),
|
||||
instance_buffer,
|
||||
queue,
|
||||
intersection_function_table,
|
||||
primitive_acceleration_structures,
|
||||
raytracing_pipeline,
|
||||
copy_pipeline,
|
||||
}
|
||||
}
|
||||
|
||||
fn create_target_descriptor(width: NSUInteger, height: NSUInteger) -> TextureDescriptor {
|
||||
let texture_descriptor = TextureDescriptor::new();
|
||||
texture_descriptor.set_pixel_format(MTLPixelFormat::RGBA32Float);
|
||||
texture_descriptor.set_texture_type(MTLTextureType::D2);
|
||||
texture_descriptor.set_width(width);
|
||||
texture_descriptor.set_height(height);
|
||||
texture_descriptor.set_storage_mode(MTLStorageMode::Private);
|
||||
texture_descriptor.set_usage(MTLTextureUsage::ShaderRead | MTLTextureUsage::ShaderWrite);
|
||||
texture_descriptor
|
||||
}
|
||||
|
||||
pub fn window_resized(&mut self, size: CGSize) {
|
||||
self.size = size;
|
||||
let texture_descriptor =
|
||||
Self::create_target_descriptor(size.width as NSUInteger, size.height as NSUInteger);
|
||||
self.accumulation_targets[0] = self.device.new_texture(&texture_descriptor);
|
||||
self.accumulation_targets[1] = self.device.new_texture(&texture_descriptor);
|
||||
texture_descriptor.set_pixel_format(MTLPixelFormat::R32Uint);
|
||||
texture_descriptor.set_usage(MTLTextureUsage::ShaderRead);
|
||||
texture_descriptor.set_storage_mode(MTLStorageMode::Managed);
|
||||
self.random_texture = self.device.new_texture(&texture_descriptor);
|
||||
let mut rng = thread_rng();
|
||||
let mut random_values = vec![0u32; (size.width * size.height) as usize];
|
||||
for v in &mut random_values {
|
||||
*v = rng.next_u32();
|
||||
}
|
||||
self.random_texture.replace_region(
|
||||
MTLRegion::new_2d(0, 0, size.width as NSUInteger, size.height as NSUInteger),
|
||||
0,
|
||||
random_values.as_ptr() as *const c_void,
|
||||
size_of::<u32>() as NSUInteger * size.width as NSUInteger,
|
||||
);
|
||||
self.frame_index = 0;
|
||||
}
|
||||
|
||||
fn update_uniforms(&mut self) {
|
||||
self.uniform_buffer_offset = ALIGNED_UNIFORMS_SIZE * self.uniform_buffer_index;
|
||||
|
||||
let uniforms = unsafe {
|
||||
&mut *((self.uniform_buffer.contents() as *mut u8)
|
||||
.add(self.uniform_buffer_offset as usize) as *mut Uniforms)
|
||||
};
|
||||
|
||||
let position = self.scene.camera.position;
|
||||
let target = self.scene.camera.forward;
|
||||
let up = self.scene.camera.up;
|
||||
|
||||
let forward = Vec3::normalize(target.xyz() - position.xyz());
|
||||
let right = Vec3::normalize(Vec3::cross(forward, up.xyz()));
|
||||
let up = Vec3::normalize(Vec3::cross(right, forward));
|
||||
|
||||
uniforms.camera.position = position;
|
||||
uniforms.camera.forward = Vec4::from((forward, 0.0));
|
||||
uniforms.camera.right = Vec4::from((right, 0.0));
|
||||
uniforms.camera.up = Vec4::from((up, 0.0));
|
||||
|
||||
let field_of_view = 45.0 * (std::f32::consts::PI / 180.0);
|
||||
let aspect_ratio = self.size.width as f32 / self.size.height as f32;
|
||||
let image_plane_height = f32::tan(field_of_view / 2.0);
|
||||
let image_plane_width = aspect_ratio * image_plane_height;
|
||||
|
||||
uniforms.camera.right *= image_plane_width;
|
||||
uniforms.camera.up *= image_plane_height;
|
||||
|
||||
uniforms.width = self.size.width as u32;
|
||||
uniforms.height = self.size.height as u32;
|
||||
|
||||
uniforms.frame_index = self.frame_index as u32;
|
||||
self.frame_index += 1;
|
||||
|
||||
uniforms.light_count = self.scene.lights.len() as u32;
|
||||
|
||||
self.uniform_buffer.did_modify_range(NSRange {
|
||||
location: self.uniform_buffer_offset,
|
||||
length: ALIGNED_UNIFORMS_SIZE,
|
||||
});
|
||||
|
||||
self.uniform_buffer_index = (self.uniform_buffer_index + 1) % MAX_FRAMES_IN_FLIGHT;
|
||||
}
|
||||
|
||||
pub fn draw(&mut self, layer: &MetalLayer) {
|
||||
self.semaphore.acquire();
|
||||
self.update_uniforms();
|
||||
let command_buffer = self.queue.new_command_buffer();
|
||||
let sem = self.semaphore.clone();
|
||||
let block = block::ConcreteBlock::new(move |_| {
|
||||
sem.release();
|
||||
})
|
||||
.copy();
|
||||
command_buffer.add_completed_handler(&block);
|
||||
let width = self.size.width as NSUInteger;
|
||||
let height = self.size.height as NSUInteger;
|
||||
let threads_per_thread_group = MTLSize::new(8, 8, 1);
|
||||
let thread_groups = MTLSize::new(
|
||||
(width + threads_per_thread_group.width - 1) / threads_per_thread_group.width,
|
||||
(height + threads_per_thread_group.height - 1) / threads_per_thread_group.height,
|
||||
1,
|
||||
);
|
||||
let compute_encoder = command_buffer.new_compute_command_encoder();
|
||||
compute_encoder.set_buffer(0, Some(&self.uniform_buffer), self.uniform_buffer_offset);
|
||||
compute_encoder.set_buffer(2, Some(&self.instance_buffer), 0);
|
||||
compute_encoder.set_buffer(3, Some(&self.scene.lights_buffer), 0);
|
||||
compute_encoder.set_acceleration_structure(4, Some(&self.instance_acceleration_structure));
|
||||
compute_encoder.set_intersection_function_table(5, Some(&self.intersection_function_table));
|
||||
compute_encoder.set_texture(0, Some(&self.random_texture));
|
||||
compute_encoder.set_texture(1, Some(&self.accumulation_targets[0]));
|
||||
compute_encoder.set_texture(2, Some(&self.accumulation_targets[1]));
|
||||
for geometry in &self.scene.geometries {
|
||||
for resource in geometry.get_resources() {
|
||||
compute_encoder.use_resource(&resource, MTLResourceUsage::Read);
|
||||
}
|
||||
}
|
||||
for primitive_acceleration_structure in &self.primitive_acceleration_structures {
|
||||
let resource: Resource = From::from(primitive_acceleration_structure.clone());
|
||||
compute_encoder.use_resource(&resource, MTLResourceUsage::Read);
|
||||
}
|
||||
compute_encoder.set_compute_pipeline_state(&self.raytracing_pipeline);
|
||||
compute_encoder.dispatch_thread_groups(thread_groups, threads_per_thread_group);
|
||||
compute_encoder.end_encoding();
|
||||
(self.accumulation_targets[0], self.accumulation_targets[1]) = (
|
||||
self.accumulation_targets[1].clone(),
|
||||
self.accumulation_targets[0].clone(),
|
||||
);
|
||||
if let Some(drawable) = layer.next_drawable() {
|
||||
let render_pass_descriptor = RenderPassDescriptor::new();
|
||||
let colour_attachment = render_pass_descriptor
|
||||
.color_attachments()
|
||||
.object_at(0)
|
||||
.unwrap();
|
||||
colour_attachment.set_texture(Some(drawable.texture()));
|
||||
colour_attachment.set_load_action(MTLLoadAction::Clear);
|
||||
colour_attachment.set_clear_color(MTLClearColor::new(0.0, 0.0, 0.0, 1.0));
|
||||
let render_encoder = command_buffer.new_render_command_encoder(render_pass_descriptor);
|
||||
render_encoder.set_render_pipeline_state(&self.copy_pipeline);
|
||||
render_encoder.set_fragment_texture(0, Some(&self.accumulation_targets[0]));
|
||||
render_encoder.draw_primitives(MTLPrimitiveType::Triangle, 0, 6);
|
||||
render_encoder.end_encoding();
|
||||
command_buffer.present_drawable(&drawable);
|
||||
}
|
||||
command_buffer.commit();
|
||||
}
|
||||
|
||||
fn new_acceleration_structure_with_descriptor(
|
||||
device: &Device,
|
||||
queue: &CommandQueue,
|
||||
descriptor: &AccelerationStructureDescriptorRef,
|
||||
) -> AccelerationStructure {
|
||||
let accel_sizes = device.acceleration_structure_sizes_with_descriptor(descriptor);
|
||||
let acceleration_structure =
|
||||
device.new_acceleration_structure_with_size(accel_sizes.acceleration_structure_size);
|
||||
let scratch_buffer = device.new_buffer(
|
||||
accel_sizes.build_scratch_buffer_size,
|
||||
MTLResourceOptions::StorageModePrivate,
|
||||
);
|
||||
let command_buffer = queue.new_command_buffer();
|
||||
let command_encoder = command_buffer.new_acceleration_structure_command_encoder();
|
||||
let compacted_size_buffer = device.new_buffer(
|
||||
size_of::<u32>() as NSUInteger,
|
||||
MTLResourceOptions::StorageModeShared,
|
||||
);
|
||||
command_encoder.build_acceleration_structure(
|
||||
&acceleration_structure,
|
||||
&descriptor,
|
||||
&scratch_buffer,
|
||||
0,
|
||||
);
|
||||
command_encoder.write_compacted_acceleration_structure_size(
|
||||
&acceleration_structure,
|
||||
&compacted_size_buffer,
|
||||
0,
|
||||
);
|
||||
command_encoder.end_encoding();
|
||||
command_buffer.commit();
|
||||
command_buffer.wait_until_completed();
|
||||
let compacted_size: *const u32 = unsafe { transmute(compacted_size_buffer.contents()) };
|
||||
let compacted_size = unsafe { *compacted_size } as NSUInteger;
|
||||
let compacted_acceleration_structure =
|
||||
device.new_acceleration_structure_with_size(compacted_size);
|
||||
let command_buffer = queue.new_command_buffer();
|
||||
let command_encoder = command_buffer.new_acceleration_structure_command_encoder();
|
||||
command_encoder.copy_and_compact_acceleration_structure(
|
||||
&acceleration_structure,
|
||||
&compacted_acceleration_structure,
|
||||
);
|
||||
command_encoder.end_encoding();
|
||||
command_buffer.commit();
|
||||
compacted_acceleration_structure
|
||||
}
|
||||
|
||||
fn new_specialised_function_with_name(
|
||||
library: &Library,
|
||||
resources_stride: u32,
|
||||
name: &str,
|
||||
) -> Function {
|
||||
let constants = FunctionConstantValues::new();
|
||||
let resources_stride = resources_stride * size_of::<u64>() as u32;
|
||||
constants.set_constant_value_at_index(
|
||||
&resources_stride as *const u32 as *const c_void,
|
||||
MTLDataType::UInt,
|
||||
0,
|
||||
);
|
||||
let v = true;
|
||||
constants.set_constant_value_at_index(
|
||||
&v as *const bool as *const c_void,
|
||||
MTLDataType::Bool,
|
||||
1,
|
||||
);
|
||||
constants.set_constant_value_at_index(
|
||||
&v as *const bool as *const c_void,
|
||||
MTLDataType::Bool,
|
||||
2,
|
||||
);
|
||||
library.get_function(name, Some(constants)).unwrap()
|
||||
}
|
||||
|
||||
fn new_compute_pipeline_state_with_function(
|
||||
device: &Device,
|
||||
function: &Function,
|
||||
linked_functions: &[&FunctionRef],
|
||||
) -> ComputePipelineState {
|
||||
let linked_functions = {
|
||||
let lf = LinkedFunctions::new();
|
||||
lf.set_functions(linked_functions);
|
||||
lf
|
||||
};
|
||||
let descriptor = ComputePipelineDescriptor::new();
|
||||
descriptor.set_compute_function(Some(function));
|
||||
descriptor.set_linked_functions(linked_functions.as_ref());
|
||||
descriptor.set_thread_group_size_is_multiple_of_thread_execution_width(true);
|
||||
device.new_compute_pipeline_state(&descriptor).unwrap()
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue