/// Bindings of WGSL `storage` globals that contain variable-sized arrays. /// /// In order to implement bounds checks and the `arrayLength` function for /// WGSL runtime-sized arrays, we pass the entry point a struct with a /// member for each global variable that contains such an array. That member /// is a `u32` holding the variable's total size in bytes---which is simply /// the size of the `Buffer` supplying that variable's contents for the /// draw call.
sized_bindings: Vec<naga::ResourceBinding>,
unsafefn create_texture(
&self,
desc: &crate::TextureDescriptor,
) -> DeviceResult<super::Texture> { use metal::foreign_types::ForeignType as _;
let mtl_format = self.shared.private_caps.map_format(desc.format);
objc::rc::autoreleasepool(|| { let descriptor = metal::TextureDescriptor::new();
let mtl_type = match desc.dimension {
wgt::TextureDimension::D1 => metal::MTLTextureType::D1,
wgt::TextureDimension::D2 => { if desc.sample_count > 1 {
descriptor.set_sample_count(desc.sample_count as u64);
metal::MTLTextureType::D2Multisample
} elseif desc.size.depth_or_array_layers > 1 {
descriptor.set_array_length(desc.size.depth_or_array_layers as u64);
metal::MTLTextureType::D2Array
} else {
metal::MTLTextureType::D2
}
}
wgt::TextureDimension::D3 => {
descriptor.set_depth(desc.size.depth_or_array_layers as u64);
metal::MTLTextureType::D3
}
};
descriptor.set_texture_type(mtl_type);
descriptor.set_width(desc.size.width as u64);
descriptor.set_height(desc.size.height as u64);
descriptor.set_mipmap_level_count(desc.mip_level_count as u64);
descriptor.set_pixel_format(mtl_format);
descriptor.set_usage(conv::map_texture_usage(desc.format, desc.usage));
descriptor.set_storage_mode(metal::MTLStorageMode::Private);
let raw = self.shared.device.lock().new_texture(&descriptor); if raw.as_ptr().is_null() { return Err(crate::DeviceError::OutOfMemory);
} iflet Some(label) = desc.label {
raw.set_label(label);
}
let aspects = crate::FormatAspects::new(texture.format, desc.range.aspect);
let raw_format = self
.shared
.private_caps
.map_view_format(desc.format, aspects);
let format_equal = raw_format == self.shared.private_caps.map_format(texture.format); let type_equal = raw_type == texture.raw_type; let range_full_resource =
desc.range
.is_full_resource(desc.format, texture.mip_levels, texture.array_layers);
let raw = if format_equal && type_equal && range_full_resource { // Some images are marked as framebuffer-only, and we can't create aliases of them. // Also helps working around Metal bugs with aliased array textures.
texture.raw.to_owned()
} else { let mip_level_count = desc
.range
.mip_level_count
.unwrap_or(texture.mip_levels - desc.range.base_mip_level); let array_layer_count = desc
.range
.array_layer_count
.unwrap_or(texture.array_layers - desc.range.base_array_layer);
objc::rc::autoreleasepool(|| { let raw = texture.raw.new_texture_view_from_slice(
raw_format,
raw_type,
metal::NSRange {
location: desc.range.base_mip_level as _,
length: mip_level_count as _,
},
metal::NSRange {
location: desc.range.base_array_layer as _,
length: array_layer_count as _,
},
); iflet Some(label) = desc.label {
raw.set_label(label);
}
raw
})
};
// First, place the push constants letmut total_push_constants = 0; for info in stage_data.iter_mut() { for pcr in desc.push_constant_ranges { if pcr.stages.contains(map_naga_stage(info.stage)) {
debug_assert_eq!(pcr.range.end % 4, 0);
info.pc_limit = (pcr.range.end / 4).max(info.pc_limit);
}
}
// round up the limits alignment to 4, so that it matches MTL compiler logic const LIMIT_MASK: u32 = 3; //TODO: figure out what and how exactly does the alignment. Clearly, it's not // straightforward, given that value of 2 stays non-aligned. if info.pc_limit > LIMIT_MASK {
info.pc_limit = (info.pc_limit + LIMIT_MASK) & !LIMIT_MASK;
}
// handle the push constant buffer assignment and shader overrides if info.pc_limit != 0 {
info.pc_buffer = Some(info.counters.buffers);
info.counters.buffers += 1;
}
// Second, place the described resources for (group_index, &bgl) in desc.bind_group_layouts.iter().enumerate() { // remember where the resources for this set start at each shader stage let base_resource_indices = stage_data.map_ref(|info| info.counters.clone());
for entry in bgl.entries.iter() { iflet wgt::BindingType::Buffer {
ty: wgt::BufferBindingType::Storage { .. },
..
} = entry.ty
{ for info in stage_data.iter_mut() { if entry.visibility.contains(map_naga_stage(info.stage)) {
info.need_sizes_buffer = true;
}
}
}
for info in stage_data.iter_mut() { if !entry.visibility.contains(map_naga_stage(info.stage)) { continue;
}
// Finally, make sure we fit the limits for info in stage_data.iter_mut() { if info.need_sizes_buffer || info.stage == naga::ShaderStage::Vertex { // Set aside space for the sizes_buffer, which is required // for variable-length buffers, or to support vertex pulling.
info.sizes_buffer = Some(info.counters.buffers);
info.counters.buffers += 1;
}
let total_counters = stage_data.map_ref(|info| info.counters.clone());
let per_stage_map = stage_data.map(|info| naga::back::msl::EntryPointResources {
push_constant_buffer: info
.pc_buffer
.map(|buffer_index| buffer_index as naga::back::msl::Slot),
sizes_buffer: info
.sizes_buffer
.map(|buffer_index| buffer_index as naga::back::msl::Slot),
resources: info.resources,
});
unsafefn create_bind_group(
&self,
desc: &crate::BindGroupDescriptor< super::BindGroupLayout, super::Buffer, super::Sampler, super::TextureView, super::AccelerationStructure,
>,
) -> DeviceResult<super::BindGroup> {
objc::rc::autoreleasepool(|| { letmut bg = super::BindGroup::default(); for (&stage, counter) insuper::NAGA_STAGES.iter().zip(bg.counters.iter_mut()) { let stage_bit = map_naga_stage(stage); letmut dynamic_offsets_count = 0u32; let layout_and_entry_iter = desc.entries.iter().map(|entry| { let layout = desc
.layout
.entries
.iter()
.find(|layout_entry| layout_entry.binding == entry.binding)
.expect("internal error: no layout entry found with binding slot");
(entry, layout)
}); for (entry, layout) in layout_and_entry_iter { // Bindless path if layout.count.is_some() { let count = entry.count;
let stages = conv::map_render_stages(layout.visibility); let uses = conv::map_resource_usage(&layout.ty);
// Create argument buffer for this array let buffer = self.shared.device.lock().new_buffer( 8 * count as u64,
metal::MTLResourceOptions::HazardTrackingModeUntracked
| metal::MTLResourceOptions::StorageModeShared,
);
let contents: &mut [metal::MTLResourceID] = unsafe {
std::slice::from_raw_parts_mut(buffer.contents().cast(), count as usize)
};
match layout.ty {
wgt::BindingType::Texture { .. }
| wgt::BindingType::StorageTexture { .. } => { let start = entry.resource_index as usize; let end = start + count as usize; let textures = &desc.textures[start..end];
for (idx, tex) in textures.iter().enumerate() {
contents[idx] = tex.view.raw.gpu_resource_id();
let use_info = bg
.resources_to_use
.entry(tex.view.as_raw().cast())
.or_default();
use_info.stages |= stages;
use_info.uses |= uses;
use_info.visible_in_compute |=
layout.visibility.contains(wgt::ShaderStages::COMPUTE);
}
}
wgt::BindingType::Sampler { .. } => { let start = entry.resource_index as usize; let end = start + count as usize; let samplers = &desc.samplers[start..end];
for (idx, &sampler) in samplers.iter().enumerate() {
contents[idx] = sampler.raw.gpu_resource_id(); // Samplers aren't resources like buffers and textures, so don't // need to be passed to useResource
}
}
_ => {
unimplemented!();
}
}
match shader { crate::ShaderInput::Naga(naga) => Ok(super::ShaderModule {
naga,
bounds_checks: desc.runtime_checks,
}), crate::ShaderInput::SpirV(_) => {
panic!("SPIRV_SHADER_PASSTHROUGH is not enabled for this backend")
}
}
}
let raw_triangle_fill_mode = match desc.primitive.polygon_mode {
wgt::PolygonMode::Fill => metal::MTLTriangleFillMode::Fill,
wgt::PolygonMode::Line => metal::MTLTriangleFillMode::Lines,
wgt::PolygonMode::Point => panic!( "{:?} is not enabled for this backend",
wgt::Features::POLYGON_MODE_POINT
),
};
let (primitive_class, raw_primitive_type) =
conv::map_primitive_topology(desc.primitive.topology);
// Vertex shader let (vs_lib, vs_info) = { letmut vertex_buffer_mappings = Vec::<naga::back::msl::VertexBufferMapping>::new(); for (i, vbl) in desc.vertex_buffers.iter().enumerate() { letmut attributes = Vec::<naga::back::msl::AttributeMapping>::new(); for attribute in vbl.attributes.iter() {
attributes.push(naga::back::msl::AttributeMapping {
shader_location: attribute.shader_location,
offset: attribute.offset as u32,
format: convert_vertex_format_to_naga(attribute.format),
});
}
let info = super::PipelineStageInfo {
push_constants: desc.layout.push_constants_infos.fs,
sizes_slot: desc.layout.per_stage_map.fs.sizes_buffer,
sized_bindings: fs.sized_bindings,
vertex_buffer_mappings: vec![],
};
(Some(fs.library), Some(info))
}
None => { // TODO: This is a workaround for what appears to be a Metal validation bug // A pixel format is required even though no attachments are provided if desc.color_targets.is_empty() && desc.depth_stencil.is_none() {
descriptor
.set_depth_attachment_pixel_format(metal::MTLPixelFormat::Depth32Float);
}
(None, None)
}
};
for (i, ct) in desc.color_targets.iter().enumerate() { let at_descriptor = descriptor.color_attachments().object_at(i as u64).unwrap(); let ct = iflet Some(color_target) = ct.as_ref() {
color_target
} else {
at_descriptor.set_pixel_format(metal::MTLPixelFormat::Invalid); continue;
};
let raw_format = self.shared.private_caps.map_format(ct.format);
at_descriptor.set_pixel_format(raw_format);
at_descriptor.set_write_mask(conv::map_color_write(ct.write_mask));
iflet Some(ref blend) = ct.blend {
at_descriptor.set_blending_enabled(true); let (color_op, color_src, color_dst) = conv::map_blend_component(&blend.color); let (alpha_op, alpha_src, alpha_dst) = conv::map_blend_component(&blend.alpha);
let depth_stencil = match desc.depth_stencil {
Some(ref ds) => { let raw_format = self.shared.private_caps.map_format(ds.format); let aspects = crate::FormatAspects::from(ds.format); if aspects.contains(crate::FormatAspects::DEPTH) {
descriptor.set_depth_attachment_pixel_format(raw_format);
} if aspects.contains(crate::FormatAspects::STENCIL) {
descriptor.set_stencil_attachment_pixel_format(raw_format);
}
let ds_descriptor = create_depth_stencil_desc(ds); let raw = self
.shared
.device
.lock()
.new_depth_stencil_state(&ds_descriptor);
Some((raw, ds.bias))
}
None => None,
};
if desc.layout.total_counters.vs.buffers + (desc.vertex_buffers.len() as u32)
> self.shared.private_caps.max_vertex_buffers
{ let msg = format!( "pipeline needs too many buffers in the vertex stage: {} vertex and {} layout",
desc.vertex_buffers.len(),
desc.layout.total_counters.vs.buffers
); return Err(crate::PipelineError::Linkage(
wgt::ShaderStages::VERTEX,
msg,
));
}
if !desc.vertex_buffers.is_empty() { let vertex_descriptor = metal::VertexDescriptor::new(); for (i, vb) in desc.vertex_buffers.iter().enumerate() { let buffer_index = self.shared.private_caps.max_vertex_buffers as u64 - 1 - i as u64; let buffer_desc = vertex_descriptor.layouts().object_at(buffer_index).unwrap();
// Metal expects the stride to be the actual size of the attributes. // The semantics of array_stride == 0 can be achieved by setting // the step function to constant and rate to 0. if vb.array_stride == 0 { let stride = vb
.attributes
.iter()
.map(|attribute| attribute.offset + attribute.format.size())
.max()
.unwrap_or(0);
buffer_desc.set_stride(wgt::math::align_to(stride, 4));
buffer_desc.set_step_function(metal::MTLVertexStepFunction::Constant);
buffer_desc.set_step_rate(0);
} else {
buffer_desc.set_stride(vb.array_stride);
buffer_desc.set_step_function(conv::map_step_mode(vb.step_mode));
}
for at in vb.attributes { let attribute_desc = vertex_descriptor
.attributes()
.object_at(at.shader_location as u64)
.unwrap();
attribute_desc.set_format(conv::map_vertex_format(at.format));
attribute_desc.set_buffer_index(buffer_index);
attribute_desc.set_offset(at.offset);
}
}
descriptor.set_vertex_descriptor(Some(vertex_descriptor));
}
if desc.multisample.count != 1 { //TODO: handle sample mask
descriptor.set_sample_count(desc.multisample.count as u64);
descriptor
.set_alpha_to_coverage_enabled(desc.multisample.alpha_to_coverage_enabled); //descriptor.set_alpha_to_one_enabled(desc.multisample.alpha_to_one_enabled);
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.