зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1810495 - Update wgpu to fac4731288117d951d0944d96cf0b00fa006dd6c. r=webgpu-reviewers,teoxoy
Differential Revision: https://phabricator.services.mozilla.com/D166916
This commit is contained in:
Родитель
5a7dea7d91
Коммит
db956b65c9
|
@ -27,12 +27,12 @@ rev = "722b30d2f1634714befab967ecae627813fa4cf0"
|
|||
|
||||
[source."https://github.com/gfx-rs/naga"]
|
||||
git = "https://github.com/gfx-rs/naga"
|
||||
rev = "e7fc8e6"
|
||||
rev = "e98bd92"
|
||||
replace-with = "vendored-sources"
|
||||
|
||||
[source."https://github.com/gfx-rs/wgpu"]
|
||||
git = "https://github.com/gfx-rs/wgpu"
|
||||
rev = "186a29c34d54d8628bbebb998a537210ac565b71"
|
||||
rev = "fac4731288117d951d0944d96cf0b00fa006dd6c"
|
||||
replace-with = "vendored-sources"
|
||||
|
||||
[source."https://github.com/glandium/warp"]
|
||||
|
|
|
@ -140,9 +140,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "ash"
|
||||
version = "0.37.1+1.3.235"
|
||||
version = "0.37.2+1.3.238"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "911015c962d56e2e4052f40182ca5462ba60a3d2ff04e827c365a0ab3d65726d"
|
||||
checksum = "28bf19c1f0a470be5fbf7522a308a05df06610252c5bcf5143e1b23f629a9a03"
|
||||
dependencies = [
|
||||
"libloading",
|
||||
]
|
||||
|
@ -3638,7 +3638,7 @@ checksum = "a2983372caf4480544083767bf2d27defafe32af49ab4df3a0b7fc90793a3664"
|
|||
[[package]]
|
||||
name = "naga"
|
||||
version = "0.10.0"
|
||||
source = "git+https://github.com/gfx-rs/naga?rev=e7fc8e6#e7fc8e64f2f23397b149217ecce6e123c5aa5092"
|
||||
source = "git+https://github.com/gfx-rs/naga?rev=e98bd92#e98bd9264c3a6b04dff15a6b1213c0c80201740a"
|
||||
dependencies = [
|
||||
"bit-set",
|
||||
"bitflags",
|
||||
|
@ -6347,7 +6347,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "wgpu-core"
|
||||
version = "0.14.0"
|
||||
source = "git+https://github.com/gfx-rs/wgpu?rev=186a29c34d54d8628bbebb998a537210ac565b71#186a29c34d54d8628bbebb998a537210ac565b71"
|
||||
source = "git+https://github.com/gfx-rs/wgpu?rev=fac4731288117d951d0944d96cf0b00fa006dd6c#fac4731288117d951d0944d96cf0b00fa006dd6c"
|
||||
dependencies = [
|
||||
"arrayvec",
|
||||
"bit-vec",
|
||||
|
@ -6356,7 +6356,7 @@ dependencies = [
|
|||
"fxhash",
|
||||
"log",
|
||||
"naga",
|
||||
"parking_lot 0.12.999",
|
||||
"parking_lot 0.11.2",
|
||||
"profiling",
|
||||
"ron",
|
||||
"serde",
|
||||
|
@ -6370,7 +6370,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "wgpu-hal"
|
||||
version = "0.14.0"
|
||||
source = "git+https://github.com/gfx-rs/wgpu?rev=186a29c34d54d8628bbebb998a537210ac565b71#186a29c34d54d8628bbebb998a537210ac565b71"
|
||||
source = "git+https://github.com/gfx-rs/wgpu?rev=fac4731288117d951d0944d96cf0b00fa006dd6c#fac4731288117d951d0944d96cf0b00fa006dd6c"
|
||||
dependencies = [
|
||||
"android_system_properties",
|
||||
"arrayvec",
|
||||
|
@ -6392,7 +6392,7 @@ dependencies = [
|
|||
"metal",
|
||||
"naga",
|
||||
"objc",
|
||||
"parking_lot 0.12.999",
|
||||
"parking_lot 0.11.2",
|
||||
"profiling",
|
||||
"range-alloc",
|
||||
"raw-window-handle",
|
||||
|
@ -6408,7 +6408,7 @@ dependencies = [
|
|||
[[package]]
|
||||
name = "wgpu-types"
|
||||
version = "0.14.0"
|
||||
source = "git+https://github.com/gfx-rs/wgpu?rev=186a29c34d54d8628bbebb998a537210ac565b71#186a29c34d54d8628bbebb998a537210ac565b71"
|
||||
source = "git+https://github.com/gfx-rs/wgpu?rev=fac4731288117d951d0944d96cf0b00fa006dd6c#fac4731288117d951d0944d96cf0b00fa006dd6c"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"serde",
|
||||
|
|
|
@ -17,7 +17,7 @@ default = []
|
|||
[dependencies.wgc]
|
||||
package = "wgpu-core"
|
||||
git = "https://github.com/gfx-rs/wgpu"
|
||||
rev = "186a29c34d54d8628bbebb998a537210ac565b71"
|
||||
rev = "fac4731288117d951d0944d96cf0b00fa006dd6c"
|
||||
#Note: "replay" shouldn't ideally be needed,
|
||||
# but it allows us to serialize everything across IPC.
|
||||
features = ["replay", "trace", "serial-pass", "strict_asserts", "wgsl", "renderdoc"]
|
||||
|
@ -27,32 +27,32 @@ features = ["replay", "trace", "serial-pass", "strict_asserts", "wgsl", "renderd
|
|||
[target.'cfg(any(target_os = "macos", target_os = "ios"))'.dependencies.wgc]
|
||||
package = "wgpu-core"
|
||||
git = "https://github.com/gfx-rs/wgpu"
|
||||
rev = "186a29c34d54d8628bbebb998a537210ac565b71"
|
||||
rev = "fac4731288117d951d0944d96cf0b00fa006dd6c"
|
||||
features = ["metal"]
|
||||
|
||||
# We want the wgpu-core Direct3D backends on Windows.
|
||||
[target.'cfg(windows)'.dependencies.wgc]
|
||||
package = "wgpu-core"
|
||||
git = "https://github.com/gfx-rs/wgpu"
|
||||
rev = "186a29c34d54d8628bbebb998a537210ac565b71"
|
||||
rev = "fac4731288117d951d0944d96cf0b00fa006dd6c"
|
||||
features = ["dx11", "dx12"]
|
||||
|
||||
# We want the wgpu-core Vulkan backend on Linux and Windows.
|
||||
[target.'cfg(any(windows, all(unix, not(any(target_os = "macos", target_os = "ios")))))'.dependencies.wgc]
|
||||
package = "wgpu-core"
|
||||
git = "https://github.com/gfx-rs/wgpu"
|
||||
rev = "186a29c34d54d8628bbebb998a537210ac565b71"
|
||||
rev = "fac4731288117d951d0944d96cf0b00fa006dd6c"
|
||||
features = ["vulkan"]
|
||||
|
||||
[dependencies.wgt]
|
||||
package = "wgpu-types"
|
||||
git = "https://github.com/gfx-rs/wgpu"
|
||||
rev = "186a29c34d54d8628bbebb998a537210ac565b71"
|
||||
rev = "fac4731288117d951d0944d96cf0b00fa006dd6c"
|
||||
|
||||
[dependencies.wgh]
|
||||
package = "wgpu-hal"
|
||||
git = "https://github.com/gfx-rs/wgpu"
|
||||
rev = "186a29c34d54d8628bbebb998a537210ac565b71"
|
||||
rev = "fac4731288117d951d0944d96cf0b00fa006dd6c"
|
||||
|
||||
[dependencies]
|
||||
bincode = "1"
|
||||
|
|
|
@ -20,11 +20,11 @@ origin:
|
|||
|
||||
# Human-readable identifier for this version/release
|
||||
# Generally "version NNN", "tag SSS", "bookmark SSS"
|
||||
release: commit f14bee67
|
||||
release: commit fac4731288117d951d0944d96cf0b00fa006dd6c
|
||||
|
||||
# Revision to pull in
|
||||
# Must be a long or short commit SHA (long preferred)
|
||||
revision: 186a29c34d54d8628bbebb998a537210ac565b71
|
||||
revision: fac4731288117d951d0944d96cf0b00fa006dd6c
|
||||
|
||||
license: ['MIT', 'Apache-2.0']
|
||||
|
||||
|
|
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
|
@ -13,7 +13,7 @@
|
|||
edition = "2018"
|
||||
rust-version = "1.59.0"
|
||||
name = "ash"
|
||||
version = "0.37.1+1.3.235"
|
||||
version = "0.37.2+1.3.238"
|
||||
authors = [
|
||||
"Maik Klein <maikklein@googlemail.com>",
|
||||
"Benjamin Saunders <ben.e.saunders@gmail.com>",
|
||||
|
|
|
@ -51,7 +51,7 @@ impl DrawIndirectCount {
|
|||
max_draw_count: u32,
|
||||
stride: u32,
|
||||
) {
|
||||
(self.fp.cmd_draw_indexed_indirect_count_khr)(
|
||||
(self.fp.cmd_draw_indirect_count_khr)(
|
||||
command_buffer,
|
||||
buffer,
|
||||
offset,
|
||||
|
|
|
@ -1143,6 +1143,26 @@ impl DeviceAddressBindingFlagsEXT {
|
|||
}
|
||||
#[repr(transparent)]
|
||||
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
#[doc = "<https://www.khronos.org/registry/vulkan/specs/1.3-extensions/man/html/VkPresentScalingFlagBitsEXT.html>"]
|
||||
pub struct PresentScalingFlagsEXT(pub(crate) Flags);
|
||||
vk_bitflags_wrapped!(PresentScalingFlagsEXT, Flags);
|
||||
impl PresentScalingFlagsEXT {
|
||||
pub const ONE_TO_ONE: Self = Self(0b1);
|
||||
pub const ASPECT_RATIO_STRETCH: Self = Self(0b10);
|
||||
pub const STRETCH: Self = Self(0b100);
|
||||
}
|
||||
#[repr(transparent)]
|
||||
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
#[doc = "<https://www.khronos.org/registry/vulkan/specs/1.3-extensions/man/html/VkPresentGravityFlagBitsEXT.html>"]
|
||||
pub struct PresentGravityFlagsEXT(pub(crate) Flags);
|
||||
vk_bitflags_wrapped!(PresentGravityFlagsEXT, Flags);
|
||||
impl PresentGravityFlagsEXT {
|
||||
pub const MIN: Self = Self(0b1);
|
||||
pub const MAX: Self = Self(0b10);
|
||||
pub const CENTERED: Self = Self(0b100);
|
||||
}
|
||||
#[repr(transparent)]
|
||||
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
#[doc = "<https://www.khronos.org/registry/vulkan/specs/1.3-extensions/man/html/VkVideoCodecOperationFlagBitsKHR.html>"]
|
||||
pub struct VideoCodecOperationFlagsKHR(pub(crate) Flags);
|
||||
vk_bitflags_wrapped!(VideoCodecOperationFlagsKHR, Flags);
|
||||
|
@ -1191,10 +1211,10 @@ impl VideoSessionCreateFlagsKHR {
|
|||
}
|
||||
#[repr(transparent)]
|
||||
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
#[doc = "<https://www.khronos.org/registry/vulkan/specs/1.3-extensions/man/html/VkVideoDecodeH264PictureLayoutFlagBitsEXT.html>"]
|
||||
pub struct VideoDecodeH264PictureLayoutFlagsEXT(pub(crate) Flags);
|
||||
vk_bitflags_wrapped!(VideoDecodeH264PictureLayoutFlagsEXT, Flags);
|
||||
impl VideoDecodeH264PictureLayoutFlagsEXT {
|
||||
#[doc = "<https://www.khronos.org/registry/vulkan/specs/1.3-extensions/man/html/VkVideoDecodeH264PictureLayoutFlagBitsKHR.html>"]
|
||||
pub struct VideoDecodeH264PictureLayoutFlagsKHR(pub(crate) Flags);
|
||||
vk_bitflags_wrapped!(VideoDecodeH264PictureLayoutFlagsKHR, Flags);
|
||||
impl VideoDecodeH264PictureLayoutFlagsKHR {
|
||||
pub const PROGRESSIVE: Self = Self(0);
|
||||
pub const INTERLACED_INTERLEAVED_LINES: Self = Self(0b1);
|
||||
pub const INTERLACED_SEPARATE_PLANES: Self = Self(0b10);
|
||||
|
|
|
@ -1404,6 +1404,26 @@ impl fmt::Debug for DeviceQueueCreateFlags {
|
|||
debug_flags(f, KNOWN, self.0)
|
||||
}
|
||||
}
|
||||
impl fmt::Debug for DirectDriverLoadingFlagsLUNARG {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
const KNOWN: &[(Flags, &str)] = &[];
|
||||
debug_flags(f, KNOWN, self.0)
|
||||
}
|
||||
}
|
||||
impl fmt::Debug for DirectDriverLoadingModeLUNARG {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
let name = match *self {
|
||||
Self::EXCLUSIVE => Some("EXCLUSIVE"),
|
||||
Self::INCLUSIVE => Some("INCLUSIVE"),
|
||||
_ => None,
|
||||
};
|
||||
if let Some(x) = name {
|
||||
f.write_str(x)
|
||||
} else {
|
||||
self.0.fmt(f)
|
||||
}
|
||||
}
|
||||
}
|
||||
impl fmt::Debug for DirectFBSurfaceCreateFlagsEXT {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
const KNOWN: &[(Flags, &str)] = &[];
|
||||
|
@ -3842,6 +3862,16 @@ impl fmt::Debug for PolygonMode {
|
|||
}
|
||||
}
|
||||
}
|
||||
impl fmt::Debug for PresentGravityFlagsEXT {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
const KNOWN: &[(Flags, &str)] = &[
|
||||
(PresentGravityFlagsEXT::MIN.0, "MIN"),
|
||||
(PresentGravityFlagsEXT::MAX.0, "MAX"),
|
||||
(PresentGravityFlagsEXT::CENTERED.0, "CENTERED"),
|
||||
];
|
||||
debug_flags(f, KNOWN, self.0)
|
||||
}
|
||||
}
|
||||
impl fmt::Debug for PresentModeKHR {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
let name = match *self {
|
||||
|
@ -3860,6 +3890,19 @@ impl fmt::Debug for PresentModeKHR {
|
|||
}
|
||||
}
|
||||
}
|
||||
impl fmt::Debug for PresentScalingFlagsEXT {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
const KNOWN: &[(Flags, &str)] = &[
|
||||
(PresentScalingFlagsEXT::ONE_TO_ONE.0, "ONE_TO_ONE"),
|
||||
(
|
||||
PresentScalingFlagsEXT::ASPECT_RATIO_STRETCH.0,
|
||||
"ASPECT_RATIO_STRETCH",
|
||||
),
|
||||
(PresentScalingFlagsEXT::STRETCH.0, "STRETCH"),
|
||||
];
|
||||
debug_flags(f, KNOWN, self.0)
|
||||
}
|
||||
}
|
||||
impl fmt::Debug for PrimitiveTopology {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
let name = match *self {
|
||||
|
@ -4744,17 +4787,17 @@ impl fmt::Debug for StructureType {
|
|||
Self::VIDEO_ENCODE_H265_RATE_CONTROL_LAYER_INFO_EXT => {
|
||||
Some("VIDEO_ENCODE_H265_RATE_CONTROL_LAYER_INFO_EXT")
|
||||
}
|
||||
Self::VIDEO_DECODE_H264_CAPABILITIES_EXT => Some("VIDEO_DECODE_H264_CAPABILITIES_EXT"),
|
||||
Self::VIDEO_DECODE_H264_PICTURE_INFO_EXT => Some("VIDEO_DECODE_H264_PICTURE_INFO_EXT"),
|
||||
Self::VIDEO_DECODE_H264_PROFILE_INFO_EXT => Some("VIDEO_DECODE_H264_PROFILE_INFO_EXT"),
|
||||
Self::VIDEO_DECODE_H264_SESSION_PARAMETERS_CREATE_INFO_EXT => {
|
||||
Some("VIDEO_DECODE_H264_SESSION_PARAMETERS_CREATE_INFO_EXT")
|
||||
Self::VIDEO_DECODE_H264_CAPABILITIES_KHR => Some("VIDEO_DECODE_H264_CAPABILITIES_KHR"),
|
||||
Self::VIDEO_DECODE_H264_PICTURE_INFO_KHR => Some("VIDEO_DECODE_H264_PICTURE_INFO_KHR"),
|
||||
Self::VIDEO_DECODE_H264_PROFILE_INFO_KHR => Some("VIDEO_DECODE_H264_PROFILE_INFO_KHR"),
|
||||
Self::VIDEO_DECODE_H264_SESSION_PARAMETERS_CREATE_INFO_KHR => {
|
||||
Some("VIDEO_DECODE_H264_SESSION_PARAMETERS_CREATE_INFO_KHR")
|
||||
}
|
||||
Self::VIDEO_DECODE_H264_SESSION_PARAMETERS_ADD_INFO_EXT => {
|
||||
Some("VIDEO_DECODE_H264_SESSION_PARAMETERS_ADD_INFO_EXT")
|
||||
Self::VIDEO_DECODE_H264_SESSION_PARAMETERS_ADD_INFO_KHR => {
|
||||
Some("VIDEO_DECODE_H264_SESSION_PARAMETERS_ADD_INFO_KHR")
|
||||
}
|
||||
Self::VIDEO_DECODE_H264_DPB_SLOT_INFO_EXT => {
|
||||
Some("VIDEO_DECODE_H264_DPB_SLOT_INFO_EXT")
|
||||
Self::VIDEO_DECODE_H264_DPB_SLOT_INFO_KHR => {
|
||||
Some("VIDEO_DECODE_H264_DPB_SLOT_INFO_KHR")
|
||||
}
|
||||
Self::TEXTURE_LOD_GATHER_FORMAT_PROPERTIES_AMD => {
|
||||
Some("TEXTURE_LOD_GATHER_FORMAT_PROPERTIES_AMD")
|
||||
|
@ -5122,17 +5165,17 @@ impl fmt::Debug for StructureType {
|
|||
Self::PHYSICAL_DEVICE_SHADER_CORE_PROPERTIES_AMD => {
|
||||
Some("PHYSICAL_DEVICE_SHADER_CORE_PROPERTIES_AMD")
|
||||
}
|
||||
Self::VIDEO_DECODE_H265_CAPABILITIES_EXT => Some("VIDEO_DECODE_H265_CAPABILITIES_EXT"),
|
||||
Self::VIDEO_DECODE_H265_SESSION_PARAMETERS_CREATE_INFO_EXT => {
|
||||
Some("VIDEO_DECODE_H265_SESSION_PARAMETERS_CREATE_INFO_EXT")
|
||||
Self::VIDEO_DECODE_H265_CAPABILITIES_KHR => Some("VIDEO_DECODE_H265_CAPABILITIES_KHR"),
|
||||
Self::VIDEO_DECODE_H265_SESSION_PARAMETERS_CREATE_INFO_KHR => {
|
||||
Some("VIDEO_DECODE_H265_SESSION_PARAMETERS_CREATE_INFO_KHR")
|
||||
}
|
||||
Self::VIDEO_DECODE_H265_SESSION_PARAMETERS_ADD_INFO_EXT => {
|
||||
Some("VIDEO_DECODE_H265_SESSION_PARAMETERS_ADD_INFO_EXT")
|
||||
Self::VIDEO_DECODE_H265_SESSION_PARAMETERS_ADD_INFO_KHR => {
|
||||
Some("VIDEO_DECODE_H265_SESSION_PARAMETERS_ADD_INFO_KHR")
|
||||
}
|
||||
Self::VIDEO_DECODE_H265_PROFILE_INFO_EXT => Some("VIDEO_DECODE_H265_PROFILE_INFO_EXT"),
|
||||
Self::VIDEO_DECODE_H265_PICTURE_INFO_EXT => Some("VIDEO_DECODE_H265_PICTURE_INFO_EXT"),
|
||||
Self::VIDEO_DECODE_H265_DPB_SLOT_INFO_EXT => {
|
||||
Some("VIDEO_DECODE_H265_DPB_SLOT_INFO_EXT")
|
||||
Self::VIDEO_DECODE_H265_PROFILE_INFO_KHR => Some("VIDEO_DECODE_H265_PROFILE_INFO_KHR"),
|
||||
Self::VIDEO_DECODE_H265_PICTURE_INFO_KHR => Some("VIDEO_DECODE_H265_PICTURE_INFO_KHR"),
|
||||
Self::VIDEO_DECODE_H265_DPB_SLOT_INFO_KHR => {
|
||||
Some("VIDEO_DECODE_H265_DPB_SLOT_INFO_KHR")
|
||||
}
|
||||
Self::DEVICE_QUEUE_GLOBAL_PRIORITY_CREATE_INFO_KHR => {
|
||||
Some("DEVICE_QUEUE_GLOBAL_PRIORITY_CREATE_INFO_KHR")
|
||||
|
@ -5334,6 +5377,25 @@ impl fmt::Debug for StructureType {
|
|||
Self::PHYSICAL_DEVICE_SHADER_ATOMIC_FLOAT_2_FEATURES_EXT => {
|
||||
Some("PHYSICAL_DEVICE_SHADER_ATOMIC_FLOAT_2_FEATURES_EXT")
|
||||
}
|
||||
Self::SURFACE_PRESENT_MODE_EXT => Some("SURFACE_PRESENT_MODE_EXT"),
|
||||
Self::SURFACE_PRESENT_SCALING_CAPABILITIES_EXT => {
|
||||
Some("SURFACE_PRESENT_SCALING_CAPABILITIES_EXT")
|
||||
}
|
||||
Self::SURFACE_PRESENT_MODE_COMPATIBILITY_EXT => {
|
||||
Some("SURFACE_PRESENT_MODE_COMPATIBILITY_EXT")
|
||||
}
|
||||
Self::PHYSICAL_DEVICE_SWAPCHAIN_MAINTENANCE_1_FEATURES_EXT => {
|
||||
Some("PHYSICAL_DEVICE_SWAPCHAIN_MAINTENANCE_1_FEATURES_EXT")
|
||||
}
|
||||
Self::SWAPCHAIN_PRESENT_FENCE_INFO_EXT => Some("SWAPCHAIN_PRESENT_FENCE_INFO_EXT"),
|
||||
Self::SWAPCHAIN_PRESENT_MODES_CREATE_INFO_EXT => {
|
||||
Some("SWAPCHAIN_PRESENT_MODES_CREATE_INFO_EXT")
|
||||
}
|
||||
Self::SWAPCHAIN_PRESENT_MODE_INFO_EXT => Some("SWAPCHAIN_PRESENT_MODE_INFO_EXT"),
|
||||
Self::SWAPCHAIN_PRESENT_SCALING_CREATE_INFO_EXT => {
|
||||
Some("SWAPCHAIN_PRESENT_SCALING_CREATE_INFO_EXT")
|
||||
}
|
||||
Self::RELEASE_SWAPCHAIN_IMAGES_INFO_EXT => Some("RELEASE_SWAPCHAIN_IMAGES_INFO_EXT"),
|
||||
Self::PHYSICAL_DEVICE_DEVICE_GENERATED_COMMANDS_PROPERTIES_NV => {
|
||||
Some("PHYSICAL_DEVICE_DEVICE_GENERATED_COMMANDS_PROPERTIES_NV")
|
||||
}
|
||||
|
@ -5766,6 +5828,8 @@ impl fmt::Debug for StructureType {
|
|||
Self::RENDER_PASS_SUBPASS_FEEDBACK_CREATE_INFO_EXT => {
|
||||
Some("RENDER_PASS_SUBPASS_FEEDBACK_CREATE_INFO_EXT")
|
||||
}
|
||||
Self::DIRECT_DRIVER_LOADING_INFO_LUNARG => Some("DIRECT_DRIVER_LOADING_INFO_LUNARG"),
|
||||
Self::DIRECT_DRIVER_LOADING_LIST_LUNARG => Some("DIRECT_DRIVER_LOADING_LIST_LUNARG"),
|
||||
Self::PHYSICAL_DEVICE_SHADER_MODULE_IDENTIFIER_FEATURES_EXT => {
|
||||
Some("PHYSICAL_DEVICE_SHADER_MODULE_IDENTIFIER_FEATURES_EXT")
|
||||
}
|
||||
|
@ -5810,6 +5874,9 @@ impl fmt::Debug for StructureType {
|
|||
Some("PHYSICAL_DEVICE_AMIGO_PROFILING_FEATURES_SEC")
|
||||
}
|
||||
Self::AMIGO_PROFILING_SUBMIT_INFO_SEC => Some("AMIGO_PROFILING_SUBMIT_INFO_SEC"),
|
||||
Self::PHYSICAL_DEVICE_MULTIVIEW_PER_VIEW_VIEWPORTS_FEATURES_QCOM => {
|
||||
Some("PHYSICAL_DEVICE_MULTIVIEW_PER_VIEW_VIEWPORTS_FEATURES_QCOM")
|
||||
}
|
||||
Self::PHYSICAL_DEVICE_RAY_TRACING_INVOCATION_REORDER_FEATURES_NV => {
|
||||
Some("PHYSICAL_DEVICE_RAY_TRACING_INVOCATION_REORDER_FEATURES_NV")
|
||||
}
|
||||
|
@ -6322,8 +6389,11 @@ impl fmt::Debug for SwapchainCreateFlagsKHR {
|
|||
),
|
||||
(SwapchainCreateFlagsKHR::PROTECTED.0, "PROTECTED"),
|
||||
(SwapchainCreateFlagsKHR::MUTABLE_FORMAT.0, "MUTABLE_FORMAT"),
|
||||
(
|
||||
SwapchainCreateFlagsKHR::DEFERRED_MEMORY_ALLOCATION_EXT.0,
|
||||
"DEFERRED_MEMORY_ALLOCATION_EXT",
|
||||
),
|
||||
(SwapchainCreateFlagsKHR::RESERVED_4_EXT.0, "RESERVED_4_EXT"),
|
||||
(SwapchainCreateFlagsKHR::RESERVED_3_SEC.0, "RESERVED_3_SEC"),
|
||||
];
|
||||
debug_flags(f, KNOWN, self.0)
|
||||
}
|
||||
|
@ -6554,14 +6624,8 @@ impl fmt::Debug for VideoCodecOperationFlagsKHR {
|
|||
VideoCodecOperationFlagsKHR::ENCODE_H265_EXT.0,
|
||||
"ENCODE_H265_EXT",
|
||||
),
|
||||
(
|
||||
VideoCodecOperationFlagsKHR::DECODE_H264_EXT.0,
|
||||
"DECODE_H264_EXT",
|
||||
),
|
||||
(
|
||||
VideoCodecOperationFlagsKHR::DECODE_H265_EXT.0,
|
||||
"DECODE_H265_EXT",
|
||||
),
|
||||
(VideoCodecOperationFlagsKHR::DECODE_H264.0, "DECODE_H264"),
|
||||
(VideoCodecOperationFlagsKHR::DECODE_H265.0, "DECODE_H265"),
|
||||
];
|
||||
debug_flags(f, KNOWN, self.0)
|
||||
}
|
||||
|
@ -6614,19 +6678,19 @@ impl fmt::Debug for VideoDecodeFlagsKHR {
|
|||
debug_flags(f, KNOWN, self.0)
|
||||
}
|
||||
}
|
||||
impl fmt::Debug for VideoDecodeH264PictureLayoutFlagsEXT {
|
||||
impl fmt::Debug for VideoDecodeH264PictureLayoutFlagsKHR {
|
||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
||||
const KNOWN: &[(Flags, &str)] = &[
|
||||
(
|
||||
VideoDecodeH264PictureLayoutFlagsEXT::PROGRESSIVE.0,
|
||||
VideoDecodeH264PictureLayoutFlagsKHR::PROGRESSIVE.0,
|
||||
"PROGRESSIVE",
|
||||
),
|
||||
(
|
||||
VideoDecodeH264PictureLayoutFlagsEXT::INTERLACED_INTERLEAVED_LINES.0,
|
||||
VideoDecodeH264PictureLayoutFlagsKHR::INTERLACED_INTERLEAVED_LINES.0,
|
||||
"INTERLACED_INTERLEAVED_LINES",
|
||||
),
|
||||
(
|
||||
VideoDecodeH264PictureLayoutFlagsEXT::INTERLACED_SEPARATE_PLANES.0,
|
||||
VideoDecodeH264PictureLayoutFlagsKHR::INTERLACED_SEPARATE_PLANES.0,
|
||||
"INTERLACED_SEPARATE_PLANES",
|
||||
),
|
||||
];
|
||||
|
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -966,7 +966,7 @@ impl Result {
|
|||
pub const ERROR_OUT_OF_DEVICE_MEMORY: Self = Self(-2);
|
||||
#[doc = "Initialization of an object has failed"]
|
||||
pub const ERROR_INITIALIZATION_FAILED: Self = Self(-3);
|
||||
#[doc = "The logical device has been lost. See <<devsandqueues-lost-device>>"]
|
||||
#[doc = "The logical device has been lost. See <https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#devsandqueues-lost-device>"]
|
||||
pub const ERROR_DEVICE_LOST: Self = Self(-4);
|
||||
#[doc = "Mapping of a memory object has failed"]
|
||||
pub const ERROR_MEMORY_MAP_FAILED: Self = Self(-5);
|
||||
|
@ -990,40 +990,7 @@ impl Result {
|
|||
impl ::std::error::Error for Result {}
|
||||
impl fmt::Display for Result {
|
||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
||||
let name = match *self {
|
||||
Self::SUCCESS => Some("Command completed successfully"),
|
||||
Self::NOT_READY => Some("A fence or query has not yet completed"),
|
||||
Self::TIMEOUT => Some("A wait operation has not completed in the specified time"),
|
||||
Self::EVENT_SET => Some("An event is signaled"),
|
||||
Self::EVENT_RESET => Some("An event is unsignaled"),
|
||||
Self::INCOMPLETE => Some("A return array was too small for the result"),
|
||||
Self::ERROR_OUT_OF_HOST_MEMORY => Some("A host memory allocation has failed"),
|
||||
Self::ERROR_OUT_OF_DEVICE_MEMORY => Some("A device memory allocation has failed"),
|
||||
Self::ERROR_INITIALIZATION_FAILED => Some("Initialization of an object has failed"),
|
||||
Self::ERROR_DEVICE_LOST => {
|
||||
Some("The logical device has been lost. See <<devsandqueues-lost-device>>")
|
||||
}
|
||||
Self::ERROR_MEMORY_MAP_FAILED => Some("Mapping of a memory object has failed"),
|
||||
Self::ERROR_LAYER_NOT_PRESENT => Some("Layer specified does not exist"),
|
||||
Self::ERROR_EXTENSION_NOT_PRESENT => Some("Extension specified does not exist"),
|
||||
Self::ERROR_FEATURE_NOT_PRESENT => {
|
||||
Some("Requested feature is not available on this device")
|
||||
}
|
||||
Self::ERROR_INCOMPATIBLE_DRIVER => Some("Unable to find a Vulkan driver"),
|
||||
Self::ERROR_TOO_MANY_OBJECTS => {
|
||||
Some("Too many objects of the type have already been created")
|
||||
}
|
||||
Self::ERROR_FORMAT_NOT_SUPPORTED => {
|
||||
Some("Requested format is not supported on this device")
|
||||
}
|
||||
Self::ERROR_FRAGMENTED_POOL => Some(
|
||||
"A requested pool allocation has failed due to fragmentation of the pool's memory",
|
||||
),
|
||||
Self::ERROR_UNKNOWN => {
|
||||
Some("An unknown error has occurred, due to an implementation or application bug")
|
||||
}
|
||||
_ => None,
|
||||
};
|
||||
let name = match * self { Self :: SUCCESS => Some ("Command completed successfully") , Self :: NOT_READY => Some ("A fence or query has not yet completed") , Self :: TIMEOUT => Some ("A wait operation has not completed in the specified time") , Self :: EVENT_SET => Some ("An event is signaled") , Self :: EVENT_RESET => Some ("An event is unsignaled") , Self :: INCOMPLETE => Some ("A return array was too small for the result") , Self :: ERROR_OUT_OF_HOST_MEMORY => Some ("A host memory allocation has failed") , Self :: ERROR_OUT_OF_DEVICE_MEMORY => Some ("A device memory allocation has failed") , Self :: ERROR_INITIALIZATION_FAILED => Some ("Initialization of an object has failed") , Self :: ERROR_DEVICE_LOST => Some ("The logical device has been lost. See <https://registry.khronos.org/vulkan/specs/1.3-extensions/html/vkspec.html#devsandqueues-lost-device>") , Self :: ERROR_MEMORY_MAP_FAILED => Some ("Mapping of a memory object has failed") , Self :: ERROR_LAYER_NOT_PRESENT => Some ("Layer specified does not exist") , Self :: ERROR_EXTENSION_NOT_PRESENT => Some ("Extension specified does not exist") , Self :: ERROR_FEATURE_NOT_PRESENT => Some ("Requested feature is not available on this device") , Self :: ERROR_INCOMPATIBLE_DRIVER => Some ("Unable to find a Vulkan driver") , Self :: ERROR_TOO_MANY_OBJECTS => Some ("Too many objects of the type have already been created") , Self :: ERROR_FORMAT_NOT_SUPPORTED => Some ("Requested format is not supported on this device") , Self :: ERROR_FRAGMENTED_POOL => Some ("A requested pool allocation has failed due to fragmentation of the pool's memory") , Self :: ERROR_UNKNOWN => Some ("An unknown error has occurred, due to an implementation or application bug") , _ => None , } ;
|
||||
if let Some(x) = name {
|
||||
fmt.write_str(x)
|
||||
} else {
|
||||
|
@ -1136,6 +1103,24 @@ impl RayTracingInvocationReorderModeNV {
|
|||
}
|
||||
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
|
||||
#[repr(transparent)]
|
||||
#[doc = "<https://www.khronos.org/registry/vulkan/specs/1.3-extensions/man/html/VkDirectDriverLoadingModeLUNARG.html>"]
|
||||
pub struct DirectDriverLoadingModeLUNARG(pub(crate) i32);
|
||||
impl DirectDriverLoadingModeLUNARG {
|
||||
#[inline]
|
||||
pub const fn from_raw(x: i32) -> Self {
|
||||
Self(x)
|
||||
}
|
||||
#[inline]
|
||||
pub const fn as_raw(self) -> i32 {
|
||||
self.0
|
||||
}
|
||||
}
|
||||
impl DirectDriverLoadingModeLUNARG {
|
||||
pub const EXCLUSIVE: Self = Self(0);
|
||||
pub const INCLUSIVE: Self = Self(1);
|
||||
}
|
||||
#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Default)]
|
||||
#[repr(transparent)]
|
||||
#[doc = "<https://www.khronos.org/registry/vulkan/specs/1.3-extensions/man/html/VkSemaphoreType.html>"]
|
||||
pub struct SemaphoreType(pub(crate) i32);
|
||||
impl SemaphoreType {
|
||||
|
|
|
@ -1846,7 +1846,7 @@ impl KhrVideoQueueFn {
|
|||
pub const fn name() -> &'static ::std::ffi::CStr {
|
||||
unsafe { ::std::ffi::CStr::from_bytes_with_nul_unchecked(b"VK_KHR_video_queue\0") }
|
||||
}
|
||||
pub const SPEC_VERSION: u32 = 7u32;
|
||||
pub const SPEC_VERSION: u32 = 8u32;
|
||||
}
|
||||
#[allow(non_camel_case_types)]
|
||||
pub type PFN_vkGetPhysicalDeviceVideoCapabilitiesKHR = unsafe extern "system" fn(
|
||||
|
@ -2248,7 +2248,7 @@ impl KhrVideoDecodeQueueFn {
|
|||
pub const fn name() -> &'static ::std::ffi::CStr {
|
||||
unsafe { ::std::ffi::CStr::from_bytes_with_nul_unchecked(b"VK_KHR_video_decode_queue\0") }
|
||||
}
|
||||
pub const SPEC_VERSION: u32 = 6u32;
|
||||
pub const SPEC_VERSION: u32 = 7u32;
|
||||
}
|
||||
#[allow(non_camel_case_types)]
|
||||
pub type PFN_vkCmdDecodeVideoKHR = unsafe extern "system" fn(
|
||||
|
@ -3165,18 +3165,18 @@ impl StructureType {
|
|||
impl VideoCodecOperationFlagsKHR {
|
||||
pub const ENCODE_H265_EXT: Self = Self(0b10_0000_0000_0000_0000);
|
||||
}
|
||||
impl ExtVideoDecodeH264Fn {
|
||||
impl KhrVideoDecodeH264Fn {
|
||||
#[inline]
|
||||
pub const fn name() -> &'static ::std::ffi::CStr {
|
||||
unsafe { ::std::ffi::CStr::from_bytes_with_nul_unchecked(b"VK_EXT_video_decode_h264\0") }
|
||||
unsafe { ::std::ffi::CStr::from_bytes_with_nul_unchecked(b"VK_KHR_video_decode_h264\0") }
|
||||
}
|
||||
pub const SPEC_VERSION: u32 = 7u32;
|
||||
pub const SPEC_VERSION: u32 = 8u32;
|
||||
}
|
||||
#[derive(Clone)]
|
||||
pub struct ExtVideoDecodeH264Fn {}
|
||||
unsafe impl Send for ExtVideoDecodeH264Fn {}
|
||||
unsafe impl Sync for ExtVideoDecodeH264Fn {}
|
||||
impl ExtVideoDecodeH264Fn {
|
||||
pub struct KhrVideoDecodeH264Fn {}
|
||||
unsafe impl Send for KhrVideoDecodeH264Fn {}
|
||||
unsafe impl Sync for KhrVideoDecodeH264Fn {}
|
||||
impl KhrVideoDecodeH264Fn {
|
||||
pub fn load<F>(mut _f: F) -> Self
|
||||
where
|
||||
F: FnMut(&::std::ffi::CStr) -> *const c_void,
|
||||
|
@ -3184,18 +3184,18 @@ impl ExtVideoDecodeH264Fn {
|
|||
Self {}
|
||||
}
|
||||
}
|
||||
#[doc = "Generated from 'VK_EXT_video_decode_h264'"]
|
||||
#[doc = "Generated from 'VK_KHR_video_decode_h264'"]
|
||||
impl StructureType {
|
||||
pub const VIDEO_DECODE_H264_CAPABILITIES_EXT: Self = Self(1_000_040_000);
|
||||
pub const VIDEO_DECODE_H264_PICTURE_INFO_EXT: Self = Self(1_000_040_001);
|
||||
pub const VIDEO_DECODE_H264_PROFILE_INFO_EXT: Self = Self(1_000_040_003);
|
||||
pub const VIDEO_DECODE_H264_SESSION_PARAMETERS_CREATE_INFO_EXT: Self = Self(1_000_040_004);
|
||||
pub const VIDEO_DECODE_H264_SESSION_PARAMETERS_ADD_INFO_EXT: Self = Self(1_000_040_005);
|
||||
pub const VIDEO_DECODE_H264_DPB_SLOT_INFO_EXT: Self = Self(1_000_040_006);
|
||||
pub const VIDEO_DECODE_H264_CAPABILITIES_KHR: Self = Self(1_000_040_000);
|
||||
pub const VIDEO_DECODE_H264_PICTURE_INFO_KHR: Self = Self(1_000_040_001);
|
||||
pub const VIDEO_DECODE_H264_PROFILE_INFO_KHR: Self = Self(1_000_040_003);
|
||||
pub const VIDEO_DECODE_H264_SESSION_PARAMETERS_CREATE_INFO_KHR: Self = Self(1_000_040_004);
|
||||
pub const VIDEO_DECODE_H264_SESSION_PARAMETERS_ADD_INFO_KHR: Self = Self(1_000_040_005);
|
||||
pub const VIDEO_DECODE_H264_DPB_SLOT_INFO_KHR: Self = Self(1_000_040_006);
|
||||
}
|
||||
#[doc = "Generated from 'VK_EXT_video_decode_h264'"]
|
||||
#[doc = "Generated from 'VK_KHR_video_decode_h264'"]
|
||||
impl VideoCodecOperationFlagsKHR {
|
||||
pub const DECODE_H264_EXT: Self = Self(0b1);
|
||||
pub const DECODE_H264: Self = Self(0b1);
|
||||
}
|
||||
impl AmdTextureGatherBiasLodFn {
|
||||
#[inline]
|
||||
|
@ -11682,18 +11682,18 @@ impl AmdExtension187Fn {
|
|||
Self {}
|
||||
}
|
||||
}
|
||||
impl ExtVideoDecodeH265Fn {
|
||||
impl KhrVideoDecodeH265Fn {
|
||||
#[inline]
|
||||
pub const fn name() -> &'static ::std::ffi::CStr {
|
||||
unsafe { ::std::ffi::CStr::from_bytes_with_nul_unchecked(b"VK_EXT_video_decode_h265\0") }
|
||||
unsafe { ::std::ffi::CStr::from_bytes_with_nul_unchecked(b"VK_KHR_video_decode_h265\0") }
|
||||
}
|
||||
pub const SPEC_VERSION: u32 = 5u32;
|
||||
pub const SPEC_VERSION: u32 = 7u32;
|
||||
}
|
||||
#[derive(Clone)]
|
||||
pub struct ExtVideoDecodeH265Fn {}
|
||||
unsafe impl Send for ExtVideoDecodeH265Fn {}
|
||||
unsafe impl Sync for ExtVideoDecodeH265Fn {}
|
||||
impl ExtVideoDecodeH265Fn {
|
||||
pub struct KhrVideoDecodeH265Fn {}
|
||||
unsafe impl Send for KhrVideoDecodeH265Fn {}
|
||||
unsafe impl Sync for KhrVideoDecodeH265Fn {}
|
||||
impl KhrVideoDecodeH265Fn {
|
||||
pub fn load<F>(mut _f: F) -> Self
|
||||
where
|
||||
F: FnMut(&::std::ffi::CStr) -> *const c_void,
|
||||
|
@ -11701,18 +11701,18 @@ impl ExtVideoDecodeH265Fn {
|
|||
Self {}
|
||||
}
|
||||
}
|
||||
#[doc = "Generated from 'VK_EXT_video_decode_h265'"]
|
||||
#[doc = "Generated from 'VK_KHR_video_decode_h265'"]
|
||||
impl StructureType {
|
||||
pub const VIDEO_DECODE_H265_CAPABILITIES_EXT: Self = Self(1_000_187_000);
|
||||
pub const VIDEO_DECODE_H265_SESSION_PARAMETERS_CREATE_INFO_EXT: Self = Self(1_000_187_001);
|
||||
pub const VIDEO_DECODE_H265_SESSION_PARAMETERS_ADD_INFO_EXT: Self = Self(1_000_187_002);
|
||||
pub const VIDEO_DECODE_H265_PROFILE_INFO_EXT: Self = Self(1_000_187_003);
|
||||
pub const VIDEO_DECODE_H265_PICTURE_INFO_EXT: Self = Self(1_000_187_004);
|
||||
pub const VIDEO_DECODE_H265_DPB_SLOT_INFO_EXT: Self = Self(1_000_187_005);
|
||||
pub const VIDEO_DECODE_H265_CAPABILITIES_KHR: Self = Self(1_000_187_000);
|
||||
pub const VIDEO_DECODE_H265_SESSION_PARAMETERS_CREATE_INFO_KHR: Self = Self(1_000_187_001);
|
||||
pub const VIDEO_DECODE_H265_SESSION_PARAMETERS_ADD_INFO_KHR: Self = Self(1_000_187_002);
|
||||
pub const VIDEO_DECODE_H265_PROFILE_INFO_KHR: Self = Self(1_000_187_003);
|
||||
pub const VIDEO_DECODE_H265_PICTURE_INFO_KHR: Self = Self(1_000_187_004);
|
||||
pub const VIDEO_DECODE_H265_DPB_SLOT_INFO_KHR: Self = Self(1_000_187_005);
|
||||
}
|
||||
#[doc = "Generated from 'VK_EXT_video_decode_h265'"]
|
||||
#[doc = "Generated from 'VK_KHR_video_decode_h265'"]
|
||||
impl VideoCodecOperationFlagsKHR {
|
||||
pub const DECODE_H265_EXT: Self = Self(0b10);
|
||||
pub const DECODE_H265: Self = Self(0b10);
|
||||
}
|
||||
impl KhrGlobalPriorityFn {
|
||||
#[inline]
|
||||
|
@ -15598,18 +15598,18 @@ impl ExtShaderAtomicFloat2Fn {
|
|||
impl StructureType {
|
||||
pub const PHYSICAL_DEVICE_SHADER_ATOMIC_FLOAT_2_FEATURES_EXT: Self = Self(1_000_273_000);
|
||||
}
|
||||
impl KhrExtension275Fn {
|
||||
impl ExtSurfaceMaintenance1Fn {
|
||||
#[inline]
|
||||
pub const fn name() -> &'static ::std::ffi::CStr {
|
||||
unsafe { ::std::ffi::CStr::from_bytes_with_nul_unchecked(b"VK_KHR_extension_275\0") }
|
||||
unsafe { ::std::ffi::CStr::from_bytes_with_nul_unchecked(b"VK_EXT_surface_maintenance1\0") }
|
||||
}
|
||||
pub const SPEC_VERSION: u32 = 0u32;
|
||||
pub const SPEC_VERSION: u32 = 1u32;
|
||||
}
|
||||
#[derive(Clone)]
|
||||
pub struct KhrExtension275Fn {}
|
||||
unsafe impl Send for KhrExtension275Fn {}
|
||||
unsafe impl Sync for KhrExtension275Fn {}
|
||||
impl KhrExtension275Fn {
|
||||
pub struct ExtSurfaceMaintenance1Fn {}
|
||||
unsafe impl Send for ExtSurfaceMaintenance1Fn {}
|
||||
unsafe impl Sync for ExtSurfaceMaintenance1Fn {}
|
||||
impl ExtSurfaceMaintenance1Fn {
|
||||
pub fn load<F>(mut _f: F) -> Self
|
||||
where
|
||||
F: FnMut(&::std::ffi::CStr) -> *const c_void,
|
||||
|
@ -15617,25 +15617,74 @@ impl KhrExtension275Fn {
|
|||
Self {}
|
||||
}
|
||||
}
|
||||
impl KhrExtension276Fn {
|
||||
#[doc = "Generated from 'VK_EXT_surface_maintenance1'"]
|
||||
impl StructureType {
|
||||
pub const SURFACE_PRESENT_MODE_EXT: Self = Self(1_000_274_000);
|
||||
pub const SURFACE_PRESENT_SCALING_CAPABILITIES_EXT: Self = Self(1_000_274_001);
|
||||
pub const SURFACE_PRESENT_MODE_COMPATIBILITY_EXT: Self = Self(1_000_274_002);
|
||||
}
|
||||
impl ExtSwapchainMaintenance1Fn {
|
||||
#[inline]
|
||||
pub const fn name() -> &'static ::std::ffi::CStr {
|
||||
unsafe { ::std::ffi::CStr::from_bytes_with_nul_unchecked(b"VK_KHR_extension_276\0") }
|
||||
unsafe {
|
||||
::std::ffi::CStr::from_bytes_with_nul_unchecked(b"VK_EXT_swapchain_maintenance1\0")
|
||||
}
|
||||
}
|
||||
pub const SPEC_VERSION: u32 = 0u32;
|
||||
pub const SPEC_VERSION: u32 = 1u32;
|
||||
}
|
||||
#[allow(non_camel_case_types)]
|
||||
pub type PFN_vkReleaseSwapchainImagesEXT = unsafe extern "system" fn(
|
||||
device: Device,
|
||||
p_release_info: *const ReleaseSwapchainImagesInfoEXT,
|
||||
) -> Result;
|
||||
#[derive(Clone)]
|
||||
pub struct KhrExtension276Fn {}
|
||||
unsafe impl Send for KhrExtension276Fn {}
|
||||
unsafe impl Sync for KhrExtension276Fn {}
|
||||
impl KhrExtension276Fn {
|
||||
pub struct ExtSwapchainMaintenance1Fn {
|
||||
pub release_swapchain_images_ext: PFN_vkReleaseSwapchainImagesEXT,
|
||||
}
|
||||
unsafe impl Send for ExtSwapchainMaintenance1Fn {}
|
||||
unsafe impl Sync for ExtSwapchainMaintenance1Fn {}
|
||||
impl ExtSwapchainMaintenance1Fn {
|
||||
pub fn load<F>(mut _f: F) -> Self
|
||||
where
|
||||
F: FnMut(&::std::ffi::CStr) -> *const c_void,
|
||||
{
|
||||
Self {}
|
||||
Self {
|
||||
release_swapchain_images_ext: unsafe {
|
||||
unsafe extern "system" fn release_swapchain_images_ext(
|
||||
_device: Device,
|
||||
_p_release_info: *const ReleaseSwapchainImagesInfoEXT,
|
||||
) -> Result {
|
||||
panic!(concat!(
|
||||
"Unable to load ",
|
||||
stringify!(release_swapchain_images_ext)
|
||||
))
|
||||
}
|
||||
let cname = ::std::ffi::CStr::from_bytes_with_nul_unchecked(
|
||||
b"vkReleaseSwapchainImagesEXT\0",
|
||||
);
|
||||
let val = _f(cname);
|
||||
if val.is_null() {
|
||||
release_swapchain_images_ext
|
||||
} else {
|
||||
::std::mem::transmute(val)
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
#[doc = "Generated from 'VK_EXT_swapchain_maintenance1'"]
|
||||
impl StructureType {
|
||||
pub const PHYSICAL_DEVICE_SWAPCHAIN_MAINTENANCE_1_FEATURES_EXT: Self = Self(1_000_275_000);
|
||||
pub const SWAPCHAIN_PRESENT_FENCE_INFO_EXT: Self = Self(1_000_275_001);
|
||||
pub const SWAPCHAIN_PRESENT_MODES_CREATE_INFO_EXT: Self = Self(1_000_275_002);
|
||||
pub const SWAPCHAIN_PRESENT_MODE_INFO_EXT: Self = Self(1_000_275_003);
|
||||
pub const SWAPCHAIN_PRESENT_SCALING_CREATE_INFO_EXT: Self = Self(1_000_275_004);
|
||||
pub const RELEASE_SWAPCHAIN_IMAGES_INFO_EXT: Self = Self(1_000_275_005);
|
||||
}
|
||||
#[doc = "Generated from 'VK_EXT_swapchain_maintenance1'"]
|
||||
impl SwapchainCreateFlagsKHR {
|
||||
pub const DEFERRED_MEMORY_ALLOCATION_EXT: Self = Self(0b1000);
|
||||
}
|
||||
impl ExtShaderDemoteToHelperInvocationFn {
|
||||
#[inline]
|
||||
pub const fn name() -> &'static ::std::ffi::CStr {
|
||||
|
@ -22881,10 +22930,6 @@ impl SecExtension448Fn {
|
|||
Self {}
|
||||
}
|
||||
}
|
||||
#[doc = "Generated from 'VK_SEC_extension_448'"]
|
||||
impl SwapchainCreateFlagsKHR {
|
||||
pub const RESERVED_3_SEC: Self = Self(0b1000);
|
||||
}
|
||||
impl SecExtension449Fn {
|
||||
#[inline]
|
||||
pub const fn name() -> &'static ::std::ffi::CStr {
|
||||
|
@ -23945,18 +23990,20 @@ impl StructureType {
|
|||
pub const RENDER_PASS_CREATION_FEEDBACK_CREATE_INFO_EXT: Self = Self(1_000_458_002);
|
||||
pub const RENDER_PASS_SUBPASS_FEEDBACK_CREATE_INFO_EXT: Self = Self(1_000_458_003);
|
||||
}
|
||||
impl ExtExtension460Fn {
|
||||
impl LunargDirectDriverLoadingFn {
|
||||
#[inline]
|
||||
pub const fn name() -> &'static ::std::ffi::CStr {
|
||||
unsafe { ::std::ffi::CStr::from_bytes_with_nul_unchecked(b"VK_EXT_extension_460\0") }
|
||||
unsafe {
|
||||
::std::ffi::CStr::from_bytes_with_nul_unchecked(b"VK_LUNARG_direct_driver_loading\0")
|
||||
}
|
||||
}
|
||||
pub const SPEC_VERSION: u32 = 0u32;
|
||||
pub const SPEC_VERSION: u32 = 1u32;
|
||||
}
|
||||
#[derive(Clone)]
|
||||
pub struct ExtExtension460Fn {}
|
||||
unsafe impl Send for ExtExtension460Fn {}
|
||||
unsafe impl Sync for ExtExtension460Fn {}
|
||||
impl ExtExtension460Fn {
|
||||
pub struct LunargDirectDriverLoadingFn {}
|
||||
unsafe impl Send for LunargDirectDriverLoadingFn {}
|
||||
unsafe impl Sync for LunargDirectDriverLoadingFn {}
|
||||
impl LunargDirectDriverLoadingFn {
|
||||
pub fn load<F>(mut _f: F) -> Self
|
||||
where
|
||||
F: FnMut(&::std::ffi::CStr) -> *const c_void,
|
||||
|
@ -23964,6 +24011,11 @@ impl ExtExtension460Fn {
|
|||
Self {}
|
||||
}
|
||||
}
|
||||
#[doc = "Generated from 'VK_LUNARG_direct_driver_loading'"]
|
||||
impl StructureType {
|
||||
pub const DIRECT_DRIVER_LOADING_INFO_LUNARG: Self = Self(1_000_459_000);
|
||||
pub const DIRECT_DRIVER_LOADING_LIST_LUNARG: Self = Self(1_000_459_001);
|
||||
}
|
||||
impl ExtExtension461Fn {
|
||||
#[inline]
|
||||
pub const fn name() -> &'static ::std::ffi::CStr {
|
||||
|
@ -24882,18 +24934,22 @@ impl ExtExtension488Fn {
|
|||
Self {}
|
||||
}
|
||||
}
|
||||
impl QcomExtension489Fn {
|
||||
impl QcomMultiviewPerViewViewportsFn {
|
||||
#[inline]
|
||||
pub const fn name() -> &'static ::std::ffi::CStr {
|
||||
unsafe { ::std::ffi::CStr::from_bytes_with_nul_unchecked(b"VK_QCOM_extension_489\0") }
|
||||
unsafe {
|
||||
::std::ffi::CStr::from_bytes_with_nul_unchecked(
|
||||
b"VK_QCOM_multiview_per_view_viewports\0",
|
||||
)
|
||||
}
|
||||
}
|
||||
pub const SPEC_VERSION: u32 = 0u32;
|
||||
pub const SPEC_VERSION: u32 = 1u32;
|
||||
}
|
||||
#[derive(Clone)]
|
||||
pub struct QcomExtension489Fn {}
|
||||
unsafe impl Send for QcomExtension489Fn {}
|
||||
unsafe impl Sync for QcomExtension489Fn {}
|
||||
impl QcomExtension489Fn {
|
||||
pub struct QcomMultiviewPerViewViewportsFn {}
|
||||
unsafe impl Send for QcomMultiviewPerViewViewportsFn {}
|
||||
unsafe impl Sync for QcomMultiviewPerViewViewportsFn {}
|
||||
impl QcomMultiviewPerViewViewportsFn {
|
||||
pub fn load<F>(mut _f: F) -> Self
|
||||
where
|
||||
F: FnMut(&::std::ffi::CStr) -> *const c_void,
|
||||
|
@ -24901,6 +24957,11 @@ impl QcomExtension489Fn {
|
|||
Self {}
|
||||
}
|
||||
}
|
||||
#[doc = "Generated from 'VK_QCOM_multiview_per_view_viewports'"]
|
||||
impl StructureType {
|
||||
pub const PHYSICAL_DEVICE_MULTIVIEW_PER_VIEW_VIEWPORTS_FEATURES_QCOM: Self =
|
||||
Self(1_000_488_000);
|
||||
}
|
||||
impl NvExtension490Fn {
|
||||
#[inline]
|
||||
pub const fn name() -> &'static ::std::ffi::CStr {
|
||||
|
@ -25221,3 +25282,41 @@ impl NvExtension504Fn {
|
|||
Self {}
|
||||
}
|
||||
}
|
||||
impl ExtExtension505Fn {
|
||||
#[inline]
|
||||
pub const fn name() -> &'static ::std::ffi::CStr {
|
||||
unsafe { ::std::ffi::CStr::from_bytes_with_nul_unchecked(b"VK_EXT_extension_505\0") }
|
||||
}
|
||||
pub const SPEC_VERSION: u32 = 0u32;
|
||||
}
|
||||
#[derive(Clone)]
|
||||
pub struct ExtExtension505Fn {}
|
||||
unsafe impl Send for ExtExtension505Fn {}
|
||||
unsafe impl Sync for ExtExtension505Fn {}
|
||||
impl ExtExtension505Fn {
|
||||
pub fn load<F>(mut _f: F) -> Self
|
||||
where
|
||||
F: FnMut(&::std::ffi::CStr) -> *const c_void,
|
||||
{
|
||||
Self {}
|
||||
}
|
||||
}
|
||||
impl NvExtension506Fn {
|
||||
#[inline]
|
||||
pub const fn name() -> &'static ::std::ffi::CStr {
|
||||
unsafe { ::std::ffi::CStr::from_bytes_with_nul_unchecked(b"VK_NV_extension_506\0") }
|
||||
}
|
||||
pub const SPEC_VERSION: u32 = 0u32;
|
||||
}
|
||||
#[derive(Clone)]
|
||||
pub struct NvExtension506Fn {}
|
||||
unsafe impl Send for NvExtension506Fn {}
|
||||
unsafe impl Sync for NvExtension506Fn {}
|
||||
impl NvExtension506Fn {
|
||||
pub fn load<F>(mut _f: F) -> Self
|
||||
where
|
||||
F: FnMut(&::std::ffi::CStr) -> *const c_void,
|
||||
{
|
||||
Self {}
|
||||
}
|
||||
}
|
||||
|
|
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
|
@ -11,7 +11,7 @@ jobs:
|
|||
- uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
profile: minimal
|
||||
toolchain: "1.56.0"
|
||||
toolchain: "1.63.0"
|
||||
override: true
|
||||
- uses: taiki-e/install-action@cargo-hack
|
||||
- uses: taiki-e/install-action@cargo-minimal-versions
|
||||
|
|
|
@ -10,7 +10,7 @@ keywords = ["shader", "SPIR-V", "GLSL", "MSL"]
|
|||
license = "MIT OR Apache-2.0"
|
||||
exclude = ["bin/**/*", "tests/**/*", "Cargo.lock", "target/**/*"]
|
||||
resolver = "2"
|
||||
rust-version = "1.56"
|
||||
rust-version = "1.63"
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
all-features = true
|
||||
|
@ -73,7 +73,7 @@ diff = "0.1"
|
|||
ron = "~0.7.1"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
spirv = { version = "0.2", features = ["deserialize"] }
|
||||
rspirv = "0.11"
|
||||
rspirv = { version = "0.11", git = "https://github.com/gfx-rs/rspirv", rev = "b969f175d5663258b4891e44b76c1544da9661ab" }
|
||||
env_logger = "0.9"
|
||||
|
||||
[workspace]
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
[![Crates.io](https://img.shields.io/crates/v/naga.svg?label=naga)](https://crates.io/crates/naga)
|
||||
[![Docs.rs](https://docs.rs/naga/badge.svg)](https://docs.rs/naga)
|
||||
[![Build Status](https://github.com/gfx-rs/naga/workflows/pipeline/badge.svg)](https://github.com/gfx-rs/naga/actions)
|
||||
![MSRV](https://img.shields.io/badge/rustc-1.56+-blue.svg)
|
||||
![MSRV](https://img.shields.io/badge/rustc-1.63+-blue.svg)
|
||||
[![codecov.io](https://codecov.io/gh/gfx-rs/naga/branch/master/graph/badge.svg?token=9VOKYO8BM2)](https://codecov.io/gh/gfx-rs/naga)
|
||||
|
||||
The shader translation library for the needs of [wgpu](https://github.com/gfx-rs/wgpu).
|
||||
|
|
|
@ -10,7 +10,7 @@ fn gather_inputs(folder: &str, extension: &str) -> Vec<Box<[u8]>> {
|
|||
for file_entry in read_dir {
|
||||
match file_entry {
|
||||
Ok(entry) => match entry.path().extension() {
|
||||
Some(ostr) if &*ostr == extension => {
|
||||
Some(ostr) if ostr == extension => {
|
||||
let input = fs::read(entry.path()).unwrap_or_default();
|
||||
list.push(input.into_boxed_slice());
|
||||
}
|
||||
|
@ -250,7 +250,9 @@ fn backends(c: &mut Criterion) {
|
|||
entry_point: ep.name.clone(),
|
||||
multiview: None,
|
||||
};
|
||||
match naga::back::glsl::Writer::new(
|
||||
|
||||
// might be `Err` if missing features
|
||||
if let Ok(mut writer) = naga::back::glsl::Writer::new(
|
||||
&mut string,
|
||||
module,
|
||||
info,
|
||||
|
@ -258,13 +260,9 @@ fn backends(c: &mut Criterion) {
|
|||
&pipeline_options,
|
||||
naga::proc::BoundsCheckPolicies::default(),
|
||||
) {
|
||||
Ok(mut writer) => {
|
||||
let _ = writer.write(); // can error if unsupported
|
||||
}
|
||||
Err(_) => {
|
||||
// missing features
|
||||
}
|
||||
};
|
||||
let _ = writer.write(); // might be `Err` if unsupported
|
||||
}
|
||||
|
||||
string.clear();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,6 +15,15 @@ pub struct BadHandle {
|
|||
pub index: usize,
|
||||
}
|
||||
|
||||
impl BadHandle {
|
||||
fn new<T>(handle: Handle<T>) -> Self {
|
||||
Self {
|
||||
kind: std::any::type_name::<T>(),
|
||||
index: handle.index(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A strongly typed reference to an arena item.
|
||||
///
|
||||
/// A `Handle` value can be used as an index into an [`Arena`] or [`UniqueArena`].
|
||||
|
@ -123,6 +132,35 @@ pub struct Range<T> {
|
|||
marker: PhantomData<T>,
|
||||
}
|
||||
|
||||
impl<T> Range<T> {
|
||||
pub(crate) const fn erase_type(self) -> Range<()> {
|
||||
let Self { inner, marker: _ } = self;
|
||||
Range {
|
||||
inner,
|
||||
marker: PhantomData,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE: Keep this diagnostic in sync with that of [`BadHandle`].
|
||||
#[derive(Clone, Debug, thiserror::Error)]
|
||||
#[error("Handle range {range:?} of {kind} is either not present, or inaccessible yet")]
|
||||
pub struct BadRangeError {
|
||||
// This error is used for many `Handle` types, but there's no point in making this generic, so
|
||||
// we just flatten them all to `Handle<()>` here.
|
||||
kind: &'static str,
|
||||
range: Range<()>,
|
||||
}
|
||||
|
||||
impl BadRangeError {
|
||||
pub fn new<T>(range: Range<T>) -> Self {
|
||||
Self {
|
||||
kind: std::any::type_name::<T>(),
|
||||
range: range.erase_type(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Clone for Range<T> {
|
||||
fn clone(&self) -> Self {
|
||||
Range {
|
||||
|
@ -153,6 +191,15 @@ impl<T> Iterator for Range<T> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<T> Range<T> {
|
||||
pub fn new_from_bounds(first: Handle<T>, last: Handle<T>) -> Self {
|
||||
Self {
|
||||
inner: (first.index() as u32)..(last.index() as u32 + 1),
|
||||
marker: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// An arena holding some kind of component (e.g., type, constant,
|
||||
/// instruction, etc.) that can be referenced.
|
||||
///
|
||||
|
@ -273,10 +320,9 @@ impl<T> Arena<T> {
|
|||
}
|
||||
|
||||
pub fn try_get(&self, handle: Handle<T>) -> Result<&T, BadHandle> {
|
||||
self.data.get(handle.index()).ok_or_else(|| BadHandle {
|
||||
kind: std::any::type_name::<T>(),
|
||||
index: handle.index(),
|
||||
})
|
||||
self.data
|
||||
.get(handle.index())
|
||||
.ok_or_else(|| BadHandle::new(handle))
|
||||
}
|
||||
|
||||
/// Get a mutable reference to an element in the arena.
|
||||
|
@ -311,6 +357,31 @@ impl<T> Arena<T> {
|
|||
Span::default()
|
||||
}
|
||||
}
|
||||
|
||||
/// Assert that `handle` is valid for this arena.
|
||||
pub fn check_contains_handle(&self, handle: Handle<T>) -> Result<(), BadHandle> {
|
||||
if handle.index() < self.data.len() {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(BadHandle::new(handle))
|
||||
}
|
||||
}
|
||||
|
||||
/// Assert that `range` is valid for this arena.
|
||||
pub fn check_contains_range(&self, range: &Range<T>) -> Result<(), BadRangeError> {
|
||||
// Since `range.inner` is a `Range<u32>`, we only need to
|
||||
// check that the start precedes the end, and that the end is
|
||||
// in range.
|
||||
if range.inner.start > range.inner.end
|
||||
|| self
|
||||
.check_contains_handle(Handle::new(range.inner.end.try_into().unwrap()))
|
||||
.is_err()
|
||||
{
|
||||
Err(BadRangeError::new(range.clone()))
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "deserialize")]
|
||||
|
@ -531,10 +602,18 @@ impl<T: Eq + hash::Hash> UniqueArena<T> {
|
|||
|
||||
/// Return this arena's value at `handle`, if that is a valid handle.
|
||||
pub fn get_handle(&self, handle: Handle<T>) -> Result<&T, BadHandle> {
|
||||
self.set.get_index(handle.index()).ok_or_else(|| BadHandle {
|
||||
kind: std::any::type_name::<T>(),
|
||||
index: handle.index(),
|
||||
})
|
||||
self.set
|
||||
.get_index(handle.index())
|
||||
.ok_or_else(|| BadHandle::new(handle))
|
||||
}
|
||||
|
||||
/// Assert that `handle` is valid for this arena.
|
||||
pub fn check_contains_handle(&self, handle: Handle<T>) -> Result<(), BadHandle> {
|
||||
if handle.index() < self.set.len() {
|
||||
Ok(())
|
||||
} else {
|
||||
Err(BadHandle::new(handle))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1807,23 +1807,29 @@ impl<'a, W: Write> Writer<'a, W> {
|
|||
for case in cases {
|
||||
match case.value {
|
||||
crate::SwitchValue::Integer(value) => {
|
||||
writeln!(self.out, "{}case {}{}:", l2, value, type_postfix)?
|
||||
write!(self.out, "{}case {}{}:", l2, value, type_postfix)?
|
||||
}
|
||||
crate::SwitchValue::Default => writeln!(self.out, "{}default:", l2)?,
|
||||
crate::SwitchValue::Default => write!(self.out, "{}default:", l2)?,
|
||||
}
|
||||
|
||||
let write_block_braces = !(case.fall_through && case.body.is_empty());
|
||||
if write_block_braces {
|
||||
writeln!(self.out, " {{")?;
|
||||
} else {
|
||||
writeln!(self.out)?;
|
||||
}
|
||||
|
||||
for sta in case.body.iter() {
|
||||
self.write_stmt(sta, ctx, l2.next())?;
|
||||
}
|
||||
|
||||
// Write fallthrough comment if the case is fallthrough,
|
||||
// otherwise write a break, if the case is not already
|
||||
// broken out of at the end of its body.
|
||||
if case.fall_through {
|
||||
writeln!(self.out, "{}/* fallthrough */", l2.next())?;
|
||||
} else if case.body.last().map_or(true, |s| !s.is_terminator()) {
|
||||
if !case.fall_through && case.body.last().map_or(true, |s| !s.is_terminator()) {
|
||||
writeln!(self.out, "{}break;", l2.next())?;
|
||||
}
|
||||
|
||||
if write_block_braces {
|
||||
writeln!(self.out, "{}}}", l2)?;
|
||||
}
|
||||
}
|
||||
|
||||
writeln!(self.out, "{}}}", level)?
|
||||
|
@ -2519,7 +2525,7 @@ impl<'a, W: Write> Writer<'a, W> {
|
|||
},
|
||||
};
|
||||
|
||||
write!(self.out, "({}", operator)?;
|
||||
write!(self.out, "{}(", operator)?;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3138,12 +3144,32 @@ impl<'a, W: Write> Writer<'a, W> {
|
|||
}
|
||||
// Otherwise write just the expression (and the 1D hack if needed)
|
||||
None => {
|
||||
let uvec_size = match *ctx.info[coordinate].ty.inner_with(&self.module.types) {
|
||||
TypeInner::Scalar {
|
||||
kind: crate::ScalarKind::Uint,
|
||||
..
|
||||
} => Some(None),
|
||||
TypeInner::Vector {
|
||||
size,
|
||||
kind: crate::ScalarKind::Uint,
|
||||
..
|
||||
} => Some(Some(size as u32)),
|
||||
_ => None,
|
||||
};
|
||||
if tex_1d_hack {
|
||||
write!(self.out, "ivec2(")?;
|
||||
} else if uvec_size.is_some() {
|
||||
match uvec_size {
|
||||
Some(None) => write!(self.out, "int(")?,
|
||||
Some(Some(size)) => write!(self.out, "ivec{}(", size)?,
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
self.write_expr(coordinate, ctx)?;
|
||||
if tex_1d_hack {
|
||||
write!(self.out, ", 0)")?;
|
||||
} else if uvec_size.is_some() {
|
||||
write!(self.out, ")")?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3608,13 +3634,6 @@ impl<'a, W: Write> Writer<'a, W> {
|
|||
continue;
|
||||
}
|
||||
match self.module.types[var.ty].inner {
|
||||
crate::TypeInner::Struct { .. } => match var.space {
|
||||
crate::AddressSpace::Uniform | crate::AddressSpace::Storage { .. } => {
|
||||
let name = self.reflection_names_globals[&handle].clone();
|
||||
uniforms.insert(handle, name);
|
||||
}
|
||||
_ => (),
|
||||
},
|
||||
crate::TypeInner::Image { .. } => {
|
||||
let tex_name = self.reflection_names_globals[&handle].clone();
|
||||
match texture_mapping.entry(tex_name) {
|
||||
|
@ -3629,7 +3648,13 @@ impl<'a, W: Write> Writer<'a, W> {
|
|||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
_ => match var.space {
|
||||
crate::AddressSpace::Uniform | crate::AddressSpace::Storage { .. } => {
|
||||
let name = self.reflection_names_globals[&handle].clone();
|
||||
uniforms.insert(handle, name);
|
||||
}
|
||||
_ => (),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3717,6 +3742,7 @@ const fn glsl_built_in(
|
|||
Bi::VertexIndex => "uint(gl_VertexID)",
|
||||
// fragment
|
||||
Bi::FragDepth => "gl_FragDepth",
|
||||
Bi::PointCoord => "gl_PointCoord",
|
||||
Bi::FrontFacing => "gl_FrontFacing",
|
||||
Bi::PrimitiveIndex => "uint(gl_PrimitiveID)",
|
||||
Bi::SampleIndex => "gl_SampleID",
|
||||
|
|
|
@ -40,12 +40,12 @@ impl crate::TypeInner {
|
|||
}
|
||||
}
|
||||
|
||||
pub(super) fn try_size_hlsl(
|
||||
pub(super) fn size_hlsl(
|
||||
&self,
|
||||
types: &crate::UniqueArena<crate::Type>,
|
||||
constants: &crate::Arena<crate::Constant>,
|
||||
) -> Result<u32, crate::arena::BadHandle> {
|
||||
Ok(match *self {
|
||||
) -> u32 {
|
||||
match *self {
|
||||
Self::Matrix {
|
||||
columns,
|
||||
rows,
|
||||
|
@ -58,22 +58,20 @@ impl crate::TypeInner {
|
|||
Self::Array { base, size, stride } => {
|
||||
let count = match size {
|
||||
crate::ArraySize::Constant(handle) => {
|
||||
let constant = constants.try_get(handle)?;
|
||||
constant.to_array_length().unwrap_or(1)
|
||||
constants[handle].to_array_length().unwrap_or(1)
|
||||
}
|
||||
// A dynamically-sized array has to have at least one element
|
||||
crate::ArraySize::Dynamic => 1,
|
||||
};
|
||||
let last_el_size = types[base].inner.try_size_hlsl(types, constants)?;
|
||||
let last_el_size = types[base].inner.size_hlsl(types, constants);
|
||||
((count - 1) * stride) + last_el_size
|
||||
}
|
||||
_ => self.try_size(constants)?,
|
||||
})
|
||||
_ => self.size(constants),
|
||||
}
|
||||
}
|
||||
|
||||
/// Used to generate the name of the wrapped type constructor
|
||||
pub(super) fn hlsl_type_id<'a>(
|
||||
&self,
|
||||
base: crate::Handle<crate::Type>,
|
||||
types: &crate::UniqueArena<crate::Type>,
|
||||
constants: &crate::Arena<crate::Constant>,
|
||||
|
@ -103,7 +101,7 @@ impl crate::TypeInner {
|
|||
} => Cow::Owned(format!(
|
||||
"array{}_{}_",
|
||||
constants[size].to_array_length().unwrap(),
|
||||
self.hlsl_type_id(base, types, constants, names)?
|
||||
Self::hlsl_type_id(base, types, constants, names)?
|
||||
)),
|
||||
crate::TypeInner::Struct { .. } => {
|
||||
Cow::Borrowed(&names[&crate::proc::NameKey::Type(base)])
|
||||
|
@ -179,7 +177,7 @@ impl crate::BuiltIn {
|
|||
Self::BaseInstance | Self::BaseVertex | Self::WorkGroupSize => {
|
||||
return Err(Error::Unimplemented(format!("builtin {:?}", self)))
|
||||
}
|
||||
Self::ViewIndex => {
|
||||
Self::ViewIndex | Self::PointCoord => {
|
||||
return Err(Error::Custom(format!("Unsupported builtin {:?}", self)))
|
||||
}
|
||||
})
|
||||
|
|
|
@ -274,7 +274,7 @@ impl<'a, W: Write> super::Writer<'a, W> {
|
|||
// Write function body
|
||||
writeln!(self.out, "{{")?;
|
||||
|
||||
let array_coords = if wiq.arrayed { 1 } else { 0 };
|
||||
let array_coords = usize::from(wiq.arrayed);
|
||||
// extra parameter is the mip level count or the sample count
|
||||
let extra_coords = match wiq.class {
|
||||
crate::ImageClass::Storage { .. } => 0,
|
||||
|
@ -358,7 +358,7 @@ impl<'a, W: Write> super::Writer<'a, W> {
|
|||
module: &crate::Module,
|
||||
constructor: WrappedConstructor,
|
||||
) -> BackendResult {
|
||||
let name = module.types[constructor.ty].inner.hlsl_type_id(
|
||||
let name = crate::TypeInner::hlsl_type_id(
|
||||
constructor.ty,
|
||||
&module.types,
|
||||
&module.constants,
|
||||
|
@ -927,7 +927,7 @@ impl<'a, W: Write> super::Writer<'a, W> {
|
|||
|
||||
if let Some(crate::AddressSpace::Storage { .. }) = pointer_space {
|
||||
if let Some(ty) = func_ctx.info[handle].ty.handle() {
|
||||
write_wrapped_constructor(self, ty, module, func_ctx)?;
|
||||
write_wrapped_constructor(self, ty, module)?;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -935,12 +935,11 @@ impl<'a, W: Write> super::Writer<'a, W> {
|
|||
writer: &mut super::Writer<'_, W>,
|
||||
ty: Handle<crate::Type>,
|
||||
module: &crate::Module,
|
||||
func_ctx: &FunctionCtx,
|
||||
) -> BackendResult {
|
||||
match module.types[ty].inner {
|
||||
crate::TypeInner::Struct { ref members, .. } => {
|
||||
for member in members {
|
||||
write_wrapped_constructor(writer, member.ty, module, func_ctx)?;
|
||||
write_wrapped_constructor(writer, member.ty, module)?;
|
||||
}
|
||||
|
||||
let constructor = WrappedConstructor { ty };
|
||||
|
@ -951,7 +950,7 @@ impl<'a, W: Write> super::Writer<'a, W> {
|
|||
}
|
||||
}
|
||||
crate::TypeInner::Array { base, .. } => {
|
||||
write_wrapped_constructor(writer, base, module, func_ctx)?;
|
||||
write_wrapped_constructor(writer, base, module)?;
|
||||
}
|
||||
_ => {}
|
||||
};
|
||||
|
|
|
@ -829,10 +829,7 @@ impl<'a, W: fmt::Write> super::Writer<'a, W> {
|
|||
}
|
||||
}
|
||||
let ty_inner = &module.types[member.ty].inner;
|
||||
last_offset = member.offset
|
||||
+ ty_inner
|
||||
.try_size_hlsl(&module.types, &module.constants)
|
||||
.unwrap();
|
||||
last_offset = member.offset + ty_inner.size_hlsl(&module.types, &module.constants);
|
||||
|
||||
// The indentation is only for readability
|
||||
write!(self.out, "{}", back::INDENT)?;
|
||||
|
@ -1825,18 +1822,47 @@ impl<'a, W: fmt::Write> super::Writer<'a, W> {
|
|||
|
||||
for (i, case) in cases.iter().enumerate() {
|
||||
match case.value {
|
||||
crate::SwitchValue::Integer(value) => writeln!(
|
||||
crate::SwitchValue::Integer(value) => write!(
|
||||
self.out,
|
||||
"{}case {}{}: {{",
|
||||
"{}case {}{}:",
|
||||
indent_level_1, value, type_postfix
|
||||
)?,
|
||||
crate::SwitchValue::Default => {
|
||||
writeln!(self.out, "{}default: {{", indent_level_1)?
|
||||
write!(self.out, "{}default:", indent_level_1)?
|
||||
}
|
||||
}
|
||||
|
||||
// FXC doesn't support fallthrough so we duplicate the body of the following case blocks
|
||||
if case.fall_through {
|
||||
// The new block is not only stylistic, it plays a role here:
|
||||
// We might end up having to write the same case body
|
||||
// multiple times due to FXC not supporting fallthrough.
|
||||
// Therefore, some `Expression`s written by `Statement::Emit`
|
||||
// will end up having the same name (`_expr<handle_index>`).
|
||||
// So we need to put each case in its own scope.
|
||||
let write_block_braces = !(case.fall_through && case.body.is_empty());
|
||||
if write_block_braces {
|
||||
writeln!(self.out, " {{")?;
|
||||
} else {
|
||||
writeln!(self.out)?;
|
||||
}
|
||||
|
||||
// Although FXC does support a series of case clauses before
|
||||
// a block[^yes], it does not support fallthrough from a
|
||||
// non-empty case block to the next[^no]. If this case has a
|
||||
// non-empty body with a fallthrough, emulate that by
|
||||
// duplicating the bodies of all the cases it would fall
|
||||
// into as extensions of this case's own body. This makes
|
||||
// the HLSL output potentially quadratic in the size of the
|
||||
// Naga IR.
|
||||
//
|
||||
// [^yes]: ```hlsl
|
||||
// case 1:
|
||||
// case 2: do_stuff()
|
||||
// ```
|
||||
// [^no]: ```hlsl
|
||||
// case 1: do_this();
|
||||
// case 2: do_that();
|
||||
// ```
|
||||
if case.fall_through && !case.body.is_empty() {
|
||||
let curr_len = i + 1;
|
||||
let end_case_idx = curr_len
|
||||
+ cases
|
||||
|
@ -1861,12 +1887,16 @@ impl<'a, W: fmt::Write> super::Writer<'a, W> {
|
|||
for sta in case.body.iter() {
|
||||
self.write_stmt(module, sta, func_ctx, indent_level_2)?;
|
||||
}
|
||||
if case.body.last().map_or(true, |s| !s.is_terminator()) {
|
||||
if !case.fall_through
|
||||
&& case.body.last().map_or(true, |s| !s.is_terminator())
|
||||
{
|
||||
writeln!(self.out, "{}break;", indent_level_2)?;
|
||||
}
|
||||
}
|
||||
|
||||
writeln!(self.out, "{}}}", indent_level_1)?;
|
||||
if write_block_braces {
|
||||
writeln!(self.out, "{}}}", indent_level_1)?;
|
||||
}
|
||||
}
|
||||
|
||||
writeln!(self.out, "{}}}", level)?
|
||||
|
@ -2395,8 +2425,9 @@ impl<'a, W: fmt::Write> super::Writer<'a, W> {
|
|||
}
|
||||
},
|
||||
};
|
||||
write!(self.out, "{}", op_str)?;
|
||||
write!(self.out, "{}(", op_str)?;
|
||||
self.write_expr(module, expr, func_ctx)?;
|
||||
write!(self.out, ")")?;
|
||||
}
|
||||
Expression::As {
|
||||
expr,
|
||||
|
|
|
@ -156,8 +156,10 @@ pub enum Error {
|
|||
#[cfg_attr(feature = "serialize", derive(serde::Serialize))]
|
||||
#[cfg_attr(feature = "deserialize", derive(serde::Deserialize))]
|
||||
pub enum EntryPointError {
|
||||
#[error("global '{0}' doesn't have a binding")]
|
||||
MissingBinding(String),
|
||||
#[error("mapping of {0:?} is missing")]
|
||||
MissingBinding(crate::ResourceBinding),
|
||||
MissingBindTarget(crate::ResourceBinding),
|
||||
#[error("mapping for push constants is missing")]
|
||||
MissingPushConstants,
|
||||
#[error("mapping for sizes buffer is missing")]
|
||||
|
@ -303,7 +305,7 @@ impl Options {
|
|||
index: 0,
|
||||
interpolation: None,
|
||||
}),
|
||||
None => Err(EntryPointError::MissingBinding(res_binding.clone())),
|
||||
None => Err(EntryPointError::MissingBindTarget(res_binding.clone())),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -391,6 +393,7 @@ impl ResolvedBinding {
|
|||
Bi::VertexIndex => "vertex_id",
|
||||
// fragment
|
||||
Bi::FragDepth => "depth(any)",
|
||||
Bi::PointCoord => "point_coord",
|
||||
Bi::FrontFacing => "front_facing",
|
||||
Bi::PrimitiveIndex => "primitive_id",
|
||||
Bi::SampleIndex => "sample_id",
|
||||
|
|
|
@ -1442,7 +1442,7 @@ impl<W: Write> Writer<W> {
|
|||
if offset.is_none() && !is_cube_map {
|
||||
write!(self.out, ", {}::int2(0)", NAMESPACE)?;
|
||||
}
|
||||
let letter = ['x', 'y', 'z', 'w'][component as usize];
|
||||
let letter = back::COMPONENTS[component as usize];
|
||||
write!(self.out, ", {}::component::{}", NAMESPACE, letter)?;
|
||||
}
|
||||
}
|
||||
|
@ -1500,8 +1500,9 @@ impl<W: Write> Writer<W> {
|
|||
_ => return Err(Error::Validation),
|
||||
},
|
||||
};
|
||||
write!(self.out, "{}", op_str)?;
|
||||
write!(self.out, "{}(", op_str)?;
|
||||
self.put_expression(expr, context, false)?;
|
||||
write!(self.out, ")")?;
|
||||
}
|
||||
crate::Expression::Binary { op, left, right } => {
|
||||
let op_str = crate::back::binary_operation_str(op);
|
||||
|
@ -2545,19 +2546,30 @@ impl<W: Write> Writer<W> {
|
|||
for case in cases.iter() {
|
||||
match case.value {
|
||||
crate::SwitchValue::Integer(value) => {
|
||||
writeln!(self.out, "{}case {}{}: {{", lcase, value, type_postfix)?;
|
||||
write!(self.out, "{}case {}{}:", lcase, value, type_postfix)?;
|
||||
}
|
||||
crate::SwitchValue::Default => {
|
||||
writeln!(self.out, "{}default: {{", lcase)?;
|
||||
write!(self.out, "{}default:", lcase)?;
|
||||
}
|
||||
}
|
||||
|
||||
let write_block_braces = !(case.fall_through && case.body.is_empty());
|
||||
if write_block_braces {
|
||||
writeln!(self.out, " {{")?;
|
||||
} else {
|
||||
writeln!(self.out)?;
|
||||
}
|
||||
|
||||
self.put_block(lcase.next(), &case.body, context)?;
|
||||
if !case.fall_through
|
||||
&& case.body.last().map_or(true, |s| !s.is_terminator())
|
||||
{
|
||||
writeln!(self.out, "{}break;", lcase.next())?;
|
||||
}
|
||||
writeln!(self.out, "{}}}", lcase)?;
|
||||
|
||||
if write_block_braces {
|
||||
writeln!(self.out, "{}}}", lcase)?;
|
||||
}
|
||||
}
|
||||
writeln!(self.out, "{}}}", level)?;
|
||||
}
|
||||
|
@ -3387,33 +3399,52 @@ impl<W: Write> Writer<W> {
|
|||
if fun_info[var_handle].is_empty() {
|
||||
continue;
|
||||
}
|
||||
if let Some(ref br) = var.binding {
|
||||
let good = match options.per_stage_map[ep.stage].resources.get(br) {
|
||||
Some(target) => {
|
||||
let binding_ty = match module.types[var.ty].inner {
|
||||
crate::TypeInner::BindingArray { base, .. } => {
|
||||
&module.types[base].inner
|
||||
}
|
||||
ref ty => ty,
|
||||
};
|
||||
match *binding_ty {
|
||||
crate::TypeInner::Image { .. } => target.texture.is_some(),
|
||||
crate::TypeInner::Sampler { .. } => target.sampler.is_some(),
|
||||
_ => target.buffer.is_some(),
|
||||
match var.space {
|
||||
crate::AddressSpace::Uniform
|
||||
| crate::AddressSpace::Storage { .. }
|
||||
| crate::AddressSpace::Handle => {
|
||||
let br = match var.binding {
|
||||
Some(ref br) => br,
|
||||
None => {
|
||||
let var_name = var.name.clone().unwrap_or_default();
|
||||
ep_error =
|
||||
Some(super::EntryPointError::MissingBinding(var_name));
|
||||
break;
|
||||
}
|
||||
};
|
||||
let good = match options.per_stage_map[ep.stage].resources.get(br) {
|
||||
Some(target) => {
|
||||
let binding_ty = match module.types[var.ty].inner {
|
||||
crate::TypeInner::BindingArray { base, .. } => {
|
||||
&module.types[base].inner
|
||||
}
|
||||
ref ty => ty,
|
||||
};
|
||||
match *binding_ty {
|
||||
crate::TypeInner::Image { .. } => target.texture.is_some(),
|
||||
crate::TypeInner::Sampler { .. } => {
|
||||
target.sampler.is_some()
|
||||
}
|
||||
_ => target.buffer.is_some(),
|
||||
}
|
||||
}
|
||||
None => false,
|
||||
};
|
||||
if !good {
|
||||
ep_error =
|
||||
Some(super::EntryPointError::MissingBindTarget(br.clone()));
|
||||
break;
|
||||
}
|
||||
None => false,
|
||||
};
|
||||
if !good {
|
||||
ep_error = Some(super::EntryPointError::MissingBinding(br.clone()));
|
||||
break;
|
||||
}
|
||||
}
|
||||
if var.space == crate::AddressSpace::PushConstant {
|
||||
if let Err(e) = options.resolve_push_constants(ep.stage) {
|
||||
ep_error = Some(e);
|
||||
break;
|
||||
crate::AddressSpace::PushConstant => {
|
||||
if let Err(e) = options.resolve_push_constants(ep.stage) {
|
||||
ep_error = Some(e);
|
||||
break;
|
||||
}
|
||||
}
|
||||
crate::AddressSpace::Function
|
||||
| crate::AddressSpace::Private
|
||||
| crate::AddressSpace::WorkGroup => {}
|
||||
}
|
||||
}
|
||||
if supports_array_length {
|
||||
|
@ -3955,12 +3986,13 @@ fn test_stack_size() {
|
|||
|
||||
{
|
||||
// check expression stack
|
||||
let mut addresses = usize::MAX..0usize;
|
||||
let mut addresses_start = usize::MAX;
|
||||
let mut addresses_end = 0usize;
|
||||
for pointer in writer.put_expression_stack_pointers {
|
||||
addresses.start = addresses.start.min(pointer as usize);
|
||||
addresses.end = addresses.end.max(pointer as usize);
|
||||
addresses_start = addresses_start.min(pointer as usize);
|
||||
addresses_end = addresses_end.max(pointer as usize);
|
||||
}
|
||||
let stack_size = addresses.end - addresses.start;
|
||||
let stack_size = addresses_end - addresses_start;
|
||||
// check the size (in debug only)
|
||||
// last observed macOS value: 20528 (CI)
|
||||
if !(11000..=25000).contains(&stack_size) {
|
||||
|
@ -3970,12 +4002,13 @@ fn test_stack_size() {
|
|||
|
||||
{
|
||||
// check block stack
|
||||
let mut addresses = usize::MAX..0usize;
|
||||
let mut addresses_start = usize::MAX;
|
||||
let mut addresses_end = 0usize;
|
||||
for pointer in writer.put_block_stack_pointers {
|
||||
addresses.start = addresses.start.min(pointer as usize);
|
||||
addresses.end = addresses.end.max(pointer as usize);
|
||||
addresses_start = addresses_start.min(pointer as usize);
|
||||
addresses_end = addresses_end.max(pointer as usize);
|
||||
}
|
||||
let stack_size = addresses.end - addresses.start;
|
||||
let stack_size = addresses_end - addresses_start;
|
||||
// check the size (in debug only)
|
||||
// last observed macOS value: 19152 (CI)
|
||||
if !(9500..=20000).contains(&stack_size) {
|
||||
|
|
|
@ -1680,32 +1680,37 @@ impl<'w> BlockContext<'w> {
|
|||
spirv::SelectionControl::NONE,
|
||||
));
|
||||
|
||||
let default_id = self.gen_id();
|
||||
let mut default_id = None;
|
||||
// id of previous empty fall-through case
|
||||
let mut last_id = None;
|
||||
|
||||
let mut reached_default = false;
|
||||
let mut raw_cases = Vec::with_capacity(cases.len());
|
||||
let mut case_ids = Vec::with_capacity(cases.len());
|
||||
for case in cases.iter() {
|
||||
// take id of previous empty fall-through case or generate a new one
|
||||
let label_id = last_id.take().unwrap_or_else(|| self.gen_id());
|
||||
|
||||
if case.fall_through && case.body.is_empty() {
|
||||
last_id = Some(label_id);
|
||||
}
|
||||
|
||||
case_ids.push(label_id);
|
||||
|
||||
match case.value {
|
||||
crate::SwitchValue::Integer(value) => {
|
||||
let label_id = self.gen_id();
|
||||
// No cases should be added after the default case is encountered
|
||||
// since the default case catches all
|
||||
if !reached_default {
|
||||
raw_cases.push(super::instructions::Case {
|
||||
value: value as Word,
|
||||
label_id,
|
||||
});
|
||||
}
|
||||
case_ids.push(label_id);
|
||||
raw_cases.push(super::instructions::Case {
|
||||
value: value as Word,
|
||||
label_id,
|
||||
});
|
||||
}
|
||||
crate::SwitchValue::Default => {
|
||||
case_ids.push(default_id);
|
||||
reached_default = true;
|
||||
default_id = Some(label_id);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let default_id = default_id.unwrap();
|
||||
|
||||
self.function.consume(
|
||||
block,
|
||||
Instruction::switch(selector_id, default_id, &raw_cases),
|
||||
|
@ -1716,7 +1721,12 @@ impl<'w> BlockContext<'w> {
|
|||
..loop_context
|
||||
};
|
||||
|
||||
for (i, (case, label_id)) in cases.iter().zip(case_ids.iter()).enumerate() {
|
||||
for (i, (case, label_id)) in cases
|
||||
.iter()
|
||||
.zip(case_ids.iter())
|
||||
.filter(|&(case, _)| !(case.fall_through && case.body.is_empty()))
|
||||
.enumerate()
|
||||
{
|
||||
let case_finish_id = if case.fall_through {
|
||||
case_ids[i + 1]
|
||||
} else {
|
||||
|
@ -1732,17 +1742,6 @@ impl<'w> BlockContext<'w> {
|
|||
)?;
|
||||
}
|
||||
|
||||
// If no default was encountered write a empty block to satisfy the presence of
|
||||
// a block the default label
|
||||
if !reached_default {
|
||||
self.write_block(
|
||||
default_id,
|
||||
&[],
|
||||
BlockExit::Branch { target: merge_id },
|
||||
inner_context,
|
||||
)?;
|
||||
}
|
||||
|
||||
block = Block::new(merge_id);
|
||||
}
|
||||
crate::Statement::Loop {
|
||||
|
@ -2079,8 +2078,50 @@ impl<'w> BlockContext<'w> {
|
|||
value_id,
|
||||
)
|
||||
}
|
||||
crate::AtomicFunction::Exchange { compare: Some(_) } => {
|
||||
return Err(Error::FeatureNotImplemented("atomic CompareExchange"));
|
||||
crate::AtomicFunction::Exchange { compare: Some(cmp) } => {
|
||||
let scalar_type_id = match *value_inner {
|
||||
crate::TypeInner::Scalar { kind, width } => {
|
||||
self.get_type_id(LookupType::Local(LocalType::Value {
|
||||
vector_size: None,
|
||||
kind,
|
||||
width,
|
||||
pointer_space: None,
|
||||
}))
|
||||
}
|
||||
_ => unimplemented!(),
|
||||
};
|
||||
let bool_type_id =
|
||||
self.get_type_id(LookupType::Local(LocalType::Value {
|
||||
vector_size: None,
|
||||
kind: crate::ScalarKind::Bool,
|
||||
width: crate::BOOL_WIDTH,
|
||||
pointer_space: None,
|
||||
}));
|
||||
|
||||
let cas_result_id = self.gen_id();
|
||||
let equality_result_id = self.gen_id();
|
||||
let mut cas_instr = Instruction::new(spirv::Op::AtomicCompareExchange);
|
||||
cas_instr.set_type(scalar_type_id);
|
||||
cas_instr.set_result(cas_result_id);
|
||||
cas_instr.add_operand(pointer_id);
|
||||
cas_instr.add_operand(scope_constant_id);
|
||||
cas_instr.add_operand(semantics_id); // semantics if equal
|
||||
cas_instr.add_operand(semantics_id); // semantics if not equal
|
||||
cas_instr.add_operand(value_id);
|
||||
cas_instr.add_operand(self.cached[cmp]);
|
||||
block.body.push(cas_instr);
|
||||
block.body.push(Instruction::binary(
|
||||
spirv::Op::IEqual,
|
||||
bool_type_id,
|
||||
equality_result_id,
|
||||
cas_result_id,
|
||||
self.cached[cmp],
|
||||
));
|
||||
Instruction::composite_construct(
|
||||
result_type_id,
|
||||
id,
|
||||
&[cas_result_id, equality_result_id],
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
@ -317,6 +317,22 @@ impl<'w> BlockContext<'w> {
|
|||
let array_index_i32_id = self.cached[array_index];
|
||||
let reconciled_array_index_id = if component_kind == crate::ScalarKind::Sint {
|
||||
array_index_i32_id
|
||||
} else if component_kind == crate::ScalarKind::Uint {
|
||||
let u32_id = self.get_type_id(LookupType::Local(LocalType::Value {
|
||||
vector_size: None,
|
||||
kind: crate::ScalarKind::Uint,
|
||||
width: 4,
|
||||
pointer_space: None,
|
||||
}));
|
||||
|
||||
let reconciled_id = self.gen_id();
|
||||
block.body.push(Instruction::unary(
|
||||
spirv::Op::Bitcast,
|
||||
u32_id,
|
||||
reconciled_id,
|
||||
array_index_i32_id,
|
||||
));
|
||||
reconciled_id
|
||||
} else {
|
||||
let component_type_id = self.get_type_id(LookupType::Local(LocalType::Value {
|
||||
vector_size: None,
|
||||
|
@ -1017,7 +1033,7 @@ impl<'w> BlockContext<'w> {
|
|||
Id::D3 => 3,
|
||||
};
|
||||
let extended_size_type_id = {
|
||||
let array_coords = if arrayed { 1 } else { 0 };
|
||||
let array_coords = usize::from(arrayed);
|
||||
let vector_size = match dim_coords + array_coords {
|
||||
2 => Some(crate::VectorSize::Bi),
|
||||
3 => Some(crate::VectorSize::Tri),
|
||||
|
|
|
@ -1231,6 +1231,7 @@ impl Writer {
|
|||
Bi::VertexIndex => BuiltIn::VertexIndex,
|
||||
// fragment
|
||||
Bi::FragDepth => BuiltIn::FragDepth,
|
||||
Bi::PointCoord => BuiltIn::PointCoord,
|
||||
Bi::FrontFacing => BuiltIn::FrontFacing,
|
||||
Bi::PrimitiveIndex => {
|
||||
self.require_any(
|
||||
|
|
|
@ -452,11 +452,11 @@ impl<W: Write> Writer<W> {
|
|||
/// Adds no trailing or leading whitespace
|
||||
fn write_value_type(&mut self, module: &Module, inner: &TypeInner) -> BackendResult {
|
||||
match *inner {
|
||||
TypeInner::Vector { size, kind, .. } => write!(
|
||||
TypeInner::Vector { size, kind, width } => write!(
|
||||
self.out,
|
||||
"vec{}<{}>",
|
||||
back::vector_size_str(size),
|
||||
scalar_kind_str(kind),
|
||||
scalar_kind_str(kind, width),
|
||||
)?,
|
||||
TypeInner::Sampler { comparison: false } => {
|
||||
write!(self.out, "sampler")?;
|
||||
|
@ -478,7 +478,7 @@ impl<W: Write> Writer<W> {
|
|||
Ic::Sampled { kind, multi } => (
|
||||
"",
|
||||
if multi { "multisampled_" } else { "" },
|
||||
scalar_kind_str(kind),
|
||||
scalar_kind_str(kind, 4),
|
||||
"",
|
||||
),
|
||||
Ic::Depth { multi } => {
|
||||
|
@ -508,11 +508,11 @@ impl<W: Write> Writer<W> {
|
|||
write!(self.out, "<{}{}>", format_str, storage_str)?;
|
||||
}
|
||||
}
|
||||
TypeInner::Scalar { kind, .. } => {
|
||||
write!(self.out, "{}", scalar_kind_str(kind))?;
|
||||
TypeInner::Scalar { kind, width } => {
|
||||
write!(self.out, "{}", scalar_kind_str(kind, width))?;
|
||||
}
|
||||
TypeInner::Atomic { kind, .. } => {
|
||||
write!(self.out, "atomic<{}>", scalar_kind_str(kind))?;
|
||||
TypeInner::Atomic { kind, width } => {
|
||||
write!(self.out, "atomic<{}>", scalar_kind_str(kind, width))?;
|
||||
}
|
||||
TypeInner::Array {
|
||||
base,
|
||||
|
@ -582,12 +582,12 @@ impl<W: Write> Writer<W> {
|
|||
TypeInner::ValuePointer {
|
||||
size: None,
|
||||
kind,
|
||||
width: _,
|
||||
width,
|
||||
space,
|
||||
} => {
|
||||
let (address, maybe_access) = address_space_str(space);
|
||||
if let Some(space) = address {
|
||||
write!(self.out, "ptr<{}, {}", space, scalar_kind_str(kind))?;
|
||||
write!(self.out, "ptr<{}, {}", space, scalar_kind_str(kind, width))?;
|
||||
if let Some(access) = maybe_access {
|
||||
write!(self.out, ", {}", access)?;
|
||||
}
|
||||
|
@ -602,7 +602,7 @@ impl<W: Write> Writer<W> {
|
|||
TypeInner::ValuePointer {
|
||||
size: Some(size),
|
||||
kind,
|
||||
width: _,
|
||||
width,
|
||||
space,
|
||||
} => {
|
||||
let (address, maybe_access) = address_space_str(space);
|
||||
|
@ -612,7 +612,7 @@ impl<W: Write> Writer<W> {
|
|||
"ptr<{}, vec{}<{}>",
|
||||
space,
|
||||
back::vector_size_str(size),
|
||||
scalar_kind_str(kind)
|
||||
scalar_kind_str(kind, width)
|
||||
)?;
|
||||
if let Some(access) = maybe_access {
|
||||
write!(self.out, ", {}", access)?;
|
||||
|
@ -880,25 +880,47 @@ impl<W: Write> Writer<W> {
|
|||
};
|
||||
|
||||
let l2 = level.next();
|
||||
if !cases.is_empty() {
|
||||
for case in cases {
|
||||
match case.value {
|
||||
crate::SwitchValue::Integer(value) => {
|
||||
writeln!(self.out, "{}case {}{}: {{", l2, value, type_postfix)?;
|
||||
let mut new_case = true;
|
||||
for case in cases {
|
||||
if case.fall_through && !case.body.is_empty() {
|
||||
// TODO: we could do the same workaround as we did for the HLSL backend
|
||||
return Err(Error::Unimplemented(
|
||||
"fall-through switch case block".into(),
|
||||
));
|
||||
}
|
||||
|
||||
match case.value {
|
||||
crate::SwitchValue::Integer(value) => {
|
||||
if new_case {
|
||||
write!(self.out, "{}case ", l2)?;
|
||||
}
|
||||
crate::SwitchValue::Default => {
|
||||
writeln!(self.out, "{}default: {{", l2)?;
|
||||
write!(self.out, "{}{}", value, type_postfix)?;
|
||||
}
|
||||
crate::SwitchValue::Default => {
|
||||
if new_case {
|
||||
if case.fall_through {
|
||||
write!(self.out, "{}case ", l2)?;
|
||||
} else {
|
||||
write!(self.out, "{}", l2)?;
|
||||
}
|
||||
}
|
||||
write!(self.out, "default")?;
|
||||
}
|
||||
}
|
||||
|
||||
for sta in case.body.iter() {
|
||||
self.write_stmt(module, sta, func_ctx, l2.next())?;
|
||||
}
|
||||
new_case = !case.fall_through;
|
||||
|
||||
if case.fall_through {
|
||||
writeln!(self.out, "{}fallthrough;", l2.next())?;
|
||||
}
|
||||
if case.fall_through {
|
||||
write!(self.out, ", ")?;
|
||||
} else {
|
||||
writeln!(self.out, ": {{")?;
|
||||
}
|
||||
|
||||
for sta in case.body.iter() {
|
||||
self.write_stmt(module, sta, func_ctx, l2.next())?;
|
||||
}
|
||||
|
||||
if !case.fall_through {
|
||||
writeln!(self.out, "{}}}", l2)?;
|
||||
}
|
||||
}
|
||||
|
@ -1402,17 +1424,24 @@ impl<W: Write> Writer<W> {
|
|||
} => {
|
||||
let inner = func_ctx.info[expr].ty.inner_with(&module.types);
|
||||
match *inner {
|
||||
TypeInner::Matrix { columns, rows, .. } => {
|
||||
TypeInner::Matrix {
|
||||
columns,
|
||||
rows,
|
||||
width,
|
||||
..
|
||||
} => {
|
||||
let scalar_kind_str = scalar_kind_str(kind, convert.unwrap_or(width));
|
||||
write!(
|
||||
self.out,
|
||||
"mat{}x{}<f32>",
|
||||
"mat{}x{}<{}>",
|
||||
back::vector_size_str(columns),
|
||||
back::vector_size_str(rows)
|
||||
back::vector_size_str(rows),
|
||||
scalar_kind_str
|
||||
)?;
|
||||
}
|
||||
TypeInner::Vector { size, .. } => {
|
||||
TypeInner::Vector { size, width, .. } => {
|
||||
let vector_size_str = back::vector_size_str(size);
|
||||
let scalar_kind_str = scalar_kind_str(kind);
|
||||
let scalar_kind_str = scalar_kind_str(kind, convert.unwrap_or(width));
|
||||
if convert.is_some() {
|
||||
write!(self.out, "vec{}<{}>", vector_size_str, scalar_kind_str)?;
|
||||
} else {
|
||||
|
@ -1423,11 +1452,12 @@ impl<W: Write> Writer<W> {
|
|||
)?;
|
||||
}
|
||||
}
|
||||
TypeInner::Scalar { .. } => {
|
||||
TypeInner::Scalar { width, .. } => {
|
||||
let scalar_kind_str = scalar_kind_str(kind, convert.unwrap_or(width));
|
||||
if convert.is_some() {
|
||||
write!(self.out, "{}", scalar_kind_str(kind))?
|
||||
write!(self.out, "{}", scalar_kind_str)?
|
||||
} else {
|
||||
write!(self.out, "bitcast<{}>", scalar_kind_str(kind))?
|
||||
write!(self.out, "bitcast<{}>", scalar_kind_str)?
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
|
@ -1443,8 +1473,8 @@ impl<W: Write> Writer<W> {
|
|||
}
|
||||
Expression::Splat { size, value } => {
|
||||
let inner = func_ctx.info[value].ty.inner_with(&module.types);
|
||||
let scalar_kind = match *inner {
|
||||
crate::TypeInner::Scalar { kind, .. } => kind,
|
||||
let (scalar_kind, scalar_width) = match *inner {
|
||||
crate::TypeInner::Scalar { kind, width } => (kind, width),
|
||||
_ => {
|
||||
return Err(Error::Unimplemented(format!(
|
||||
"write_expr expression::splat {:?}",
|
||||
|
@ -1452,7 +1482,7 @@ impl<W: Write> Writer<W> {
|
|||
)));
|
||||
}
|
||||
};
|
||||
let scalar = scalar_kind_str(scalar_kind);
|
||||
let scalar = scalar_kind_str(scalar_kind, scalar_width);
|
||||
let size = back::vector_size_str(size);
|
||||
|
||||
write!(self.out, "vec{}<{}>(", size, scalar)?;
|
||||
|
@ -1888,7 +1918,6 @@ const fn builtin_str(built_in: crate::BuiltIn) -> Option<&'static str> {
|
|||
Bi::LocalInvocationIndex => Some("local_invocation_index"),
|
||||
Bi::GlobalInvocationId => Some("global_invocation_id"),
|
||||
Bi::WorkGroupId => Some("workgroup_id"),
|
||||
Bi::WorkGroupSize => Some("workgroup_size"),
|
||||
Bi::NumWorkGroups => Some("num_workgroups"),
|
||||
Bi::SampleIndex => Some("sample_index"),
|
||||
Bi::SampleMask => Some("sample_mask"),
|
||||
|
@ -1909,14 +1938,16 @@ const fn image_dimension_str(dim: crate::ImageDimension) -> &'static str {
|
|||
}
|
||||
}
|
||||
|
||||
const fn scalar_kind_str(kind: crate::ScalarKind) -> &'static str {
|
||||
const fn scalar_kind_str(kind: crate::ScalarKind, width: u8) -> &'static str {
|
||||
use crate::ScalarKind as Sk;
|
||||
|
||||
match kind {
|
||||
Sk::Float => "f32",
|
||||
Sk::Sint => "i32",
|
||||
Sk::Uint => "u32",
|
||||
Sk::Bool => "bool",
|
||||
match (kind, width) {
|
||||
(Sk::Float, 8) => "f64",
|
||||
(Sk::Float, 4) => "f32",
|
||||
(Sk::Sint, 4) => "i32",
|
||||
(Sk::Uint, 4) => "u32",
|
||||
(Sk::Bool, 1) => "bool",
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -224,12 +224,12 @@ impl<'a> ConstantSolver<'a> {
|
|||
ScalarValue::Sint(a),
|
||||
ScalarValue::Sint(b),
|
||||
ScalarValue::Sint(c),
|
||||
) => ScalarValue::Sint(a.max(b).min(c)),
|
||||
) => ScalarValue::Sint(a.clamp(b, c)),
|
||||
(
|
||||
ScalarValue::Uint(a),
|
||||
ScalarValue::Uint(b),
|
||||
ScalarValue::Uint(c),
|
||||
) => ScalarValue::Uint(a.max(b).min(c)),
|
||||
) => ScalarValue::Uint(a.clamp(b, c)),
|
||||
(
|
||||
ScalarValue::Float(a),
|
||||
ScalarValue::Float(b),
|
||||
|
@ -739,8 +739,11 @@ mod tests {
|
|||
|
||||
let res3_inner = &constants[res3].inner;
|
||||
|
||||
match res3_inner {
|
||||
ConstantInner::Composite { ty, components } => {
|
||||
match *res3_inner {
|
||||
ConstantInner::Composite {
|
||||
ref ty,
|
||||
ref components,
|
||||
} => {
|
||||
assert_eq!(*ty, vec_ty);
|
||||
let mut components_iter = components.iter().copied();
|
||||
assert_eq!(
|
||||
|
@ -933,8 +936,11 @@ mod tests {
|
|||
|
||||
let res1_inner = &constants[res1].inner;
|
||||
|
||||
match res1_inner {
|
||||
ConstantInner::Composite { ty, components } => {
|
||||
match *res1_inner {
|
||||
ConstantInner::Composite {
|
||||
ref ty,
|
||||
ref components,
|
||||
} => {
|
||||
assert_eq!(*ty, vec_ty);
|
||||
let mut components_iter = components.iter().copied();
|
||||
assert_eq!(
|
||||
|
|
|
@ -34,7 +34,10 @@ impl ExprPos {
|
|||
/// Returns an lhs position if the current position is lhs otherwise AccessBase
|
||||
const fn maybe_access_base(&self, constant_index: bool) -> Self {
|
||||
match *self {
|
||||
ExprPos::Lhs => *self,
|
||||
ExprPos::Lhs
|
||||
| ExprPos::AccessBase {
|
||||
constant_index: false,
|
||||
} => *self,
|
||||
_ => ExprPos::AccessBase { constant_index },
|
||||
}
|
||||
}
|
||||
|
@ -476,7 +479,7 @@ impl Context {
|
|||
) -> Result<(Option<Handle<Expression>>, Span)> {
|
||||
let HirExpr { ref kind, meta } = stmt.hir_exprs[expr];
|
||||
|
||||
log::debug!("Lowering {:?}", expr);
|
||||
log::debug!("Lowering {:?} (kind {:?}, pos {:?})", expr, kind, pos);
|
||||
|
||||
let handle = match *kind {
|
||||
HirExprKind::Access { base, index } => {
|
||||
|
@ -537,9 +540,7 @@ impl Context {
|
|||
pointer
|
||||
}
|
||||
HirExprKind::Select { base, ref field } => {
|
||||
let base = self
|
||||
.lower_expect_inner(stmt, parser, base, pos.maybe_access_base(true), body)?
|
||||
.0;
|
||||
let base = self.lower_expect_inner(stmt, parser, base, pos, body)?.0;
|
||||
|
||||
parser.field_selection(self, pos, body, base, field, meta)?
|
||||
}
|
||||
|
|
|
@ -105,7 +105,7 @@ impl ShaderMetadata {
|
|||
self.version = 0;
|
||||
self.profile = Profile::Core;
|
||||
self.stage = stage;
|
||||
self.workgroup_size = [if stage == ShaderStage::Compute { 1 } else { 0 }; 3];
|
||||
self.workgroup_size = [u32::from(stage == ShaderStage::Compute); 3];
|
||||
self.early_fragment_tests = false;
|
||||
self.extensions.clear();
|
||||
}
|
||||
|
|
|
@ -128,6 +128,16 @@ impl Parser {
|
|||
mutable: false,
|
||||
storage: StorageQualifier::Input,
|
||||
},
|
||||
"gl_PointCoord" => BuiltInData {
|
||||
inner: TypeInner::Vector {
|
||||
size: VectorSize::Bi,
|
||||
kind: ScalarKind::Float,
|
||||
width: 4,
|
||||
},
|
||||
builtin: BuiltIn::PointCoord,
|
||||
mutable: false,
|
||||
storage: StorageQualifier::Input,
|
||||
},
|
||||
"gl_GlobalInvocationID"
|
||||
| "gl_NumWorkGroups"
|
||||
| "gl_WorkGroupSize"
|
||||
|
|
|
@ -137,6 +137,7 @@ pub(super) fn map_builtin(word: spirv::Word, invariant: bool) -> Result<crate::B
|
|||
Some(Bi::VertexIndex) => crate::BuiltIn::VertexIndex,
|
||||
// fragment
|
||||
Some(Bi::FragDepth) => crate::BuiltIn::FragDepth,
|
||||
Some(Bi::PointCoord) => crate::BuiltIn::PointCoord,
|
||||
Some(Bi::FrontFacing) => crate::BuiltIn::FrontFacing,
|
||||
Some(Bi::PrimitiveId) => crate::BuiltIn::PrimitiveIndex,
|
||||
Some(Bi::SampleId) => crate::BuiltIn::SampleIndex,
|
||||
|
|
|
@ -32,7 +32,6 @@ pub fn map_built_in(word: &str, span: Span) -> Result<crate::BuiltIn, Error<'_>>
|
|||
"local_invocation_id" => crate::BuiltIn::LocalInvocationId,
|
||||
"local_invocation_index" => crate::BuiltIn::LocalInvocationIndex,
|
||||
"workgroup_id" => crate::BuiltIn::WorkGroupId,
|
||||
"workgroup_size" => crate::BuiltIn::WorkGroupSize,
|
||||
"num_workgroups" => crate::BuiltIn::NumWorkGroups,
|
||||
_ => return Err(Error::UnknownBuiltin(span)),
|
||||
})
|
||||
|
@ -97,17 +96,11 @@ pub fn map_storage_format(word: &str, span: Span) -> Result<crate::StorageFormat
|
|||
|
||||
pub fn get_scalar_type(word: &str) -> Option<(crate::ScalarKind, crate::Bytes)> {
|
||||
match word {
|
||||
"f16" => Some((crate::ScalarKind::Float, 2)),
|
||||
// "f16" => Some((crate::ScalarKind::Float, 2)),
|
||||
"f32" => Some((crate::ScalarKind::Float, 4)),
|
||||
"f64" => Some((crate::ScalarKind::Float, 8)),
|
||||
"i8" => Some((crate::ScalarKind::Sint, 1)),
|
||||
"i16" => Some((crate::ScalarKind::Sint, 2)),
|
||||
"i32" => Some((crate::ScalarKind::Sint, 4)),
|
||||
"i64" => Some((crate::ScalarKind::Sint, 8)),
|
||||
"u8" => Some((crate::ScalarKind::Uint, 1)),
|
||||
"u16" => Some((crate::ScalarKind::Uint, 2)),
|
||||
"u32" => Some((crate::ScalarKind::Uint, 4)),
|
||||
"u64" => Some((crate::ScalarKind::Uint, 8)),
|
||||
"bool" => Some((crate::ScalarKind::Bool, crate::BOOL_WIDTH)),
|
||||
_ => None,
|
||||
}
|
||||
|
|
|
@ -279,10 +279,7 @@ impl<'a> Error<'a> {
|
|||
Error::UnknownScalarType(ref bad_span) => ParseError {
|
||||
message: format!("unknown scalar type: '{}'", &source[bad_span.clone()]),
|
||||
labels: vec![(bad_span.clone(), "unknown scalar type".into())],
|
||||
notes: vec!["Valid scalar types are f16, f32, f64, \
|
||||
i8, i16, i32, i64, \
|
||||
u8, u16, u32, u64, bool"
|
||||
.into()],
|
||||
notes: vec!["Valid scalar types are f32, f64, i32, u32, bool".into()],
|
||||
},
|
||||
Error::BadTextureSampleType {
|
||||
ref span,
|
||||
|
@ -1513,13 +1510,20 @@ impl Parser {
|
|||
lexer.span_from(initial)
|
||||
}
|
||||
|
||||
fn parse_switch_value<'a>(lexer: &mut Lexer<'a>, uint: bool) -> Result<i32, Error<'a>> {
|
||||
let token_span = lexer.next();
|
||||
match token_span.0 {
|
||||
Token::Number(Ok(Number::U32(num))) if uint => Ok(num as i32),
|
||||
Token::Number(Ok(Number::I32(num))) if !uint => Ok(num),
|
||||
Token::Number(Err(e)) => Err(Error::BadNumber(token_span.1, e)),
|
||||
_ => Err(Error::Unexpected(token_span.1, ExpectedToken::Integer)),
|
||||
fn parse_switch_value<'a>(
|
||||
lexer: &mut Lexer<'a>,
|
||||
uint: bool,
|
||||
) -> Result<crate::SwitchValue, Error<'a>> {
|
||||
match lexer.next() {
|
||||
(Token::Word("default"), _) => Ok(crate::SwitchValue::Default),
|
||||
(Token::Number(Ok(Number::U32(num))), _) if uint => {
|
||||
Ok(crate::SwitchValue::Integer(num as i32))
|
||||
}
|
||||
(Token::Number(Ok(Number::I32(num))), _) if !uint => {
|
||||
Ok(crate::SwitchValue::Integer(num))
|
||||
}
|
||||
(Token::Number(Err(e)), span) => Err(Error::BadNumber(span, e)),
|
||||
(_, span) => Err(Error::Unexpected(span, ExpectedToken::Integer)),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1626,8 +1630,13 @@ impl Parser {
|
|||
|
||||
let expression = match *ctx.resolve_type(value)? {
|
||||
crate::TypeInner::Scalar { kind, width } => crate::Expression::AtomicResult {
|
||||
kind,
|
||||
width,
|
||||
ty: ctx.types.insert(
|
||||
crate::Type {
|
||||
name: None,
|
||||
inner: crate::TypeInner::Scalar { kind, width },
|
||||
},
|
||||
NagaSpan::UNDEFINED,
|
||||
),
|
||||
comparison: false,
|
||||
},
|
||||
_ => return Err(Error::InvalidAtomicOperandType(value_span)),
|
||||
|
@ -1857,9 +1866,48 @@ impl Parser {
|
|||
|
||||
let expression = match *ctx.resolve_type(value)? {
|
||||
crate::TypeInner::Scalar { kind, width } => {
|
||||
let bool_ty = ctx.types.insert(
|
||||
crate::Type {
|
||||
name: None,
|
||||
inner: crate::TypeInner::Scalar {
|
||||
kind: crate::ScalarKind::Bool,
|
||||
width: crate::BOOL_WIDTH,
|
||||
},
|
||||
},
|
||||
NagaSpan::UNDEFINED,
|
||||
);
|
||||
let scalar_ty = ctx.types.insert(
|
||||
crate::Type {
|
||||
name: None,
|
||||
inner: crate::TypeInner::Scalar { kind, width },
|
||||
},
|
||||
NagaSpan::UNDEFINED,
|
||||
);
|
||||
let struct_ty = ctx.types.insert(
|
||||
crate::Type {
|
||||
name: Some("__atomic_compare_exchange_result".to_string()),
|
||||
inner: crate::TypeInner::Struct {
|
||||
members: vec![
|
||||
crate::StructMember {
|
||||
name: Some("old_value".to_string()),
|
||||
ty: scalar_ty,
|
||||
binding: None,
|
||||
offset: 0,
|
||||
},
|
||||
crate::StructMember {
|
||||
name: Some("exchanged".to_string()),
|
||||
ty: bool_ty,
|
||||
binding: None,
|
||||
offset: 4,
|
||||
},
|
||||
],
|
||||
span: 8,
|
||||
},
|
||||
},
|
||||
NagaSpan::UNDEFINED,
|
||||
);
|
||||
crate::Expression::AtomicResult {
|
||||
kind,
|
||||
width,
|
||||
ty: struct_ty,
|
||||
comparison: true,
|
||||
}
|
||||
}
|
||||
|
@ -3576,34 +3624,6 @@ impl Parser {
|
|||
Ok(())
|
||||
}
|
||||
|
||||
fn parse_switch_case_body<'a, 'out>(
|
||||
&mut self,
|
||||
lexer: &mut Lexer<'a>,
|
||||
mut context: StatementContext<'a, '_, 'out>,
|
||||
) -> Result<(bool, crate::Block), Error<'a>> {
|
||||
let mut body = crate::Block::new();
|
||||
// Push a new lexical scope for the switch case body
|
||||
context.symbol_table.push_scope();
|
||||
|
||||
lexer.expect(Token::Paren('{'))?;
|
||||
let fall_through = loop {
|
||||
// default statements
|
||||
if lexer.skip(Token::Word("fallthrough")) {
|
||||
lexer.expect(Token::Separator(';'))?;
|
||||
lexer.expect(Token::Paren('}'))?;
|
||||
break true;
|
||||
}
|
||||
if lexer.skip(Token::Paren('}')) {
|
||||
break false;
|
||||
}
|
||||
self.parse_statement(lexer, context.reborrow(), &mut body, false)?;
|
||||
};
|
||||
// Pop the switch case body lexical scope
|
||||
context.symbol_table.pop_scope();
|
||||
|
||||
Ok((fall_through, body))
|
||||
}
|
||||
|
||||
fn parse_statement<'a, 'out>(
|
||||
&mut self,
|
||||
lexer: &mut Lexer<'a>,
|
||||
|
@ -3619,26 +3639,9 @@ impl Parser {
|
|||
return Ok(());
|
||||
}
|
||||
(Token::Paren('{'), _) => {
|
||||
self.push_rule_span(Rule::Block, lexer);
|
||||
// Push a new lexical scope for the block statement
|
||||
context.symbol_table.push_scope();
|
||||
|
||||
let _ = lexer.next();
|
||||
let mut statements = crate::Block::new();
|
||||
while !lexer.skip(Token::Paren('}')) {
|
||||
self.parse_statement(
|
||||
lexer,
|
||||
context.reborrow(),
|
||||
&mut statements,
|
||||
is_uniform_control_flow,
|
||||
)?;
|
||||
}
|
||||
// Pop the block statement lexical scope
|
||||
context.symbol_table.pop_scope();
|
||||
|
||||
self.pop_rule_span(lexer);
|
||||
let span = NagaSpan::from(self.pop_rule_span(lexer));
|
||||
block.push(crate::Statement::Block(statements), span);
|
||||
let body = self.parse_block(lexer, context, is_uniform_control_flow)?;
|
||||
let span = self.pop_rule_span(lexer);
|
||||
block.push(crate::Statement::Block(body), NagaSpan::from(span));
|
||||
return Ok(());
|
||||
}
|
||||
(Token::Word(word), _) => {
|
||||
|
@ -3945,37 +3948,34 @@ impl Parser {
|
|||
break value;
|
||||
}
|
||||
cases.push(crate::SwitchCase {
|
||||
value: crate::SwitchValue::Integer(value),
|
||||
value,
|
||||
body: crate::Block::new(),
|
||||
fall_through: true,
|
||||
});
|
||||
};
|
||||
|
||||
let (fall_through, body) =
|
||||
self.parse_switch_case_body(lexer, context.reborrow())?;
|
||||
|
||||
let body =
|
||||
self.parse_block(lexer, context.reborrow(), false)?;
|
||||
cases.push(crate::SwitchCase {
|
||||
value: crate::SwitchValue::Integer(value),
|
||||
value,
|
||||
body,
|
||||
fall_through,
|
||||
fall_through: false,
|
||||
});
|
||||
}
|
||||
(Token::Word("default"), _) => {
|
||||
lexer.skip(Token::Separator(':'));
|
||||
let (fall_through, body) =
|
||||
self.parse_switch_case_body(lexer, context.reborrow())?;
|
||||
|
||||
let body =
|
||||
self.parse_block(lexer, context.reborrow(), false)?;
|
||||
cases.push(crate::SwitchCase {
|
||||
value: crate::SwitchValue::Default,
|
||||
body,
|
||||
fall_through,
|
||||
fall_through: false,
|
||||
});
|
||||
}
|
||||
(Token::Paren('}'), _) => break,
|
||||
other => {
|
||||
return Err(Error::Unexpected(
|
||||
other.1,
|
||||
ExpectedToken::SwitchItem,
|
||||
))
|
||||
(_, span) => {
|
||||
return Err(Error::Unexpected(span, ExpectedToken::SwitchItem))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3993,7 +3993,6 @@ impl Parser {
|
|||
lexer,
|
||||
context.as_expression(&mut body, &mut emitter),
|
||||
)?;
|
||||
lexer.expect(Token::Paren('{'))?;
|
||||
body.extend(emitter.finish(context.expressions));
|
||||
Ok(condition)
|
||||
})?;
|
||||
|
@ -4007,14 +4006,8 @@ impl Parser {
|
|||
},
|
||||
NagaSpan::from(span),
|
||||
);
|
||||
// Push a lexical scope for the while loop body
|
||||
context.symbol_table.push_scope();
|
||||
|
||||
while !lexer.skip(Token::Paren('}')) {
|
||||
self.parse_statement(lexer, context.reborrow(), &mut body, false)?;
|
||||
}
|
||||
// Pop the while loop body lexical scope
|
||||
context.symbol_table.pop_scope();
|
||||
body.extend_block(self.parse_block(lexer, context.reborrow(), false)?);
|
||||
|
||||
Some(crate::Statement::Loop {
|
||||
body,
|
||||
|
@ -4093,11 +4086,9 @@ impl Parser {
|
|||
}
|
||||
lexer.expect(Token::Paren(')'))?;
|
||||
}
|
||||
lexer.expect(Token::Paren('{'))?;
|
||||
|
||||
while !lexer.skip(Token::Paren('}')) {
|
||||
self.parse_statement(lexer, context.reborrow(), &mut body, false)?;
|
||||
}
|
||||
body.extend_block(self.parse_block(lexer, context.reborrow(), false)?);
|
||||
|
||||
// Pop the for loop lexical scope
|
||||
context.symbol_table.pop_scope();
|
||||
|
||||
|
@ -4306,6 +4297,7 @@ impl Parser {
|
|||
})
|
||||
}
|
||||
|
||||
/// compound_statement
|
||||
fn parse_block<'a>(
|
||||
&mut self,
|
||||
lexer: &mut Lexer<'a>,
|
||||
|
@ -4326,7 +4318,7 @@ impl Parser {
|
|||
is_uniform_control_flow,
|
||||
)?;
|
||||
}
|
||||
//Pop the block lexical scope
|
||||
// Pop the block lexical scope
|
||||
context.symbol_table.pop_scope();
|
||||
|
||||
self.pop_rule_span(lexer);
|
||||
|
|
|
@ -397,13 +397,13 @@ fn parse_dec_float(input: &str, kind: Option<FloatKind>) -> Result<Number, Numbe
|
|||
None => {
|
||||
let num = input.parse::<f64>().unwrap(); // will never fail
|
||||
num.is_finite()
|
||||
.then(|| Number::AbstractFloat(num))
|
||||
.then_some(Number::AbstractFloat(num))
|
||||
.ok_or(NumberError::NotRepresentable)
|
||||
}
|
||||
Some(FloatKind::F32) => {
|
||||
let num = input.parse::<f32>().unwrap(); // will never fail
|
||||
num.is_finite()
|
||||
.then(|| Number::F32(num))
|
||||
.then_some(Number::F32(num))
|
||||
.ok_or(NumberError::NotRepresentable)
|
||||
}
|
||||
Some(FloatKind::F16) => Err(NumberError::UnimplementedF16),
|
||||
|
|
|
@ -249,8 +249,7 @@ fn parse_switch() {
|
|||
var pos: f32;
|
||||
switch (3) {
|
||||
case 0, 1: { pos = 0.0; }
|
||||
case 2: { pos = 1.0; fallthrough; }
|
||||
case 3: {}
|
||||
case 2: { pos = 1.0; }
|
||||
default: { pos = 3.0; }
|
||||
}
|
||||
}
|
||||
|
@ -267,8 +266,7 @@ fn parse_switch_optional_colon_in_case() {
|
|||
var pos: f32;
|
||||
switch (3) {
|
||||
case 0, 1 { pos = 0.0; }
|
||||
case 2 { pos = 1.0; fallthrough; }
|
||||
case 3 {}
|
||||
case 2 { pos = 1.0; }
|
||||
default { pos = 3.0; }
|
||||
}
|
||||
}
|
||||
|
@ -277,6 +275,23 @@ fn parse_switch_optional_colon_in_case() {
|
|||
.unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_switch_default_in_case() {
|
||||
parse_str(
|
||||
"
|
||||
fn main() {
|
||||
var pos: f32;
|
||||
switch (3) {
|
||||
case 0, 1: { pos = 0.0; }
|
||||
case 2: {}
|
||||
case default, 3: { pos = 3.0; }
|
||||
}
|
||||
}
|
||||
",
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_parentheses_switch() {
|
||||
parse_str(
|
||||
|
|
|
@ -5,8 +5,9 @@ Keywords for [WGSL][wgsl] (WebGPU Shading Language).
|
|||
*/
|
||||
|
||||
// https://gpuweb.github.io/gpuweb/wgsl/#keyword-summary
|
||||
// last sync: https://github.com/gpuweb/gpuweb/blob/39f2321f547c8f0b7f473cf1d47fba30b1691303/wgsl/index.bs
|
||||
pub const RESERVED: &[&str] = &[
|
||||
// type-defining
|
||||
// Type-defining Keywords
|
||||
"array",
|
||||
"atomic",
|
||||
"bool",
|
||||
|
@ -22,11 +23,9 @@ pub const RESERVED: &[&str] = &[
|
|||
"mat4x2",
|
||||
"mat4x3",
|
||||
"mat4x4",
|
||||
"override",
|
||||
"ptr",
|
||||
"sampler",
|
||||
"sampler_comparison",
|
||||
"struct",
|
||||
"texture_1d",
|
||||
"texture_2d",
|
||||
"texture_2d_array",
|
||||
|
@ -47,7 +46,7 @@ pub const RESERVED: &[&str] = &[
|
|||
"vec2",
|
||||
"vec3",
|
||||
"vec4",
|
||||
// other
|
||||
// Other Keywords
|
||||
"bitcast",
|
||||
"break",
|
||||
"case",
|
||||
|
@ -58,68 +57,29 @@ pub const RESERVED: &[&str] = &[
|
|||
"discard",
|
||||
"else",
|
||||
"enable",
|
||||
"fallthrough",
|
||||
"false",
|
||||
"fn",
|
||||
"for",
|
||||
"function",
|
||||
"if",
|
||||
"let",
|
||||
"loop",
|
||||
"private",
|
||||
"override",
|
||||
"return",
|
||||
"storage",
|
||||
"static_assert",
|
||||
"struct",
|
||||
"switch",
|
||||
"true",
|
||||
"type",
|
||||
"uniform",
|
||||
"var",
|
||||
"while",
|
||||
"workgroup",
|
||||
// reserved
|
||||
"AppendStructuredBuffer",
|
||||
"BlendState",
|
||||
"Buffer",
|
||||
"ByteAddressBuffer",
|
||||
// Reserved Words
|
||||
"CompileShader",
|
||||
"ComputeShader",
|
||||
"ConsumeStructuredBuffer",
|
||||
"DepthStencilState",
|
||||
"DepthStencilView",
|
||||
"DomainShader",
|
||||
"GeometryShader",
|
||||
"Hullshader",
|
||||
"InputPatch",
|
||||
"LineStream",
|
||||
"NULL",
|
||||
"OutputPatch",
|
||||
"PixelShader",
|
||||
"PointStream",
|
||||
"RWBuffer",
|
||||
"RWByteAddressBuffer",
|
||||
"RWStructuredBuffer",
|
||||
"RWTexture1D",
|
||||
"RWTexture1DArray",
|
||||
"RWTexture2D",
|
||||
"RWTexture2DArray",
|
||||
"RWTexture3D",
|
||||
"RasterizerState",
|
||||
"RenderTargetView",
|
||||
"SamplerComparisonState",
|
||||
"SamplerState",
|
||||
"Self",
|
||||
"StructuredBuffer",
|
||||
"Texture1D",
|
||||
"Texture1DArray",
|
||||
"Texture2D",
|
||||
"Texture2DArray",
|
||||
"Texture2DMS",
|
||||
"Texture2DMSArray",
|
||||
"Texture3D",
|
||||
"TextureCube",
|
||||
"TextureCubeArray",
|
||||
"TriangleStream",
|
||||
"VertexShader",
|
||||
"abstract",
|
||||
"active",
|
||||
"alignas",
|
||||
|
@ -128,17 +88,13 @@ pub const RESERVED: &[&str] = &[
|
|||
"asm",
|
||||
"asm_fragment",
|
||||
"async",
|
||||
"atomic_uint",
|
||||
"attribute",
|
||||
"auto",
|
||||
"await",
|
||||
"become",
|
||||
"bf16",
|
||||
"binding_array",
|
||||
"cast",
|
||||
"catch",
|
||||
"cbuffer",
|
||||
"char",
|
||||
"class",
|
||||
"co_await",
|
||||
"co_return",
|
||||
|
@ -160,7 +116,6 @@ pub const RESERVED: &[&str] = &[
|
|||
"demote",
|
||||
"demote_to_helper",
|
||||
"do",
|
||||
"dword",
|
||||
"dynamic_cast",
|
||||
"enum",
|
||||
"explicit",
|
||||
|
@ -168,50 +123,18 @@ pub const RESERVED: &[&str] = &[
|
|||
"extends",
|
||||
"extern",
|
||||
"external",
|
||||
"f64",
|
||||
"fallthrough",
|
||||
"filter",
|
||||
"final",
|
||||
"finally",
|
||||
"fixed",
|
||||
"friend",
|
||||
"from",
|
||||
"fvec2",
|
||||
"fvec3",
|
||||
"fvec4",
|
||||
"fxgroup",
|
||||
"get",
|
||||
"goto",
|
||||
"groupshared",
|
||||
"handle",
|
||||
"highp",
|
||||
"hvec2",
|
||||
"hvec3",
|
||||
"hvec4",
|
||||
"i16",
|
||||
"i64",
|
||||
"i8",
|
||||
"iimage1D",
|
||||
"iimage1DArray",
|
||||
"iimage2D",
|
||||
"iimage2DArray",
|
||||
"iimage2DMS",
|
||||
"iimage2DMSArray",
|
||||
"iimage2DRect",
|
||||
"iimage3D",
|
||||
"iimageBuffer",
|
||||
"iimageCube",
|
||||
"iimageCubeArray",
|
||||
"image1D",
|
||||
"image1DArray",
|
||||
"image2D",
|
||||
"image2DArray",
|
||||
"image2DMS",
|
||||
"image2DMSArray",
|
||||
"image2DRect",
|
||||
"image3D",
|
||||
"imageBuffer",
|
||||
"imageCube",
|
||||
"imageCubeArray",
|
||||
"impl",
|
||||
"implements",
|
||||
"import",
|
||||
|
@ -219,40 +142,11 @@ pub const RESERVED: &[&str] = &[
|
|||
"inout",
|
||||
"instanceof",
|
||||
"interface",
|
||||
"invariant",
|
||||
"isampler1D",
|
||||
"isampler1DArray",
|
||||
"isampler2D",
|
||||
"isampler2DArray",
|
||||
"isampler2DMS",
|
||||
"isampler2DMSArray",
|
||||
"isampler2DRect",
|
||||
"isampler3D",
|
||||
"isamplerBuffer",
|
||||
"isamplerCube",
|
||||
"isamplerCubeArray",
|
||||
"isubpassInput",
|
||||
"isubpassInputMS",
|
||||
"itexture1D",
|
||||
"itexture1DArray",
|
||||
"itexture2D",
|
||||
"itexture2DArray",
|
||||
"itexture2DMS",
|
||||
"itexture2DMSArray",
|
||||
"itexture2DRect",
|
||||
"itexture3D",
|
||||
"itextureBuffer",
|
||||
"itextureCube",
|
||||
"itextureCubeArray",
|
||||
"layout",
|
||||
"line",
|
||||
"lineadj",
|
||||
"lowp",
|
||||
"macro",
|
||||
"macro_rules",
|
||||
"mat",
|
||||
"match",
|
||||
"matrix",
|
||||
"mediump",
|
||||
"meta",
|
||||
"mod",
|
||||
|
@ -277,7 +171,6 @@ pub const RESERVED: &[&str] = &[
|
|||
"pass",
|
||||
"patch",
|
||||
"pixelfragment",
|
||||
"point",
|
||||
"precise",
|
||||
"precision",
|
||||
"premerge",
|
||||
|
@ -293,28 +186,6 @@ pub const RESERVED: &[&str] = &[
|
|||
"requires",
|
||||
"resource",
|
||||
"restrict",
|
||||
"row_major",
|
||||
"samper",
|
||||
"sampler1D",
|
||||
"sampler1DArray",
|
||||
"sampler1DArrayShadow",
|
||||
"sampler1DShadow",
|
||||
"sampler2D",
|
||||
"sampler2DArray",
|
||||
"sampler2DArrayShadow",
|
||||
"sampler2DMS",
|
||||
"sampler2DMSArray",
|
||||
"sampler2DRect",
|
||||
"sampler2DRectShadow",
|
||||
"sampler2DShadow",
|
||||
"sampler3D",
|
||||
"sampler3DRect",
|
||||
"samplerBuffer",
|
||||
"samplerCube",
|
||||
"samplerCubeArray",
|
||||
"samplerCubeArrayShadow",
|
||||
"samplerCubeShadow",
|
||||
"samplerShadow",
|
||||
"self",
|
||||
"set",
|
||||
"shared",
|
||||
|
@ -322,100 +193,33 @@ pub const RESERVED: &[&str] = &[
|
|||
"sizeof",
|
||||
"smooth",
|
||||
"snorm",
|
||||
"stateblock",
|
||||
"stateblock_state",
|
||||
"static",
|
||||
"static_assert",
|
||||
"static_cast",
|
||||
"std",
|
||||
"string",
|
||||
"subpassInput",
|
||||
"subpassInputMS",
|
||||
"subroutine",
|
||||
"super",
|
||||
"superp",
|
||||
"target",
|
||||
"tbuffer",
|
||||
"technique",
|
||||
"technique10",
|
||||
"technique11",
|
||||
"template",
|
||||
"texture1D",
|
||||
"texture1DArray",
|
||||
"texture2D",
|
||||
"texture2DArray",
|
||||
"texture2DMS",
|
||||
"texture2DMSArray",
|
||||
"texture2DRect",
|
||||
"texture3D",
|
||||
"textureBuffer",
|
||||
"textureCube",
|
||||
"textureCubeArray",
|
||||
"this",
|
||||
"thread_local",
|
||||
"throw",
|
||||
"trait",
|
||||
"triangle",
|
||||
"triangleadj",
|
||||
"try",
|
||||
"typedef",
|
||||
"typeid",
|
||||
"typename",
|
||||
"typeof",
|
||||
"u16",
|
||||
"u64",
|
||||
"u8",
|
||||
"uimage1D",
|
||||
"uimage1DArray",
|
||||
"uimage2D",
|
||||
"uimage2DArray",
|
||||
"uimage2DMS",
|
||||
"uimage2DMSArray",
|
||||
"uimage2DRect",
|
||||
"uimage3D",
|
||||
"uimageBuffer",
|
||||
"uimageCube",
|
||||
"uimageCubeArray",
|
||||
"union",
|
||||
"unless",
|
||||
"unorm",
|
||||
"unsafe",
|
||||
"unsigned",
|
||||
"unsized",
|
||||
"usampler1D",
|
||||
"usampler1DArray",
|
||||
"usampler2D",
|
||||
"usampler2DArray",
|
||||
"usampler2DMS",
|
||||
"usampler2DMSArray",
|
||||
"usampler2DRect",
|
||||
"usampler3D",
|
||||
"usamplerBuffer",
|
||||
"usamplerCube",
|
||||
"usamplerCubeArray",
|
||||
"use",
|
||||
"using",
|
||||
"usubpassInput",
|
||||
"usubpassInputMS",
|
||||
"utexture1D",
|
||||
"utexture1DArray",
|
||||
"utexture2D",
|
||||
"utexture2DArray",
|
||||
"utexture2DMS",
|
||||
"utexture2DMSArray",
|
||||
"utexture2DRect",
|
||||
"utexture3D",
|
||||
"utextureBuffer",
|
||||
"utextureCube",
|
||||
"utextureCubeArray",
|
||||
"varying",
|
||||
"vec",
|
||||
"vector",
|
||||
"vertexfragment",
|
||||
"virtual",
|
||||
"void",
|
||||
"volatile",
|
||||
"wchar_t",
|
||||
"wgsl",
|
||||
"where",
|
||||
"with",
|
||||
|
|
|
@ -192,6 +192,7 @@ tree.
|
|||
clippy::unneeded_field_pattern,
|
||||
clippy::match_like_matches_macro,
|
||||
clippy::if_same_then_else,
|
||||
clippy::collapsible_if,
|
||||
clippy::derive_partial_eq_without_eq
|
||||
)]
|
||||
#![warn(
|
||||
|
@ -202,7 +203,7 @@ tree.
|
|||
clippy::pattern_type_mismatch,
|
||||
clippy::missing_const_for_fn
|
||||
)]
|
||||
#![deny(clippy::panic)]
|
||||
#![cfg_attr(not(test), deny(clippy::panic))]
|
||||
|
||||
mod arena;
|
||||
pub mod back;
|
||||
|
@ -246,6 +247,7 @@ pub(crate) type NamedExpressions = FastHashMap<Handle<Expression>, String>;
|
|||
/// - GLSL: `layout(early_fragment_tests) in;`
|
||||
/// - HLSL: `Attribute earlydepthstencil`
|
||||
/// - SPIR-V: `ExecutionMode EarlyFragmentTests`
|
||||
/// - WGSL: `@early_depth_test`
|
||||
///
|
||||
/// For more, see:
|
||||
/// - <https://www.khronos.org/opengl/wiki/Early_Fragment_Test#Explicit_specification>
|
||||
|
@ -265,6 +267,7 @@ pub struct EarlyDepthTest {
|
|||
/// - `depth_any` option behaves as if the layout qualifier was not present.
|
||||
/// - HLSL: `SV_DepthGreaterEqual`/`SV_DepthLessEqual`/`SV_Depth`
|
||||
/// - SPIR-V: `ExecutionMode Depth<Greater/Less/Unchanged>`
|
||||
/// - WGSL: `@early_depth_test(greater_equal/less_equal/unchanged)`
|
||||
///
|
||||
/// For more, see:
|
||||
/// - <https://www.khronos.org/registry/OpenGL/extensions/ARB/ARB_conservative_depth.txt>
|
||||
|
@ -337,6 +340,7 @@ pub enum BuiltIn {
|
|||
VertexIndex,
|
||||
// fragment
|
||||
FragDepth,
|
||||
PointCoord,
|
||||
FrontFacing,
|
||||
PrimitiveIndex,
|
||||
SampleIndex,
|
||||
|
@ -1259,7 +1263,7 @@ pub enum Expression {
|
|||
/// Load a value indirectly.
|
||||
///
|
||||
/// For [`TypeInner::Atomic`] the result is a corresponding scalar.
|
||||
/// For other types behind the pointer<T>, the result is T.
|
||||
/// For other types behind the `pointer<T>`, the result is `T`.
|
||||
Load { pointer: Handle<Expression> },
|
||||
/// Sample a point from a sampled or a depth image.
|
||||
ImageSample {
|
||||
|
@ -1399,11 +1403,7 @@ pub enum Expression {
|
|||
/// Result of calling another function.
|
||||
CallResult(Handle<Function>),
|
||||
/// Result of an atomic operation.
|
||||
AtomicResult {
|
||||
kind: ScalarKind,
|
||||
width: Bytes,
|
||||
comparison: bool,
|
||||
},
|
||||
AtomicResult { ty: Handle<Type>, comparison: bool },
|
||||
/// Get the length of an array.
|
||||
/// The expression must resolve to a pointer to an array with a dynamic size.
|
||||
///
|
||||
|
@ -1464,6 +1464,22 @@ pub enum Statement {
|
|||
reject: Block,
|
||||
},
|
||||
/// Conditionally executes one of multiple blocks, based on the value of the selector.
|
||||
///
|
||||
/// Each case must have a distinct [`value`], exactly one of which must be
|
||||
/// [`Default`]. The `Default` may appear at any position, and covers all
|
||||
/// values not explicitly appearing in other cases. A `Default` appearing in
|
||||
/// the midst of the list of cases does not shadow the cases that follow.
|
||||
///
|
||||
/// Some backend languages don't support fallthrough (HLSL due to FXC,
|
||||
/// WGSL), and may translate fallthrough cases in the IR by duplicating
|
||||
/// code. However, all backend languages do support cases selected by
|
||||
/// multiple values, like `case 1: case 2: case 3: { ... }`. This is
|
||||
/// represented in the IR as a series of fallthrough cases with empty
|
||||
/// bodies, except for the last.
|
||||
///
|
||||
/// [`value`]: SwitchCase::value
|
||||
/// [`body`]: SwitchCase::body
|
||||
/// [`Default`]: SwitchValue::Default
|
||||
Switch {
|
||||
selector: Handle<Expression>, //int
|
||||
cases: Vec<SwitchCase>,
|
||||
|
@ -1547,7 +1563,7 @@ pub enum Statement {
|
|||
///
|
||||
/// For [`TypeInner::Atomic`] type behind the pointer, the value
|
||||
/// has to be a corresponding scalar.
|
||||
/// For other types behind the pointer<T>, the value is T.
|
||||
/// For other types behind the `pointer<T>`, the value is `T`.
|
||||
///
|
||||
/// This statement is a barrier for any operations on the
|
||||
/// `Expression::LocalVariable` or `Expression::GlobalVariable`
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use crate::arena::{Arena, BadHandle, Handle, UniqueArena};
|
||||
use crate::arena::{Arena, Handle, UniqueArena};
|
||||
use std::{fmt::Display, num::NonZeroU32, ops};
|
||||
|
||||
/// A newtype struct where its only valid values are powers of 2
|
||||
|
@ -130,8 +130,6 @@ pub enum LayoutErrorInner {
|
|||
InvalidStructMemberType(u32, Handle<crate::Type>),
|
||||
#[error("Type width must be a power of two")]
|
||||
NonPowerOfTwoWidth,
|
||||
#[error("Array size is a bad handle")]
|
||||
BadHandle(#[from] BadHandle),
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, thiserror::Error)]
|
||||
|
@ -175,10 +173,7 @@ impl Layouter {
|
|||
use crate::TypeInner as Ti;
|
||||
|
||||
for (ty_handle, ty) in types.iter().skip(self.layouts.len()) {
|
||||
let size = ty
|
||||
.inner
|
||||
.try_size(constants)
|
||||
.map_err(|error| LayoutErrorInner::BadHandle(error).with(ty_handle))?;
|
||||
let size = ty.inner.size(constants);
|
||||
let layout = match ty.inner {
|
||||
Ti::Scalar { width, .. } | Ti::Atomic { width, .. } => {
|
||||
let alignment = Alignment::new(width as u32)
|
||||
|
|
|
@ -97,11 +97,9 @@ impl super::TypeInner {
|
|||
}
|
||||
}
|
||||
|
||||
pub fn try_size(
|
||||
&self,
|
||||
constants: &super::Arena<super::Constant>,
|
||||
) -> Result<u32, crate::arena::BadHandle> {
|
||||
Ok(match *self {
|
||||
/// Get the size of this type.
|
||||
pub fn size(&self, constants: &super::Arena<super::Constant>) -> u32 {
|
||||
match *self {
|
||||
Self::Scalar { kind: _, width } | Self::Atomic { kind: _, width } => width as u32,
|
||||
Self::Vector {
|
||||
size,
|
||||
|
@ -122,8 +120,7 @@ impl super::TypeInner {
|
|||
} => {
|
||||
let count = match size {
|
||||
super::ArraySize::Constant(handle) => {
|
||||
let constant = constants.try_get(handle)?;
|
||||
constant.to_array_length().unwrap_or(1)
|
||||
constants[handle].to_array_length().unwrap_or(1)
|
||||
}
|
||||
// A dynamically-sized array has to have at least one element
|
||||
super::ArraySize::Dynamic => 1,
|
||||
|
@ -132,13 +129,7 @@ impl super::TypeInner {
|
|||
}
|
||||
Self::Struct { span, .. } => span,
|
||||
Self::Image { .. } | Self::Sampler { .. } | Self::BindingArray { .. } => 0,
|
||||
})
|
||||
}
|
||||
|
||||
/// Get the size of this type. Panics if the `constants` doesn't contain
|
||||
/// a referenced handle. This may not happen in a properly validated IR module.
|
||||
pub fn size(&self, constants: &super::Arena<super::Constant>) -> u32 {
|
||||
self.try_size(constants).unwrap()
|
||||
}
|
||||
}
|
||||
|
||||
/// Return the canonical form of `self`, or `None` if it's already in
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
use crate::arena::{Arena, BadHandle, Handle, UniqueArena};
|
||||
use crate::arena::{Arena, Handle, UniqueArena};
|
||||
|
||||
use thiserror::Error;
|
||||
|
||||
|
@ -162,8 +162,6 @@ impl crate::ConstantInner {
|
|||
|
||||
#[derive(Clone, Debug, Error, PartialEq)]
|
||||
pub enum ResolveError {
|
||||
#[error(transparent)]
|
||||
BadHandle(#[from] BadHandle),
|
||||
#[error("Index {index} is out of bounds for expression {expr:?}")]
|
||||
OutOfBoundsIndex {
|
||||
expr: Handle<crate::Expression>,
|
||||
|
@ -195,8 +193,6 @@ pub enum ResolveError {
|
|||
IncompatibleOperands(String),
|
||||
#[error("Function argument {0} doesn't exist")]
|
||||
FunctionArgumentNotFound(u32),
|
||||
#[error("Expression {0:?} depends on expressions that follow")]
|
||||
ExpressionForwardDependency(Handle<crate::Expression>),
|
||||
}
|
||||
|
||||
pub struct ResolveContext<'a> {
|
||||
|
@ -403,20 +399,15 @@ impl<'a> ResolveContext<'a> {
|
|||
}
|
||||
}
|
||||
}
|
||||
crate::Expression::Constant(h) => {
|
||||
let constant = self.constants.try_get(h)?;
|
||||
match constant.inner {
|
||||
crate::ConstantInner::Scalar { width, ref value } => {
|
||||
TypeResolution::Value(Ti::Scalar {
|
||||
kind: value.scalar_kind(),
|
||||
width,
|
||||
})
|
||||
}
|
||||
crate::ConstantInner::Composite { ty, components: _ } => {
|
||||
TypeResolution::Handle(ty)
|
||||
}
|
||||
crate::Expression::Constant(h) => match self.constants[h].inner {
|
||||
crate::ConstantInner::Scalar { width, ref value } => {
|
||||
TypeResolution::Value(Ti::Scalar {
|
||||
kind: value.scalar_kind(),
|
||||
width,
|
||||
})
|
||||
}
|
||||
}
|
||||
crate::ConstantInner::Composite { ty, components: _ } => TypeResolution::Handle(ty),
|
||||
},
|
||||
crate::Expression::Splat { size, value } => match *past(value)?.inner_with(types) {
|
||||
Ti::Scalar { kind, width } => {
|
||||
TypeResolution::Value(Ti::Vector { size, kind, width })
|
||||
|
@ -450,7 +441,7 @@ impl<'a> ResolveContext<'a> {
|
|||
TypeResolution::Handle(arg.ty)
|
||||
}
|
||||
crate::Expression::GlobalVariable(h) => {
|
||||
let var = self.global_vars.try_get(h)?;
|
||||
let var = &self.global_vars[h];
|
||||
if var.space == crate::AddressSpace::Handle {
|
||||
TypeResolution::Handle(var.ty)
|
||||
} else {
|
||||
|
@ -461,7 +452,7 @@ impl<'a> ResolveContext<'a> {
|
|||
}
|
||||
}
|
||||
crate::Expression::LocalVariable(h) => {
|
||||
let var = self.local_vars.try_get(h)?;
|
||||
let var = &self.local_vars[h];
|
||||
TypeResolution::Value(Ti::Pointer {
|
||||
base: var.ty,
|
||||
space: crate::AddressSpace::Function,
|
||||
|
@ -644,21 +635,7 @@ impl<'a> ResolveContext<'a> {
|
|||
| crate::BinaryOperator::ShiftLeft
|
||||
| crate::BinaryOperator::ShiftRight => past(left)?.clone(),
|
||||
},
|
||||
crate::Expression::AtomicResult {
|
||||
kind,
|
||||
width,
|
||||
comparison,
|
||||
} => {
|
||||
if comparison {
|
||||
TypeResolution::Value(Ti::Vector {
|
||||
size: crate::VectorSize::Bi,
|
||||
kind,
|
||||
width,
|
||||
})
|
||||
} else {
|
||||
TypeResolution::Value(Ti::Scalar { kind, width })
|
||||
}
|
||||
}
|
||||
crate::Expression::AtomicResult { ty, .. } => TypeResolution::Handle(ty),
|
||||
crate::Expression::Select { accept, .. } => past(accept)?.clone(),
|
||||
crate::Expression::Derivative { axis: _, expr } => past(expr)?.clone(),
|
||||
crate::Expression::Relational { fun, argument } => match fun {
|
||||
|
|
|
@ -10,7 +10,7 @@ use super::{CallError, ExpressionError, FunctionError, ModuleInfo, ShaderStages,
|
|||
use crate::span::{AddSpan as _, WithSpan};
|
||||
use crate::{
|
||||
arena::{Arena, Handle},
|
||||
proc::{ResolveContext, ResolveError, TypeResolution},
|
||||
proc::{ResolveContext, TypeResolution},
|
||||
};
|
||||
use std::ops;
|
||||
|
||||
|
@ -706,12 +706,7 @@ impl FunctionInfo {
|
|||
},
|
||||
};
|
||||
|
||||
let ty = resolve_context.resolve(expression, |h| {
|
||||
self.expressions
|
||||
.get(h.index())
|
||||
.map(|ei| &ei.ty)
|
||||
.ok_or(ResolveError::ExpressionForwardDependency(h))
|
||||
})?;
|
||||
let ty = resolve_context.resolve(expression, |h| Ok(&self[h].ty))?;
|
||||
self.expressions[handle.index()] = ExpressionInfo {
|
||||
uniformity,
|
||||
ref_count: 0,
|
||||
|
|
|
@ -4,13 +4,11 @@ use crate::{
|
|||
proc::TypeResolution,
|
||||
};
|
||||
|
||||
use crate::arena::{BadHandle, Handle};
|
||||
use crate::arena::Handle;
|
||||
|
||||
#[derive(Clone, Debug, thiserror::Error)]
|
||||
#[cfg_attr(test, derive(PartialEq))]
|
||||
pub enum ComposeError {
|
||||
#[error(transparent)]
|
||||
BadHandle(#[from] BadHandle),
|
||||
#[error("Composing of type {0:?} can't be done")]
|
||||
Type(Handle<crate::Type>),
|
||||
#[error("Composing expects {expected} components but {given} were given")]
|
||||
|
@ -28,8 +26,7 @@ pub fn validate_compose(
|
|||
) -> Result<(), ComposeError> {
|
||||
use crate::TypeInner as Ti;
|
||||
|
||||
let self_ty = type_arena.get_handle(self_ty_handle)?;
|
||||
match self_ty.inner {
|
||||
match type_arena[self_ty_handle].inner {
|
||||
// vectors are composed from scalars or other vectors
|
||||
Ti::Vector { size, kind, width } => {
|
||||
let mut total = 0;
|
||||
|
|
|
@ -1,10 +1,16 @@
|
|||
#[cfg(feature = "validate")]
|
||||
use super::{compose::validate_compose, FunctionInfo, ShaderStages, TypeFlags};
|
||||
use std::ops::Index;
|
||||
|
||||
#[cfg(feature = "validate")]
|
||||
use super::{
|
||||
compose::validate_compose, validate_atomic_compare_exchange_struct, FunctionInfo, ShaderStages,
|
||||
TypeFlags,
|
||||
};
|
||||
#[cfg(feature = "validate")]
|
||||
use crate::arena::UniqueArena;
|
||||
|
||||
use crate::{
|
||||
arena::{BadHandle, Handle},
|
||||
arena::Handle,
|
||||
proc::{IndexableLengthError, ResolveError},
|
||||
};
|
||||
|
||||
|
@ -15,10 +21,6 @@ pub enum ExpressionError {
|
|||
DoesntExist,
|
||||
#[error("Used by a statement before it was introduced into the scope by any of the dominating blocks")]
|
||||
NotInScope,
|
||||
#[error("Depends on {0:?}, which has not been processed yet")]
|
||||
ForwardDependency(Handle<crate::Expression>),
|
||||
#[error(transparent)]
|
||||
BadDependency(#[from] BadHandle),
|
||||
#[error("Base type {0:?} is not compatible with this expression")]
|
||||
InvalidBaseType(Handle<crate::Expression>),
|
||||
#[error("Accessing with index {0:?} can't be done")]
|
||||
|
@ -115,8 +117,8 @@ pub enum ExpressionError {
|
|||
WrongArgumentCount(crate::MathFunction),
|
||||
#[error("Argument [{1}] to {0:?} as expression {2:?} has an invalid type.")]
|
||||
InvalidArgumentType(crate::MathFunction, u32, Handle<crate::Expression>),
|
||||
#[error("Atomic result type can't be {0:?} of {1} bytes")]
|
||||
InvalidAtomicResultType(crate::ScalarKind, crate::Bytes),
|
||||
#[error("Atomic result type can't be {0:?}")]
|
||||
InvalidAtomicResultType(Handle<crate::Type>),
|
||||
#[error("Shader requires capability {0:?}")]
|
||||
MissingCapabilities(super::Capabilities),
|
||||
}
|
||||
|
@ -129,15 +131,19 @@ struct ExpressionTypeResolver<'a> {
|
|||
}
|
||||
|
||||
#[cfg(feature = "validate")]
|
||||
impl<'a> ExpressionTypeResolver<'a> {
|
||||
fn resolve(
|
||||
&self,
|
||||
handle: Handle<crate::Expression>,
|
||||
) -> Result<&'a crate::TypeInner, ExpressionError> {
|
||||
impl<'a> Index<Handle<crate::Expression>> for ExpressionTypeResolver<'a> {
|
||||
type Output = crate::TypeInner;
|
||||
|
||||
#[allow(clippy::panic)]
|
||||
fn index(&self, handle: Handle<crate::Expression>) -> &Self::Output {
|
||||
if handle < self.root {
|
||||
Ok(self.info[handle].ty.inner_with(self.types))
|
||||
self.info[handle].ty.inner_with(self.types)
|
||||
} else {
|
||||
Err(ExpressionError::ForwardDependency(handle))
|
||||
// `Validator::validate_module_handles` should have caught this.
|
||||
panic!(
|
||||
"Depends on {:?}, which has not been processed yet",
|
||||
self.root
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -163,7 +169,7 @@ impl super::Validator {
|
|||
|
||||
let stages = match *expression {
|
||||
E::Access { base, index } => {
|
||||
let base_type = resolver.resolve(base)?;
|
||||
let base_type = &resolver[base];
|
||||
// See the documentation for `Expression::Access`.
|
||||
let dynamic_indexing_restricted = match *base_type {
|
||||
Ti::Vector { .. } => false,
|
||||
|
@ -176,7 +182,7 @@ impl super::Validator {
|
|||
return Err(ExpressionError::InvalidBaseType(base));
|
||||
}
|
||||
};
|
||||
match *resolver.resolve(index)? {
|
||||
match resolver[index] {
|
||||
//TODO: only allow one of these
|
||||
Ti::Scalar {
|
||||
kind: Sk::Sint | Sk::Uint,
|
||||
|
@ -254,7 +260,7 @@ impl super::Validator {
|
|||
Ok(limit)
|
||||
}
|
||||
|
||||
let limit = resolve_index_limit(module, base, resolver.resolve(base)?, true)?;
|
||||
let limit = resolve_index_limit(module, base, &resolver[base], true)?;
|
||||
if index >= limit {
|
||||
return Err(ExpressionError::IndexOutOfBounds(
|
||||
base,
|
||||
|
@ -263,11 +269,8 @@ impl super::Validator {
|
|||
}
|
||||
ShaderStages::all()
|
||||
}
|
||||
E::Constant(handle) => {
|
||||
let _ = module.constants.try_get(handle)?;
|
||||
ShaderStages::all()
|
||||
}
|
||||
E::Splat { size: _, value } => match *resolver.resolve(value)? {
|
||||
E::Constant(_handle) => ShaderStages::all(),
|
||||
E::Splat { size: _, value } => match resolver[value] {
|
||||
Ti::Scalar { .. } => ShaderStages::all(),
|
||||
ref other => {
|
||||
log::error!("Splat scalar type {:?}", other);
|
||||
|
@ -279,7 +282,7 @@ impl super::Validator {
|
|||
vector,
|
||||
pattern,
|
||||
} => {
|
||||
let vec_size = match *resolver.resolve(vector)? {
|
||||
let vec_size = match resolver[vector] {
|
||||
Ti::Vector { size: vec_size, .. } => vec_size,
|
||||
ref other => {
|
||||
log::error!("Swizzle vector type {:?}", other);
|
||||
|
@ -294,11 +297,6 @@ impl super::Validator {
|
|||
ShaderStages::all()
|
||||
}
|
||||
E::Compose { ref components, ty } => {
|
||||
for &handle in components {
|
||||
if handle >= root {
|
||||
return Err(ExpressionError::ForwardDependency(handle));
|
||||
}
|
||||
}
|
||||
validate_compose(
|
||||
ty,
|
||||
&module.constants,
|
||||
|
@ -313,16 +311,10 @@ impl super::Validator {
|
|||
}
|
||||
ShaderStages::all()
|
||||
}
|
||||
E::GlobalVariable(handle) => {
|
||||
let _ = module.global_variables.try_get(handle)?;
|
||||
ShaderStages::all()
|
||||
}
|
||||
E::LocalVariable(handle) => {
|
||||
let _ = function.local_variables.try_get(handle)?;
|
||||
ShaderStages::all()
|
||||
}
|
||||
E::GlobalVariable(_handle) => ShaderStages::all(),
|
||||
E::LocalVariable(_handle) => ShaderStages::all(),
|
||||
E::Load { pointer } => {
|
||||
match *resolver.resolve(pointer)? {
|
||||
match resolver[pointer] {
|
||||
Ti::Pointer { base, .. }
|
||||
if self.types[base.index()]
|
||||
.flags
|
||||
|
@ -365,7 +357,7 @@ impl super::Validator {
|
|||
return Err(ExpressionError::InvalidImageArrayIndex);
|
||||
}
|
||||
if let Some(expr) = array_index {
|
||||
match *resolver.resolve(expr)? {
|
||||
match resolver[expr] {
|
||||
Ti::Scalar {
|
||||
kind: Sk::Sint,
|
||||
width: _,
|
||||
|
@ -384,6 +376,10 @@ impl super::Validator {
|
|||
kind: crate::ScalarKind::Float,
|
||||
multi: false,
|
||||
} => false,
|
||||
crate::ImageClass::Sampled {
|
||||
kind: crate::ScalarKind::Uint | crate::ScalarKind::Sint,
|
||||
multi: false,
|
||||
} if gather.is_some() => false,
|
||||
crate::ImageClass::Depth { multi: false } => true,
|
||||
_ => return Err(ExpressionError::InvalidImageClass(class)),
|
||||
};
|
||||
|
@ -401,7 +397,7 @@ impl super::Validator {
|
|||
crate::ImageDimension::D2 => 2,
|
||||
crate::ImageDimension::D3 | crate::ImageDimension::Cube => 3,
|
||||
};
|
||||
match *resolver.resolve(coordinate)? {
|
||||
match resolver[coordinate] {
|
||||
Ti::Scalar {
|
||||
kind: Sk::Float, ..
|
||||
} if num_components == 1 => {}
|
||||
|
@ -439,7 +435,7 @@ impl super::Validator {
|
|||
|
||||
// check depth reference type
|
||||
if let Some(expr) = depth_ref {
|
||||
match *resolver.resolve(expr)? {
|
||||
match resolver[expr] {
|
||||
Ti::Scalar {
|
||||
kind: Sk::Float, ..
|
||||
} => {}
|
||||
|
@ -476,7 +472,7 @@ impl super::Validator {
|
|||
crate::SampleLevel::Auto => ShaderStages::FRAGMENT,
|
||||
crate::SampleLevel::Zero => ShaderStages::all(),
|
||||
crate::SampleLevel::Exact(expr) => {
|
||||
match *resolver.resolve(expr)? {
|
||||
match resolver[expr] {
|
||||
Ti::Scalar {
|
||||
kind: Sk::Float, ..
|
||||
} => {}
|
||||
|
@ -485,7 +481,7 @@ impl super::Validator {
|
|||
ShaderStages::all()
|
||||
}
|
||||
crate::SampleLevel::Bias(expr) => {
|
||||
match *resolver.resolve(expr)? {
|
||||
match resolver[expr] {
|
||||
Ti::Scalar {
|
||||
kind: Sk::Float, ..
|
||||
} => {}
|
||||
|
@ -494,7 +490,7 @@ impl super::Validator {
|
|||
ShaderStages::all()
|
||||
}
|
||||
crate::SampleLevel::Gradient { x, y } => {
|
||||
match *resolver.resolve(x)? {
|
||||
match resolver[x] {
|
||||
Ti::Scalar {
|
||||
kind: Sk::Float, ..
|
||||
} if num_components == 1 => {}
|
||||
|
@ -507,7 +503,7 @@ impl super::Validator {
|
|||
return Err(ExpressionError::InvalidSampleLevelGradientType(dim, x))
|
||||
}
|
||||
}
|
||||
match *resolver.resolve(y)? {
|
||||
match resolver[y] {
|
||||
Ti::Scalar {
|
||||
kind: Sk::Float, ..
|
||||
} if num_components == 1 => {}
|
||||
|
@ -538,7 +534,7 @@ impl super::Validator {
|
|||
arrayed,
|
||||
dim,
|
||||
} => {
|
||||
match resolver.resolve(coordinate)?.image_storage_coordinates() {
|
||||
match resolver[coordinate].image_storage_coordinates() {
|
||||
Some(coord_dim) if coord_dim == dim => {}
|
||||
_ => {
|
||||
return Err(ExpressionError::InvalidImageCoordinateType(
|
||||
|
@ -550,7 +546,7 @@ impl super::Validator {
|
|||
return Err(ExpressionError::InvalidImageArrayIndex);
|
||||
}
|
||||
if let Some(expr) = array_index {
|
||||
match *resolver.resolve(expr)? {
|
||||
match resolver[expr] {
|
||||
Ti::Scalar {
|
||||
kind: Sk::Sint,
|
||||
width: _,
|
||||
|
@ -562,7 +558,7 @@ impl super::Validator {
|
|||
match (sample, class.is_multisampled()) {
|
||||
(None, false) => {}
|
||||
(Some(sample), true) => {
|
||||
if resolver.resolve(sample)?.scalar_kind() != Some(Sk::Sint) {
|
||||
if resolver[sample].scalar_kind() != Some(Sk::Sint) {
|
||||
return Err(ExpressionError::InvalidImageOtherIndexType(
|
||||
sample,
|
||||
));
|
||||
|
@ -576,7 +572,7 @@ impl super::Validator {
|
|||
match (level, class.is_mipmapped()) {
|
||||
(None, false) => {}
|
||||
(Some(level), true) => {
|
||||
if resolver.resolve(level)?.scalar_kind() != Some(Sk::Sint) {
|
||||
if resolver[level].scalar_kind() != Some(Sk::Sint) {
|
||||
return Err(ExpressionError::InvalidImageOtherIndexType(level));
|
||||
}
|
||||
}
|
||||
|
@ -610,7 +606,7 @@ impl super::Validator {
|
|||
}
|
||||
E::Unary { op, expr } => {
|
||||
use crate::UnaryOperator as Uo;
|
||||
let inner = resolver.resolve(expr)?;
|
||||
let inner = &resolver[expr];
|
||||
match (op, inner.scalar_kind()) {
|
||||
(_, Some(Sk::Sint | Sk::Bool))
|
||||
//TODO: restrict Negate for bools?
|
||||
|
@ -625,8 +621,8 @@ impl super::Validator {
|
|||
}
|
||||
E::Binary { op, left, right } => {
|
||||
use crate::BinaryOperator as Bo;
|
||||
let left_inner = resolver.resolve(left)?;
|
||||
let right_inner = resolver.resolve(right)?;
|
||||
let left_inner = &resolver[left];
|
||||
let right_inner = &resolver[right];
|
||||
let good = match op {
|
||||
Bo::Add | Bo::Subtract => match *left_inner {
|
||||
Ti::Scalar { kind, .. } | Ti::Vector { kind, .. } => match kind {
|
||||
|
@ -807,9 +803,9 @@ impl super::Validator {
|
|||
accept,
|
||||
reject,
|
||||
} => {
|
||||
let accept_inner = resolver.resolve(accept)?;
|
||||
let reject_inner = resolver.resolve(reject)?;
|
||||
let condition_good = match *resolver.resolve(condition)? {
|
||||
let accept_inner = &resolver[accept];
|
||||
let reject_inner = &resolver[reject];
|
||||
let condition_good = match resolver[condition] {
|
||||
Ti::Scalar {
|
||||
kind: Sk::Bool,
|
||||
width: _,
|
||||
|
@ -839,7 +835,7 @@ impl super::Validator {
|
|||
ShaderStages::all()
|
||||
}
|
||||
E::Derivative { axis: _, expr } => {
|
||||
match *resolver.resolve(expr)? {
|
||||
match resolver[expr] {
|
||||
Ti::Scalar {
|
||||
kind: Sk::Float, ..
|
||||
}
|
||||
|
@ -852,7 +848,7 @@ impl super::Validator {
|
|||
}
|
||||
E::Relational { fun, argument } => {
|
||||
use crate::RelationalFunction as Rf;
|
||||
let argument_inner = resolver.resolve(argument)?;
|
||||
let argument_inner = &resolver[argument];
|
||||
match fun {
|
||||
Rf::All | Rf::Any => match *argument_inner {
|
||||
Ti::Vector { kind: Sk::Bool, .. } => {}
|
||||
|
@ -885,10 +881,11 @@ impl super::Validator {
|
|||
} => {
|
||||
use crate::MathFunction as Mf;
|
||||
|
||||
let arg_ty = resolver.resolve(arg)?;
|
||||
let arg1_ty = arg1.map(|expr| resolver.resolve(expr)).transpose()?;
|
||||
let arg2_ty = arg2.map(|expr| resolver.resolve(expr)).transpose()?;
|
||||
let arg3_ty = arg3.map(|expr| resolver.resolve(expr)).transpose()?;
|
||||
let resolve = |arg| &resolver[arg];
|
||||
let arg_ty = resolve(arg);
|
||||
let arg1_ty = arg1.map(resolve);
|
||||
let arg2_ty = arg2.map(resolve);
|
||||
let arg3_ty = arg3.map(resolve);
|
||||
match fun {
|
||||
Mf::Abs => {
|
||||
if arg1_ty.is_some() | arg2_ty.is_some() | arg3_ty.is_some() {
|
||||
|
@ -1372,7 +1369,7 @@ impl super::Validator {
|
|||
kind,
|
||||
convert,
|
||||
} => {
|
||||
let base_width = match *resolver.resolve(expr)? {
|
||||
let base_width = match resolver[expr] {
|
||||
crate::TypeInner::Scalar { width, .. }
|
||||
| crate::TypeInner::Vector { width, .. }
|
||||
| crate::TypeInner::Matrix { width, .. } => width,
|
||||
|
@ -1385,25 +1382,33 @@ impl super::Validator {
|
|||
ShaderStages::all()
|
||||
}
|
||||
E::CallResult(function) => other_infos[function.index()].available_stages,
|
||||
E::AtomicResult {
|
||||
kind,
|
||||
width,
|
||||
comparison: _,
|
||||
} => {
|
||||
let good = match kind {
|
||||
crate::ScalarKind::Uint | crate::ScalarKind::Sint => {
|
||||
self.check_width(kind, width)
|
||||
E::AtomicResult { ty, comparison } => {
|
||||
let scalar_predicate = |ty: &crate::TypeInner| match ty {
|
||||
&crate::TypeInner::Scalar {
|
||||
kind: kind @ (crate::ScalarKind::Uint | crate::ScalarKind::Sint),
|
||||
width,
|
||||
} => self.check_width(kind, width),
|
||||
_ => false,
|
||||
};
|
||||
let good = match &module.types[ty].inner {
|
||||
ty if !comparison => scalar_predicate(ty),
|
||||
&crate::TypeInner::Struct { ref members, .. } if comparison => {
|
||||
validate_atomic_compare_exchange_struct(
|
||||
&module.types,
|
||||
members,
|
||||
scalar_predicate,
|
||||
)
|
||||
}
|
||||
_ => false,
|
||||
};
|
||||
if !good {
|
||||
return Err(ExpressionError::InvalidAtomicResultType(kind, width));
|
||||
return Err(ExpressionError::InvalidAtomicResultType(ty));
|
||||
}
|
||||
ShaderStages::all()
|
||||
}
|
||||
E::ArrayLength(expr) => match *resolver.resolve(expr)? {
|
||||
E::ArrayLength(expr) => match resolver[expr] {
|
||||
Ti::Pointer { base, .. } => {
|
||||
let base_ty = resolver.types.get_handle(base)?;
|
||||
let base_ty = &resolver.types[base];
|
||||
if let Ti::Array {
|
||||
size: crate::ArraySize::Dynamic,
|
||||
..
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
use crate::arena::Handle;
|
||||
#[cfg(feature = "validate")]
|
||||
use crate::arena::{Arena, UniqueArena};
|
||||
use crate::arena::{BadHandle, Handle};
|
||||
|
||||
#[cfg(feature = "validate")]
|
||||
use super::validate_atomic_compare_exchange_struct;
|
||||
|
||||
use super::{
|
||||
analyzer::{UniformityDisruptor, UniformityRequirements},
|
||||
|
@ -16,8 +19,6 @@ use bit_set::BitSet;
|
|||
#[derive(Clone, Debug, thiserror::Error)]
|
||||
#[cfg_attr(test, derive(PartialEq))]
|
||||
pub enum CallError {
|
||||
#[error(transparent)]
|
||||
BadHandle(#[from] BadHandle),
|
||||
#[error("The callee is declared after the caller")]
|
||||
ForwardDeclaredFunction,
|
||||
#[error("Argument {index} expression is invalid")]
|
||||
|
@ -66,8 +67,6 @@ pub enum LocalVariableError {
|
|||
#[derive(Clone, Debug, thiserror::Error)]
|
||||
#[cfg_attr(test, derive(PartialEq))]
|
||||
pub enum FunctionError {
|
||||
#[error(transparent)]
|
||||
BadHandle(#[from] BadHandle),
|
||||
#[error("Expression {handle:?} is invalid")]
|
||||
Expression {
|
||||
handle: Handle<crate::Expression>,
|
||||
|
@ -200,11 +199,8 @@ impl<'a> BlockContext<'a> {
|
|||
BlockContext { abilities, ..*self }
|
||||
}
|
||||
|
||||
fn get_expression(
|
||||
&self,
|
||||
handle: Handle<crate::Expression>,
|
||||
) -> Result<&'a crate::Expression, FunctionError> {
|
||||
Ok(self.expressions.try_get(handle)?)
|
||||
fn get_expression(&self, handle: Handle<crate::Expression>) -> &'a crate::Expression {
|
||||
&self.expressions[handle]
|
||||
}
|
||||
|
||||
fn resolve_type_impl(
|
||||
|
@ -254,11 +250,7 @@ impl super::Validator {
|
|||
result: Option<Handle<crate::Expression>>,
|
||||
context: &BlockContext,
|
||||
) -> Result<super::ShaderStages, WithSpan<CallError>> {
|
||||
let fun = context
|
||||
.functions
|
||||
.try_get(function)
|
||||
.map_err(CallError::BadHandle)
|
||||
.map_err(WithSpan::new)?;
|
||||
let fun = &context.functions[function];
|
||||
if fun.arguments.len() != arguments.len() {
|
||||
return Err(CallError::ArgumentCount {
|
||||
required: fun.arguments.len(),
|
||||
|
@ -363,12 +355,26 @@ impl super::Validator {
|
|||
.into_other());
|
||||
}
|
||||
match context.expressions[result] {
|
||||
//TODO: support atomic result with comparison
|
||||
crate::Expression::AtomicResult {
|
||||
kind,
|
||||
width,
|
||||
comparison: false,
|
||||
} if kind == ptr_kind && width == ptr_width => {}
|
||||
crate::Expression::AtomicResult { ty, comparison }
|
||||
if {
|
||||
let scalar_predicate = |ty: &crate::TypeInner| {
|
||||
*ty == crate::TypeInner::Scalar {
|
||||
kind: ptr_kind,
|
||||
width: ptr_width,
|
||||
}
|
||||
};
|
||||
match &context.types[ty].inner {
|
||||
ty if !comparison => scalar_predicate(ty),
|
||||
&crate::TypeInner::Struct { ref members, .. } if comparison => {
|
||||
validate_atomic_compare_exchange_struct(
|
||||
context.types,
|
||||
members,
|
||||
scalar_predicate,
|
||||
)
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
} => {}
|
||||
_ => {
|
||||
return Err(AtomicError::ResultTypeMismatch(result)
|
||||
.with_span_handle(result, context.expressions)
|
||||
|
@ -672,14 +678,14 @@ impl super::Validator {
|
|||
} => {
|
||||
//Note: this code uses a lot of `FunctionError::InvalidImageStore`,
|
||||
// and could probably be refactored.
|
||||
let var = match *context.get_expression(image).map_err(|e| e.with_span())? {
|
||||
let var = match *context.get_expression(image) {
|
||||
crate::Expression::GlobalVariable(var_handle) => {
|
||||
&context.global_vars[var_handle]
|
||||
}
|
||||
// We're looking at a binding index situation, so punch through the index and look at the global behind it.
|
||||
crate::Expression::Access { base, .. }
|
||||
| crate::Expression::AccessIndex { base, .. } => {
|
||||
match *context.get_expression(base).map_err(|e| e.with_span())? {
|
||||
match *context.get_expression(base) {
|
||||
crate::Expression::GlobalVariable(var_handle) => {
|
||||
&context.global_vars[var_handle]
|
||||
}
|
||||
|
@ -882,10 +888,7 @@ impl super::Validator {
|
|||
|
||||
#[cfg(feature = "validate")]
|
||||
for (index, argument) in fun.arguments.iter().enumerate() {
|
||||
let ty = module.types.get_handle(argument.ty).map_err(|err| {
|
||||
FunctionError::from(err).with_span_handle(argument.ty, &module.types)
|
||||
})?;
|
||||
match ty.inner.pointer_space() {
|
||||
match module.types[argument.ty].inner.pointer_space() {
|
||||
Some(
|
||||
crate::AddressSpace::Private
|
||||
| crate::AddressSpace::Function
|
||||
|
|
|
@ -0,0 +1,616 @@
|
|||
//! Implementation of [`super::Validator::validate_module_handles`].
|
||||
|
||||
use crate::{
|
||||
arena::{BadHandle, BadRangeError},
|
||||
Handle,
|
||||
};
|
||||
|
||||
#[cfg(feature = "validate")]
|
||||
use crate::{Arena, UniqueArena};
|
||||
|
||||
#[cfg(feature = "validate")]
|
||||
use super::{TypeError, ValidationError};
|
||||
|
||||
#[cfg(feature = "validate")]
|
||||
use std::{convert::TryInto, hash::Hash, num::NonZeroU32};
|
||||
|
||||
#[cfg(feature = "validate")]
|
||||
impl super::Validator {
|
||||
/// Validates that all handles within `module` are:
|
||||
///
|
||||
/// * Valid, in the sense that they contain indices within each arena structure inside the
|
||||
/// [`crate::Module`] type.
|
||||
/// * No arena contents contain any items that have forward dependencies; that is, the value
|
||||
/// associated with a handle only may contain references to handles in the same arena that
|
||||
/// were constructed before it.
|
||||
///
|
||||
/// By validating the above conditions, we free up subsequent logic to assume that handle
|
||||
/// accesses are infallible.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// Errors returned by this method are intentionally sparse, for simplicity of implementation.
|
||||
/// It is expected that only buggy frontends or fuzzers should ever emit IR that fails this
|
||||
/// validation pass.
|
||||
pub(super) fn validate_module_handles(module: &crate::Module) -> Result<(), ValidationError> {
|
||||
let &crate::Module {
|
||||
ref constants,
|
||||
ref entry_points,
|
||||
ref functions,
|
||||
ref global_variables,
|
||||
ref types,
|
||||
} = module;
|
||||
|
||||
// NOTE: Types being first is important. All other forms of validation depend on this.
|
||||
for (this_handle, ty) in types.iter() {
|
||||
let &crate::Type {
|
||||
ref name,
|
||||
ref inner,
|
||||
} = ty;
|
||||
|
||||
let validate_array_size = |size| {
|
||||
match size {
|
||||
crate::ArraySize::Constant(constant) => {
|
||||
let &crate::Constant {
|
||||
name: _,
|
||||
specialization: _,
|
||||
ref inner,
|
||||
} = constants.try_get(constant)?;
|
||||
if !matches!(inner, &crate::ConstantInner::Scalar { .. }) {
|
||||
return Err(ValidationError::Type {
|
||||
handle: this_handle,
|
||||
name: name.clone().unwrap_or_default(),
|
||||
source: TypeError::InvalidArraySizeConstant(constant),
|
||||
});
|
||||
}
|
||||
}
|
||||
crate::ArraySize::Dynamic => (),
|
||||
};
|
||||
Ok(this_handle)
|
||||
};
|
||||
|
||||
match *inner {
|
||||
crate::TypeInner::Scalar { .. }
|
||||
| crate::TypeInner::Vector { .. }
|
||||
| crate::TypeInner::Matrix { .. }
|
||||
| crate::TypeInner::ValuePointer { .. }
|
||||
| crate::TypeInner::Atomic { .. }
|
||||
| crate::TypeInner::Image { .. }
|
||||
| crate::TypeInner::Sampler { .. } => (),
|
||||
crate::TypeInner::Pointer { base, space: _ } => {
|
||||
this_handle.check_dep(base)?;
|
||||
}
|
||||
crate::TypeInner::Array {
|
||||
base,
|
||||
size,
|
||||
stride: _,
|
||||
}
|
||||
| crate::TypeInner::BindingArray { base, size } => {
|
||||
this_handle.check_dep(base)?;
|
||||
validate_array_size(size)?;
|
||||
}
|
||||
crate::TypeInner::Struct {
|
||||
ref members,
|
||||
span: _,
|
||||
} => {
|
||||
this_handle.check_dep_iter(members.iter().map(|m| m.ty))?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let validate_type = |handle| Self::validate_type_handle(handle, types);
|
||||
|
||||
for (this_handle, constant) in constants.iter() {
|
||||
let &crate::Constant {
|
||||
name: _,
|
||||
specialization: _,
|
||||
ref inner,
|
||||
} = constant;
|
||||
match *inner {
|
||||
crate::ConstantInner::Scalar { .. } => (),
|
||||
crate::ConstantInner::Composite { ty, ref components } => {
|
||||
validate_type(ty)?;
|
||||
this_handle.check_dep_iter(components.iter().copied())?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let validate_constant = |handle| Self::validate_constant_handle(handle, constants);
|
||||
|
||||
for (_handle, global_variable) in global_variables.iter() {
|
||||
let &crate::GlobalVariable {
|
||||
name: _,
|
||||
space: _,
|
||||
binding: _,
|
||||
ty,
|
||||
init,
|
||||
} = global_variable;
|
||||
validate_type(ty)?;
|
||||
if let Some(init_expr) = init {
|
||||
validate_constant(init_expr)?;
|
||||
}
|
||||
}
|
||||
|
||||
let validate_function = |function: &_| -> Result<_, InvalidHandleError> {
|
||||
let &crate::Function {
|
||||
name: _,
|
||||
ref arguments,
|
||||
ref result,
|
||||
ref local_variables,
|
||||
ref expressions,
|
||||
ref named_expressions,
|
||||
ref body,
|
||||
} = function;
|
||||
|
||||
for arg in arguments.iter() {
|
||||
let &crate::FunctionArgument {
|
||||
name: _,
|
||||
ty,
|
||||
binding: _,
|
||||
} = arg;
|
||||
validate_type(ty)?;
|
||||
}
|
||||
|
||||
if let &Some(crate::FunctionResult { ty, binding: _ }) = result {
|
||||
validate_type(ty)?;
|
||||
}
|
||||
|
||||
for (_handle, local_variable) in local_variables.iter() {
|
||||
let &crate::LocalVariable { name: _, ty, init } = local_variable;
|
||||
validate_type(ty)?;
|
||||
if let Some(init_constant) = init {
|
||||
validate_constant(init_constant)?;
|
||||
}
|
||||
}
|
||||
|
||||
for handle in named_expressions.keys().copied() {
|
||||
Self::validate_expression_handle(handle, expressions)?;
|
||||
}
|
||||
|
||||
for handle_and_expr in expressions.iter() {
|
||||
Self::validate_expression_handles(
|
||||
handle_and_expr,
|
||||
constants,
|
||||
types,
|
||||
local_variables,
|
||||
global_variables,
|
||||
functions,
|
||||
)?;
|
||||
}
|
||||
|
||||
Self::validate_block_handles(body, expressions, functions)?;
|
||||
|
||||
Ok(())
|
||||
};
|
||||
|
||||
for entry_point in entry_points.iter() {
|
||||
validate_function(&entry_point.function)?;
|
||||
}
|
||||
|
||||
for (_function_handle, function) in functions.iter() {
|
||||
validate_function(function)?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn validate_type_handle(
|
||||
handle: Handle<crate::Type>,
|
||||
types: &UniqueArena<crate::Type>,
|
||||
) -> Result<(), InvalidHandleError> {
|
||||
handle.check_valid_for_uniq(types).map(|_| ())
|
||||
}
|
||||
|
||||
fn validate_constant_handle(
|
||||
handle: Handle<crate::Constant>,
|
||||
constants: &Arena<crate::Constant>,
|
||||
) -> Result<(), InvalidHandleError> {
|
||||
handle.check_valid_for(constants).map(|_| ())
|
||||
}
|
||||
|
||||
fn validate_expression_handle(
|
||||
handle: Handle<crate::Expression>,
|
||||
expressions: &Arena<crate::Expression>,
|
||||
) -> Result<(), InvalidHandleError> {
|
||||
handle.check_valid_for(expressions).map(|_| ())
|
||||
}
|
||||
|
||||
fn validate_function_handle(
|
||||
handle: Handle<crate::Function>,
|
||||
functions: &Arena<crate::Function>,
|
||||
) -> Result<(), InvalidHandleError> {
|
||||
handle.check_valid_for(functions).map(|_| ())
|
||||
}
|
||||
|
||||
fn validate_expression_handles(
|
||||
(handle, expression): (Handle<crate::Expression>, &crate::Expression),
|
||||
constants: &Arena<crate::Constant>,
|
||||
types: &UniqueArena<crate::Type>,
|
||||
local_variables: &Arena<crate::LocalVariable>,
|
||||
global_variables: &Arena<crate::GlobalVariable>,
|
||||
functions: &Arena<crate::Function>,
|
||||
) -> Result<(), InvalidHandleError> {
|
||||
let validate_constant = |handle| Self::validate_constant_handle(handle, constants);
|
||||
let validate_type = |handle| Self::validate_type_handle(handle, types);
|
||||
|
||||
match *expression {
|
||||
crate::Expression::Access { base, index } => {
|
||||
handle.check_dep(base)?.check_dep(index)?;
|
||||
}
|
||||
crate::Expression::AccessIndex { base, .. } => {
|
||||
handle.check_dep(base)?;
|
||||
}
|
||||
crate::Expression::Constant(constant) => {
|
||||
validate_constant(constant)?;
|
||||
}
|
||||
crate::Expression::Splat { value, .. } => {
|
||||
handle.check_dep(value)?;
|
||||
}
|
||||
crate::Expression::Swizzle { vector, .. } => {
|
||||
handle.check_dep(vector)?;
|
||||
}
|
||||
crate::Expression::Compose { ty, ref components } => {
|
||||
validate_type(ty)?;
|
||||
handle.check_dep_iter(components.iter().copied())?;
|
||||
}
|
||||
crate::Expression::FunctionArgument(_arg_idx) => (),
|
||||
crate::Expression::GlobalVariable(global_variable) => {
|
||||
global_variable.check_valid_for(global_variables)?;
|
||||
}
|
||||
crate::Expression::LocalVariable(local_variable) => {
|
||||
local_variable.check_valid_for(local_variables)?;
|
||||
}
|
||||
crate::Expression::Load { pointer } => {
|
||||
handle.check_dep(pointer)?;
|
||||
}
|
||||
crate::Expression::ImageSample {
|
||||
image,
|
||||
sampler,
|
||||
gather: _,
|
||||
coordinate,
|
||||
array_index,
|
||||
offset,
|
||||
level,
|
||||
depth_ref,
|
||||
} => {
|
||||
if let Some(offset) = offset {
|
||||
validate_constant(offset)?;
|
||||
}
|
||||
|
||||
handle
|
||||
.check_dep(image)?
|
||||
.check_dep(sampler)?
|
||||
.check_dep(coordinate)?
|
||||
.check_dep_opt(array_index)?;
|
||||
|
||||
match level {
|
||||
crate::SampleLevel::Auto | crate::SampleLevel::Zero => (),
|
||||
crate::SampleLevel::Exact(expr) => {
|
||||
handle.check_dep(expr)?;
|
||||
}
|
||||
crate::SampleLevel::Bias(expr) => {
|
||||
handle.check_dep(expr)?;
|
||||
}
|
||||
crate::SampleLevel::Gradient { x, y } => {
|
||||
handle.check_dep(x)?.check_dep(y)?;
|
||||
}
|
||||
};
|
||||
|
||||
handle.check_dep_opt(depth_ref)?;
|
||||
}
|
||||
crate::Expression::ImageLoad {
|
||||
image,
|
||||
coordinate,
|
||||
array_index,
|
||||
sample,
|
||||
level,
|
||||
} => {
|
||||
handle
|
||||
.check_dep(image)?
|
||||
.check_dep(coordinate)?
|
||||
.check_dep_opt(array_index)?
|
||||
.check_dep_opt(sample)?
|
||||
.check_dep_opt(level)?;
|
||||
}
|
||||
crate::Expression::ImageQuery { image, query } => {
|
||||
handle.check_dep(image)?;
|
||||
match query {
|
||||
crate::ImageQuery::Size { level } => {
|
||||
handle.check_dep_opt(level)?;
|
||||
}
|
||||
crate::ImageQuery::NumLevels
|
||||
| crate::ImageQuery::NumLayers
|
||||
| crate::ImageQuery::NumSamples => (),
|
||||
};
|
||||
}
|
||||
crate::Expression::Unary {
|
||||
op: _,
|
||||
expr: operand,
|
||||
} => {
|
||||
handle.check_dep(operand)?;
|
||||
}
|
||||
crate::Expression::Binary { op: _, left, right } => {
|
||||
handle.check_dep(left)?.check_dep(right)?;
|
||||
}
|
||||
crate::Expression::Select {
|
||||
condition,
|
||||
accept,
|
||||
reject,
|
||||
} => {
|
||||
handle
|
||||
.check_dep(condition)?
|
||||
.check_dep(accept)?
|
||||
.check_dep(reject)?;
|
||||
}
|
||||
crate::Expression::Derivative {
|
||||
axis: _,
|
||||
expr: argument,
|
||||
} => {
|
||||
handle.check_dep(argument)?;
|
||||
}
|
||||
crate::Expression::Relational { fun: _, argument } => {
|
||||
handle.check_dep(argument)?;
|
||||
}
|
||||
crate::Expression::Math {
|
||||
fun: _,
|
||||
arg,
|
||||
arg1,
|
||||
arg2,
|
||||
arg3,
|
||||
} => {
|
||||
handle
|
||||
.check_dep(arg)?
|
||||
.check_dep_opt(arg1)?
|
||||
.check_dep_opt(arg2)?
|
||||
.check_dep_opt(arg3)?;
|
||||
}
|
||||
crate::Expression::As {
|
||||
expr: input,
|
||||
kind: _,
|
||||
convert: _,
|
||||
} => {
|
||||
handle.check_dep(input)?;
|
||||
}
|
||||
crate::Expression::CallResult(function) => {
|
||||
Self::validate_function_handle(function, functions)?;
|
||||
}
|
||||
crate::Expression::AtomicResult { .. } => (),
|
||||
crate::Expression::ArrayLength(array) => {
|
||||
handle.check_dep(array)?;
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn validate_block_handles(
|
||||
block: &crate::Block,
|
||||
expressions: &Arena<crate::Expression>,
|
||||
functions: &Arena<crate::Function>,
|
||||
) -> Result<(), InvalidHandleError> {
|
||||
let validate_block = |block| Self::validate_block_handles(block, expressions, functions);
|
||||
let validate_expr = |handle| Self::validate_expression_handle(handle, expressions);
|
||||
let validate_expr_opt = |handle_opt| {
|
||||
if let Some(handle) = handle_opt {
|
||||
validate_expr(handle)?;
|
||||
}
|
||||
Ok(())
|
||||
};
|
||||
|
||||
block.iter().try_for_each(|stmt| match *stmt {
|
||||
crate::Statement::Emit(ref expr_range) => {
|
||||
expr_range.check_valid_for(expressions)?;
|
||||
Ok(())
|
||||
}
|
||||
crate::Statement::Block(ref block) => {
|
||||
validate_block(block)?;
|
||||
Ok(())
|
||||
}
|
||||
crate::Statement::If {
|
||||
condition,
|
||||
ref accept,
|
||||
ref reject,
|
||||
} => {
|
||||
validate_expr(condition)?;
|
||||
validate_block(accept)?;
|
||||
validate_block(reject)?;
|
||||
Ok(())
|
||||
}
|
||||
crate::Statement::Switch {
|
||||
selector,
|
||||
ref cases,
|
||||
} => {
|
||||
validate_expr(selector)?;
|
||||
for &crate::SwitchCase {
|
||||
value: _,
|
||||
ref body,
|
||||
fall_through: _,
|
||||
} in cases
|
||||
{
|
||||
validate_block(body)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
crate::Statement::Loop {
|
||||
ref body,
|
||||
ref continuing,
|
||||
break_if,
|
||||
} => {
|
||||
validate_block(body)?;
|
||||
validate_block(continuing)?;
|
||||
validate_expr_opt(break_if)?;
|
||||
Ok(())
|
||||
}
|
||||
crate::Statement::Return { value } => validate_expr_opt(value),
|
||||
crate::Statement::Store { pointer, value } => {
|
||||
validate_expr(pointer)?;
|
||||
validate_expr(value)?;
|
||||
Ok(())
|
||||
}
|
||||
crate::Statement::ImageStore {
|
||||
image,
|
||||
coordinate,
|
||||
array_index,
|
||||
value,
|
||||
} => {
|
||||
validate_expr(image)?;
|
||||
validate_expr(coordinate)?;
|
||||
validate_expr_opt(array_index)?;
|
||||
validate_expr(value)?;
|
||||
Ok(())
|
||||
}
|
||||
crate::Statement::Atomic {
|
||||
pointer,
|
||||
fun,
|
||||
value,
|
||||
result,
|
||||
} => {
|
||||
validate_expr(pointer)?;
|
||||
match fun {
|
||||
crate::AtomicFunction::Add
|
||||
| crate::AtomicFunction::Subtract
|
||||
| crate::AtomicFunction::And
|
||||
| crate::AtomicFunction::ExclusiveOr
|
||||
| crate::AtomicFunction::InclusiveOr
|
||||
| crate::AtomicFunction::Min
|
||||
| crate::AtomicFunction::Max => (),
|
||||
crate::AtomicFunction::Exchange { compare } => validate_expr_opt(compare)?,
|
||||
};
|
||||
validate_expr(value)?;
|
||||
validate_expr(result)?;
|
||||
Ok(())
|
||||
}
|
||||
crate::Statement::Call {
|
||||
function,
|
||||
ref arguments,
|
||||
result,
|
||||
} => {
|
||||
Self::validate_function_handle(function, functions)?;
|
||||
for arg in arguments.iter().copied() {
|
||||
validate_expr(arg)?;
|
||||
}
|
||||
validate_expr_opt(result)?;
|
||||
Ok(())
|
||||
}
|
||||
crate::Statement::Break
|
||||
| crate::Statement::Continue
|
||||
| crate::Statement::Kill
|
||||
| crate::Statement::Barrier(_) => Ok(()),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "validate")]
|
||||
impl From<BadHandle> for ValidationError {
|
||||
fn from(source: BadHandle) -> Self {
|
||||
Self::InvalidHandle(source.into())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "validate")]
|
||||
impl From<FwdDepError> for ValidationError {
|
||||
fn from(source: FwdDepError) -> Self {
|
||||
Self::InvalidHandle(source.into())
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "validate")]
|
||||
impl From<BadRangeError> for ValidationError {
|
||||
fn from(source: BadRangeError) -> Self {
|
||||
Self::InvalidHandle(source.into())
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, thiserror::Error)]
|
||||
pub enum InvalidHandleError {
|
||||
#[error(transparent)]
|
||||
BadHandle(#[from] BadHandle),
|
||||
#[error(transparent)]
|
||||
ForwardDependency(#[from] FwdDepError),
|
||||
#[error(transparent)]
|
||||
BadRange(#[from] BadRangeError),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, thiserror::Error)]
|
||||
#[error(
|
||||
"{subject:?} of kind depends on {depends_on:?} of kind {depends_on_kind}, which has not been \
|
||||
processed yet"
|
||||
)]
|
||||
pub struct FwdDepError {
|
||||
// This error is used for many `Handle` types, but there's no point in making this generic, so
|
||||
// we just flatten them all to `Handle<()>` here.
|
||||
subject: Handle<()>,
|
||||
subject_kind: &'static str,
|
||||
depends_on: Handle<()>,
|
||||
depends_on_kind: &'static str,
|
||||
}
|
||||
|
||||
#[cfg(feature = "validate")]
|
||||
impl<T> Handle<T> {
|
||||
/// Check that `self` is valid within `arena` using [`Arena::check_contains_handle`].
|
||||
pub(self) fn check_valid_for(self, arena: &Arena<T>) -> Result<(), InvalidHandleError> {
|
||||
arena.check_contains_handle(self)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Check that `self` is valid within `arena` using [`UniqueArena::check_contains_handle`].
|
||||
pub(self) fn check_valid_for_uniq(
|
||||
self,
|
||||
arena: &UniqueArena<T>,
|
||||
) -> Result<(), InvalidHandleError>
|
||||
where
|
||||
T: Eq + Hash,
|
||||
{
|
||||
arena.check_contains_handle(self)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Check that `depends_on` was constructed before `self` by comparing handle indices.
|
||||
///
|
||||
/// If `self` is a valid handle (i.e., it has been validated using [`Self::check_valid_for`])
|
||||
/// and this function returns [`Ok`], then it may be assumed that `depends_on` is also valid.
|
||||
/// In [`naga`](crate)'s current arena-based implementation, this is useful for validating
|
||||
/// recursive definitions of arena-based values in linear time.
|
||||
///
|
||||
/// # Errors
|
||||
///
|
||||
/// If `depends_on`'s handle is from the same [`Arena`] as `self'`s, but not constructed earlier
|
||||
/// than `self`'s, this function returns an error.
|
||||
pub(self) fn check_dep(self, depends_on: Self) -> Result<Self, FwdDepError> {
|
||||
if depends_on < self {
|
||||
Ok(self)
|
||||
} else {
|
||||
let erase_handle_type = |handle: Handle<_>| {
|
||||
Handle::new(NonZeroU32::new(handle.index().try_into().unwrap()).unwrap())
|
||||
};
|
||||
Err(FwdDepError {
|
||||
subject: erase_handle_type(self),
|
||||
subject_kind: std::any::type_name::<T>(),
|
||||
depends_on: erase_handle_type(depends_on),
|
||||
depends_on_kind: std::any::type_name::<T>(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// Like [`Self::check_dep`], except for [`Option`]al handle values.
|
||||
pub(self) fn check_dep_opt(self, depends_on: Option<Self>) -> Result<Self, FwdDepError> {
|
||||
self.check_dep_iter(depends_on.into_iter())
|
||||
}
|
||||
|
||||
/// Like [`Self::check_dep`], except for [`Iterator`]s over handle values.
|
||||
pub(self) fn check_dep_iter(
|
||||
self,
|
||||
depends_on: impl Iterator<Item = Self>,
|
||||
) -> Result<Self, FwdDepError> {
|
||||
for handle in depends_on {
|
||||
self.check_dep(handle)?;
|
||||
}
|
||||
Ok(self)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "validate")]
|
||||
impl<T> crate::arena::Range<T> {
|
||||
pub(self) fn check_valid_for(&self, arena: &Arena<T>) -> Result<(), BadRangeError> {
|
||||
arena.check_contains_range(self)
|
||||
}
|
||||
}
|
|
@ -2,7 +2,7 @@ use super::{
|
|||
analyzer::{FunctionInfo, GlobalUse},
|
||||
Capabilities, Disalignment, FunctionError, ModuleInfo,
|
||||
};
|
||||
use crate::arena::{BadHandle, Handle, UniqueArena};
|
||||
use crate::arena::{Handle, UniqueArena};
|
||||
|
||||
use crate::span::{AddSpan as _, MapErrWithSpan as _, SpanProvider as _, WithSpan};
|
||||
use bit_set::BitSet;
|
||||
|
@ -12,8 +12,6 @@ const MAX_WORKGROUP_SIZE: u32 = 0x4000;
|
|||
|
||||
#[derive(Clone, Debug, thiserror::Error)]
|
||||
pub enum GlobalVariableError {
|
||||
#[error(transparent)]
|
||||
BadHandle(#[from] BadHandle),
|
||||
#[error("Usage isn't compatible with address space {0:?}")]
|
||||
InvalidUsage(crate::AddressSpace),
|
||||
#[error("Type isn't compatible with address space {0:?}")]
|
||||
|
@ -107,6 +105,9 @@ struct VaryingContext<'a> {
|
|||
location_mask: &'a mut BitSet,
|
||||
built_ins: &'a mut crate::FastHashSet<crate::BuiltIn>,
|
||||
capabilities: Capabilities,
|
||||
|
||||
#[cfg(feature = "validate")]
|
||||
flags: super::ValidationFlags,
|
||||
}
|
||||
|
||||
impl VaryingContext<'_> {
|
||||
|
@ -176,6 +177,15 @@ impl VaryingContext<'_> {
|
|||
width,
|
||||
},
|
||||
),
|
||||
Bi::PointCoord => (
|
||||
self.stage == St::Fragment && !self.output,
|
||||
*ty_inner
|
||||
== Ti::Vector {
|
||||
size: Vs::Bi,
|
||||
kind: Sk::Float,
|
||||
width,
|
||||
},
|
||||
),
|
||||
Bi::Position { .. } => (
|
||||
match self.stage {
|
||||
St::Vertex => self.output,
|
||||
|
@ -284,7 +294,10 @@ impl VaryingContext<'_> {
|
|||
return Err(VaryingError::NotIOShareableType(ty));
|
||||
}
|
||||
if !self.location_mask.insert(location as usize) {
|
||||
return Err(VaryingError::BindingCollision { location });
|
||||
#[cfg(feature = "validate")]
|
||||
if self.flags.contains(super::ValidationFlags::BINDINGS) {
|
||||
return Err(VaryingError::BindingCollision { location });
|
||||
}
|
||||
}
|
||||
|
||||
let needs_interpolation = match self.stage {
|
||||
|
@ -336,8 +349,15 @@ impl VaryingContext<'_> {
|
|||
let span_context = self.types.get_span_context(ty);
|
||||
match member.binding {
|
||||
None => {
|
||||
return Err(VaryingError::MemberMissingBinding(index as u32)
|
||||
.with_span_context(span_context))
|
||||
#[cfg(feature = "validate")]
|
||||
if self.flags.contains(super::ValidationFlags::BINDINGS) {
|
||||
return Err(VaryingError::MemberMissingBinding(
|
||||
index as u32,
|
||||
)
|
||||
.with_span_context(span_context));
|
||||
}
|
||||
#[cfg(not(feature = "validate"))]
|
||||
let _ = index;
|
||||
}
|
||||
// TODO: shouldn't this be validate?
|
||||
Some(ref binding) => self
|
||||
|
@ -346,7 +366,13 @@ impl VaryingContext<'_> {
|
|||
}
|
||||
}
|
||||
}
|
||||
_ => return Err(VaryingError::MissingBinding.with_span()),
|
||||
_ =>
|
||||
{
|
||||
#[cfg(feature = "validate")]
|
||||
if self.flags.contains(super::ValidationFlags::BINDINGS) {
|
||||
return Err(VaryingError::MissingBinding.with_span());
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
@ -364,10 +390,7 @@ impl super::Validator {
|
|||
use super::TypeFlags;
|
||||
|
||||
log::debug!("var {:?}", var);
|
||||
let type_info = self.types.get(var.ty.index()).ok_or_else(|| BadHandle {
|
||||
kind: "type",
|
||||
index: var.ty.index(),
|
||||
})?;
|
||||
let type_info = &self.types[var.ty.index()];
|
||||
|
||||
let (required_type_flags, is_resource) = match var.space {
|
||||
crate::AddressSpace::Function => {
|
||||
|
@ -441,7 +464,9 @@ impl super::Validator {
|
|||
}
|
||||
|
||||
if is_resource != var.binding.is_some() {
|
||||
return Err(GlobalVariableError::InvalidBinding);
|
||||
if self.flags.contains(super::ValidationFlags::BINDINGS) {
|
||||
return Err(GlobalVariableError::InvalidBinding);
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
@ -502,6 +527,9 @@ impl super::Validator {
|
|||
location_mask: &mut self.location_mask,
|
||||
built_ins: &mut argument_built_ins,
|
||||
capabilities: self.capabilities,
|
||||
|
||||
#[cfg(feature = "validate")]
|
||||
flags: self.flags,
|
||||
};
|
||||
ctx.validate(fa.ty, fa.binding.as_ref())
|
||||
.map_err_inner(|e| EntryPointError::Argument(index as u32, e).with_span())?;
|
||||
|
@ -518,6 +546,9 @@ impl super::Validator {
|
|||
location_mask: &mut self.location_mask,
|
||||
built_ins: &mut result_built_ins,
|
||||
capabilities: self.capabilities,
|
||||
|
||||
#[cfg(feature = "validate")]
|
||||
flags: self.flags,
|
||||
};
|
||||
ctx.validate(fr.ty, fr.binding.as_ref())
|
||||
.map_err_inner(|e| EntryPointError::Result(e).with_span())?;
|
||||
|
@ -571,8 +602,10 @@ impl super::Validator {
|
|||
self.bind_group_masks.push(BitSet::new());
|
||||
}
|
||||
if !self.bind_group_masks[bind.group as usize].insert(bind.binding as usize) {
|
||||
return Err(EntryPointError::BindingCollision(var_handle)
|
||||
.with_span_handle(var_handle, &module.global_variables));
|
||||
if self.flags.contains(super::ValidationFlags::BINDINGS) {
|
||||
return Err(EntryPointError::BindingCollision(var_handle)
|
||||
.with_span_handle(var_handle, &module.global_variables));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@ mod analyzer;
|
|||
mod compose;
|
||||
mod expression;
|
||||
mod function;
|
||||
mod handles;
|
||||
mod interface;
|
||||
mod r#type;
|
||||
|
||||
|
@ -13,7 +14,7 @@ mod r#type;
|
|||
use crate::arena::{Arena, UniqueArena};
|
||||
|
||||
use crate::{
|
||||
arena::{BadHandle, Handle},
|
||||
arena::Handle,
|
||||
proc::{LayoutError, Layouter},
|
||||
FastHashSet,
|
||||
};
|
||||
|
@ -31,6 +32,8 @@ pub use function::{CallError, FunctionError, LocalVariableError};
|
|||
pub use interface::{EntryPointError, GlobalVariableError, VaryingError};
|
||||
pub use r#type::{Disalignment, TypeError, TypeFlags};
|
||||
|
||||
use self::handles::InvalidHandleError;
|
||||
|
||||
bitflags::bitflags! {
|
||||
/// Validation flags.
|
||||
///
|
||||
|
@ -66,6 +69,9 @@ bitflags::bitflags! {
|
|||
/// Constants.
|
||||
#[cfg(feature = "validate")]
|
||||
const CONSTANTS = 0x10;
|
||||
/// Group, binding, and location attributes.
|
||||
#[cfg(feature = "validate")]
|
||||
const BINDINGS = 0x20;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -143,8 +149,6 @@ pub struct Validator {
|
|||
|
||||
#[derive(Clone, Debug, thiserror::Error)]
|
||||
pub enum ConstantError {
|
||||
#[error(transparent)]
|
||||
BadHandle(#[from] BadHandle),
|
||||
#[error("The type doesn't match the constant")]
|
||||
InvalidType,
|
||||
#[error("The component handle {0:?} can not be resolved")]
|
||||
|
@ -157,6 +161,8 @@ pub enum ConstantError {
|
|||
|
||||
#[derive(Clone, Debug, thiserror::Error)]
|
||||
pub enum ValidationError {
|
||||
#[error(transparent)]
|
||||
InvalidHandle(#[from] InvalidHandleError),
|
||||
#[error(transparent)]
|
||||
Layouter(#[from] LayoutError),
|
||||
#[error("Type {handle:?} '{name}' is invalid")]
|
||||
|
@ -220,17 +226,17 @@ impl crate::TypeInner {
|
|||
const fn image_storage_coordinates(&self) -> Option<crate::ImageDimension> {
|
||||
match *self {
|
||||
Self::Scalar {
|
||||
kind: crate::ScalarKind::Sint,
|
||||
kind: crate::ScalarKind::Sint | crate::ScalarKind::Uint,
|
||||
..
|
||||
} => Some(crate::ImageDimension::D1),
|
||||
Self::Vector {
|
||||
size: crate::VectorSize::Bi,
|
||||
kind: crate::ScalarKind::Sint,
|
||||
kind: crate::ScalarKind::Sint | crate::ScalarKind::Uint,
|
||||
..
|
||||
} => Some(crate::ImageDimension::D2),
|
||||
Self::Vector {
|
||||
size: crate::VectorSize::Tri,
|
||||
kind: crate::ScalarKind::Sint,
|
||||
kind: crate::ScalarKind::Sint | crate::ScalarKind::Uint,
|
||||
..
|
||||
} => Some(crate::ImageDimension::D3),
|
||||
_ => None,
|
||||
|
@ -280,7 +286,7 @@ impl Validator {
|
|||
}
|
||||
}
|
||||
crate::ConstantInner::Composite { ty, ref components } => {
|
||||
match types.get_handle(ty)?.inner {
|
||||
match types[ty].inner {
|
||||
crate::TypeInner::Array {
|
||||
size: crate::ArraySize::Constant(size_handle),
|
||||
..
|
||||
|
@ -313,6 +319,9 @@ impl Validator {
|
|||
self.reset();
|
||||
self.reset_types(module.types.len());
|
||||
|
||||
#[cfg(feature = "validate")]
|
||||
Self::validate_module_handles(module).map_err(|e| e.with_span())?;
|
||||
|
||||
self.layouter
|
||||
.update(&module.types, &module.constants)
|
||||
.map_err(|e| {
|
||||
|
@ -412,3 +421,20 @@ impl Validator {
|
|||
Ok(mod_info)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "validate")]
|
||||
fn validate_atomic_compare_exchange_struct(
|
||||
types: &UniqueArena<crate::Type>,
|
||||
members: &[crate::StructMember],
|
||||
scalar_predicate: impl FnOnce(&crate::TypeInner) -> bool,
|
||||
) -> bool {
|
||||
members.len() == 2
|
||||
&& members[0].name.as_deref() == Some("old_value")
|
||||
&& scalar_predicate(&types[members[0].ty].inner)
|
||||
&& members[1].name.as_deref() == Some("exchanged")
|
||||
&& types[members[1].ty].inner
|
||||
== crate::TypeInner::Scalar {
|
||||
kind: crate::ScalarKind::Bool,
|
||||
width: crate::BOOL_WIDTH,
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
use super::Capabilities;
|
||||
use crate::{
|
||||
arena::{Arena, BadHandle, Handle, UniqueArena},
|
||||
arena::{Arena, Handle, UniqueArena},
|
||||
proc::Alignment,
|
||||
};
|
||||
|
||||
|
@ -88,8 +88,6 @@ pub enum Disalignment {
|
|||
|
||||
#[derive(Clone, Debug, thiserror::Error)]
|
||||
pub enum TypeError {
|
||||
#[error(transparent)]
|
||||
BadHandle(#[from] BadHandle),
|
||||
#[error("The {0:?} scalar width {1} is not supported")]
|
||||
InvalidWidth(crate::ScalarKind, crate::Bytes),
|
||||
#[error("The {0:?} scalar width {1} is not supported for an atomic")]
|
||||
|
@ -418,7 +416,7 @@ impl super::Validator {
|
|||
|
||||
let sized_flag = match size {
|
||||
crate::ArraySize::Constant(const_handle) => {
|
||||
let constant = constants.try_get(const_handle)?;
|
||||
let constant = &constants[const_handle];
|
||||
let length_is_positive = match *constant {
|
||||
crate::Constant {
|
||||
specialization: Some(_),
|
||||
|
|
|
@ -1 +1 @@
|
|||
{"files":{"Cargo.toml":"70b60e663e9ad3847924f1a5aa12e88b634aae3a1dff92b691c1c7586e520b75","LICENSE.APACHE":"a6cba85bc92e0cff7a450b1d873c0eaa2e9fc96bf472df0247a26bec77bf3ff9","LICENSE.MIT":"c7fea58d1cfe49634cd92e54fc10a9d871f4b275321a4cd8c09e449122caaeb4","src/binding_model.rs":"3b9180c91a30ffaffaddaa734ee99e7a0e68b1da1bf3963eae447fa69cfae2ef","src/command/bind.rs":"88ca2bedfcd8392efc960f39ca8506024af495dcf1852cf16ae28fbadbdf748d","src/command/bundle.rs":"9420e25cf05f28170cce81df0d20e67f4dbe5b54588c0541e01008dde4bf7b45","src/command/clear.rs":"cc118937b7a0b05225a2417db2f11bb95a095b0ca63561db092a8dfab35d9158","src/command/compute.rs":"f2a08528e39e3cc7049757e9d63d591c8adc9bffa3af66d57eee0e6ab7bc3015","src/command/draw.rs":"ac3541fb46435dd1687b24b24d9896104adde8f662111f232b7b4e8e34a869ea","src/command/memory_init.rs":"347117a84f35927de91e7ab4cc69d19b5b4507a942596c0c725f27b50c428b1e","src/command/mod.rs":"31623431b6dc1c061dedcb838949d4c81d4e95d5516be47524d53b3d2faef3e6","src/command/query.rs":"2c107b22806a003808da2f886ecc74764b016851b7a6cec8caf21385a2e25dee","src/command/render.rs":"d62d2ec5344312f78714d294ffc53686f993accd84925af29700073dc1d1b117","src/command/transfer.rs":"5321de8440653e3c69970816d3df8e628ae31fc7c5a03125f1cc8ed244e13f68","src/conv.rs":"c4506a7f715461d67c2bcfef002f13d3e75d10b2e4f18b0ab9ea8d5bdeb7bc61","src/device/life.rs":"5f58817365cf8b0a76cab83fdb50b3ee69a15b2bd868bc3702c7c29f3189326c","src/device/mod.rs":"5019dcbdda406700b3e7121055eba7936b8cd403517e5711fabaf1421f8fd6a6","src/device/queue.rs":"883a0bd1c8d3cddc46393e9eeb76bedce9ce48d1467a767d9839db7934252ad9","src/device/trace.rs":"9c03f5ec06cae37f294179a2285e2af7a5497db8a5572bf1dbe50136943d69be","src/error.rs":"7e914b25a2b9578672eb4a8b52c778ad947e12e0f24b44a8453cda48a249b39c","src/hub.rs":"1be76bb6c1cae694e891d6798789ed9748535b42b042f44cad09d939dbc802c1","src/id.rs":"608dfbdc118905638762d0a16e4f69b0ecae16bbe20accead2d7d9b7e7281dda","src/init_tracker/buffer.rs":"a0ebf54a1e6d269c7b4aa0ac7bb8b04fd2cea3221a1d058ff33cb683b2aea3e9","src/init_tracker/mod.rs":"0867f79f83555390d0982d1dc6dcf0d4340e10cb89aa633d3c3ecc45deb3c78c","src/init_tracker/texture.rs":"11de3b1968829aec1f165930d9e4f946d6c02c7994ce32925a62f883820aa1e3","src/instance.rs":"12f4204755c67eaf82013142c4e6ff8e3ad4e74adf07c00f7d20caa1fd2a09e6","src/lib.rs":"206c07ef386628a9b8e0f8ad45415c9eb9b55418c3636cc9e6aba63eb6ed5978","src/pipeline.rs":"38e9fff13e7e152378e95dc13822dab62542d4b172f006a34d84eebb70bed0f7","src/present.rs":"7c00b8bc3475d9a100adf5c9e9d0af823aa14b626e923f87ec988022bd4766d1","src/resource.rs":"c23933592838688d86986a0f6c449df6eaefa8b770e10c512d849a7a497b92a7","src/track/buffer.rs":"0b7a8c6acfe55e3f45dd1e7ef1b3ee8bd4844f61bbabf62aa4f0c391b822803c","src/track/metadata.rs":"9565f700661e81fd534a751a0e3d26022238ad8784cc868333da7030b3571473","src/track/mod.rs":"995a8cd73499a26b593d0a419630681ea7942cec8db132ae8a13baf69fe13cdc","src/track/range.rs":"5bbfed6e103b3234d9de8e42057022da6d628c2cc1db6bb51b88f87f2d8adf8b","src/track/stateless.rs":"34942ecdf81b05b75892c891c87e065329870459ff8edfca1f99ec9533c334aa","src/track/texture.rs":"7d38b2f4e0cdb3e56ce1e777d8bca4d73ef40e21c56e11fedd69dc27e789256d","src/validation.rs":"03d3a99410c5f6ec937358206ea76eafff31823ebbe443ab71facf8a8b6a95e8"},"package":null}
|
||||
{"files":{"Cargo.toml":"f60e44d9830853a7ed495f083d9aa032987a85ef07b84fdb9efe6d0aab7dba41","LICENSE.APACHE":"a6cba85bc92e0cff7a450b1d873c0eaa2e9fc96bf472df0247a26bec77bf3ff9","LICENSE.MIT":"c7fea58d1cfe49634cd92e54fc10a9d871f4b275321a4cd8c09e449122caaeb4","src/binding_model.rs":"3b9180c91a30ffaffaddaa734ee99e7a0e68b1da1bf3963eae447fa69cfae2ef","src/command/bind.rs":"88ca2bedfcd8392efc960f39ca8506024af495dcf1852cf16ae28fbadbdf748d","src/command/bundle.rs":"9420e25cf05f28170cce81df0d20e67f4dbe5b54588c0541e01008dde4bf7b45","src/command/clear.rs":"cc118937b7a0b05225a2417db2f11bb95a095b0ca63561db092a8dfab35d9158","src/command/compute.rs":"f2a08528e39e3cc7049757e9d63d591c8adc9bffa3af66d57eee0e6ab7bc3015","src/command/draw.rs":"ac3541fb46435dd1687b24b24d9896104adde8f662111f232b7b4e8e34a869ea","src/command/memory_init.rs":"347117a84f35927de91e7ab4cc69d19b5b4507a942596c0c725f27b50c428b1e","src/command/mod.rs":"31623431b6dc1c061dedcb838949d4c81d4e95d5516be47524d53b3d2faef3e6","src/command/query.rs":"2c107b22806a003808da2f886ecc74764b016851b7a6cec8caf21385a2e25dee","src/command/render.rs":"d62d2ec5344312f78714d294ffc53686f993accd84925af29700073dc1d1b117","src/command/transfer.rs":"5321de8440653e3c69970816d3df8e628ae31fc7c5a03125f1cc8ed244e13f68","src/conv.rs":"c4506a7f715461d67c2bcfef002f13d3e75d10b2e4f18b0ab9ea8d5bdeb7bc61","src/device/life.rs":"5f58817365cf8b0a76cab83fdb50b3ee69a15b2bd868bc3702c7c29f3189326c","src/device/mod.rs":"e55e6508c1e268fc6509b5a7c504cf6b208492a30dcabb15ac428c9459e455d2","src/device/queue.rs":"62f473e01320f334f72ee2b06bcfb0c070c945d1785dee1e42ca80a69e9f31d1","src/device/trace.rs":"9c03f5ec06cae37f294179a2285e2af7a5497db8a5572bf1dbe50136943d69be","src/error.rs":"7e914b25a2b9578672eb4a8b52c778ad947e12e0f24b44a8453cda48a249b39c","src/hub.rs":"1be76bb6c1cae694e891d6798789ed9748535b42b042f44cad09d939dbc802c1","src/id.rs":"10e101272faf04495b25e37284b0c7620392bb8ee7f2a82247d8e0a858087d04","src/init_tracker/buffer.rs":"a0ebf54a1e6d269c7b4aa0ac7bb8b04fd2cea3221a1d058ff33cb683b2aea3e9","src/init_tracker/mod.rs":"0867f79f83555390d0982d1dc6dcf0d4340e10cb89aa633d3c3ecc45deb3c78c","src/init_tracker/texture.rs":"11de3b1968829aec1f165930d9e4f946d6c02c7994ce32925a62f883820aa1e3","src/instance.rs":"12f4204755c67eaf82013142c4e6ff8e3ad4e74adf07c00f7d20caa1fd2a09e6","src/lib.rs":"206c07ef386628a9b8e0f8ad45415c9eb9b55418c3636cc9e6aba63eb6ed5978","src/pipeline.rs":"38e9fff13e7e152378e95dc13822dab62542d4b172f006a34d84eebb70bed0f7","src/present.rs":"7c00b8bc3475d9a100adf5c9e9d0af823aa14b626e923f87ec988022bd4766d1","src/resource.rs":"0ae6b65c8ece54f04bef98451a348571d9757360bd95d765c84832ac811ddaff","src/track/buffer.rs":"0b7a8c6acfe55e3f45dd1e7ef1b3ee8bd4844f61bbabf62aa4f0c391b822803c","src/track/metadata.rs":"9565f700661e81fd534a751a0e3d26022238ad8784cc868333da7030b3571473","src/track/mod.rs":"995a8cd73499a26b593d0a419630681ea7942cec8db132ae8a13baf69fe13cdc","src/track/range.rs":"5bbfed6e103b3234d9de8e42057022da6d628c2cc1db6bb51b88f87f2d8adf8b","src/track/stateless.rs":"34942ecdf81b05b75892c891c87e065329870459ff8edfca1f99ec9533c334aa","src/track/texture.rs":"7d38b2f4e0cdb3e56ce1e777d8bca4d73ef40e21c56e11fedd69dc27e789256d","src/validation.rs":"432c83c310c655781ba6a053e1a48c4b79e6450610c1c4dee715845efc11e76b"},"package":null}
|
|
@ -45,7 +45,6 @@ serial-pass = ["serde", "wgt/serde", "arrayvec/serde"]
|
|||
id32 = []
|
||||
# Enable `ShaderModuleSource::Wgsl`
|
||||
wgsl = ["naga/wgsl-in"]
|
||||
vulkan-portability = ["hal/vulkan"]
|
||||
|
||||
# Features that are intended to work on all platforms.
|
||||
portable_features = ["gles", "strict_asserts", "trace", "replay", "serial-pass", "id32", "wgsl"]
|
||||
|
@ -68,7 +67,7 @@ thiserror = "1"
|
|||
|
||||
[dependencies.naga]
|
||||
git = "https://github.com/gfx-rs/naga"
|
||||
rev = "e7fc8e6"
|
||||
rev = "e98bd92"
|
||||
version = "0.10"
|
||||
features = ["clone", "span", "validate"]
|
||||
|
||||
|
|
|
@ -1170,6 +1170,12 @@ impl<A: HalApi> Device<A> {
|
|||
self.require_features(wgt::Features::ADDRESS_MODE_CLAMP_TO_ZERO)?;
|
||||
}
|
||||
|
||||
if desc.lod_min_clamp < 0.0 || desc.lod_max_clamp < desc.lod_min_clamp {
|
||||
return Err(resource::CreateSamplerError::InvalidLodClamp(
|
||||
desc.lod_min_clamp..desc.lod_max_clamp,
|
||||
));
|
||||
}
|
||||
|
||||
let lod_clamp = if desc.lod_min_clamp > 0.0 || desc.lod_max_clamp < 32.0 {
|
||||
Some(desc.lod_min_clamp..desc.lod_max_clamp)
|
||||
} else {
|
||||
|
|
|
@ -595,7 +595,9 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
|||
}
|
||||
|
||||
let (mut texture_guard, _) = hub.textures.write(&mut token); // For clear we need write access to the texture. TODO: Can we acquire write lock later?
|
||||
let dst = texture_guard.get_mut(destination.texture).unwrap();
|
||||
let dst = texture_guard
|
||||
.get_mut(destination.texture)
|
||||
.map_err(|_| TransferError::InvalidTexture(destination.texture))?;
|
||||
|
||||
let (selector, dst_base, texture_format) =
|
||||
extract_texture_selector(destination, size, dst)?;
|
||||
|
@ -707,6 +709,10 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
|||
}
|
||||
}
|
||||
|
||||
// Re-get `dst` immutably here, so that the mutable borrow of the
|
||||
// `texture_guard.get_mut` above ends in time for the `clear_texture`
|
||||
// call above. Since we've held `texture_guard` the whole time, we know
|
||||
// the texture hasn't gone away in the mean time, so we can unwrap.
|
||||
let dst = texture_guard.get(destination.texture).unwrap();
|
||||
let transition = trackers
|
||||
.textures
|
||||
|
|
|
@ -172,7 +172,6 @@ pub(crate) struct Valid<I>(pub I);
|
|||
/// need to construct `Id` values directly, or access their components, like the
|
||||
/// WGPU recording player, may use this trait to do so.
|
||||
pub trait TypedId: Copy {
|
||||
fn as_raw(&self) -> NonZeroId;
|
||||
fn zip(index: Index, epoch: Epoch, backend: Backend) -> Self;
|
||||
fn unzip(self) -> (Index, Epoch, Backend);
|
||||
fn into_raw(self) -> NonZeroId;
|
||||
|
@ -180,10 +179,6 @@ pub trait TypedId: Copy {
|
|||
|
||||
#[allow(trivial_numeric_casts)]
|
||||
impl<T> TypedId for Id<T> {
|
||||
fn as_raw(&self) -> NonZeroId {
|
||||
self.0
|
||||
}
|
||||
|
||||
fn zip(index: Index, epoch: Epoch, backend: Backend) -> Self {
|
||||
assert_eq!(0, epoch >> EPOCH_BITS);
|
||||
assert_eq!(0, (index as IdType) >> INDEX_BITS);
|
||||
|
|
|
@ -701,6 +701,8 @@ pub struct Sampler<A: hal::Api> {
|
|||
pub enum CreateSamplerError {
|
||||
#[error(transparent)]
|
||||
Device(#[from] DeviceError),
|
||||
#[error("invalid lod clamp lod_min_clamp:{} lod_max_clamp:{}, must satisfy lod_min_clamp >= 0 and lod_max_clamp >= lod_min_clamp ", .0.start, .0.end)]
|
||||
InvalidLodClamp(Range<f32>),
|
||||
#[error("invalid anisotropic clamp {0}, must be one of 1, 2, 4, 8 or 16")]
|
||||
InvalidClamp(u8),
|
||||
#[error("cannot create any more samplers")]
|
||||
|
|
|
@ -212,10 +212,10 @@ pub enum BindingError {
|
|||
|
||||
#[derive(Clone, Debug, Error)]
|
||||
pub enum FilteringError {
|
||||
#[error("integer textures can't be sampled")]
|
||||
#[error("integer textures can't be sampled with a filtering sampler")]
|
||||
Integer,
|
||||
#[error("non-filterable float texture")]
|
||||
NonFilterable,
|
||||
#[error("non-filterable float textures can't be sampled with a filtering sampler")]
|
||||
Float,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Error)]
|
||||
|
@ -1049,27 +1049,22 @@ impl Interface {
|
|||
assert!(texture_layout.visibility.contains(stage_bit));
|
||||
assert!(sampler_layout.visibility.contains(stage_bit));
|
||||
|
||||
let error = match texture_layout.ty {
|
||||
wgt::BindingType::Texture {
|
||||
sample_type: wgt::TextureSampleType::Float { filterable },
|
||||
..
|
||||
} => match sampler_layout.ty {
|
||||
wgt::BindingType::Sampler(wgt::SamplerBindingType::Filtering)
|
||||
if !filterable =>
|
||||
{
|
||||
Some(FilteringError::NonFilterable)
|
||||
}
|
||||
_ => None,
|
||||
},
|
||||
wgt::BindingType::Texture {
|
||||
sample_type: wgt::TextureSampleType::Sint,
|
||||
..
|
||||
let sampler_filtering = matches!(
|
||||
sampler_layout.ty,
|
||||
wgt::BindingType::Sampler(wgt::SamplerBindingType::Filtering)
|
||||
);
|
||||
let texture_sample_type = match texture_layout.ty {
|
||||
BindingType::Texture { sample_type, .. } => sample_type,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
let error = match (sampler_filtering, texture_sample_type) {
|
||||
(true, wgt::TextureSampleType::Float { filterable: false }) => {
|
||||
Some(FilteringError::Float)
|
||||
}
|
||||
| wgt::BindingType::Texture {
|
||||
sample_type: wgt::TextureSampleType::Uint,
|
||||
..
|
||||
} => Some(FilteringError::Integer),
|
||||
_ => None, // unreachable, really
|
||||
(true, wgt::TextureSampleType::Sint) => Some(FilteringError::Integer),
|
||||
(true, wgt::TextureSampleType::Uint) => Some(FilteringError::Integer),
|
||||
_ => None,
|
||||
};
|
||||
|
||||
if let Some(error) = error {
|
||||
|
|
Различия файлов скрыты, потому что одна или несколько строк слишком длинны
|
@ -63,7 +63,7 @@ block = { version = "0.1", optional = true }
|
|||
foreign-types = { version = "0.3", optional = true }
|
||||
|
||||
# backend: Vulkan
|
||||
ash = { version = "0.37.1", optional = true }
|
||||
ash = { version = "0.37.2", optional = true }
|
||||
gpu-alloc = { version = "0.5", optional = true }
|
||||
gpu-descriptor = { version = "0.2", optional = true }
|
||||
smallvec = { version = "1", optional = true, features = ["union"] }
|
||||
|
@ -111,14 +111,14 @@ android_system_properties = "0.1.1"
|
|||
|
||||
[dependencies.naga]
|
||||
git = "https://github.com/gfx-rs/naga"
|
||||
rev = "e7fc8e6"
|
||||
rev = "e98bd92"
|
||||
version = "0.10"
|
||||
features = ["clone"]
|
||||
|
||||
# DEV dependencies
|
||||
[dev-dependencies.naga]
|
||||
git = "https://github.com/gfx-rs/naga"
|
||||
rev = "e7fc8e6"
|
||||
rev = "e98bd92"
|
||||
version = "0.10"
|
||||
features = ["wgsl-in"]
|
||||
|
||||
|
|
|
@ -228,20 +228,34 @@ impl super::CommandEncoder {
|
|||
|
||||
impl crate::CommandEncoder<super::Api> for super::CommandEncoder {
|
||||
unsafe fn begin_encoding(&mut self, label: crate::Label) -> Result<(), crate::DeviceError> {
|
||||
let list = match self.free_lists.pop() {
|
||||
Some(list) => {
|
||||
list.reset(self.allocator, native::PipelineState::null());
|
||||
list
|
||||
let list = loop {
|
||||
if let Some(list) = self.free_lists.pop() {
|
||||
let reset_result = list
|
||||
.reset(self.allocator, native::PipelineState::null())
|
||||
.into_result();
|
||||
if reset_result.is_ok() {
|
||||
break Some(list);
|
||||
} else {
|
||||
unsafe {
|
||||
list.destroy();
|
||||
}
|
||||
}
|
||||
} else {
|
||||
break None;
|
||||
}
|
||||
None => self
|
||||
.device
|
||||
};
|
||||
|
||||
let list = if let Some(list) = list {
|
||||
list
|
||||
} else {
|
||||
self.device
|
||||
.create_graphics_command_list(
|
||||
native::CmdListType::Direct,
|
||||
self.allocator,
|
||||
native::PipelineState::null(),
|
||||
0,
|
||||
)
|
||||
.into_device_result("Create command list")?,
|
||||
.into_device_result("Create command list")?
|
||||
};
|
||||
|
||||
if let Some(label) = label {
|
||||
|
@ -256,18 +270,29 @@ impl crate::CommandEncoder<super::Api> for super::CommandEncoder {
|
|||
}
|
||||
unsafe fn discard_encoding(&mut self) {
|
||||
if let Some(list) = self.list.take() {
|
||||
list.close();
|
||||
self.free_lists.push(list);
|
||||
if list.close().into_result().is_ok() {
|
||||
self.free_lists.push(list);
|
||||
} else {
|
||||
unsafe {
|
||||
list.destroy();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
unsafe fn end_encoding(&mut self) -> Result<super::CommandBuffer, crate::DeviceError> {
|
||||
let raw = self.list.take().unwrap();
|
||||
raw.close();
|
||||
Ok(super::CommandBuffer { raw })
|
||||
let closed = raw.close().into_result().is_ok();
|
||||
Ok(super::CommandBuffer { raw, closed })
|
||||
}
|
||||
unsafe fn reset_all<I: Iterator<Item = super::CommandBuffer>>(&mut self, command_buffers: I) {
|
||||
for cmd_buf in command_buffers {
|
||||
self.free_lists.push(cmd_buf.raw);
|
||||
if cmd_buf.closed {
|
||||
self.free_lists.push(cmd_buf.raw);
|
||||
} else {
|
||||
unsafe {
|
||||
cmd_buf.raw.destroy();
|
||||
}
|
||||
}
|
||||
}
|
||||
self.allocator.reset();
|
||||
}
|
||||
|
|
|
@ -367,6 +367,7 @@ impl fmt::Debug for CommandEncoder {
|
|||
#[derive(Debug)]
|
||||
pub struct CommandBuffer {
|
||||
raw: native::GraphicsCommandList,
|
||||
closed: bool,
|
||||
}
|
||||
|
||||
unsafe impl Send for CommandBuffer {}
|
||||
|
|
|
@ -439,6 +439,52 @@ pub(super) fn is_sampler(glsl_uniform_type: u32) -> bool {
|
|||
}
|
||||
}
|
||||
|
||||
pub(super) fn is_image(glsl_uniform_type: u32) -> bool {
|
||||
match glsl_uniform_type {
|
||||
glow::INT_IMAGE_1D
|
||||
| glow::INT_IMAGE_1D_ARRAY
|
||||
| glow::INT_IMAGE_2D
|
||||
| glow::INT_IMAGE_2D_ARRAY
|
||||
| glow::INT_IMAGE_2D_MULTISAMPLE
|
||||
| glow::INT_IMAGE_2D_MULTISAMPLE_ARRAY
|
||||
| glow::INT_IMAGE_2D_RECT
|
||||
| glow::INT_IMAGE_3D
|
||||
| glow::INT_IMAGE_CUBE
|
||||
| glow::INT_IMAGE_CUBE_MAP_ARRAY
|
||||
| glow::UNSIGNED_INT_IMAGE_1D
|
||||
| glow::UNSIGNED_INT_IMAGE_1D_ARRAY
|
||||
| glow::UNSIGNED_INT_IMAGE_2D
|
||||
| glow::UNSIGNED_INT_IMAGE_2D_ARRAY
|
||||
| glow::UNSIGNED_INT_IMAGE_2D_MULTISAMPLE
|
||||
| glow::UNSIGNED_INT_IMAGE_2D_MULTISAMPLE_ARRAY
|
||||
| glow::UNSIGNED_INT_IMAGE_2D_RECT
|
||||
| glow::UNSIGNED_INT_IMAGE_3D
|
||||
| glow::UNSIGNED_INT_IMAGE_CUBE
|
||||
| glow::UNSIGNED_INT_IMAGE_CUBE_MAP_ARRAY
|
||||
| glow::IMAGE_1D
|
||||
| glow::IMAGE_1D_ARRAY
|
||||
| glow::IMAGE_2D
|
||||
| glow::IMAGE_2D_ARRAY
|
||||
| glow::IMAGE_2D_MULTISAMPLE
|
||||
| glow::IMAGE_2D_MULTISAMPLE_ARRAY
|
||||
| glow::IMAGE_2D_RECT
|
||||
| glow::IMAGE_3D
|
||||
| glow::IMAGE_CUBE
|
||||
| glow::IMAGE_CUBE_MAP_ARRAY => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn is_atomic_counter(glsl_uniform_type: u32) -> bool {
|
||||
glsl_uniform_type == glow::UNSIGNED_INT_ATOMIC_COUNTER
|
||||
}
|
||||
|
||||
pub(super) fn is_opaque_type(glsl_uniform_type: u32) -> bool {
|
||||
is_sampler(glsl_uniform_type)
|
||||
|| is_image(glsl_uniform_type)
|
||||
|| is_atomic_counter(glsl_uniform_type)
|
||||
}
|
||||
|
||||
pub(super) fn uniform_byte_size(glsl_uniform_type: u32) -> u32 {
|
||||
match glsl_uniform_type {
|
||||
glow::FLOAT | glow::INT => 4,
|
||||
|
@ -448,6 +494,6 @@ pub(super) fn uniform_byte_size(glsl_uniform_type: u32) -> u32 {
|
|||
glow::FLOAT_MAT2 => 16,
|
||||
glow::FLOAT_MAT3 => 36,
|
||||
glow::FLOAT_MAT4 => 64,
|
||||
_ => panic!("Unsupported uniform datatype!"),
|
||||
_ => panic!("Unsupported uniform datatype! {glsl_uniform_type:#X}"),
|
||||
}
|
||||
}
|
||||
|
|
|
@ -372,7 +372,8 @@ impl super::Device {
|
|||
}
|
||||
}
|
||||
|
||||
let mut uniforms: [super::UniformDesc; super::MAX_PUSH_CONSTANTS] = Default::default();
|
||||
let mut uniforms: [super::UniformDesc; super::MAX_PUSH_CONSTANTS] =
|
||||
[None; super::MAX_PUSH_CONSTANTS].map(|_: Option<()>| Default::default());
|
||||
let count = unsafe { gl.get_active_uniforms(program) };
|
||||
let mut offset = 0;
|
||||
|
||||
|
@ -380,7 +381,7 @@ impl super::Device {
|
|||
let glow::ActiveUniform { utype, name, .. } =
|
||||
unsafe { gl.get_active_uniform(program, uniform) }.unwrap();
|
||||
|
||||
if conv::is_sampler(utype) {
|
||||
if conv::is_opaque_type(utype) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
|
@ -95,7 +95,7 @@ const MAX_TEXTURE_SLOTS: usize = 16;
|
|||
const MAX_SAMPLERS: usize = 16;
|
||||
const MAX_VERTEX_ATTRIBUTES: usize = 16;
|
||||
const ZERO_BUFFER_SIZE: usize = 256 << 10;
|
||||
const MAX_PUSH_CONSTANTS: usize = 16;
|
||||
const MAX_PUSH_CONSTANTS: usize = 64;
|
||||
|
||||
impl crate::Api for Api {
|
||||
type Instance = Instance;
|
||||
|
|
|
@ -5,6 +5,10 @@ use parking_lot::Mutex;
|
|||
|
||||
use std::{collections::BTreeMap, ffi::CStr, sync::Arc};
|
||||
|
||||
fn depth_stencil_required_flags() -> vk::FormatFeatureFlags {
|
||||
vk::FormatFeatureFlags::SAMPLED_IMAGE | vk::FormatFeatureFlags::DEPTH_STENCIL_ATTACHMENT
|
||||
}
|
||||
|
||||
//TODO: const fn?
|
||||
fn indexing_features() -> wgt::Features {
|
||||
wgt::Features::SAMPLED_TEXTURE_AND_STORAGE_BUFFER_ARRAY_NON_UNIFORM_INDEXING
|
||||
|
@ -172,9 +176,7 @@ impl PhysicalDeviceFeatures {
|
|||
//.shader_resource_residency(requested_features.contains(wgt::Features::SHADER_RESOURCE_RESIDENCY))
|
||||
.geometry_shader(requested_features.contains(wgt::Features::SHADER_PRIMITIVE_INDEX))
|
||||
.build(),
|
||||
descriptor_indexing: if enabled_extensions
|
||||
.contains(&vk::ExtDescriptorIndexingFn::name())
|
||||
{
|
||||
descriptor_indexing: if requested_features.intersects(indexing_features()) {
|
||||
Some(
|
||||
vk::PhysicalDeviceDescriptorIndexingFeaturesEXT::builder()
|
||||
.shader_sampled_image_array_non_uniform_indexing(
|
||||
|
@ -229,7 +231,9 @@ impl PhysicalDeviceFeatures {
|
|||
} else {
|
||||
None
|
||||
},
|
||||
image_robustness: if enabled_extensions.contains(&vk::ExtImageRobustnessFn::name()) {
|
||||
image_robustness: if effective_api_version >= vk::API_VERSION_1_3
|
||||
|| enabled_extensions.contains(&vk::ExtImageRobustnessFn::name())
|
||||
{
|
||||
Some(
|
||||
vk::PhysicalDeviceImageRobustnessFeaturesEXT::builder()
|
||||
.robust_image_access(private_caps.robust_image_access)
|
||||
|
@ -325,7 +329,6 @@ impl PhysicalDeviceFeatures {
|
|||
| Df::VERTEX_STORAGE
|
||||
| Df::FRAGMENT_STORAGE
|
||||
| Df::DEPTH_TEXTURE_AND_BUFFER_COPIES
|
||||
| Df::WEBGPU_TEXTURE_FORMAT_SUPPORT
|
||||
| Df::BUFFER_BINDINGS_NOT_16_BYTE_ALIGNED
|
||||
| Df::UNRESTRICTED_INDEX_BUFFER
|
||||
| Df::INDIRECT_EXECUTION;
|
||||
|
@ -412,7 +415,7 @@ impl PhysicalDeviceFeatures {
|
|||
//if caps.supports_extension(vk::ExtSamplerFilterMinmaxFn::name()) {
|
||||
features.set(
|
||||
F::MULTI_DRAW_INDIRECT_COUNT,
|
||||
caps.supports_extension(khr::DrawIndirectCount::name()),
|
||||
caps.supports_extension(vk::KhrDrawIndirectCountFn::name()),
|
||||
);
|
||||
features.set(
|
||||
F::CONSERVATIVE_RASTERIZATION,
|
||||
|
@ -487,17 +490,31 @@ impl PhysicalDeviceFeatures {
|
|||
);
|
||||
}
|
||||
|
||||
features.set(
|
||||
F::DEPTH32FLOAT_STENCIL8,
|
||||
let supports_depth_format = |format| {
|
||||
supports_format(
|
||||
instance,
|
||||
phd,
|
||||
vk::Format::D32_SFLOAT_S8_UINT,
|
||||
format,
|
||||
vk::ImageTiling::OPTIMAL,
|
||||
vk::FormatFeatureFlags::DEPTH_STENCIL_ATTACHMENT,
|
||||
),
|
||||
depth_stencil_required_flags(),
|
||||
)
|
||||
};
|
||||
|
||||
let texture_s8 = supports_depth_format(vk::Format::S8_UINT);
|
||||
let texture_d32 = supports_depth_format(vk::Format::D32_SFLOAT);
|
||||
let texture_d24_s8 = supports_depth_format(vk::Format::D24_UNORM_S8_UINT);
|
||||
let texture_d32_s8 = supports_depth_format(vk::Format::D32_SFLOAT_S8_UINT);
|
||||
|
||||
let stencil8 = texture_s8 || texture_d24_s8;
|
||||
let depth24_plus_stencil8 = texture_d24_s8 || texture_d32_s8;
|
||||
|
||||
dl_flags.set(
|
||||
Df::WEBGPU_TEXTURE_FORMAT_SUPPORT,
|
||||
stencil8 && depth24_plus_stencil8 && texture_d32,
|
||||
);
|
||||
|
||||
features.set(F::DEPTH32FLOAT_STENCIL8, texture_d32_s8);
|
||||
|
||||
(features, dl_flags)
|
||||
}
|
||||
|
||||
|
@ -554,78 +571,118 @@ impl PhysicalDeviceCapabilities {
|
|||
fn get_required_extensions(&self, requested_features: wgt::Features) -> Vec<&'static CStr> {
|
||||
let mut extensions = Vec::new();
|
||||
|
||||
extensions.push(khr::Swapchain::name());
|
||||
// Note that quite a few extensions depend on the `VK_KHR_get_physical_device_properties2` instance extension.
|
||||
// We enable `VK_KHR_get_physical_device_properties2` unconditionally (if available).
|
||||
|
||||
// Require `VK_KHR_swapchain`
|
||||
extensions.push(vk::KhrSwapchainFn::name());
|
||||
|
||||
if self.effective_api_version < vk::API_VERSION_1_1 {
|
||||
extensions.push(vk::KhrMaintenance1Fn::name());
|
||||
extensions.push(vk::KhrMaintenance2Fn::name());
|
||||
|
||||
// `VK_KHR_storage_buffer_storage_class` required for Naga on Vulkan 1.0 devices
|
||||
extensions.push(vk::KhrStorageBufferStorageClassFn::name());
|
||||
|
||||
// Below Vulkan 1.1 we can get multiview from an extension
|
||||
if requested_features.contains(wgt::Features::MULTIVIEW) {
|
||||
extensions.push(vk::KhrMultiviewFn::name());
|
||||
// Require either `VK_KHR_maintenance1` or `VK_AMD_negative_viewport_height`
|
||||
if self.supports_extension(vk::KhrMaintenance1Fn::name()) {
|
||||
extensions.push(vk::KhrMaintenance1Fn::name());
|
||||
} else {
|
||||
// `VK_AMD_negative_viewport_height` is obsoleted by `VK_KHR_maintenance1` and must not be enabled alongside it
|
||||
extensions.push(vk::AmdNegativeViewportHeightFn::name());
|
||||
}
|
||||
|
||||
// `VK_AMD_negative_viewport_height` is obsoleted by `VK_KHR_maintenance1` and must not be enabled alongside `VK_KHR_maintenance1` or a 1.1+ device.
|
||||
if !self.supports_extension(vk::KhrMaintenance1Fn::name()) {
|
||||
extensions.push(vk::AmdNegativeViewportHeightFn::name());
|
||||
// Optional `VK_KHR_maintenance2`
|
||||
if self.supports_extension(vk::KhrMaintenance2Fn::name()) {
|
||||
extensions.push(vk::KhrMaintenance2Fn::name());
|
||||
}
|
||||
|
||||
// Require `VK_KHR_storage_buffer_storage_class`
|
||||
extensions.push(vk::KhrStorageBufferStorageClassFn::name());
|
||||
|
||||
// Require `VK_KHR_multiview` if the associated feature was requested
|
||||
if requested_features.contains(wgt::Features::MULTIVIEW) {
|
||||
extensions.push(vk::KhrMultiviewFn::name());
|
||||
}
|
||||
}
|
||||
|
||||
if self.effective_api_version < vk::API_VERSION_1_2 {
|
||||
// Optional `VK_KHR_imageless_framebuffer`
|
||||
if self.supports_extension(vk::KhrImagelessFramebufferFn::name()) {
|
||||
extensions.push(vk::KhrImagelessFramebufferFn::name());
|
||||
extensions.push(vk::KhrImageFormatListFn::name()); // Required for `KhrImagelessFramebufferFn`
|
||||
// Require `VK_KHR_image_format_list` due to it being a dependency
|
||||
extensions.push(vk::KhrImageFormatListFn::name());
|
||||
// Require `VK_KHR_maintenance2` due to it being a dependency
|
||||
if self.effective_api_version < vk::API_VERSION_1_1 {
|
||||
extensions.push(vk::KhrMaintenance2Fn::name());
|
||||
}
|
||||
}
|
||||
|
||||
// This extension is core in Vulkan 1.2
|
||||
// Optional `VK_KHR_driver_properties`
|
||||
if self.supports_extension(vk::KhrDriverPropertiesFn::name()) {
|
||||
extensions.push(vk::KhrDriverPropertiesFn::name());
|
||||
}
|
||||
|
||||
extensions.push(vk::ExtSamplerFilterMinmaxFn::name());
|
||||
extensions.push(vk::KhrTimelineSemaphoreFn::name());
|
||||
// Optional `VK_KHR_timeline_semaphore`
|
||||
if self.supports_extension(vk::KhrTimelineSemaphoreFn::name()) {
|
||||
extensions.push(vk::KhrTimelineSemaphoreFn::name());
|
||||
}
|
||||
|
||||
// Require `VK_EXT_descriptor_indexing` if one of the associated features was requested
|
||||
if requested_features.intersects(indexing_features()) {
|
||||
extensions.push(vk::ExtDescriptorIndexingFn::name());
|
||||
|
||||
// Require `VK_KHR_maintenance3` due to it being a dependency
|
||||
if self.effective_api_version < vk::API_VERSION_1_1 {
|
||||
extensions.push(vk::KhrMaintenance3Fn::name());
|
||||
}
|
||||
}
|
||||
|
||||
// Require `VK_KHR_shader_float16_int8` and `VK_KHR_16bit_storage` if the associated feature was requested
|
||||
if requested_features.contains(wgt::Features::SHADER_FLOAT16) {
|
||||
extensions.push(vk::KhrShaderFloat16Int8Fn::name());
|
||||
// `VK_KHR_16bit_storage` requires `VK_KHR_storage_buffer_storage_class`, however we require that one already
|
||||
if self.effective_api_version < vk::API_VERSION_1_1 {
|
||||
extensions.push(vk::Khr16bitStorageFn::name());
|
||||
}
|
||||
}
|
||||
|
||||
//extensions.push(vk::KhrSamplerMirrorClampToEdgeFn::name());
|
||||
//extensions.push(vk::ExtSamplerFilterMinmaxFn::name());
|
||||
}
|
||||
|
||||
if self.effective_api_version < vk::API_VERSION_1_3 {
|
||||
// Optional `VK_EXT_image_robustness`
|
||||
if self.supports_extension(vk::ExtImageRobustnessFn::name()) {
|
||||
extensions.push(vk::ExtImageRobustnessFn::name());
|
||||
}
|
||||
}
|
||||
|
||||
// Optional `VK_EXT_robustness2`
|
||||
if self.supports_extension(vk::ExtRobustness2Fn::name()) {
|
||||
extensions.push(vk::ExtRobustness2Fn::name());
|
||||
}
|
||||
|
||||
// Require `VK_KHR_draw_indirect_count` if the associated feature was requested
|
||||
// Even though Vulkan 1.2 has promoted the extension to core, we must require the extension to avoid
|
||||
// large amounts of spaghetti involved with using PhysicalDeviceVulkan12Features.
|
||||
if requested_features.contains(wgt::Features::MULTI_DRAW_INDIRECT_COUNT) {
|
||||
extensions.push(vk::KhrDrawIndirectCountFn::name());
|
||||
}
|
||||
|
||||
// Require `VK_EXT_conservative_rasterization` if the associated feature was requested
|
||||
if requested_features.contains(wgt::Features::CONSERVATIVE_RASTERIZATION) {
|
||||
extensions.push(vk::ExtConservativeRasterizationFn::name());
|
||||
}
|
||||
|
||||
// Require `VK_EXT_depth_clip_enable` if the associated feature was requested
|
||||
if requested_features.contains(wgt::Features::DEPTH_CLIP_CONTROL) {
|
||||
extensions.push(vk::ExtDepthClipEnableFn::name());
|
||||
}
|
||||
|
||||
// Require `VK_KHR_portability_subset` on macOS/iOS
|
||||
#[cfg(any(target_os = "macos", target_os = "ios"))]
|
||||
extensions.push(vk::KhrPortabilitySubsetFn::name());
|
||||
|
||||
// Require `VK_EXT_texture_compression_astc_hdr` if the associated feature was requested
|
||||
if requested_features.contains(wgt::Features::TEXTURE_COMPRESSION_ASTC_HDR) {
|
||||
extensions.push(vk::ExtTextureCompressionAstcHdrFn::name());
|
||||
}
|
||||
|
||||
if requested_features.contains(wgt::Features::SHADER_FLOAT16) {
|
||||
extensions.push(vk::KhrShaderFloat16Int8Fn::name());
|
||||
extensions.push(vk::Khr16bitStorageFn::name());
|
||||
}
|
||||
|
||||
extensions
|
||||
}
|
||||
|
||||
|
@ -762,13 +819,12 @@ impl super::InstanceShared {
|
|||
self.get_physical_device_properties
|
||||
{
|
||||
// Get these now to avoid borrowing conflicts later
|
||||
let supports_descriptor_indexing =
|
||||
capabilities.supports_extension(vk::ExtDescriptorIndexingFn::name());
|
||||
let supports_driver_properties = capabilities.properties.api_version
|
||||
>= vk::API_VERSION_1_2
|
||||
let supports_descriptor_indexing = self.driver_api_version >= vk::API_VERSION_1_2
|
||||
|| capabilities.supports_extension(vk::ExtDescriptorIndexingFn::name());
|
||||
let supports_driver_properties = self.driver_api_version >= vk::API_VERSION_1_2
|
||||
|| capabilities.supports_extension(vk::KhrDriverPropertiesFn::name());
|
||||
|
||||
let mut builder = vk::PhysicalDeviceProperties2::builder();
|
||||
let mut builder = vk::PhysicalDeviceProperties2KHR::builder();
|
||||
|
||||
if supports_descriptor_indexing {
|
||||
let next = capabilities
|
||||
|
@ -1002,27 +1058,27 @@ impl super::Instance {
|
|||
.timeline_semaphore
|
||||
.map_or(false, |ext| ext.timeline_semaphore != 0),
|
||||
},
|
||||
texture_d24: unsafe {
|
||||
self.shared
|
||||
.raw
|
||||
.get_physical_device_format_properties(phd, vk::Format::X8_D24_UNORM_PACK32)
|
||||
.optimal_tiling_features
|
||||
.contains(vk::FormatFeatureFlags::DEPTH_STENCIL_ATTACHMENT)
|
||||
},
|
||||
texture_d24_s8: unsafe {
|
||||
self.shared
|
||||
.raw
|
||||
.get_physical_device_format_properties(phd, vk::Format::D24_UNORM_S8_UINT)
|
||||
.optimal_tiling_features
|
||||
.contains(vk::FormatFeatureFlags::DEPTH_STENCIL_ATTACHMENT)
|
||||
},
|
||||
texture_s8: unsafe {
|
||||
self.shared
|
||||
.raw
|
||||
.get_physical_device_format_properties(phd, vk::Format::S8_UINT)
|
||||
.optimal_tiling_features
|
||||
.contains(vk::FormatFeatureFlags::DEPTH_STENCIL_ATTACHMENT)
|
||||
},
|
||||
texture_d24: supports_format(
|
||||
&self.shared.raw,
|
||||
phd,
|
||||
vk::Format::X8_D24_UNORM_PACK32,
|
||||
vk::ImageTiling::OPTIMAL,
|
||||
depth_stencil_required_flags(),
|
||||
),
|
||||
texture_d24_s8: supports_format(
|
||||
&self.shared.raw,
|
||||
phd,
|
||||
vk::Format::D24_UNORM_S8_UINT,
|
||||
vk::ImageTiling::OPTIMAL,
|
||||
depth_stencil_required_flags(),
|
||||
),
|
||||
texture_s8: supports_format(
|
||||
&self.shared.raw,
|
||||
phd,
|
||||
vk::Format::S8_UINT,
|
||||
vk::ImageTiling::OPTIMAL,
|
||||
depth_stencil_required_flags(),
|
||||
),
|
||||
non_coherent_map_mask: phd_capabilities.properties.limits.non_coherent_atom_size - 1,
|
||||
can_present: true,
|
||||
//TODO: make configurable
|
||||
|
@ -1411,10 +1467,10 @@ impl crate::Adapter<super::Api> for super::Adapter {
|
|||
Tfc::SAMPLED_LINEAR,
|
||||
features.contains(vk::FormatFeatureFlags::SAMPLED_IMAGE_FILTER_LINEAR),
|
||||
);
|
||||
flags.set(
|
||||
Tfc::SAMPLED_MINMAX,
|
||||
features.contains(vk::FormatFeatureFlags::SAMPLED_IMAGE_FILTER_MINMAX),
|
||||
);
|
||||
// flags.set(
|
||||
// Tfc::SAMPLED_MINMAX,
|
||||
// features.contains(vk::FormatFeatureFlags::SAMPLED_IMAGE_FILTER_MINMAX),
|
||||
// );
|
||||
flags.set(
|
||||
Tfc::STORAGE | Tfc::STORAGE_READ_WRITE,
|
||||
features.contains(vk::FormatFeatureFlags::STORAGE_IMAGE),
|
||||
|
@ -1437,15 +1493,11 @@ impl crate::Adapter<super::Api> for super::Adapter {
|
|||
);
|
||||
flags.set(
|
||||
Tfc::COPY_SRC,
|
||||
features.intersects(
|
||||
vk::FormatFeatureFlags::TRANSFER_SRC | vk::FormatFeatureFlags::BLIT_SRC,
|
||||
),
|
||||
features.intersects(vk::FormatFeatureFlags::TRANSFER_SRC),
|
||||
);
|
||||
flags.set(
|
||||
Tfc::COPY_DST,
|
||||
features.intersects(
|
||||
vk::FormatFeatureFlags::TRANSFER_DST | vk::FormatFeatureFlags::BLIT_DST,
|
||||
),
|
||||
features.intersects(vk::FormatFeatureFlags::TRANSFER_DST),
|
||||
);
|
||||
// Vulkan is very permissive about MSAA
|
||||
flags.set(Tfc::MULTISAMPLE_RESOLVE, !format.describe().is_compressed());
|
||||
|
|
|
@ -153,6 +153,7 @@ impl super::Instance {
|
|||
|
||||
pub fn required_extensions(
|
||||
entry: &ash::Entry,
|
||||
_driver_api_version: u32,
|
||||
flags: crate::InstanceFlags,
|
||||
) -> Result<Vec<&'static CStr>, crate::InstanceError> {
|
||||
let instance_extensions = entry
|
||||
|
@ -164,6 +165,8 @@ impl super::Instance {
|
|||
|
||||
// Check our extensions against the available extensions
|
||||
let mut extensions: Vec<&'static CStr> = Vec::new();
|
||||
|
||||
// VK_KHR_surface
|
||||
extensions.push(khr::Surface::name());
|
||||
|
||||
// Platform-specific WSI extensions
|
||||
|
@ -172,29 +175,40 @@ impl super::Instance {
|
|||
not(target_os = "android"),
|
||||
not(target_os = "macos")
|
||||
)) {
|
||||
// VK_KHR_xlib_surface
|
||||
extensions.push(khr::XlibSurface::name());
|
||||
// VK_KHR_xcb_surface
|
||||
extensions.push(khr::XcbSurface::name());
|
||||
// VK_KHR_wayland_surface
|
||||
extensions.push(khr::WaylandSurface::name());
|
||||
}
|
||||
if cfg!(target_os = "android") {
|
||||
// VK_KHR_android_surface
|
||||
extensions.push(khr::AndroidSurface::name());
|
||||
}
|
||||
if cfg!(target_os = "windows") {
|
||||
// VK_KHR_win32_surface
|
||||
extensions.push(khr::Win32Surface::name());
|
||||
}
|
||||
if cfg!(target_os = "macos") {
|
||||
// VK_EXT_metal_surface
|
||||
extensions.push(ext::MetalSurface::name());
|
||||
}
|
||||
|
||||
if flags.contains(crate::InstanceFlags::DEBUG) {
|
||||
// VK_EXT_debug_utils
|
||||
extensions.push(ext::DebugUtils::name());
|
||||
}
|
||||
|
||||
extensions.push(vk::KhrGetPhysicalDeviceProperties2Fn::name());
|
||||
|
||||
// VK_EXT_swapchain_colorspace
|
||||
// Provid wide color gamut
|
||||
extensions.push(vk::ExtSwapchainColorspaceFn::name());
|
||||
|
||||
// VK_KHR_get_physical_device_properties2
|
||||
// Even though the extension was promoted to Vulkan 1.1, we still require the extension
|
||||
// so that we don't have to conditionally use the functions provided by the 1.1 instance
|
||||
extensions.push(vk::KhrGetPhysicalDeviceProperties2Fn::name());
|
||||
|
||||
// Only keep available extensions.
|
||||
extensions.retain(|&ext| {
|
||||
if instance_extensions.iter().any(|inst_ext| {
|
||||
|
@ -262,19 +276,16 @@ impl super::Instance {
|
|||
None
|
||||
};
|
||||
|
||||
// We can't use any of Vulkan-1.1+ abilities on Vk 1.0 instance,
|
||||
// so disabling this query helps.
|
||||
let get_physical_device_properties = if driver_api_version >= vk::API_VERSION_1_1
|
||||
&& extensions.contains(&khr::GetPhysicalDeviceProperties2::name())
|
||||
{
|
||||
log::info!("Enabling device properties2");
|
||||
Some(khr::GetPhysicalDeviceProperties2::new(
|
||||
&entry,
|
||||
&raw_instance,
|
||||
))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let get_physical_device_properties =
|
||||
if extensions.contains(&khr::GetPhysicalDeviceProperties2::name()) {
|
||||
log::info!("Enabling device properties2");
|
||||
Some(khr::GetPhysicalDeviceProperties2::new(
|
||||
&entry,
|
||||
&raw_instance,
|
||||
))
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
Ok(Self {
|
||||
shared: Arc::new(super::InstanceShared {
|
||||
|
@ -519,7 +530,7 @@ impl crate::Instance<super::Api> for super::Instance {
|
|||
},
|
||||
);
|
||||
|
||||
let extensions = Self::required_extensions(&entry, desc.flags)?;
|
||||
let extensions = Self::required_extensions(&entry, driver_api_version, desc.flags)?;
|
||||
|
||||
let instance_layers = entry.enumerate_instance_layer_properties().map_err(|e| {
|
||||
log::info!("enumerate_instance_layer_properties: {:?}", e);
|
||||
|
|
|
@ -1 +1 @@
|
|||
{"files":{"Cargo.toml":"578dd177eb3fbce10c928d2800e3b433c36e092ff57305c916bdeb17602b8990","LICENSE.APACHE":"a6cba85bc92e0cff7a450b1d873c0eaa2e9fc96bf472df0247a26bec77bf3ff9","LICENSE.MIT":"c7fea58d1cfe49634cd92e54fc10a9d871f4b275321a4cd8c09e449122caaeb4","src/assertions.rs":"3fe98027aa73970c8ab7874a3e13dbfd6faa87df2081beb5c83aeec4c60f372f","src/lib.rs":"49b663315b42255315e393c7406de643525d9562219715634630db9b811c77c9"},"package":null}
|
||||
{"files":{"Cargo.toml":"578dd177eb3fbce10c928d2800e3b433c36e092ff57305c916bdeb17602b8990","LICENSE.APACHE":"a6cba85bc92e0cff7a450b1d873c0eaa2e9fc96bf472df0247a26bec77bf3ff9","LICENSE.MIT":"c7fea58d1cfe49634cd92e54fc10a9d871f4b275321a4cd8c09e449122caaeb4","src/assertions.rs":"3fe98027aa73970c8ab7874a3e13dbfd6faa87df2081beb5c83aeec4c60f372f","src/lib.rs":"b5e5bde17ab66efd8b8707c0460e55984e70abeec03c3b913ab90991c844a596"},"package":null}
|
|
@ -3847,6 +3847,7 @@ pub enum PresentMode {
|
|||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
#[cfg_attr(feature = "trace", derive(Serialize))]
|
||||
#[cfg_attr(feature = "replay", derive(Deserialize))]
|
||||
#[cfg_attr(feature = "serde", serde(rename_all = "camelCase"))]
|
||||
pub enum CompositeAlphaMode {
|
||||
/// Chooses either `Opaque` or `Inherit` automatically,depending on the
|
||||
/// `alpha_mode` that the current surface can support.
|
||||
|
|
Загрузка…
Ссылка в новой задаче