hal/metal: instance,adapter, and surface

This commit is contained in:
Dzmitry Malyshau 2021-06-09 00:55:49 -04:00
parent 6d229847be
commit 3475d839f9
9 changed files with 1478 additions and 54 deletions

3
Cargo.lock generated
View File

@ -1915,8 +1915,11 @@ name = "wgpu-hal"
version = "0.1.0"
dependencies = [
"bitflags",
"log",
"metal",
"naga",
"objc",
"parking_lot",
"raw-window-handle",
"thiserror",
"wgpu-types",

View File

@ -12,16 +12,20 @@ license = "MIT OR Apache-2.0"
[lib]
[features]
metal = ["mtl"]
default = ["metal"]
metal = ["mtl", "objc", "parking_lot"]
[dependencies]
bitflags = "1.0"
log = "0.4"
parking_lot = { version = "0.11", optional = true }
raw-window-handle = "0.3"
thiserror = "1"
wgt = { package = "wgpu-types", path = "../wgpu-types" }
# backends
mtl = { package = "metal", version = "0.22", optional = true }
objc = { version = "0.2.5", optional = true }
[dependencies.naga]
git = "https://github.com/gfx-rs/naga"

View File

@ -0,0 +1,991 @@
use mtl::{MTLFeatureSet, MTLGPUFamily, MTLLanguageVersion};
use objc::{class, msg_send, sel, sel_impl};
use std::{sync::Arc, thread};
unsafe impl Send for super::Adapter {}
unsafe impl Sync for super::Adapter {}
impl super::Adapter {
pub(super) fn new(shared: Arc<super::AdapterShared>) -> Self {
Self { shared }
}
}
impl crate::Adapter<super::Api> for super::Adapter {
unsafe fn open(
&self,
features: wgt::Features,
) -> Result<crate::OpenDevice<super::Api>, crate::DeviceError> {
let raw_device = self.shared.device.lock();
Ok(crate::OpenDevice {
device: super::Device {
shared: Arc::clone(&self.shared),
features,
},
queue: super::Queue {
raw: raw_device.new_command_queue(),
},
})
}
unsafe fn close(&self, _device: super::Device) {}
unsafe fn texture_format_capabilities(
&self,
format: wgt::TextureFormat,
) -> crate::TextureFormatCapability {
use crate::TextureFormatCapability as Tfc;
use wgt::TextureFormat as Tf;
let pc = &self.shared.private_caps;
// Affected formats documented at:
// https://developer.apple.com/documentation/metal/mtlreadwritetexturetier/mtlreadwritetexturetier1?language=objc
// https://developer.apple.com/documentation/metal/mtlreadwritetexturetier/mtlreadwritetexturetier2?language=objc
let (read_write_tier1_if, read_write_tier2_if) = match pc.read_write_texture_tier {
mtl::MTLReadWriteTextureTier::TierNone => (Tfc::empty(), Tfc::empty()),
mtl::MTLReadWriteTextureTier::Tier1 => (Tfc::STORAGE_READ_WRITE, Tfc::empty()),
mtl::MTLReadWriteTextureTier::Tier2 => {
(Tfc::STORAGE_READ_WRITE, Tfc::STORAGE_READ_WRITE)
}
};
let extra = match format {
Tf::R8Unorm => {
read_write_tier2_if
| Tfc::SAMPLED_LINEAR
| Tfc::STORAGE
| Tfc::COLOR_ATTACHMENT
| Tfc::COLOR_ATTACHMENT_BLEND
}
Tf::R8Snorm => {
Tfc::SAMPLED_LINEAR
| Tfc::STORAGE
| Tfc::COLOR_ATTACHMENT
| Tfc::COLOR_ATTACHMENT_BLEND
}
Tf::R8Uint | Tf::R8Sint | Tf::R16Uint | Tf::R16Sint => {
read_write_tier2_if | Tfc::STORAGE | Tfc::COLOR_ATTACHMENT
}
Tf::R16Float => {
read_write_tier2_if
| Tfc::STORAGE
| Tfc::COLOR_ATTACHMENT
| Tfc::COLOR_ATTACHMENT_BLEND
}
Tf::Rg8Unorm | Tf::Rg8Snorm => {
Tfc::SAMPLED_LINEAR
| Tfc::STORAGE
| Tfc::COLOR_ATTACHMENT
| Tfc::COLOR_ATTACHMENT_BLEND
}
Tf::Rg8Uint | Tf::Rg8Sint => Tfc::COLOR_ATTACHMENT,
Tf::R32Uint | Tf::R32Sint => {
if pc.format_r32_all {
read_write_tier1_if | Tfc::STORAGE | Tfc::COLOR_ATTACHMENT
} else {
Tfc::COLOR_ATTACHMENT
}
}
Tf::R32Float => {
let mut flags = Tfc::COLOR_ATTACHMENT | Tfc::COLOR_ATTACHMENT_BLEND;
if pc.format_r32float_all {
flags |= read_write_tier1_if | Tfc::STORAGE | Tfc::SAMPLED_LINEAR;
} else if pc.format_r32float_no_filter {
flags |= Tfc::SAMPLED_LINEAR;
}
flags
}
Tf::Rg16Uint | Tf::Rg16Sint => {
read_write_tier2_if | Tfc::STORAGE | Tfc::COLOR_ATTACHMENT
}
Tf::Rg16Float => {
read_write_tier2_if
| Tfc::SAMPLED_LINEAR
| Tfc::STORAGE
| Tfc::COLOR_ATTACHMENT
| Tfc::COLOR_ATTACHMENT_BLEND
}
Tf::Rgba8Unorm => {
read_write_tier2_if
| Tfc::SAMPLED_LINEAR
| Tfc::STORAGE
| Tfc::COLOR_ATTACHMENT
| Tfc::COLOR_ATTACHMENT_BLEND
}
Tf::Rgba8UnormSrgb | Tf::Bgra8UnormSrgb => {
let mut flags =
Tfc::SAMPLED_LINEAR | Tfc::COLOR_ATTACHMENT | Tfc::COLOR_ATTACHMENT_BLEND;
flags.set(Tfc::STORAGE, pc.format_rgba8_srgb_all);
flags
}
Tf::Rgba8Snorm | Tf::Bgra8Unorm => {
Tfc::SAMPLED_LINEAR
| Tfc::STORAGE
| Tfc::COLOR_ATTACHMENT
| Tfc::COLOR_ATTACHMENT_BLEND
}
Tf::Rgba8Uint | Tf::Rgba8Sint => {
read_write_tier2_if | Tfc::STORAGE | Tfc::COLOR_ATTACHMENT
}
Tf::Rgb10a2Unorm => {
let mut flags =
Tfc::SAMPLED_LINEAR | Tfc::COLOR_ATTACHMENT | Tfc::COLOR_ATTACHMENT_BLEND;
flags.set(Tfc::STORAGE, pc.format_rgb10a2_unorm_all);
flags
}
Tf::Rg11b10Float => {
let mut flags =
Tfc::SAMPLED_LINEAR | Tfc::COLOR_ATTACHMENT | Tfc::COLOR_ATTACHMENT_BLEND;
flags.set(Tfc::STORAGE, pc.format_rg11b10_all);
flags
}
Tf::Rg32Uint | Tf::Rg32Sint => Tfc::COLOR_ATTACHMENT | Tfc::STORAGE,
Tf::Rg32Float => {
let mut flags = Tfc::COLOR_ATTACHMENT | Tfc::COLOR_ATTACHMENT_BLEND;
if pc.format_rg32float_all {
flags |= Tfc::STORAGE | Tfc::SAMPLED_LINEAR;
} else if pc.format_rg32float_color_blend {
flags |= Tfc::SAMPLED_LINEAR;
}
flags
}
Tf::Rgba16Uint | Tf::Rgba16Sint => {
read_write_tier2_if | Tfc::STORAGE | Tfc::COLOR_ATTACHMENT
}
Tf::Rgba16Float => {
read_write_tier2_if
| Tfc::SAMPLED_LINEAR
| Tfc::STORAGE
| Tfc::COLOR_ATTACHMENT
| Tfc::COLOR_ATTACHMENT_BLEND
}
Tf::Rgba32Uint | Tf::Rgba32Sint => {
if pc.format_rgba32int_color_write {
read_write_tier2_if | Tfc::COLOR_ATTACHMENT | Tfc::STORAGE
} else {
Tfc::COLOR_ATTACHMENT
}
}
Tf::Rgba32Float => {
if pc.format_rgba32float_all {
read_write_tier2_if
| Tfc::SAMPLED_LINEAR
| Tfc::STORAGE
| Tfc::COLOR_ATTACHMENT
| Tfc::COLOR_ATTACHMENT_BLEND
} else if pc.format_rgba32float_color_write {
read_write_tier2_if | Tfc::COLOR_ATTACHMENT | Tfc::STORAGE
} else {
Tfc::COLOR_ATTACHMENT
}
}
Tf::Depth32Float => {
if pc.format_depth32float_filter {
Tfc::DEPTH_STENCIL_ATTACHMENT | Tfc::SAMPLED_LINEAR
} else {
Tfc::DEPTH_STENCIL_ATTACHMENT
}
}
Tf::Depth24Plus | Tf::Depth24PlusStencil8 => {
Tfc::DEPTH_STENCIL_ATTACHMENT | Tfc::SAMPLED_LINEAR
}
Tf::Bc1RgbaUnorm
| Tf::Bc1RgbaUnormSrgb
| Tf::Bc2RgbaUnorm
| Tf::Bc2RgbaUnormSrgb
| Tf::Bc3RgbaUnorm
| Tf::Bc3RgbaUnormSrgb
| Tf::Bc4RUnorm
| Tf::Bc4RSnorm
| Tf::Bc5RgUnorm
| Tf::Bc6hRgbSfloat
| Tf::Bc7RgbaUnorm
| Tf::Bc7RgbaUnormSrgb => {
if pc.format_bc {
Tfc::SAMPLED_LINEAR
} else {
Tfc::empty()
}
}
Tf::Etc2RgbUnorm
| Tf::Etc2RgbUnormSrgb
| Tf::Etc2RgbA1Unorm
| Tf::Etc2RgbA1UnormSrgb
| Tf::EacRUnorm
| Tf::EacRSnorm
| Tf::EtcRgUnorm
| Tf::EtcRgSnorm => {
if pc.format_eac_etc {
Tfc::SAMPLED_LINEAR
} else {
Tfc::empty()
}
}
Tf::Astc4x4RgbaUnorm
| Tf::Astc4x4RgbaUnormSrgb
| Tf::Astc5x4RgbaUnorm
| Tf::Astc5x4RgbaUnormSrgb
| Tf::Astc5x5RgbaUnorm
| Tf::Astc5x5RgbaUnormSrgb
| Tf::Astc6x5RgbaUnorm
| Tf::Astc6x5RgbaUnormSrgb
| Tf::Astc6x6RgbaUnorm
| Tf::Astc6x6RgbaUnormSrgb
| Tf::Astc8x5RgbaUnorm
| Tf::Astc8x5RgbaUnormSrgb
| Tf::Astc8x6RgbaUnorm
| Tf::Astc8x6RgbaUnormSrgb
| Tf::Astc10x5RgbaUnorm
| Tf::Astc10x5RgbaUnormSrgb
| Tf::Astc10x6RgbaUnorm
| Tf::Astc10x6RgbaUnormSrgb
| Tf::Astc8x8RgbaUnorm
| Tf::Astc8x8RgbaUnormSrgb
| Tf::Astc10x8RgbaUnorm
| Tf::Astc10x8RgbaUnormSrgb
| Tf::Astc10x10RgbaUnorm
| Tf::Astc10x10RgbaUnormSrgb
| Tf::Astc12x10RgbaUnorm
| Tf::Astc12x10RgbaUnormSrgb
| Tf::Astc12x12RgbaUnorm
| Tf::Astc12x12RgbaUnormSrgb => {
if pc.format_astc {
Tfc::SAMPLED_LINEAR
} else {
Tfc::empty()
}
}
};
Tfc::COPY_SRC | Tfc::COPY_DST | Tfc::SAMPLED | extra
}
unsafe fn surface_capabilities(
&self,
surface: &super::Surface,
) -> Option<crate::SurfaceCapabilities> {
let current_extent = if surface.main_thread_id == thread::current().id() {
Some(surface.dimensions())
} else {
log::warn!("Unable to get the current view dimensions on a non-main thread");
None
};
let pc = &self.shared.private_caps;
Some(crate::SurfaceCapabilities {
formats: vec![
wgt::TextureFormat::Bgra8Unorm,
wgt::TextureFormat::Bgra8UnormSrgb,
wgt::TextureFormat::Rgba16Float,
],
//Note: this is hardcoded in `CAMetalLayer` documentation
swap_chain_sizes: if pc.can_set_maximum_drawables_count {
2..=3
} else {
// 3 is the default in `CAMetalLayer` documentation
// iOS 10.3 was tested to use 3 on iphone5s
3..=3
},
present_modes: if pc.can_set_display_sync {
vec![wgt::PresentMode::Fifo, wgt::PresentMode::Immediate]
} else {
vec![wgt::PresentMode::Fifo]
},
composite_alpha_modes: vec![
crate::CompositeAlphaMode::Opaque,
crate::CompositeAlphaMode::Alpha,
],
current_extent,
extents: wgt::Extent3d {
width: 4,
height: 4,
depth_or_array_layers: 1,
}..=wgt::Extent3d {
width: 4096,
height: 4096,
depth_or_array_layers: 1,
},
usage: crate::TextureUse::COLOR_TARGET, //TODO: expose more
})
}
}
const RESOURCE_HEAP_SUPPORT: &[MTLFeatureSet] = &[
MTLFeatureSet::iOS_GPUFamily1_v3,
MTLFeatureSet::iOS_GPUFamily2_v3,
MTLFeatureSet::iOS_GPUFamily3_v2,
MTLFeatureSet::iOS_GPUFamily4_v1,
MTLFeatureSet::iOS_GPUFamily5_v1,
MTLFeatureSet::tvOS_GPUFamily1_v2,
MTLFeatureSet::tvOS_GPUFamily2_v1,
MTLFeatureSet::macOS_GPUFamily1_v3,
MTLFeatureSet::macOS_GPUFamily2_v1,
];
const ARGUMENT_BUFFER_SUPPORT: &[MTLFeatureSet] = &[
MTLFeatureSet::iOS_GPUFamily1_v4,
MTLFeatureSet::iOS_GPUFamily2_v4,
MTLFeatureSet::iOS_GPUFamily3_v3,
MTLFeatureSet::iOS_GPUFamily4_v1,
MTLFeatureSet::iOS_GPUFamily5_v1,
MTLFeatureSet::tvOS_GPUFamily1_v3,
MTLFeatureSet::macOS_GPUFamily1_v3,
MTLFeatureSet::macOS_GPUFamily2_v1,
];
const MUTABLE_COMPARISON_SAMPLER_SUPPORT: &[MTLFeatureSet] = &[
MTLFeatureSet::iOS_GPUFamily3_v1,
MTLFeatureSet::iOS_GPUFamily4_v1,
MTLFeatureSet::iOS_GPUFamily5_v1,
MTLFeatureSet::macOS_GPUFamily1_v1,
MTLFeatureSet::macOS_GPUFamily2_v1,
];
const SAMPLER_CLAMP_TO_BORDER_SUPPORT: &[MTLFeatureSet] = &[
MTLFeatureSet::macOS_GPUFamily1_v2,
MTLFeatureSet::macOS_GPUFamily2_v1,
];
const ASTC_PIXEL_FORMAT_FEATURES: &[MTLFeatureSet] = &[
MTLFeatureSet::iOS_GPUFamily2_v1,
MTLFeatureSet::iOS_GPUFamily3_v1,
MTLFeatureSet::iOS_GPUFamily4_v1,
MTLFeatureSet::iOS_GPUFamily5_v1,
MTLFeatureSet::tvOS_GPUFamily1_v1,
MTLFeatureSet::tvOS_GPUFamily2_v1,
];
const ANY8_UNORM_SRGB_ALL: &[MTLFeatureSet] = &[
MTLFeatureSet::iOS_GPUFamily2_v3,
MTLFeatureSet::iOS_GPUFamily3_v1,
MTLFeatureSet::iOS_GPUFamily4_v1,
MTLFeatureSet::iOS_GPUFamily5_v1,
MTLFeatureSet::tvOS_GPUFamily1_v2,
MTLFeatureSet::tvOS_GPUFamily2_v1,
];
const ANY8_SNORM_RESOLVE: &[MTLFeatureSet] = &[
MTLFeatureSet::iOS_GPUFamily2_v1,
MTLFeatureSet::iOS_GPUFamily3_v1,
MTLFeatureSet::iOS_GPUFamily4_v1,
MTLFeatureSet::iOS_GPUFamily5_v1,
MTLFeatureSet::tvOS_GPUFamily1_v1,
MTLFeatureSet::tvOS_GPUFamily2_v1,
MTLFeatureSet::macOS_GPUFamily1_v1,
MTLFeatureSet::macOS_GPUFamily2_v1,
];
const RGBA8_SRGB: &[MTLFeatureSet] = &[
MTLFeatureSet::iOS_GPUFamily2_v3,
MTLFeatureSet::iOS_GPUFamily3_v1,
MTLFeatureSet::iOS_GPUFamily4_v1,
MTLFeatureSet::iOS_GPUFamily5_v1,
MTLFeatureSet::tvOS_GPUFamily1_v2,
MTLFeatureSet::tvOS_GPUFamily2_v1,
];
const RGB10A2UNORM_ALL: &[MTLFeatureSet] = &[
MTLFeatureSet::iOS_GPUFamily3_v1,
MTLFeatureSet::iOS_GPUFamily4_v1,
MTLFeatureSet::iOS_GPUFamily5_v1,
MTLFeatureSet::tvOS_GPUFamily2_v1,
MTLFeatureSet::macOS_GPUFamily1_v1,
MTLFeatureSet::macOS_GPUFamily2_v1,
];
const RGB10A2UINT_COLOR_WRITE: &[MTLFeatureSet] = &[
MTLFeatureSet::iOS_GPUFamily3_v1,
MTLFeatureSet::iOS_GPUFamily4_v1,
MTLFeatureSet::iOS_GPUFamily5_v1,
MTLFeatureSet::tvOS_GPUFamily2_v1,
MTLFeatureSet::macOS_GPUFamily1_v1,
MTLFeatureSet::macOS_GPUFamily2_v1,
];
const RG11B10FLOAT_ALL: &[MTLFeatureSet] = &[
MTLFeatureSet::iOS_GPUFamily3_v1,
MTLFeatureSet::iOS_GPUFamily4_v1,
MTLFeatureSet::iOS_GPUFamily5_v1,
MTLFeatureSet::tvOS_GPUFamily2_v1,
MTLFeatureSet::macOS_GPUFamily1_v1,
MTLFeatureSet::macOS_GPUFamily2_v1,
];
const RGB9E5FLOAT_ALL: &[MTLFeatureSet] = &[
MTLFeatureSet::iOS_GPUFamily3_v1,
MTLFeatureSet::iOS_GPUFamily4_v1,
MTLFeatureSet::iOS_GPUFamily5_v1,
MTLFeatureSet::tvOS_GPUFamily2_v1,
];
const BGR10A2_ALL: &[MTLFeatureSet] = &[
MTLFeatureSet::iOS_GPUFamily1_v4,
MTLFeatureSet::iOS_GPUFamily2_v4,
MTLFeatureSet::iOS_GPUFamily3_v3,
MTLFeatureSet::iOS_GPUFamily4_v1,
MTLFeatureSet::iOS_GPUFamily5_v1,
MTLFeatureSet::tvOS_GPUFamily1_v3,
MTLFeatureSet::tvOS_GPUFamily2_v1,
MTLFeatureSet::macOS_GPUFamily1_v3,
MTLFeatureSet::macOS_GPUFamily2_v1,
];
const BASE_INSTANCE_SUPPORT: &[MTLFeatureSet] = &[
MTLFeatureSet::iOS_GPUFamily3_v1,
MTLFeatureSet::iOS_GPUFamily4_v1,
MTLFeatureSet::iOS_GPUFamily5_v1,
MTLFeatureSet::tvOS_GPUFamily2_v1,
MTLFeatureSet::macOS_GPUFamily1_v1,
MTLFeatureSet::macOS_GPUFamily2_v1,
];
const BASE_VERTEX_INSTANCE_SUPPORT: &[MTLFeatureSet] = &[
MTLFeatureSet::iOS_GPUFamily3_v1,
MTLFeatureSet::iOS_GPUFamily4_v1,
MTLFeatureSet::iOS_GPUFamily5_v1,
MTLFeatureSet::tvOS_GPUFamily2_v1,
MTLFeatureSet::macOS_GPUFamily1_v1,
MTLFeatureSet::macOS_GPUFamily2_v1,
];
const TEXTURE_CUBE_ARRAY_SUPPORT: &[MTLFeatureSet] = &[
MTLFeatureSet::iOS_GPUFamily4_v1,
MTLFeatureSet::iOS_GPUFamily5_v1,
MTLFeatureSet::tvOS_GPUFamily1_v2,
MTLFeatureSet::tvOS_GPUFamily2_v1,
MTLFeatureSet::macOS_GPUFamily1_v1,
MTLFeatureSet::macOS_GPUFamily2_v1,
];
const DUAL_SOURCE_BLEND_SUPPORT: &[MTLFeatureSet] = &[
MTLFeatureSet::iOS_GPUFamily1_v4,
MTLFeatureSet::iOS_GPUFamily2_v4,
MTLFeatureSet::iOS_GPUFamily3_v3,
MTLFeatureSet::iOS_GPUFamily4_v1,
MTLFeatureSet::iOS_GPUFamily5_v1,
MTLFeatureSet::tvOS_GPUFamily1_v3,
MTLFeatureSet::tvOS_GPUFamily2_v1,
MTLFeatureSet::macOS_GPUFamily1_v2,
MTLFeatureSet::macOS_GPUFamily2_v1,
];
const LAYERED_RENDERING_SUPPORT: &[MTLFeatureSet] = &[
MTLFeatureSet::iOS_GPUFamily5_v1,
MTLFeatureSet::macOS_GPUFamily1_v1,
MTLFeatureSet::macOS_GPUFamily2_v1,
];
const FUNCTION_SPECIALIZATION_SUPPORT: &[MTLFeatureSet] = &[
MTLFeatureSet::iOS_GPUFamily1_v3,
MTLFeatureSet::iOS_GPUFamily2_v3,
MTLFeatureSet::iOS_GPUFamily3_v2,
MTLFeatureSet::iOS_GPUFamily4_v1,
MTLFeatureSet::iOS_GPUFamily5_v1,
MTLFeatureSet::tvOS_GPUFamily1_v2,
MTLFeatureSet::macOS_GPUFamily1_v2,
MTLFeatureSet::macOS_GPUFamily2_v1,
];
const DEPTH_CLIP_MODE: &[MTLFeatureSet] = &[
MTLFeatureSet::iOS_GPUFamily4_v1,
MTLFeatureSet::iOS_GPUFamily5_v1,
MTLFeatureSet::tvOS_GPUFamily1_v3,
MTLFeatureSet::macOS_GPUFamily1_v1,
MTLFeatureSet::macOS_GPUFamily2_v1,
];
impl super::PrivateCapabilities {
fn version_at_least(major: u32, minor: u32, needed_major: u32, needed_minor: u32) -> bool {
major > needed_major || (major == needed_major && minor >= needed_minor)
}
fn supports_any(raw: &mtl::DeviceRef, features_sets: &[MTLFeatureSet]) -> bool {
features_sets
.iter()
.cloned()
.any(|x| raw.supports_feature_set(x))
}
pub fn new(device: &mtl::Device) -> Self {
#[repr(C)]
#[derive(Clone, Copy, Debug)]
struct NSOperatingSystemVersion {
major: usize,
minor: usize,
patch: usize,
}
let version: NSOperatingSystemVersion = unsafe {
let process_info: *mut objc::runtime::Object =
msg_send![class!(NSProcessInfo), processInfo];
msg_send![process_info, operatingSystemVersion]
};
let major = version.major as u32;
let minor = version.minor as u32;
let os_is_mac = device.supports_feature_set(MTLFeatureSet::macOS_GPUFamily1_v1);
let family_check = if os_is_mac {
Self::version_at_least(major, minor, 10, 15)
} else {
Self::version_at_least(major, minor, 13, 0)
};
let mut sample_count_mask: u8 = 1 | 4; // 1 and 4 samples are supported on all devices
if device.supports_texture_sample_count(2) {
sample_count_mask |= 2;
}
if device.supports_texture_sample_count(8) {
sample_count_mask |= 8;
}
Self {
family_check,
msl_version: if os_is_mac {
if Self::version_at_least(major, minor, 10, 15) {
MTLLanguageVersion::V2_2
} else if Self::version_at_least(major, minor, 10, 14) {
MTLLanguageVersion::V2_1
} else if Self::version_at_least(major, minor, 10, 13) {
MTLLanguageVersion::V2_0
} else if Self::version_at_least(major, minor, 10, 12) {
MTLLanguageVersion::V1_2
} else if Self::version_at_least(major, minor, 10, 11) {
MTLLanguageVersion::V1_1
} else {
MTLLanguageVersion::V1_0
}
} else if Self::version_at_least(major, minor, 13, 0) {
MTLLanguageVersion::V2_2
} else if Self::version_at_least(major, minor, 12, 0) {
MTLLanguageVersion::V2_1
} else if Self::version_at_least(major, minor, 11, 0) {
MTLLanguageVersion::V2_0
} else if Self::version_at_least(major, minor, 10, 0) {
MTLLanguageVersion::V1_2
} else if Self::version_at_least(major, minor, 9, 0) {
MTLLanguageVersion::V1_1
} else {
MTLLanguageVersion::V1_0
},
exposed_queues: 1,
read_write_texture_tier: device.read_write_texture_support(),
resource_heaps: Self::supports_any(&device, RESOURCE_HEAP_SUPPORT),
argument_buffers: Self::supports_any(&device, ARGUMENT_BUFFER_SUPPORT),
shared_textures: !os_is_mac,
mutable_comparison_samplers: Self::supports_any(
&device,
MUTABLE_COMPARISON_SAMPLER_SUPPORT,
),
sampler_clamp_to_border: Self::supports_any(&device, SAMPLER_CLAMP_TO_BORDER_SUPPORT),
sampler_lod_average: {
let need_version = if os_is_mac { (10, 13) } else { (9, 0) };
Self::version_at_least(major, minor, need_version.0, need_version.1)
},
base_instance: Self::supports_any(&device, BASE_INSTANCE_SUPPORT),
base_vertex_instance_drawing: Self::supports_any(&device, BASE_VERTEX_INSTANCE_SUPPORT),
dual_source_blending: Self::supports_any(&device, DUAL_SOURCE_BLEND_SUPPORT),
low_power: !os_is_mac || device.is_low_power(),
headless: os_is_mac && device.is_headless(),
layered_rendering: Self::supports_any(&device, LAYERED_RENDERING_SUPPORT),
function_specialization: Self::supports_any(&device, FUNCTION_SPECIALIZATION_SUPPORT),
depth_clip_mode: Self::supports_any(&device, DEPTH_CLIP_MODE),
texture_cube_array: Self::supports_any(&device, TEXTURE_CUBE_ARRAY_SUPPORT),
format_depth24_stencil8: os_is_mac && device.d24_s8_supported(),
format_depth32_stencil8_filter: os_is_mac,
format_depth32_stencil8_none: !os_is_mac,
format_min_srgb_channels: if os_is_mac { 4 } else { 1 },
format_b5: !os_is_mac,
format_bc: os_is_mac,
format_eac_etc: !os_is_mac,
format_astc: Self::supports_any(&device, ASTC_PIXEL_FORMAT_FEATURES),
format_any8_unorm_srgb_all: Self::supports_any(&device, ANY8_UNORM_SRGB_ALL),
format_any8_unorm_srgb_no_write: !Self::supports_any(&device, ANY8_UNORM_SRGB_ALL)
&& !os_is_mac,
format_any8_snorm_all: Self::supports_any(&device, ANY8_SNORM_RESOLVE),
format_r16_norm_all: os_is_mac,
format_r32_all: !Self::supports_any(
&device,
&[
MTLFeatureSet::iOS_GPUFamily1_v1,
MTLFeatureSet::iOS_GPUFamily2_v1,
],
),
format_r32_no_write: Self::supports_any(
&device,
&[
MTLFeatureSet::iOS_GPUFamily1_v1,
MTLFeatureSet::iOS_GPUFamily2_v1,
],
),
format_r32float_no_write_no_filter: Self::supports_any(
&device,
&[
MTLFeatureSet::iOS_GPUFamily1_v1,
MTLFeatureSet::iOS_GPUFamily2_v1,
],
) && !os_is_mac,
format_r32float_no_filter: !Self::supports_any(
&device,
&[
MTLFeatureSet::iOS_GPUFamily1_v1,
MTLFeatureSet::iOS_GPUFamily2_v1,
],
) && !os_is_mac,
format_r32float_all: os_is_mac,
format_rgba8_srgb_all: Self::supports_any(&device, RGBA8_SRGB),
format_rgba8_srgb_no_write: !Self::supports_any(&device, RGBA8_SRGB),
format_rgb10a2_unorm_all: Self::supports_any(&device, RGB10A2UNORM_ALL),
format_rgb10a2_unorm_no_write: !Self::supports_any(&device, RGB10A2UNORM_ALL),
format_rgb10a2_uint_color: !Self::supports_any(&device, RGB10A2UINT_COLOR_WRITE),
format_rgb10a2_uint_color_write: Self::supports_any(&device, RGB10A2UINT_COLOR_WRITE),
format_rg11b10_all: Self::supports_any(&device, RG11B10FLOAT_ALL),
format_rg11b10_no_write: !Self::supports_any(&device, RG11B10FLOAT_ALL),
format_rgb9e5_all: Self::supports_any(&device, RGB9E5FLOAT_ALL),
format_rgb9e5_no_write: !Self::supports_any(&device, RGB9E5FLOAT_ALL) && !os_is_mac,
format_rgb9e5_filter_only: os_is_mac,
format_rg32_color: Self::supports_any(
&device,
&[
MTLFeatureSet::iOS_GPUFamily1_v1,
MTLFeatureSet::iOS_GPUFamily2_v1,
],
),
format_rg32_color_write: !Self::supports_any(
&device,
&[
MTLFeatureSet::iOS_GPUFamily1_v1,
MTLFeatureSet::iOS_GPUFamily2_v1,
],
),
format_rg32float_all: os_is_mac,
format_rg32float_color_blend: Self::supports_any(
&device,
&[
MTLFeatureSet::iOS_GPUFamily1_v1,
MTLFeatureSet::iOS_GPUFamily2_v1,
],
),
format_rg32float_no_filter: !os_is_mac
&& !Self::supports_any(
&device,
&[
MTLFeatureSet::iOS_GPUFamily1_v1,
MTLFeatureSet::iOS_GPUFamily2_v1,
],
),
format_rgba32int_color: Self::supports_any(
&device,
&[
MTLFeatureSet::iOS_GPUFamily1_v1,
MTLFeatureSet::iOS_GPUFamily2_v1,
],
),
format_rgba32int_color_write: !Self::supports_any(
&device,
&[
MTLFeatureSet::iOS_GPUFamily1_v1,
MTLFeatureSet::iOS_GPUFamily2_v1,
],
),
format_rgba32float_color: Self::supports_any(
&device,
&[
MTLFeatureSet::iOS_GPUFamily1_v1,
MTLFeatureSet::iOS_GPUFamily2_v1,
],
),
format_rgba32float_color_write: !Self::supports_any(
&device,
&[
MTLFeatureSet::iOS_GPUFamily1_v1,
MTLFeatureSet::iOS_GPUFamily2_v1,
],
) && !os_is_mac,
format_rgba32float_all: os_is_mac,
format_depth16unorm: device.supports_feature_set(MTLFeatureSet::macOS_GPUFamily1_v2),
format_depth32float_filter: device
.supports_feature_set(MTLFeatureSet::macOS_GPUFamily1_v1),
format_depth32float_none: !device
.supports_feature_set(MTLFeatureSet::macOS_GPUFamily1_v1),
format_bgr10a2_all: Self::supports_any(&device, BGR10A2_ALL),
format_bgr10a2_no_write: !device
.supports_feature_set(MTLFeatureSet::macOS_GPUFamily1_v3),
max_buffers_per_stage: 31,
max_textures_per_stage: if os_is_mac { 128 } else { 31 },
max_samplers_per_stage: 16,
buffer_alignment: if os_is_mac { 256 } else { 64 },
max_buffer_size: if device.supports_feature_set(MTLFeatureSet::macOS_GPUFamily1_v2) {
1 << 30 // 1GB on macOS 1.2 and up
} else {
1 << 28 // 256MB otherwise
},
max_texture_size: if Self::supports_any(
&device,
&[
MTLFeatureSet::iOS_GPUFamily3_v1,
MTLFeatureSet::tvOS_GPUFamily2_v1,
MTLFeatureSet::macOS_GPUFamily1_v1,
],
) {
16384
} else if Self::supports_any(
&device,
&[
MTLFeatureSet::iOS_GPUFamily1_v2,
MTLFeatureSet::iOS_GPUFamily2_v2,
MTLFeatureSet::tvOS_GPUFamily1_v1,
],
) {
8192
} else {
4096
},
max_texture_3d_size: 2048,
max_texture_layers: 2048,
max_fragment_input_components: if os_is_mac { 128 } else { 60 },
max_color_render_targets: if Self::supports_any(
&device,
&[
MTLFeatureSet::iOS_GPUFamily2_v1,
MTLFeatureSet::iOS_GPUFamily3_v1,
MTLFeatureSet::iOS_GPUFamily4_v1,
MTLFeatureSet::iOS_GPUFamily5_v1,
MTLFeatureSet::tvOS_GPUFamily1_v1,
MTLFeatureSet::tvOS_GPUFamily2_v1,
MTLFeatureSet::macOS_GPUFamily1_v1,
MTLFeatureSet::macOS_GPUFamily2_v1,
],
) {
8
} else {
4
},
max_total_threadgroup_memory: if Self::supports_any(
&device,
&[
MTLFeatureSet::iOS_GPUFamily4_v2,
MTLFeatureSet::iOS_GPUFamily5_v1,
],
) {
64 << 10
} else if Self::supports_any(
&device,
&[
MTLFeatureSet::iOS_GPUFamily4_v1,
MTLFeatureSet::macOS_GPUFamily1_v2,
MTLFeatureSet::macOS_GPUFamily2_v1,
],
) {
32 << 10
} else {
16 << 10
},
sample_count_mask,
supports_debug_markers: Self::supports_any(
&device,
&[
MTLFeatureSet::macOS_GPUFamily1_v2,
MTLFeatureSet::macOS_GPUFamily2_v1,
MTLFeatureSet::iOS_GPUFamily1_v3,
MTLFeatureSet::iOS_GPUFamily2_v3,
MTLFeatureSet::iOS_GPUFamily3_v2,
MTLFeatureSet::iOS_GPUFamily4_v1,
MTLFeatureSet::iOS_GPUFamily5_v1,
MTLFeatureSet::tvOS_GPUFamily1_v2,
MTLFeatureSet::tvOS_GPUFamily2_v1,
],
),
supports_binary_archives: family_check
&& (device.supports_family(MTLGPUFamily::Apple3)
|| device.supports_family(MTLGPUFamily::Mac1)),
can_set_maximum_drawables_count: os_is_mac
|| Self::version_at_least(major, minor, 11, 2),
can_set_display_sync: os_is_mac && Self::version_at_least(major, minor, 10, 13),
can_set_next_drawable_timeout: if is_mac {
Self::version_at_least(major, minor, 10, 13)
} else {
Self::version_at_least(major, minor, 11, 0)
},
}
}
pub fn features(&self) -> wgt::Features {
use wgt::Features as F;
let mut features = F::empty()
| F::DEPTH_CLAMPING
| F::TEXTURE_COMPRESSION_BC
| F::MAPPABLE_PRIMARY_BUFFERS
| F::VERTEX_WRITABLE_STORAGE;
features.set(
F::SAMPLED_TEXTURE_BINDING_ARRAY | F::SAMPLED_TEXTURE_ARRAY_DYNAMIC_INDEXING,
self.msl_version >= MTLLanguageVersion::V2_0,
);
features.set(
F::ADDRESS_MODE_CLAMP_TO_BORDER,
self.sampler_clamp_to_border,
);
features
}
pub fn capabilities(&self) -> crate::Capabilities {
let buffer_alignment = wgt::BufferSize::new(self.buffer_alignment).unwrap();
let mut downlevel = wgt::DownlevelCapabilities::default();
downlevel.flags.set(
wgt::DownlevelFlags::CUBE_ARRAY_TEXTURES,
self.texture_cube_array,
);
crate::Capabilities {
limits: wgt::Limits {
max_texture_dimension_1d: self.max_texture_size as u32,
max_texture_dimension_2d: self.max_texture_size as u32,
max_texture_dimension_3d: self.max_texture_3d_size as u32,
max_texture_array_layers: self.max_texture_layers as u32,
max_bind_groups: 8,
max_dynamic_uniform_buffers_per_pipeline_layout: 8,
max_dynamic_storage_buffers_per_pipeline_layout: 4,
max_sampled_textures_per_shader_stage: 16,
max_samplers_per_shader_stage: self.max_samplers_per_stage,
max_storage_buffers_per_shader_stage: 4,
max_storage_textures_per_shader_stage: 4,
max_uniform_buffers_per_shader_stage: 12,
max_uniform_buffer_binding_size: self.max_buffer_size.min(!0u32 as u64) as u32,
max_storage_buffer_binding_size: self.max_buffer_size.min(!0u32 as u64) as u32,
max_vertex_buffers: 8,
max_vertex_attributes: 16,
max_vertex_buffer_array_stride: 2048,
max_push_constant_size: 0x1000,
},
alignments: crate::Alignments {
buffer_copy_offset: buffer_alignment,
buffer_copy_pitch: wgt::BufferSize::new(4).unwrap(),
storage_buffer_offset: buffer_alignment,
uniform_buffer_offset: buffer_alignment,
},
downlevel,
}
}
pub fn map_format(&self, format: wgt::TextureFormat) -> mtl::MTLPixelFormat {
use mtl::MTLPixelFormat::*;
use wgt::TextureFormat as Tf;
match format {
Tf::R8Unorm => R8Unorm,
Tf::R8Snorm => R8Snorm,
Tf::R8Uint => R8Uint,
Tf::R8Sint => R8Sint,
Tf::R16Uint => R16Uint,
Tf::R16Sint => R16Sint,
Tf::R16Float => R16Float,
Tf::Rg8Unorm => RG8Unorm,
Tf::Rg8Snorm => RG8Snorm,
Tf::Rg8Uint => RG8Uint,
Tf::Rg8Sint => RG8Sint,
Tf::R32Uint => R32Uint,
Tf::R32Sint => R32Sint,
Tf::R32Float => R32Float,
Tf::Rg16Uint => RG16Uint,
Tf::Rg16Sint => RG16Sint,
Tf::Rg16Float => RG16Float,
Tf::Rgba8Unorm => RGBA8Unorm,
Tf::Rgba8UnormSrgb => RGBA8Unorm_sRGB,
Tf::Bgra8UnormSrgb => BGRA8Unorm_sRGB,
Tf::Rgba8Snorm => RGBA8Snorm,
Tf::Bgra8Unorm => BGRA8Unorm,
Tf::Rgba8Uint => RGBA8Uint,
Tf::Rgba8Sint => RGBA8Sint,
Tf::Rgb10a2Unorm => RGB10A2Unorm,
Tf::Rg11b10Float => RG11B10Float,
Tf::Rg32Uint => RG32Uint,
Tf::Rg32Sint => RG32Sint,
Tf::Rg32Float => RG32Float,
Tf::Rgba16Uint => RGBA16Uint,
Tf::Rgba16Sint => RGBA16Sint,
Tf::Rgba16Float => RGBA16Float,
Tf::Rgba32Uint => RGBA32Uint,
Tf::Rgba32Sint => RGBA32Sint,
Tf::Rgba32Float => RGBA32Float,
Tf::Depth32Float => Depth32Float,
Tf::Depth24Plus => {
if self.format_depth24_stencil8 {
Depth24Unorm_Stencil8
} else {
Depth32Float
}
}
Tf::Depth24PlusStencil8 => {
if self.format_depth24_stencil8 {
Depth24Unorm_Stencil8
} else {
Depth32Float_Stencil8
}
}
Tf::Bc1RgbaUnorm => BC1_RGBA,
Tf::Bc1RgbaUnormSrgb => BC1_RGBA_sRGB,
Tf::Bc2RgbaUnorm => BC2_RGBA,
Tf::Bc2RgbaUnormSrgb => BC2_RGBA_sRGB,
Tf::Bc3RgbaUnorm => BC3_RGBA,
Tf::Bc3RgbaUnormSrgb => BC3_RGBA_sRGB,
Tf::Bc4RUnorm => BC4_RUnorm,
Tf::Bc4RSnorm => BC4_RSnorm,
Tf::Bc5RgUnorm => BC5_RGUnorm,
Tf::Bc6hRgbSfloat => BC6H_RGBFloat,
Tf::Bc7RgbaUnorm => BC7_RGBAUnorm,
Tf::Bc7RgbaUnormSrgb => BC7_RGBAUnorm_sRGB,
Tf::Etc2RgbUnorm => ETC2_RGB8,
Tf::Etc2RgbUnormSrgb => ETC2_RGB8_sRGB,
Tf::Etc2RgbA1Unorm => ETC2_RGB8A1,
Tf::Etc2RgbA1UnormSrgb => ETC2_RGB8A1_sRGB,
Tf::EacRUnorm => EAC_R11Unorm,
Tf::EacRSnorm => EAC_R11Snorm,
Tf::EtcRgUnorm => EAC_RG11Unorm,
Tf::EtcRgSnorm => EAC_RG11Snorm,
Tf::Astc4x4RgbaUnorm => ASTC_4x4_LDR,
Tf::Astc4x4RgbaUnormSrgb => ASTC_4x4_sRGB,
Tf::Astc5x4RgbaUnorm => ASTC_5x4_LDR,
Tf::Astc5x4RgbaUnormSrgb => ASTC_5x4_sRGB,
Tf::Astc5x5RgbaUnorm => ASTC_5x5_LDR,
Tf::Astc5x5RgbaUnormSrgb => ASTC_5x5_sRGB,
Tf::Astc6x5RgbaUnorm => ASTC_6x5_LDR,
Tf::Astc6x5RgbaUnormSrgb => ASTC_6x5_sRGB,
Tf::Astc6x6RgbaUnorm => ASTC_6x6_LDR,
Tf::Astc6x6RgbaUnormSrgb => ASTC_6x6_sRGB,
Tf::Astc8x5RgbaUnorm => ASTC_8x5_LDR,
Tf::Astc8x5RgbaUnormSrgb => ASTC_8x5_sRGB,
Tf::Astc8x6RgbaUnorm => ASTC_8x6_LDR,
Tf::Astc8x6RgbaUnormSrgb => ASTC_8x6_sRGB,
Tf::Astc10x5RgbaUnorm => ASTC_8x8_LDR,
Tf::Astc10x5RgbaUnormSrgb => ASTC_8x8_sRGB,
Tf::Astc10x6RgbaUnorm => ASTC_10x5_LDR,
Tf::Astc10x6RgbaUnormSrgb => ASTC_10x5_sRGB,
Tf::Astc8x8RgbaUnorm => ASTC_10x6_LDR,
Tf::Astc8x8RgbaUnormSrgb => ASTC_10x6_sRGB,
Tf::Astc10x8RgbaUnorm => ASTC_10x8_LDR,
Tf::Astc10x8RgbaUnormSrgb => ASTC_10x8_sRGB,
Tf::Astc10x10RgbaUnorm => ASTC_10x10_LDR,
Tf::Astc10x10RgbaUnormSrgb => ASTC_10x10_sRGB,
Tf::Astc12x10RgbaUnorm => ASTC_12x10_LDR,
Tf::Astc12x10RgbaUnormSrgb => ASTC_12x10_sRGB,
Tf::Astc12x12RgbaUnorm => ASTC_12x12_LDR,
Tf::Astc12x12RgbaUnormSrgb => ASTC_12x12_sRGB,
}
}
}
impl super::PrivateDisabilities {
pub fn new(device: &mtl::Device) -> Self {
let is_intel = device.name().starts_with("Intel");
Self {
broken_viewport_near_depth: is_intel
&& !device.supports_feature_set(MTLFeatureSet::macOS_GPUFamily1_v4),
broken_layered_clear_image: is_intel,
}
}
}

View File

@ -0,0 +1 @@

View File

View File

@ -0,0 +1 @@

View File

@ -1,6 +1,13 @@
#![allow(unused_variables)]
use std::ops::Range;
mod adapter;
mod command;
mod device;
mod surface;
use std::{ops::Range, ptr::NonNull, sync::Arc, thread};
use parking_lot::Mutex;
#[derive(Clone)]
pub struct Api;
@ -10,19 +17,20 @@ pub struct Encoder;
pub struct Resource;
type DeviceResult<T> = Result<T, crate::DeviceError>;
type ResourceIndex = u32;
impl crate::Api for Api {
type Instance = Context;
type Surface = Context;
type Adapter = Context;
type Queue = Context;
type Device = Context;
type Instance = Instance;
type Surface = Surface;
type Adapter = Adapter;
type Queue = Queue;
type Device = Device;
type CommandBuffer = Encoder;
type Buffer = Resource;
type Texture = Resource;
type SurfaceTexture = Resource;
type SurfaceTexture = SurfaceTexture;
type TextureView = Resource;
type Sampler = Resource;
type QuerySet = Resource;
@ -36,59 +44,210 @@ impl crate::Api for Api {
type ComputePipeline = Resource;
}
impl crate::Instance<Api> for Context {
pub struct Instance {}
impl crate::Instance<Api> for Instance {
unsafe fn init() -> Result<Self, crate::InstanceError> {
Ok(Context)
Ok(Instance {})
}
unsafe fn create_surface(
&self,
rwh: &impl raw_window_handle::HasRawWindowHandle,
) -> Result<Context, crate::InstanceError> {
Ok(Context)
has_handle: &impl raw_window_handle::HasRawWindowHandle,
) -> Result<Surface, crate::InstanceError> {
match has_handle.raw_window_handle() {
#[cfg(target_os = "ios")]
raw_window_handle::RawWindowHandle::IOS(handle) => {
Ok(Surface::from_uiview(handle.ui_view))
}
unsafe fn destroy_surface(&self, surface: Context) {}
#[cfg(target_os = "macos")]
raw_window_handle::RawWindowHandle::MacOS(handle) => {
Ok(Surface::from_nsview(handle.ns_view))
}
_ => Err(crate::InstanceError),
}
}
unsafe fn destroy_surface(&self, surface: Surface) {}
unsafe fn enumerate_adapters(&self) -> Vec<crate::ExposedAdapter<Api>> {
Vec::new()
let devices = mtl::Device::all();
let mut adapters: Vec<crate::ExposedAdapter<Api>> = devices
.into_iter()
.map(|dev| {
let name = dev.name().into();
let shared = AdapterShared::new(dev);
crate::ExposedAdapter {
info: wgt::AdapterInfo {
name,
vendor: 0,
device: 0,
device_type: if shared.private_caps.low_power {
wgt::DeviceType::IntegratedGpu
} else {
wgt::DeviceType::DiscreteGpu
},
backend: wgt::Backend::Metal,
},
features: shared.private_caps.features(),
capabilities: shared.private_caps.capabilities(),
adapter: Adapter::new(Arc::new(shared)),
}
})
.collect();
adapters.sort_by_key(|ad| {
(
ad.adapter.shared.private_caps.low_power,
ad.adapter.shared.private_caps.headless,
)
});
adapters
}
}
impl crate::Surface<Api> for Context {
unsafe fn configure(
&mut self,
device: &Context,
config: &crate::SurfaceConfiguration,
) -> Result<(), crate::SurfaceError> {
Ok(())
}
unsafe fn unconfigure(&mut self, device: &Context) {}
unsafe fn acquire_texture(
&mut self,
timeout_ms: u32,
) -> Result<Option<crate::AcquiredSurfaceTexture<Api>>, crate::SurfaceError> {
Ok(None)
}
unsafe fn discard_texture(&mut self, texture: Resource) {}
#[derive(Clone, Debug)]
struct PrivateCapabilities {
family_check: bool,
msl_version: mtl::MTLLanguageVersion,
exposed_queues: usize,
read_write_texture_tier: mtl::MTLReadWriteTextureTier,
resource_heaps: bool,
argument_buffers: bool,
shared_textures: bool,
mutable_comparison_samplers: bool,
sampler_clamp_to_border: bool,
sampler_lod_average: bool,
base_instance: bool,
base_vertex_instance_drawing: bool,
dual_source_blending: bool,
low_power: bool,
headless: bool,
layered_rendering: bool,
function_specialization: bool,
depth_clip_mode: bool,
texture_cube_array: bool,
format_depth24_stencil8: bool,
format_depth32_stencil8_filter: bool,
format_depth32_stencil8_none: bool,
format_min_srgb_channels: u8,
format_b5: bool,
format_bc: bool,
format_eac_etc: bool,
format_astc: bool,
format_any8_unorm_srgb_all: bool,
format_any8_unorm_srgb_no_write: bool,
format_any8_snorm_all: bool,
format_r16_norm_all: bool,
format_r32_all: bool,
format_r32_no_write: bool,
format_r32float_no_write_no_filter: bool,
format_r32float_no_filter: bool,
format_r32float_all: bool,
format_rgba8_srgb_all: bool,
format_rgba8_srgb_no_write: bool,
format_rgb10a2_unorm_all: bool,
format_rgb10a2_unorm_no_write: bool,
format_rgb10a2_uint_color: bool,
format_rgb10a2_uint_color_write: bool,
format_rg11b10_all: bool,
format_rg11b10_no_write: bool,
format_rgb9e5_all: bool,
format_rgb9e5_no_write: bool,
format_rgb9e5_filter_only: bool,
format_rg32_color: bool,
format_rg32_color_write: bool,
format_rg32float_all: bool,
format_rg32float_color_blend: bool,
format_rg32float_no_filter: bool,
format_rgba32int_color: bool,
format_rgba32int_color_write: bool,
format_rgba32float_color: bool,
format_rgba32float_color_write: bool,
format_rgba32float_all: bool,
format_depth16unorm: bool,
format_depth32float_filter: bool,
format_depth32float_none: bool,
format_bgr10a2_all: bool,
format_bgr10a2_no_write: bool,
max_buffers_per_stage: ResourceIndex,
max_textures_per_stage: ResourceIndex,
max_samplers_per_stage: ResourceIndex,
buffer_alignment: u64,
max_buffer_size: u64,
max_texture_size: u64,
max_texture_3d_size: u64,
max_texture_layers: u64,
max_fragment_input_components: u64,
max_color_render_targets: u8,
max_total_threadgroup_memory: u32,
sample_count_mask: u8,
supports_debug_markers: bool,
supports_binary_archives: bool,
can_set_maximum_drawables_count: bool,
can_set_display_sync: bool,
can_set_next_drawable_timeout: bool,
}
impl crate::Adapter<Api> for Context {
unsafe fn open(&self, features: wgt::Features) -> DeviceResult<crate::OpenDevice<Api>> {
Err(crate::DeviceError::Lost)
#[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.
broken_layered_clear_image: bool,
}
struct AdapterShared {
device: Mutex<mtl::Device>,
disabilities: PrivateDisabilities,
private_caps: PrivateCapabilities,
}
impl AdapterShared {
fn new(device: mtl::Device) -> Self {
let private_caps = PrivateCapabilities::new(&device);
log::debug!("{:#?}", private_caps);
Self {
disabilities: PrivateDisabilities::new(&device),
private_caps: PrivateCapabilities::new(&device),
device: Mutex::new(device),
}
unsafe fn close(&self, device: Context) {}
unsafe fn texture_format_capabilities(
&self,
format: wgt::TextureFormat,
) -> crate::TextureFormatCapability {
crate::TextureFormatCapability::empty()
}
unsafe fn surface_capabilities(&self, surface: &Context) -> Option<crate::SurfaceCapabilities> {
None
}
}
impl crate::Queue<Api> for Context {
struct Adapter {
shared: Arc<AdapterShared>,
}
struct Queue {
raw: mtl::CommandQueue,
}
struct Device {
shared: Arc<AdapterShared>,
features: wgt::Features,
}
struct Surface {
view: Option<NonNull<objc::runtime::Object>>,
render_layer: Mutex<mtl::MetalLayer>,
raw_swapchain_format: mtl::MTLPixelFormat,
main_thread_id: thread::ThreadId,
// Useful for UI-intensive applications that are sensitive to
// window resizing.
pub present_with_transaction: bool,
}
#[derive(Debug)]
struct SurfaceTexture {
texture: Resource,
drawable: mtl::MetalDrawable,
present_with_transaction: bool,
}
unsafe impl Send for SurfaceTexture {}
unsafe impl Sync for SurfaceTexture {}
impl crate::Queue<Api> for Queue {
unsafe fn submit<I>(
&mut self,
command_buffers: I,
@ -98,8 +257,8 @@ impl crate::Queue<Api> for Context {
}
unsafe fn present(
&mut self,
surface: &mut Context,
texture: Resource,
surface: &mut Surface,
texture: SurfaceTexture,
) -> Result<(), crate::SurfaceError> {
Ok(())
}
@ -114,7 +273,7 @@ impl crate::Device<Api> for Context {
&self,
buffer: &Resource,
range: crate::MemoryRange,
) -> DeviceResult<std::ptr::NonNull<u8>> {
) -> DeviceResult<NonNull<u8>> {
Err(crate::DeviceError::Lost)
}
unsafe fn unmap_buffer(&self, buffer: &Resource) -> DeviceResult<()> {

View File

@ -0,0 +1,265 @@
use std::{mem, os::raw::c_void, ptr::NonNull, thread};
use objc::{
class, msg_send,
rc::autoreleasepool,
runtime::{Object, BOOL, YES},
sel, sel_impl,
};
use parking_lot::Mutex;
#[link(name = "QuartzCore", kind = "framework")]
extern "C" {
#[allow(non_upper_case_globals)]
static kCAGravityTopLeft: *mut Object;
}
#[repr(C)]
#[derive(Clone, Copy, Debug, Default)]
pub struct CGPoint {
pub x: mtl::CGFloat,
pub y: mtl::CGFloat,
}
impl CGPoint {
#[inline]
pub fn new(x: mtl::CGFloat, y: mtl::CGFloat) -> CGPoint {
CGPoint { x, y }
}
}
#[repr(C)]
#[derive(Clone, Copy, Debug, Default)]
pub struct CGRect {
pub origin: CGPoint,
pub size: mtl::CGSize,
}
impl CGRect {
#[inline]
pub fn new(origin: CGPoint, size: mtl::CGSize) -> CGRect {
CGRect { origin, size }
}
}
impl super::Surface {
fn new(view: Option<NonNull<Object>>, layer: mtl::MetalLayer) -> Self {
Self {
view,
render_layer: Mutex::new(layer),
raw_swapchain_format: mtl::MTLPixelFormat::Invalid,
main_thread_id: thread::current().id(),
present_with_transaction: false,
}
}
#[cfg(target_os = "ios")]
pub unsafe fn from_uiview(uiview: *mut c_void) -> Self {
let view: cocoa_foundation::base::id = mem::transmute(uiview);
if view.is_null() {
panic!("window does not have a valid contentView");
}
let main_layer: *mut Object = msg_send![view, layer];
let class = class!(CAMetalLayer);
let is_valid_layer: BOOL = msg_send![main_layer, isKindOfClass: class];
let render_layer = if is_valid_layer == YES {
mem::transmute::<_, &MetalLayerRef>(main_layer).to_owned()
} else {
// If the main layer is not a CAMetalLayer, we create a CAMetalLayer sublayer and use it instead.
// Unlike on macOS, we cannot replace the main view as UIView does not allow it (when NSView does).
let new_layer: mtl::MetalLayer = msg_send![class, new];
let bounds: CGRect = msg_send![main_layer, bounds];
let () = msg_send![new_layer.as_ref(), setFrame: bounds];
let () = msg_send![main_layer, addSublayer: new_layer.as_ref()];
new_layer
};
let window: *mut Object = msg_send![view, window];
if !window.is_null() {
let screen: *mut Object = msg_send![window, screen];
assert!(!screen.is_null(), "window is not attached to a screen");
let scale_factor: CGFloat = msg_send![screen, nativeScale];
let () = msg_send![view, setContentScaleFactor: scale_factor];
}
let _: *mut c_void = msg_send![view, retain];
Self::new(NonNull::new(view), render_layer)
}
#[cfg(target_os = "macos")]
pub unsafe fn from_nsview(nsview: *mut c_void) -> Self {
let view = nsview as *mut Object;
if view.is_null() {
panic!("window does not have a valid contentView");
}
let class = class!(CAMetalLayer);
// Deprecated! Clients should use `create_surface_from_layer` instead.
let is_actually_layer: BOOL = msg_send![view, isKindOfClass: class];
if is_actually_layer == YES {
return Self::from_layer(mem::transmute(view));
}
let existing: *mut Object = msg_send![view, layer];
let use_current = if existing.is_null() {
false
} else {
let result: BOOL = msg_send![existing, isKindOfClass: class];
result == YES
};
let render_layer: mtl::MetalLayer = if use_current {
mem::transmute::<_, &mtl::MetalLayerRef>(existing).to_owned()
} else {
let layer: mtl::MetalLayer = msg_send![class, new];
let () = msg_send![view, setLayer: layer.as_ref()];
let () = msg_send![view, setWantsLayer: YES];
let bounds: CGRect = msg_send![view, bounds];
let () = msg_send![layer.as_ref(), setBounds: bounds];
let window: *mut Object = msg_send![view, window];
if !window.is_null() {
let scale_factor: mtl::CGFloat = msg_send![window, backingScaleFactor];
let () = msg_send![layer, setContentsScale: scale_factor];
}
//let () = msg_send![layer, setDelegate: self.gfx_managed_metal_layer_delegate.0];
layer
};
let () = msg_send![render_layer, setContentsGravity: kCAGravityTopLeft];
let _: *mut c_void = msg_send![view, retain];
Self::new(NonNull::new(view), render_layer)
}
pub unsafe fn from_layer(layer: &mtl::MetalLayerRef) -> Self {
let class = class!(CAMetalLayer);
let proper_kind: BOOL = msg_send![layer, isKindOfClass: class];
assert_eq!(proper_kind, YES);
Self::new(None, layer.to_owned())
}
fn dimensions(&self) -> wgt::Extent3d {
let (size, scale): (mtl::CGSize, mtl::CGFloat) = match self.view {
Some(view) if !cfg!(target_os = "macos") => unsafe {
let bounds: CGRect = msg_send![view.as_ptr(), bounds];
let window: Option<NonNull<Object>> = msg_send![view.as_ptr(), window];
let screen = window.and_then(|window| -> Option<NonNull<Object>> {
msg_send![window.as_ptr(), screen]
});
match screen {
Some(screen) => {
let screen_space: *mut Object = msg_send![screen.as_ptr(), coordinateSpace];
let rect: CGRect = msg_send![view.as_ptr(), convertRect:bounds toCoordinateSpace:screen_space];
let scale_factor: mtl::CGFloat = msg_send![screen.as_ptr(), nativeScale];
(rect.size, scale_factor)
}
None => (bounds.size, 1.0),
}
},
_ => unsafe {
let render_layer_borrow = self.render_layer.lock();
let render_layer = render_layer_borrow.as_ref();
let bounds: CGRect = msg_send![render_layer, bounds];
let contents_scale: mtl::CGFloat = msg_send![render_layer, contentsScale];
(bounds.size, contents_scale)
},
};
wgt::Extent3d {
width: (size.width * scale) as u32,
height: (size.height * scale) as u32,
depth_or_array_layers: 1,
}
}
}
impl crate::Surface<super::Api> for super::Surface {
unsafe fn configure(
&mut self,
device: &super::Device,
config: &crate::SurfaceConfiguration,
) -> Result<(), crate::SurfaceError> {
log::info!("build swapchain {:?}", config);
let caps = &device.shared.private_caps;
let mtl_format = caps.map_format(config.format);
let render_layer = self.render_layer.lock();
let framebuffer_only = config.usage == crate::TextureUse::COLOR_TARGET;
let display_sync = config.present_mode != wgt::PresentMode::Immediate;
let drawable_size =
mtl::CGSize::new(config.extent.width as f64, config.extent.height as f64);
match config.composite_alpha_mode {
crate::CompositeAlphaMode::Opaque => render_layer.set_opaque(true),
crate::CompositeAlphaMode::Alpha => render_layer.set_opaque(false),
crate::CompositeAlphaMode::PremultipliedAlpha => (),
}
let device_raw = device.shared.device.lock();
unsafe {
// On iOS, unless the user supplies a view with a CAMetalLayer, we
// create one as a sublayer. However, when the view changes size,
// its sublayers are not automatically resized, and we must resize
// it here. The drawable size and the layer size don't correlate
#[cfg(target_os = "ios")]
{
if let Some(view) = self.view {
let main_layer: *mut Object = msg_send![view.as_ptr(), layer];
let bounds: CGRect = msg_send![main_layer, bounds];
let () = msg_send![*render_layer, setFrame: bounds];
}
}
render_layer.set_device(&*device_raw);
render_layer.set_pixel_format(mtl_format);
render_layer.set_framebuffer_only(framebuffer_only);
render_layer.set_presents_with_transaction(self.present_with_transaction);
// this gets ignored on iOS for certain OS/device combinations (iphone5s iOS 10.3)
let () =
msg_send![*render_layer, setMaximumDrawableCount: config.swap_chain_size as u64];
render_layer.set_drawable_size(drawable_size);
if caps.can_set_next_drawable_timeout {
let () = msg_send![*render_layer, setAllowsNextDrawableTimeout:false];
}
if caps.can_set_display_sync {
let () = msg_send![*render_layer, setDisplaySyncEnabled: display_sync];
}
};
Ok(())
}
unsafe fn unconfigure(&mut self, _device: &super::Device) {
self.raw_swapchain_format = mtl::MTLPixelFormat::Invalid;
}
unsafe fn acquire_texture(
&mut self,
_timeout_ms: u32, //TODO
) -> Result<Option<crate::AcquiredSurfaceTexture<super::Api>>, crate::SurfaceError> {
let render_layer = self.render_layer.lock();
let (drawable, _texture) = autoreleasepool(|| {
let drawable = render_layer.next_drawable().unwrap();
(drawable.to_owned(), drawable.texture().to_owned())
});
let size = render_layer.drawable_size();
let suf_texture = super::SurfaceTexture {
texture: super::Resource,
drawable,
present_with_transaction: self.present_with_transaction,
};
Ok(Some(crate::AcquiredSurfaceTexture {
texture: suf_texture,
suboptimal: false,
}))
}
unsafe fn discard_texture(&mut self, _texture: super::SurfaceTexture) {}
}

View File

@ -1434,12 +1434,12 @@ pub enum TextureFormat {
/// [0, 255] converted to/from float [0, 1] in shader.
///
/// [`Features::TEXTURE_COMPRESSION_ETC2`] must be enabled to use this texture format.
Etc2RgbA8Unorm = 56,
//Etc2RgbA8Unorm = 56,
/// 4x4 block compressed texture. 16 bytes per block (8 bit/px). Complex pallet. 8 bit integer RGB + 8 bit alpha.
/// Srgb-color [0, 255] converted to/from linear-color float [0, 1] in shader.
///
/// [`Features::TEXTURE_COMPRESSION_ETC2`] must be enabled to use this texture format.
Etc2RgbA8UnormSrgb = 57,
//Etc2RgbA8UnormSrgb = 57,
/// 4x4 block compressed texture. 8 bytes per block (4 bit/px). Complex pallet. 8 bit integer R.
/// [0, 255] converted to/from float [0, 1] in shader.
///
@ -1704,8 +1704,8 @@ impl TextureFormat {
Self::Etc2RgbUnormSrgb => (etc2, float, srgb, (4, 4), 8, basic),
Self::Etc2RgbA1Unorm => (etc2, float, linear, (4, 4), 8, basic),
Self::Etc2RgbA1UnormSrgb => (etc2, float, srgb, (4, 4), 8, basic),
Self::Etc2RgbA8Unorm => (etc2, float, linear, (4, 4), 16, basic),
Self::Etc2RgbA8UnormSrgb => (etc2, float, srgb, (4, 4), 16, basic),
//Self::Etc2RgbA8Unorm => (etc2, float, linear, (4, 4), 16, basic),
//Self::Etc2RgbA8UnormSrgb => (etc2, float, srgb, (4, 4), 16, basic),
Self::EacRUnorm => (etc2, float, linear, (4, 4), 8, basic),
Self::EacRSnorm => (etc2, float, linear, (4, 4), 8, basic),
Self::EtcRgUnorm => (etc2, float, linear, (4, 4), 16, basic),