#[allow(
deprecated,
reason = "MTLFeatureSet` is superseded by `MTLGpuFamily`.
However, `MTLGpuFamily` is only supported starting MacOS 10.15, whereas our minimum target is MacOS 10.13,
See https://github.com/gpuweb/gpuweb/issues/1069 for minimum spec.
TODO: Eventually all deprecated features should be abstracted and use new api when available."
)] mod adapter; mod command; mod conv; mod device; mod library_from_metallib; mod surface; mod time;
use alloc::{
string::{String, ToString as _},
sync::Arc,
vec::Vec,
}; use core::{fmt, iter, ops, ptr::NonNull, sync::atomic};
use bitflags::bitflags; use hashbrown::HashMap; use naga::FastHashMap; use objc2::{
available,
rc::{autoreleasepool, Retained},
runtime::ProtocolObject,
}; use objc2_foundation::ns_string; use objc2_metal::{
MTLAccelerationStructure, MTLAccelerationStructureCommandEncoder, MTLArgumentBuffersTier,
MTLBlitCommandEncoder, MTLBuffer, MTLCommandBuffer, MTLCommandBufferStatus, MTLCommandQueue,
MTLComputeCommandEncoder, MTLComputePipelineState, MTLCounterSampleBuffer, MTLCullMode,
MTLDepthClipMode, MTLDepthStencilState, MTLDevice, MTLDrawable, MTLIndexType,
MTLLanguageVersion, MTLLibrary, MTLPrimitiveType, MTLReadWriteTextureTier,
MTLRenderCommandEncoder, MTLRenderPipelineState, MTLRenderStages, MTLResource,
MTLResourceUsage, MTLSamplerState, MTLSharedEvent, MTLSize, MTLTexture, MTLTextureType,
MTLTriangleFillMode, MTLWinding,
}; use objc2_quartz_core::CAMetalLayer; use parking_lot::{Mutex, RwLock};
#[derive(Clone, Debug)] pubstruct Api;
type ResourceIndex = u32;
implcrate::Api for Api { const VARIANT: wgt::Backend = wgt::Backend::Metal;
type Instance = Instance; type Surface = Surface; type Adapter = Adapter; type Device = Device;
type Queue = Queue; type CommandEncoder = CommandEncoder; type CommandBuffer = CommandBuffer;
type Buffer = Buffer; type Texture = Texture; type SurfaceTexture = SurfaceTexture; type TextureView = TextureView; type Sampler = Sampler; type QuerySet = QuerySet; type Fence = Fence;
type BindGroupLayout = BindGroupLayout; type BindGroup = BindGroup; type PipelineLayout = PipelineLayout; type ShaderModule = ShaderModule; type RenderPipeline = RenderPipeline; type ComputePipeline = ComputePipeline; type PipelineCache = PipelineCache;
type AccelerationStructure = AccelerationStructure;
}
/// Provides availability information about Mac APIs. /// /// This may include Metal features that depend only on software support. /// Features with varying hardware support are in [`CapabilitiesQuery`] /// /// When feature detection is only needed once, it may also be done inline. struct OsFeatures;
unsafefn init(_desc: &crate::InstanceDescriptor<'_>) -> Result<Self, crate::InstanceError> {
profiling::scope!("Init Metal Backend"); // We do not enable metal validation based on the validation flags as it affects the entire // process. Instead, we enable the validation inside the test harness itself in tests/src/native.rs.
Ok(Instance {})
}
// SAFETY: The layer is an initialized instance of `CAMetalLayer`, and // we transfer the retain count to `Retained` using `into_raw`. let layer = unsafe {
Retained::from_raw(layer.into_raw().cast::<CAMetalLayer>().as_ptr()).unwrap()
};
#[derive(Clone, Debug)] struct PrivateDisabilities { /// Near depth is not respected properly on some Intel GPUs.
broken_viewport_near_depth: bool, /// Multi-target clears don't appear to work properly on Intel GPUs. #[allow(dead_code)]
broken_layered_clear_image: bool,
}
fn expose(device: Retained<ProtocolObject<dyn MTLDevice>>) -> crate::ExposedAdapter<Api> {
autoreleasepool(|_| { let name = device.name().to_string(); let capabilities_query = CapabilitiesQuery::new(&device); let shared = AdapterShared::new(device, &capabilities_query); let features = capabilities_query.features(); let capabilities = capabilities_query.capabilities(); crate::ExposedAdapter {
info: wgt::AdapterInfo {
name, // These are hardcoded based on typical values for Metal devices // // See <https://github.com/gpuweb/gpuweb/blob/main/proposals/subgroups.md#adapter-info> // for more information.
subgroup_min_size: 4,
subgroup_max_size: 64,
transient_saves_memory: shared.private_caps.supports_memoryless_storage,
..wgt::AdapterInfo::new(shared.private_caps.device_type(), wgt::Backend::Metal)
},
features,
capabilities,
adapter: Adapter::new(Arc::new(shared)),
}
})
}
}
#[derive(Debug)] pubstruct QueueShared {
raw: Retained<ProtocolObject<dyn MTLCommandQueue>>, // Tracks command buffers created via `CommandEncoder::begin_encoding` that // have not yet been submitted or discarded. Used to proactively fail // before hitting Metal's `maxCommandBufferCount`. // // (In a few places we call `.commandBuffer{,WithUnretainedReferences}` directly // to create command buffers for internal purposes. In those cases we always // commit the buffer immediately, so we don't adjust the counter for them.)
command_buffer_created_not_submitted: atomic::AtomicUsize,
}
let raw = match command_buffers.last() {
Some(&cmd_buf) => cmd_buf.raw.clone(),
None => { // We do not bother adjusting `command_buffer_created_not_submitted` // because we immediately commit this buffer. self.shared
.raw
.commandBufferWithUnretainedReferences()
.unwrap()
}
};
raw.setLabel(Some(ns_string!("(wgpu internal) Signal"))); unsafe { raw.addCompletedHandler(block2::RcBlock::as_ptr(&block)) };
iflet Some(shared_event) = &signal_fence.shared_event {
raw.encodeSignalEvent_value(shared_event.as_ref(), signal_value);
} // only return an extra one if it's extra match command_buffers.last() {
Some(_) => None,
None => Some(raw),
}
};
for cmd_buffer in command_buffers {
cmd_buffer.raw.commit(); // One command buffer per `end_encoding` call moves from the // "created but not yet submitted" bucket into the submitted // set, so update the counter. let previous = self
.shared
.command_buffer_created_not_submitted
.fetch_sub(1, atomic::Ordering::AcqRel);
debug_assert!(previous > 0);
}
iflet Some(raw) = extra_command_buffer {
raw.commit();
}
});
Ok(())
} unsafefn present(
&self,
_surface: &Surface,
texture: SurfaceTexture,
) -> Result<(), crate::SurfaceError> {
autoreleasepool(|_| { // We do not bother adjusting `command_buffer_created_not_submitted` // because we immediately commit this buffer. let command_buffer = self.shared.raw.commandBuffer().unwrap();
command_buffer.setLabel(Some(ns_string!("(wgpu internal) Present")));
type MultiStageResourceCounters = MultiStageData<ResourceData<ResourceIndex>>; type MultiStageResources = MultiStageData<naga::back::msl::EntryPointResources>;
/// The buffer's size, if it is a [`Storage`] binding. Otherwise `None`. /// /// Buffers with the [`wgt::BufferBindingType::Storage`] binding type can /// hold WGSL runtime-sized arrays. When one does, we must pass its size to /// shader entry points to implement bounds checks and WGSL's `arrayLength` /// function. See `device::CompiledShader::sized_bindings` for details. /// /// [`Storage`]: wgt::BufferBindingType::Storage
binding_size: Option<wgt::BufferSize>,
/// The buffer argument table index at which we pass runtime-sized arrays' buffer sizes. /// /// See `device::CompiledShader::sized_bindings` for more details.
sizes_slot: Option<naga::back::msl::Slot>,
/// Bindings of all WGSL `storage` globals that contain runtime-sized arrays. /// /// See `device::CompiledShader::sized_bindings` for more details.
sized_bindings: Vec<naga::ResourceBinding>,
/// Info on all bound vertex buffers.
vertex_buffer_mappings: Vec<naga::back::msl::VertexBufferMapping>,
/// The workgroup size for compute, task or mesh stages
raw_wg_size: MTLSize,
/// The workgroup memory sizes for compute task or mesh stages
work_group_memory_sizes: Vec<u32>,
}
// TODO(madsmtm): Derive this when a release with // https://github.com/madsmtm/objc2/issues/804 is available (likely 0.4). impl Default for PipelineStageInfo { fn default() -> Self { Self {
library: Default::default(),
immediates: Default::default(),
sizes_slot: Default::default(),
sized_bindings: Default::default(),
vertex_buffer_mappings: Default::default(),
raw_wg_size: MTLSize {
width: 0,
height: 0,
depth: 0,
},
work_group_memory_sizes: Default::default(),
}
}
}
/// Sizes of currently bound [`wgt::BufferBindingType::Storage`] buffers. /// /// Specifically: /// /// - The keys are [`ResourceBinding`] values (that is, the WGSL `@group` /// and `@binding` attributes) for `var<storage>` global variables in the /// current module that contain runtime-sized arrays. /// /// - The values are the actual sizes of the buffers currently bound to /// provide those globals' contents, which are needed to implement bounds /// checks and the WGSL `arrayLength` function. /// /// For each stage `S` in `stage_infos`, we consult this to find the sizes /// of the buffers listed in `stage_infos.S.sized_bindings`, which we must /// pass to the entry point. /// /// See `device::CompiledShader::sized_bindings` for more details. /// /// [`ResourceBinding`]: naga::ResourceBinding
storage_buffer_length_map: FastHashMap<naga::ResourceBinding, wgt::BufferSize>,
implcrate::DynAccelerationStructure for AccelerationStructure {} unsafeimpl Send for AccelerationStructure {} unsafeimpl Sync for AccelerationStructure {}
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.