Vendor wgpu-hal 29.0.4 and i-slint-renderer-skia 1.17.1, unmodified
The Android develop view reads its frame back through memory (TD-1) because wgpu's Vulkan swapchain never pre-rotates, and a portrait window on this tablet's landscape panel then tears. The fix is a small patch to each of these two crates, and this commit is only the ground it lands on: both are byte-for-byte the crates.io sources the lockfile already resolved, so the commits that follow are the patch and nothing else. third_party/ is excluded from the workspace, or every path dependency under the root would become a member and `--workspace` would test and lint upstream code as ours. The README says how to carry the patches across a Slint or wgpu bump, which matters because a stale version here does not fail the build — cargo just warns and uses the unpatched crate.
This commit is contained in:
+1578
File diff suppressed because it is too large
Load Diff
+1916
File diff suppressed because it is too large
Load Diff
+487
@@ -0,0 +1,487 @@
|
||||
use objc2::rc::Retained;
|
||||
use objc2_foundation::{NSArray, NSRange};
|
||||
use objc2_metal::{
|
||||
MTLAccelerationStructureBoundingBoxGeometryDescriptor, MTLAccelerationStructureDescriptor,
|
||||
MTLAccelerationStructureGeometryDescriptor, MTLAccelerationStructureInstanceDescriptorType,
|
||||
MTLAccelerationStructureTriangleGeometryDescriptor, MTLAccelerationStructureUsage,
|
||||
MTLAttributeFormat, MTLBlendFactor, MTLBlendOperation, MTLBlitOption, MTLClearColor,
|
||||
MTLColorWriteMask, MTLCompareFunction, MTLCullMode, MTLIndexType,
|
||||
MTLInstanceAccelerationStructureDescriptor, MTLOrigin,
|
||||
MTLPrimitiveAccelerationStructureDescriptor, MTLPrimitiveTopologyClass, MTLPrimitiveType,
|
||||
MTLRenderStages, MTLResourceUsage, MTLSamplerAddressMode, MTLSamplerBorderColor,
|
||||
MTLSamplerMinMagFilter, MTLSize, MTLStencilOperation, MTLStoreAction, MTLTextureType,
|
||||
MTLTextureUsage, MTLVertexFormat, MTLVertexStepFunction, MTLWinding,
|
||||
};
|
||||
|
||||
pub fn map_texture_usage(format: wgt::TextureFormat, usage: wgt::TextureUses) -> MTLTextureUsage {
|
||||
use wgt::TextureUses as Tu;
|
||||
|
||||
let mut mtl_usage = MTLTextureUsage::Unknown;
|
||||
|
||||
mtl_usage.set(
|
||||
MTLTextureUsage::RenderTarget,
|
||||
usage.intersects(Tu::COLOR_TARGET | Tu::DEPTH_STENCIL_READ | Tu::DEPTH_STENCIL_WRITE),
|
||||
);
|
||||
mtl_usage.set(
|
||||
MTLTextureUsage::ShaderRead,
|
||||
usage.intersects(
|
||||
Tu::RESOURCE | Tu::DEPTH_STENCIL_READ | Tu::STORAGE_READ_ONLY | Tu::STORAGE_READ_WRITE,
|
||||
),
|
||||
);
|
||||
mtl_usage.set(
|
||||
MTLTextureUsage::ShaderWrite,
|
||||
usage.intersects(Tu::STORAGE_WRITE_ONLY | Tu::STORAGE_READ_WRITE),
|
||||
);
|
||||
// needed for combined depth/stencil formats since we might
|
||||
// create a stencil-only view from them
|
||||
mtl_usage.set(
|
||||
MTLTextureUsage::PixelFormatView,
|
||||
format.is_combined_depth_stencil_format(),
|
||||
);
|
||||
|
||||
mtl_usage.set(
|
||||
MTLTextureUsage::ShaderAtomic,
|
||||
usage.intersects(Tu::STORAGE_ATOMIC),
|
||||
);
|
||||
|
||||
mtl_usage
|
||||
}
|
||||
|
||||
pub fn map_texture_view_dimension(dim: wgt::TextureViewDimension) -> MTLTextureType {
|
||||
use wgt::TextureViewDimension as Tvd;
|
||||
use MTLTextureType as MTL;
|
||||
match dim {
|
||||
Tvd::D1 => MTL::Type1D,
|
||||
Tvd::D2 => MTL::Type2D,
|
||||
Tvd::D2Array => MTL::Type2DArray,
|
||||
Tvd::D3 => MTL::Type3D,
|
||||
Tvd::Cube => MTL::TypeCube,
|
||||
Tvd::CubeArray => MTL::TypeCubeArray,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn map_compare_function(fun: wgt::CompareFunction) -> MTLCompareFunction {
|
||||
use wgt::CompareFunction as Cf;
|
||||
use MTLCompareFunction as MTL;
|
||||
match fun {
|
||||
Cf::Never => MTL::Never,
|
||||
Cf::Less => MTL::Less,
|
||||
Cf::LessEqual => MTL::LessEqual,
|
||||
Cf::Equal => MTL::Equal,
|
||||
Cf::GreaterEqual => MTL::GreaterEqual,
|
||||
Cf::Greater => MTL::Greater,
|
||||
Cf::NotEqual => MTL::NotEqual,
|
||||
Cf::Always => MTL::Always,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn map_filter_mode(filter: wgt::FilterMode) -> MTLSamplerMinMagFilter {
|
||||
use MTLSamplerMinMagFilter as MTL;
|
||||
match filter {
|
||||
wgt::FilterMode::Nearest => MTL::Nearest,
|
||||
wgt::FilterMode::Linear => MTL::Linear,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn map_address_mode(address: wgt::AddressMode) -> MTLSamplerAddressMode {
|
||||
use wgt::AddressMode as Fm;
|
||||
use MTLSamplerAddressMode as MTL;
|
||||
match address {
|
||||
Fm::Repeat => MTL::Repeat,
|
||||
Fm::MirrorRepeat => MTL::MirrorRepeat,
|
||||
Fm::ClampToEdge => MTL::ClampToEdge,
|
||||
Fm::ClampToBorder => MTL::ClampToBorderColor,
|
||||
//Fm::MirrorClamp => MTL::MirrorClampToEdge,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn map_border_color(border_color: wgt::SamplerBorderColor) -> MTLSamplerBorderColor {
|
||||
use MTLSamplerBorderColor as MTL;
|
||||
match border_color {
|
||||
wgt::SamplerBorderColor::TransparentBlack => MTL::TransparentBlack,
|
||||
wgt::SamplerBorderColor::OpaqueBlack => MTL::OpaqueBlack,
|
||||
wgt::SamplerBorderColor::OpaqueWhite => MTL::OpaqueWhite,
|
||||
wgt::SamplerBorderColor::Zero => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn map_primitive_topology(
|
||||
topology: wgt::PrimitiveTopology,
|
||||
) -> (MTLPrimitiveTopologyClass, MTLPrimitiveType) {
|
||||
use wgt::PrimitiveTopology as Pt;
|
||||
match topology {
|
||||
Pt::PointList => (MTLPrimitiveTopologyClass::Point, MTLPrimitiveType::Point),
|
||||
Pt::LineList => (MTLPrimitiveTopologyClass::Line, MTLPrimitiveType::Line),
|
||||
Pt::LineStrip => (MTLPrimitiveTopologyClass::Line, MTLPrimitiveType::LineStrip),
|
||||
Pt::TriangleList => (
|
||||
MTLPrimitiveTopologyClass::Triangle,
|
||||
MTLPrimitiveType::Triangle,
|
||||
),
|
||||
Pt::TriangleStrip => (
|
||||
MTLPrimitiveTopologyClass::Triangle,
|
||||
MTLPrimitiveType::TriangleStrip,
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn map_color_write(mask: wgt::ColorWrites) -> MTLColorWriteMask {
|
||||
let mut raw_mask = MTLColorWriteMask::empty();
|
||||
|
||||
if mask.contains(wgt::ColorWrites::RED) {
|
||||
raw_mask |= MTLColorWriteMask::Red;
|
||||
}
|
||||
if mask.contains(wgt::ColorWrites::GREEN) {
|
||||
raw_mask |= MTLColorWriteMask::Green;
|
||||
}
|
||||
if mask.contains(wgt::ColorWrites::BLUE) {
|
||||
raw_mask |= MTLColorWriteMask::Blue;
|
||||
}
|
||||
if mask.contains(wgt::ColorWrites::ALPHA) {
|
||||
raw_mask |= MTLColorWriteMask::Alpha;
|
||||
}
|
||||
|
||||
raw_mask
|
||||
}
|
||||
|
||||
pub fn map_blend_factor(factor: wgt::BlendFactor) -> MTLBlendFactor {
|
||||
use wgt::BlendFactor as Bf;
|
||||
use MTLBlendFactor as MTL;
|
||||
|
||||
match factor {
|
||||
Bf::Zero => MTL::Zero,
|
||||
Bf::One => MTL::One,
|
||||
Bf::Src => MTL::SourceColor,
|
||||
Bf::OneMinusSrc => MTL::OneMinusSourceColor,
|
||||
Bf::Dst => MTL::DestinationColor,
|
||||
Bf::OneMinusDst => MTL::OneMinusDestinationColor,
|
||||
Bf::SrcAlpha => MTL::SourceAlpha,
|
||||
Bf::OneMinusSrcAlpha => MTL::OneMinusSourceAlpha,
|
||||
Bf::DstAlpha => MTL::DestinationAlpha,
|
||||
Bf::OneMinusDstAlpha => MTL::OneMinusDestinationAlpha,
|
||||
Bf::Constant => MTL::BlendColor,
|
||||
Bf::OneMinusConstant => MTL::OneMinusBlendColor,
|
||||
Bf::SrcAlphaSaturated => MTL::SourceAlphaSaturated,
|
||||
Bf::Src1 => MTL::Source1Color,
|
||||
Bf::OneMinusSrc1 => MTL::OneMinusSource1Color,
|
||||
Bf::Src1Alpha => MTL::Source1Alpha,
|
||||
Bf::OneMinusSrc1Alpha => MTL::OneMinusSource1Alpha,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn map_blend_op(operation: wgt::BlendOperation) -> MTLBlendOperation {
|
||||
use wgt::BlendOperation as Bo;
|
||||
use MTLBlendOperation as MTL;
|
||||
|
||||
match operation {
|
||||
Bo::Add => MTL::Add,
|
||||
Bo::Subtract => MTL::Subtract,
|
||||
Bo::ReverseSubtract => MTL::ReverseSubtract,
|
||||
Bo::Min => MTL::Min,
|
||||
Bo::Max => MTL::Max,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn map_blend_component(
|
||||
component: &wgt::BlendComponent,
|
||||
) -> (MTLBlendOperation, MTLBlendFactor, MTLBlendFactor) {
|
||||
(
|
||||
map_blend_op(component.operation),
|
||||
map_blend_factor(component.src_factor),
|
||||
map_blend_factor(component.dst_factor),
|
||||
)
|
||||
}
|
||||
|
||||
pub fn map_vertex_format(format: wgt::VertexFormat) -> MTLVertexFormat {
|
||||
use wgt::VertexFormat as Vf;
|
||||
use MTLVertexFormat as MTL;
|
||||
|
||||
match format {
|
||||
Vf::Unorm8 => MTL::UCharNormalized,
|
||||
Vf::Snorm8 => MTL::CharNormalized,
|
||||
Vf::Uint8 => MTL::UChar,
|
||||
Vf::Sint8 => MTL::Char,
|
||||
Vf::Unorm8x2 => MTL::UChar2Normalized,
|
||||
Vf::Snorm8x2 => MTL::Char2Normalized,
|
||||
Vf::Uint8x2 => MTL::UChar2,
|
||||
Vf::Sint8x2 => MTL::Char2,
|
||||
Vf::Unorm8x4 => MTL::UChar4Normalized,
|
||||
Vf::Snorm8x4 => MTL::Char4Normalized,
|
||||
Vf::Uint8x4 => MTL::UChar4,
|
||||
Vf::Sint8x4 => MTL::Char4,
|
||||
Vf::Unorm16 => MTL::UShortNormalized,
|
||||
Vf::Snorm16 => MTL::ShortNormalized,
|
||||
Vf::Uint16 => MTL::UShort,
|
||||
Vf::Sint16 => MTL::Short,
|
||||
Vf::Float16 => MTL::Half,
|
||||
Vf::Unorm16x2 => MTL::UShort2Normalized,
|
||||
Vf::Snorm16x2 => MTL::Short2Normalized,
|
||||
Vf::Uint16x2 => MTL::UShort2,
|
||||
Vf::Sint16x2 => MTL::Short2,
|
||||
Vf::Float16x2 => MTL::Half2,
|
||||
Vf::Unorm16x4 => MTL::UShort4Normalized,
|
||||
Vf::Snorm16x4 => MTL::Short4Normalized,
|
||||
Vf::Uint16x4 => MTL::UShort4,
|
||||
Vf::Sint16x4 => MTL::Short4,
|
||||
Vf::Float16x4 => MTL::Half4,
|
||||
Vf::Uint32 => MTL::UInt,
|
||||
Vf::Sint32 => MTL::Int,
|
||||
Vf::Float32 => MTL::Float,
|
||||
Vf::Uint32x2 => MTL::UInt2,
|
||||
Vf::Sint32x2 => MTL::Int2,
|
||||
Vf::Float32x2 => MTL::Float2,
|
||||
Vf::Uint32x3 => MTL::UInt3,
|
||||
Vf::Sint32x3 => MTL::Int3,
|
||||
Vf::Float32x3 => MTL::Float3,
|
||||
Vf::Uint32x4 => MTL::UInt4,
|
||||
Vf::Sint32x4 => MTL::Int4,
|
||||
Vf::Float32x4 => MTL::Float4,
|
||||
Vf::Unorm10_10_10_2 => MTL::UInt1010102Normalized,
|
||||
Vf::Unorm8x4Bgra => MTL::UChar4Normalized_BGRA,
|
||||
Vf::Float64 | Vf::Float64x2 | Vf::Float64x3 | Vf::Float64x4 => unimplemented!(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn map_index_format(format: wgt::IndexFormat) -> (u64, MTLIndexType) {
|
||||
match format {
|
||||
wgt::IndexFormat::Uint16 => (2, MTLIndexType::UInt16),
|
||||
wgt::IndexFormat::Uint32 => (4, MTLIndexType::UInt32),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn map_step_mode(mode: wgt::VertexStepMode) -> MTLVertexStepFunction {
|
||||
match mode {
|
||||
wgt::VertexStepMode::Vertex => MTLVertexStepFunction::PerVertex,
|
||||
wgt::VertexStepMode::Instance => MTLVertexStepFunction::PerInstance,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn map_stencil_op(op: wgt::StencilOperation) -> MTLStencilOperation {
|
||||
use wgt::StencilOperation as So;
|
||||
use MTLStencilOperation as MTL;
|
||||
|
||||
match op {
|
||||
So::Keep => MTL::Keep,
|
||||
So::Zero => MTL::Zero,
|
||||
So::Replace => MTL::Replace,
|
||||
So::IncrementClamp => MTL::IncrementClamp,
|
||||
So::IncrementWrap => MTL::IncrementWrap,
|
||||
So::DecrementClamp => MTL::DecrementClamp,
|
||||
So::DecrementWrap => MTL::DecrementWrap,
|
||||
So::Invert => MTL::Invert,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn map_winding(winding: wgt::FrontFace) -> MTLWinding {
|
||||
match winding {
|
||||
wgt::FrontFace::Cw => MTLWinding::Clockwise,
|
||||
wgt::FrontFace::Ccw => MTLWinding::CounterClockwise,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn map_cull_mode(face: Option<wgt::Face>) -> MTLCullMode {
|
||||
match face {
|
||||
None => MTLCullMode::None,
|
||||
Some(wgt::Face::Front) => MTLCullMode::Front,
|
||||
Some(wgt::Face::Back) => MTLCullMode::Back,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn map_range(range: &crate::MemoryRange) -> NSRange {
|
||||
NSRange {
|
||||
location: range.start as usize,
|
||||
length: (range.end - range.start) as usize,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn map_copy_extent(extent: &crate::CopyExtent) -> MTLSize {
|
||||
MTLSize {
|
||||
width: extent.width as usize,
|
||||
height: extent.height as usize,
|
||||
depth: extent.depth as usize,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn map_origin(origin: &wgt::Origin3d) -> MTLOrigin {
|
||||
MTLOrigin {
|
||||
x: origin.x as usize,
|
||||
y: origin.y as usize,
|
||||
z: origin.z as usize,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn map_store_action(store: bool, resolve: bool) -> MTLStoreAction {
|
||||
use MTLStoreAction as MTL;
|
||||
match (store, resolve) {
|
||||
(true, true) => MTL::StoreAndMultisampleResolve,
|
||||
(false, true) => MTL::MultisampleResolve,
|
||||
(true, false) => MTL::Store,
|
||||
(false, false) => MTL::DontCare,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn map_clear_color(color: &wgt::Color) -> MTLClearColor {
|
||||
MTLClearColor {
|
||||
red: color.r,
|
||||
green: color.g,
|
||||
blue: color.b,
|
||||
alpha: color.a,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_blit_option(format: wgt::TextureFormat, aspect: crate::FormatAspects) -> MTLBlitOption {
|
||||
if format.is_combined_depth_stencil_format() {
|
||||
match aspect {
|
||||
crate::FormatAspects::DEPTH => MTLBlitOption::DepthFromDepthStencil,
|
||||
crate::FormatAspects::STENCIL => MTLBlitOption::StencilFromDepthStencil,
|
||||
_ => unreachable!(),
|
||||
}
|
||||
} else {
|
||||
MTLBlitOption::None
|
||||
}
|
||||
}
|
||||
|
||||
pub fn map_render_stages(stage: wgt::ShaderStages) -> MTLRenderStages {
|
||||
let mut raw_stages = MTLRenderStages::empty();
|
||||
|
||||
if stage.contains(wgt::ShaderStages::VERTEX) {
|
||||
raw_stages |= MTLRenderStages::Vertex;
|
||||
}
|
||||
if stage.contains(wgt::ShaderStages::FRAGMENT) {
|
||||
raw_stages |= MTLRenderStages::Fragment;
|
||||
}
|
||||
|
||||
raw_stages
|
||||
}
|
||||
|
||||
pub fn map_resource_usage(ty: &wgt::BindingType) -> MTLResourceUsage {
|
||||
match ty {
|
||||
#[allow(deprecated)]
|
||||
wgt::BindingType::Texture { .. } => MTLResourceUsage::Sample,
|
||||
wgt::BindingType::StorageTexture { access, .. } => match access {
|
||||
wgt::StorageTextureAccess::WriteOnly => MTLResourceUsage::Write,
|
||||
wgt::StorageTextureAccess::ReadOnly => MTLResourceUsage::Read,
|
||||
wgt::StorageTextureAccess::Atomic | wgt::StorageTextureAccess::ReadWrite => {
|
||||
MTLResourceUsage::Read | MTLResourceUsage::Write
|
||||
}
|
||||
},
|
||||
wgt::BindingType::Sampler(..) => MTLResourceUsage::empty(),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn map_acceleration_structure_descriptor<'a>(
|
||||
entries: &'a crate::AccelerationStructureEntries<'a, super::Buffer>,
|
||||
flags: crate::AccelerationStructureBuildFlags,
|
||||
) -> Retained<MTLAccelerationStructureDescriptor> {
|
||||
let descriptor = match entries {
|
||||
crate::AccelerationStructureEntries::Instances(instances) => {
|
||||
let descriptor = MTLInstanceAccelerationStructureDescriptor::new();
|
||||
descriptor.setInstanceDescriptorType(
|
||||
MTLAccelerationStructureInstanceDescriptorType::Indirect,
|
||||
);
|
||||
descriptor.setInstanceCount(instances.count as usize);
|
||||
descriptor.setInstanceDescriptorBuffer(Some(&instances.buffer.unwrap().raw));
|
||||
unsafe {
|
||||
descriptor.setInstanceDescriptorBufferOffset(instances.offset as usize);
|
||||
}
|
||||
descriptor.into_super()
|
||||
}
|
||||
crate::AccelerationStructureEntries::Triangles(entries) => {
|
||||
let geometry_descriptors = entries
|
||||
.iter()
|
||||
.map(|triangles| {
|
||||
let descriptor = MTLAccelerationStructureTriangleGeometryDescriptor::new();
|
||||
if let Some(indices) = triangles.indices.as_ref() {
|
||||
descriptor.setIndexBuffer(Some(&indices.buffer.unwrap().raw));
|
||||
unsafe {
|
||||
descriptor.setIndexBufferOffset(indices.offset as usize);
|
||||
}
|
||||
descriptor.setIndexType(map_index_format(indices.format).1);
|
||||
descriptor.setTriangleCount(indices.count as usize / 3);
|
||||
} else {
|
||||
descriptor.setTriangleCount(triangles.vertex_count as usize / 3);
|
||||
}
|
||||
descriptor.setVertexBuffer(Some(&triangles.vertex_buffer.unwrap().raw));
|
||||
unsafe {
|
||||
descriptor.setVertexBufferOffset(
|
||||
triangles.first_vertex as usize * triangles.vertex_stride as usize,
|
||||
);
|
||||
}
|
||||
descriptor.setVertexStride(triangles.vertex_stride as usize);
|
||||
// Safety: MTLVertexFormat and MTLAttributeFormat are identical.
|
||||
// https://docs.rs/objc2-metal/latest/objc2_metal/struct.MTLAttributeFormat.html
|
||||
// https://docs.rs/objc2-metal/latest/objc2_metal/struct.MTLVertexFormat.html
|
||||
descriptor.setVertexFormat(unsafe {
|
||||
core::mem::transmute::<MTLVertexFormat, MTLAttributeFormat>(
|
||||
map_vertex_format(triangles.vertex_format),
|
||||
)
|
||||
});
|
||||
if let Some(transform) = triangles.transform.as_ref() {
|
||||
unsafe {
|
||||
descriptor.setTransformationMatrixBuffer(Some(&transform.buffer.raw));
|
||||
descriptor
|
||||
.setTransformationMatrixBufferOffset(transform.offset as usize);
|
||||
}
|
||||
}
|
||||
descriptor.setOpaque(
|
||||
triangles
|
||||
.flags
|
||||
.contains(wgt::AccelerationStructureGeometryFlags::OPAQUE),
|
||||
);
|
||||
if !triangles.flags.contains(
|
||||
wgt::AccelerationStructureGeometryFlags::NO_DUPLICATE_ANY_HIT_INVOCATION,
|
||||
) {
|
||||
descriptor.allowDuplicateIntersectionFunctionInvocation();
|
||||
}
|
||||
// descriptor.setIntersectionFunctionTableOffset(offset);
|
||||
descriptor.into_super()
|
||||
})
|
||||
.collect::<alloc::vec::Vec<Retained<MTLAccelerationStructureGeometryDescriptor>>>();
|
||||
let descriptor = MTLPrimitiveAccelerationStructureDescriptor::new();
|
||||
descriptor.setGeometryDescriptors(Some(&NSArray::from_retained_slice(
|
||||
geometry_descriptors.as_slice(),
|
||||
)));
|
||||
descriptor.into_super()
|
||||
}
|
||||
crate::AccelerationStructureEntries::AABBs(entries) => {
|
||||
let geometry_descriptors = entries
|
||||
.iter()
|
||||
.map(|aabbs| {
|
||||
let descriptor = MTLAccelerationStructureBoundingBoxGeometryDescriptor::new();
|
||||
descriptor.setBoundingBoxBuffer(Some(&aabbs.buffer.unwrap().raw));
|
||||
descriptor.setBoundingBoxCount(aabbs.count as usize);
|
||||
unsafe {
|
||||
descriptor.setBoundingBoxStride(aabbs.stride as usize);
|
||||
descriptor.setBoundingBoxBufferOffset(aabbs.offset as usize);
|
||||
}
|
||||
descriptor.setOpaque(
|
||||
aabbs
|
||||
.flags
|
||||
.contains(wgt::AccelerationStructureGeometryFlags::OPAQUE),
|
||||
);
|
||||
if !aabbs.flags.contains(
|
||||
wgt::AccelerationStructureGeometryFlags::NO_DUPLICATE_ANY_HIT_INVOCATION,
|
||||
) {
|
||||
descriptor.allowDuplicateIntersectionFunctionInvocation();
|
||||
}
|
||||
// descriptor.setIntersectionFunctionTableOffset(offset);
|
||||
descriptor.into_super()
|
||||
})
|
||||
.collect::<alloc::vec::Vec<Retained<MTLAccelerationStructureGeometryDescriptor>>>();
|
||||
let descriptor = MTLPrimitiveAccelerationStructureDescriptor::new();
|
||||
descriptor.setGeometryDescriptors(Some(&NSArray::from_retained_slice(
|
||||
geometry_descriptors.as_slice(),
|
||||
)));
|
||||
descriptor.into_super()
|
||||
}
|
||||
};
|
||||
let mut usage = MTLAccelerationStructureUsage::None;
|
||||
if flags.contains(wgt::AccelerationStructureFlags::ALLOW_UPDATE) {
|
||||
usage |= MTLAccelerationStructureUsage::Refit;
|
||||
}
|
||||
if flags.contains(wgt::AccelerationStructureFlags::PREFER_FAST_BUILD) {
|
||||
usage |= MTLAccelerationStructureUsage::PreferFastBuild;
|
||||
}
|
||||
descriptor.setUsage(usage);
|
||||
descriptor
|
||||
}
|
||||
+2028
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,48 @@
|
||||
use core::{ffi::c_void, ptr::NonNull};
|
||||
|
||||
use objc2::{
|
||||
msg_send,
|
||||
rc::Retained,
|
||||
runtime::{AnyObject, ProtocolObject},
|
||||
};
|
||||
use objc2_foundation::NSError;
|
||||
use objc2_metal::{MTLDevice, MTLLibrary};
|
||||
|
||||
#[link(name = "System")] // libdispatch lives inside libSystem
|
||||
extern "C" {
|
||||
fn dispatch_data_create(
|
||||
buffer: NonNull<c_void>,
|
||||
size: usize,
|
||||
queue: Option<NonNull<c_void>>, // dispatch_queue_t _Nullable
|
||||
destructor: *mut c_void, // dispatch_block_t _Nullable
|
||||
) -> *mut AnyObject; // dispatch_data_t
|
||||
|
||||
fn dispatch_release(object: *mut AnyObject); // dispatch_data_t
|
||||
}
|
||||
|
||||
const DISPATCH_DATA_DESTRUCTOR_DEFAULT: *mut c_void = core::ptr::null_mut();
|
||||
|
||||
/// See <https://github.com/madsmtm/objc2/issues/819>.
|
||||
///
|
||||
/// newLibraryWithData requires DispatchData, part of the Dispatch library.
|
||||
/// Not only is this a big additional dependency for a glorified `Vec<u8>`
|
||||
/// in this case, but the code in the dispatch2 rust crate is a WIP and
|
||||
/// Mozilla has decided not to vendor it yet.
|
||||
///
|
||||
/// This function allows us to deal with DispatchData without dealing
|
||||
/// with these problems.
|
||||
pub(crate) fn new_library_from_metallib_bytes(
|
||||
device: &ProtocolObject<dyn MTLDevice>,
|
||||
data: &[u8],
|
||||
) -> Result<Retained<ProtocolObject<dyn MTLLibrary>>, Retained<NSError>> {
|
||||
let buffer = NonNull::new(data.as_ptr().cast_mut()).unwrap().cast();
|
||||
let data =
|
||||
unsafe { dispatch_data_create(buffer, data.len(), None, DISPATCH_DATA_DESTRUCTOR_DEFAULT) };
|
||||
assert!(!data.is_null());
|
||||
|
||||
let res: Result<Retained<ProtocolObject<dyn MTLLibrary>>, Retained<NSError>> =
|
||||
unsafe { msg_send![device, newLibraryWithData: data, error: _] };
|
||||
|
||||
unsafe { dispatch_release(data) };
|
||||
res
|
||||
}
|
||||
+1171
File diff suppressed because it is too large
Load Diff
+195
@@ -0,0 +1,195 @@
|
||||
use alloc::borrow::ToOwned as _;
|
||||
|
||||
use objc2::{
|
||||
available,
|
||||
rc::{autoreleasepool, Retained},
|
||||
runtime::ProtocolObject,
|
||||
ClassType, Message,
|
||||
};
|
||||
use objc2_core_foundation::CGSize;
|
||||
use objc2_foundation::NSObjectProtocol;
|
||||
use objc2_metal::MTLTextureType;
|
||||
use objc2_quartz_core::{CAMetalDrawable, CAMetalLayer};
|
||||
use parking_lot::{Mutex, RwLock};
|
||||
|
||||
use super::OsFeatures;
|
||||
|
||||
impl super::Surface {
|
||||
pub fn new(layer: Retained<CAMetalLayer>) -> Self {
|
||||
Self {
|
||||
render_layer: Mutex::new(layer),
|
||||
swapchain_format: RwLock::new(None),
|
||||
extent: RwLock::new(wgt::Extent3d::default()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn from_layer(layer: &CAMetalLayer) -> Self {
|
||||
assert!(layer.isKindOfClass(CAMetalLayer::class()));
|
||||
Self::new(layer.retain())
|
||||
}
|
||||
|
||||
pub fn render_layer(&self) -> &Mutex<Retained<CAMetalLayer>> {
|
||||
&self.render_layer
|
||||
}
|
||||
|
||||
/// Gets the current dimensions of the `Surface`.
|
||||
///
|
||||
/// This function is safe to call off of the main thread. However, note that
|
||||
/// `bounds` and `contentsScale` may be modified by the main thread while
|
||||
/// this function is running, possibly resulting in the two values being out
|
||||
/// of sync. This is sound, as these properties are accessed atomically.
|
||||
/// See: <https://github.com/gfx-rs/wgpu/pull/7692>
|
||||
pub(super) fn dimensions(&self) -> wgt::Extent3d {
|
||||
let (size, scale) = {
|
||||
let render_layer = self.render_layer.lock();
|
||||
let bounds = render_layer.bounds();
|
||||
let contents_scale = 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 for super::Surface {
|
||||
type A = super::Api;
|
||||
|
||||
unsafe fn configure(
|
||||
&self,
|
||||
device: &super::Device,
|
||||
config: &crate::SurfaceConfiguration,
|
||||
) -> Result<(), crate::SurfaceError> {
|
||||
log::debug!("build swapchain {config:?}");
|
||||
|
||||
let caps = &device.shared.private_texture_format_caps;
|
||||
*self.swapchain_format.write() = Some(config.format);
|
||||
*self.extent.write() = config.extent;
|
||||
|
||||
let render_layer = self.render_layer.lock();
|
||||
let framebuffer_only = config.usage == wgt::TextureUses::COLOR_TARGET;
|
||||
let display_sync = match config.present_mode {
|
||||
wgt::PresentMode::Fifo => true,
|
||||
wgt::PresentMode::Immediate => false,
|
||||
m => unreachable!("Unsupported present mode: {m:?}"),
|
||||
};
|
||||
let drawable_size = CGSize::new(config.extent.width as f64, config.extent.height as f64);
|
||||
|
||||
match config.composite_alpha_mode {
|
||||
wgt::CompositeAlphaMode::Opaque => render_layer.setOpaque(true),
|
||||
wgt::CompositeAlphaMode::PostMultiplied => render_layer.setOpaque(false),
|
||||
_ => (),
|
||||
}
|
||||
|
||||
let device_raw = &device.shared.device;
|
||||
render_layer.setDevice(Some(device_raw));
|
||||
render_layer.setPixelFormat(caps.map_format(config.format));
|
||||
render_layer.setFramebufferOnly(framebuffer_only);
|
||||
// opt-in to Metal EDR
|
||||
// EDR potentially more power used in display and more bandwidth, memory footprint.
|
||||
let wants_edr = config.format == wgt::TextureFormat::Rgba16Float;
|
||||
if wants_edr != render_layer.wantsExtendedDynamicRangeContent() {
|
||||
render_layer.setWantsExtendedDynamicRangeContent(wants_edr);
|
||||
}
|
||||
|
||||
// this gets ignored on iOS for certain OS/device combinations (iphone5s iOS 10.3)
|
||||
render_layer.setMaximumDrawableCount(config.maximum_frame_latency as usize + 1);
|
||||
render_layer.setDrawableSize(drawable_size);
|
||||
// https://developer.apple.com/documentation/quartzcore/cametallayer/allowsnextdrawabletimeout
|
||||
if available!(macos = 10.13, ios = 11.0, tvos = 11.0, visionos = 1.0) {
|
||||
render_layer.setAllowsNextDrawableTimeout(false);
|
||||
}
|
||||
if OsFeatures::display_sync() {
|
||||
render_layer.setDisplaySyncEnabled(display_sync);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
unsafe fn unconfigure(&self, _device: &super::Device) {
|
||||
*self.swapchain_format.write() = None;
|
||||
}
|
||||
|
||||
unsafe fn acquire_texture(
|
||||
&self,
|
||||
_timeout: Option<core::time::Duration>, // TODO
|
||||
_fence: &super::Fence,
|
||||
) -> Result<crate::AcquiredSurfaceTexture<super::Api>, crate::SurfaceError> {
|
||||
let render_layer = self.render_layer.lock();
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
{
|
||||
// Workaround for https://github.com/gfx-rs/wgpu/issues/8309
|
||||
// When the window is occluded on macOS, presented drawables get stuck waiting
|
||||
// for vsync. Check the window's occlusion state and skip acquisition if
|
||||
// the window is not visible - this avoids a 1-second hang in nextDrawable().
|
||||
use objc2::rc::Retained;
|
||||
use objc2::runtime::NSObject;
|
||||
use objc2_quartz_core::CALayer;
|
||||
|
||||
// The CAMetalLayer is typically a sublayer, so we need to traverse up
|
||||
// to find the root layer whose delegate is the NSView.
|
||||
let mut current_layer: Option<Retained<CALayer>> =
|
||||
Some(Retained::into_super(render_layer.clone()));
|
||||
|
||||
while let Some(layer) = current_layer {
|
||||
if let Some(delegate) = layer.delegate() {
|
||||
// Found a layer with a delegate - this should be the NSView
|
||||
let window: Option<Retained<NSObject>> =
|
||||
unsafe { objc2::msg_send![&*delegate, window] };
|
||||
|
||||
if let Some(window) = window {
|
||||
const NS_WINDOW_OCCLUSION_STATE_VISIBLE: usize = 1 << 1;
|
||||
let occlusion_state: usize =
|
||||
unsafe { objc2::msg_send![&*window, occlusionState] };
|
||||
let is_visible = (occlusion_state & NS_WINDOW_OCCLUSION_STATE_VISIBLE) != 0;
|
||||
|
||||
if !is_visible {
|
||||
return Err(crate::SurfaceError::Occluded);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
current_layer = layer.superlayer();
|
||||
}
|
||||
}
|
||||
|
||||
let (drawable, texture) = match autoreleasepool(|_| {
|
||||
render_layer
|
||||
.nextDrawable()
|
||||
.map(|drawable| (drawable.to_owned(), drawable.texture().to_owned()))
|
||||
}) {
|
||||
Some(pair) => pair,
|
||||
None => return Err(crate::SurfaceError::Timeout),
|
||||
};
|
||||
|
||||
let swapchain_format = self.swapchain_format.read().unwrap();
|
||||
let extent = self.extent.read();
|
||||
let suf_texture = super::SurfaceTexture {
|
||||
texture: super::Texture {
|
||||
raw: texture,
|
||||
format: swapchain_format,
|
||||
raw_type: MTLTextureType::Type2D,
|
||||
array_layers: 1,
|
||||
mip_levels: 1,
|
||||
copy_size: crate::CopyExtent {
|
||||
width: extent.width,
|
||||
height: extent.height,
|
||||
depth: 1,
|
||||
},
|
||||
},
|
||||
drawable: ProtocolObject::from_retained(drawable),
|
||||
present_with_transaction: render_layer.presentsWithTransaction(),
|
||||
};
|
||||
|
||||
Ok(crate::AcquiredSurfaceTexture {
|
||||
texture: suf_texture,
|
||||
suboptimal: false,
|
||||
})
|
||||
}
|
||||
|
||||
unsafe fn discard_texture(&self, _texture: super::SurfaceTexture) {}
|
||||
}
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
//! Handling of global timestamps.
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug)]
|
||||
struct MachTimebaseInfo {
|
||||
numerator: u32,
|
||||
denominator: u32,
|
||||
}
|
||||
extern "C" {
|
||||
fn mach_timebase_info(out: *mut MachTimebaseInfo) -> u32;
|
||||
fn mach_absolute_time() -> u64;
|
||||
}
|
||||
|
||||
/// A timer which uses mach_absolute_time to get its time. This is what the metal callbacks use.
|
||||
#[derive(Debug)]
|
||||
pub struct PresentationTimer {
|
||||
scale: MachTimebaseInfo,
|
||||
}
|
||||
impl PresentationTimer {
|
||||
/// Generates a new timer.
|
||||
pub fn new() -> Self {
|
||||
// Default to 1 / 1 in case the call to timebase_info fails.
|
||||
let mut scale = MachTimebaseInfo {
|
||||
numerator: 1,
|
||||
denominator: 1,
|
||||
};
|
||||
unsafe { mach_timebase_info(&mut scale) };
|
||||
|
||||
Self { scale }
|
||||
}
|
||||
|
||||
/// Gets the current time in nanoseconds.
|
||||
pub fn get_timestamp_ns(&self) -> u128 {
|
||||
let time = unsafe { mach_absolute_time() };
|
||||
|
||||
(time as u128 * self.scale.numerator as u128) / self.scale.denominator as u128
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user