mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-11-23 21:01:08 +00:00
Bug 1776816: Update wgpu to b370b990 (2022-6-28). r=jgilbert
Differential Revision: https://phabricator.services.mozilla.com/D150447
This commit is contained in:
parent
f8df240e86
commit
484ca146df
@ -85,12 +85,12 @@ rev = "3484d3e3ebdc8931493aa5df4d7ee9360a90e76b"
|
||||
[source."https://github.com/gfx-rs/wgpu"]
|
||||
git = "https://github.com/gfx-rs/wgpu"
|
||||
replace-with = "vendored-sources"
|
||||
rev = "32af4f56"
|
||||
rev = "b370b990"
|
||||
|
||||
[source."https://github.com/gfx-rs/naga"]
|
||||
git = "https://github.com/gfx-rs/naga"
|
||||
replace-with = "vendored-sources"
|
||||
rev = "571302e"
|
||||
rev = "27d38aae"
|
||||
|
||||
[source."https://github.com/gfx-rs/metal-rs"]
|
||||
git = "https://github.com/gfx-rs/metal-rs"
|
||||
|
19
Cargo.lock
generated
19
Cargo.lock
generated
@ -68,6 +68,15 @@ dependencies = [
|
||||
"log",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "android_system_properties"
|
||||
version = "0.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a20ae67ce26261f218e2b3f2f0d01887a9818283ca6fb260fa7c67e253d61c92"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.57"
|
||||
@ -3587,7 +3596,7 @@ checksum = "a2983372caf4480544083767bf2d27defafe32af49ab4df3a0b7fc90793a3664"
|
||||
[[package]]
|
||||
name = "naga"
|
||||
version = "0.8.0"
|
||||
source = "git+https://github.com/gfx-rs/naga?rev=571302e#571302e3ff09cb856f63a3683da308159872b7cc"
|
||||
source = "git+https://github.com/gfx-rs/naga?rev=27d38aae#27d38aae33fdbfa72197847038cb470720594cb1"
|
||||
dependencies = [
|
||||
"bit-set",
|
||||
"bitflags",
|
||||
@ -6095,7 +6104,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "wgpu-core"
|
||||
version = "0.12.0"
|
||||
source = "git+https://github.com/gfx-rs/wgpu?rev=32af4f56#32af4f56079fd2203c46c9c452cfe33fd60a5721"
|
||||
source = "git+https://github.com/gfx-rs/wgpu?rev=b370b990#b370b990e543bd8c74c895a0f856604368806e5c"
|
||||
dependencies = [
|
||||
"arrayvec",
|
||||
"bit-vec",
|
||||
@ -6112,6 +6121,7 @@ dependencies = [
|
||||
"serde",
|
||||
"smallvec",
|
||||
"thiserror",
|
||||
"web-sys",
|
||||
"wgpu-hal",
|
||||
"wgpu-types",
|
||||
]
|
||||
@ -6119,8 +6129,9 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "wgpu-hal"
|
||||
version = "0.12.0"
|
||||
source = "git+https://github.com/gfx-rs/wgpu?rev=32af4f56#32af4f56079fd2203c46c9c452cfe33fd60a5721"
|
||||
source = "git+https://github.com/gfx-rs/wgpu?rev=b370b990#b370b990e543bd8c74c895a0f856604368806e5c"
|
||||
dependencies = [
|
||||
"android_system_properties",
|
||||
"arrayvec",
|
||||
"ash",
|
||||
"bit-set",
|
||||
@ -6156,7 +6167,7 @@ dependencies = [
|
||||
[[package]]
|
||||
name = "wgpu-types"
|
||||
version = "0.12.0"
|
||||
source = "git+https://github.com/gfx-rs/wgpu?rev=32af4f56#32af4f56079fd2203c46c9c452cfe33fd60a5721"
|
||||
source = "git+https://github.com/gfx-rs/wgpu?rev=b370b990#b370b990e543bd8c74c895a0f856604368806e5c"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"bitflags_serde_shim",
|
||||
|
@ -84,13 +84,13 @@ ffi::WGPURenderPass* BeginRenderPass(
|
||||
desc.depth_stencil_attachment = &dsDesc;
|
||||
}
|
||||
|
||||
if (aDesc.mColorAttachments.Length() > WGPUMAX_COLOR_TARGETS) {
|
||||
if (aDesc.mColorAttachments.Length() > WGPUMAX_COLOR_ATTACHMENTS) {
|
||||
aParent->GetDevice()->GenerateError(nsLiteralCString(
|
||||
"Too many color attachments in GPURenderPassDescriptor"));
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
std::array<ffi::WGPURenderPassColorAttachment, WGPUMAX_COLOR_TARGETS>
|
||||
std::array<ffi::WGPURenderPassColorAttachment, WGPUMAX_COLOR_ATTACHMENTS>
|
||||
colorDescs = {};
|
||||
desc.color_attachments = colorDescs.data();
|
||||
desc.color_attachments_length = aDesc.mColorAttachments.Length();
|
||||
|
@ -17,7 +17,7 @@ default = []
|
||||
[dependencies.wgc]
|
||||
package = "wgpu-core"
|
||||
git = "https://github.com/gfx-rs/wgpu"
|
||||
rev = "32af4f56"
|
||||
rev = "b370b990"
|
||||
#Note: "replay" shouldn't ideally be needed,
|
||||
# but it allows us to serialize everything across IPC.
|
||||
features = ["replay", "trace", "serial-pass"]
|
||||
@ -25,12 +25,12 @@ features = ["replay", "trace", "serial-pass"]
|
||||
[dependencies.wgt]
|
||||
package = "wgpu-types"
|
||||
git = "https://github.com/gfx-rs/wgpu"
|
||||
rev = "32af4f56"
|
||||
rev = "b370b990"
|
||||
|
||||
[dependencies.wgh]
|
||||
package = "wgpu-hal"
|
||||
git = "https://github.com/gfx-rs/wgpu"
|
||||
rev = "32af4f56"
|
||||
rev = "b370b990"
|
||||
|
||||
[dependencies]
|
||||
bincode = "1"
|
||||
|
@ -20,11 +20,11 @@ origin:
|
||||
|
||||
# Human-readable identifier for this version/release
|
||||
# Generally "version NNN", "tag SSS", "bookmark SSS"
|
||||
release: commit 32af4f56
|
||||
release: commit b370b990
|
||||
|
||||
# Revision to pull in
|
||||
# Must be a long or short commit SHA (long preferred)
|
||||
revision: 32af4f56
|
||||
revision: b370b990e543bd8c74c895a0f856604368806e5c
|
||||
|
||||
license: ['MIT', 'Apache-2.0']
|
||||
|
||||
|
@ -114,11 +114,11 @@ impl FragmentState<'_> {
|
||||
fn to_wgpu(&self) -> wgc::pipeline::FragmentState {
|
||||
let color_targets = make_slice(self.targets, self.targets_length)
|
||||
.iter()
|
||||
.map(|ct| wgt::ColorTargetState {
|
||||
.map(|ct| Some(wgt::ColorTargetState {
|
||||
format: ct.format,
|
||||
blend: ct.blend.cloned(),
|
||||
write_mask: ct.write_mask,
|
||||
})
|
||||
}))
|
||||
.collect();
|
||||
wgc::pipeline::FragmentState {
|
||||
stage: self.stage.to_wgpu(),
|
||||
@ -600,9 +600,13 @@ pub extern "C" fn wgpu_device_create_render_bundle_encoder(
|
||||
desc: &RenderBundleEncoderDescriptor,
|
||||
bb: &mut ByteBuf,
|
||||
) -> *mut wgc::command::RenderBundleEncoder {
|
||||
let color_formats: Vec<_> = make_slice(desc.color_formats, desc.color_formats_length)
|
||||
.iter()
|
||||
.map(|format| Some(format.clone()))
|
||||
.collect();
|
||||
let descriptor = wgc::command::RenderBundleEncoderDescriptor {
|
||||
label: cow_label(&desc.label),
|
||||
color_formats: Cow::Borrowed(make_slice(desc.color_formats, desc.color_formats_length)),
|
||||
color_formats: Cow::Owned(color_formats),
|
||||
depth_stencil: desc
|
||||
.depth_stencil_format
|
||||
.map(|&format| wgt::RenderBundleDepthStencil {
|
||||
@ -701,14 +705,15 @@ pub unsafe extern "C" fn wgpu_command_encoder_begin_render_pass(
|
||||
encoder_id: id::CommandEncoderId,
|
||||
desc: &RenderPassDescriptor,
|
||||
) -> *mut wgc::command::RenderPass {
|
||||
let color_attachments: Vec<_> = make_slice(desc.color_attachments, desc.color_attachments_length)
|
||||
.iter()
|
||||
.map(|format| Some(format.clone()))
|
||||
.collect();
|
||||
let pass = wgc::command::RenderPass::new(
|
||||
encoder_id,
|
||||
&wgc::command::RenderPassDescriptor {
|
||||
label: cow_label(&desc.label),
|
||||
color_attachments: Cow::Borrowed(make_slice(
|
||||
desc.color_attachments,
|
||||
desc.color_attachments_length,
|
||||
)),
|
||||
color_attachments: Cow::Owned(color_attachments),
|
||||
depth_stencil_attachment: desc.depth_stencil_attachment.as_ref(),
|
||||
},
|
||||
);
|
||||
|
1
third_party/rust/android_system_properties/.cargo-checksum.json
vendored
Normal file
1
third_party/rust/android_system_properties/.cargo-checksum.json
vendored
Normal file
@ -0,0 +1 @@
|
||||
{"files":{"CONTRIBUTING.md":"0834cb3b5e092977688d73d219a05bed23ae0ecb54b6d6e5d866ce07f6583b5e","Cargo.toml":"813fa9eacb5751a4d0a5654283e55a5252fb90e9ce12d5226cceba1c95fe8686","LICENSE-APACHE":"216486f29671a4262efe32af6d84a75bef398127f8c5f369b5c8305983887a06","LICENSE-MIT":"80f275e90d799911ed3830a7f242a2ef5a4ade2092fe0aa07bfb2d2cf2f2b95e","README.md":"6a18a69fa94ca0a1a786d25e8df347605ba4200c47d3ac6926e235b15c9878e6","src/lib.rs":"704c78cd30205cb923e80bab8a8a9d7e2fbf667a4bb7d2c00c586f6dec244612"},"package":"a20ae67ce26261f218e2b3f2f0d01887a9818283ca6fb260fa7c67e253d61c92"}
|
40
third_party/rust/android_system_properties/CONTRIBUTING.md
vendored
Normal file
40
third_party/rust/android_system_properties/CONTRIBUTING.md
vendored
Normal file
@ -0,0 +1,40 @@
|
||||
# Contributing
|
||||
|
||||
Contributions are very much welcome. Here are the guidelines if you are thinking of helping us:
|
||||
|
||||
## Contributions
|
||||
|
||||
Contributions should be made in the form of GitHub pull requests.
|
||||
Each pull request will be reviewed by a core contributor (someone with
|
||||
permission to land patches) and either landed in the main tree or
|
||||
given feedback for changes that would be required.
|
||||
|
||||
Should you wish to work on an issue, please claim it first by commenting on
|
||||
the GitHub issue that you want to work on it. This is to prevent duplicated
|
||||
efforts from contributors on the same issue.
|
||||
|
||||
## Pull Request Checklist
|
||||
|
||||
- Branch from the master branch and, if needed, rebase to the current master
|
||||
branch before submitting your pull request. If it doesn't merge cleanly with
|
||||
master you may be asked to rebase your changes.
|
||||
|
||||
- Commits should be as small as possible, while ensuring that each commit is
|
||||
correct independently (i.e., each commit should compile and pass tests).
|
||||
|
||||
- If your patch is not getting reviewed or you need a specific person to review
|
||||
it, you can @-reply a reviewer asking for a review in the pull request or a
|
||||
comment.
|
||||
|
||||
- Whenever applicable, add tests relevant to the fixed bug or new feature.
|
||||
|
||||
For specific git instructions, see [GitHub workflow 101](https://github.com/servo/servo/wiki/Github-workflow).
|
||||
|
||||
## Conduct
|
||||
|
||||
We follow the [Rust Code of Conduct](http://www.rust-lang.org/conduct.html).
|
||||
For escalation or moderation issues, please contact Nical (nical@fastmail.com) instead of the Rust moderation team.
|
||||
|
||||
## License
|
||||
|
||||
Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be licensed dual MIT/Apache 2, without any additional terms or conditions.
|
35
third_party/rust/android_system_properties/Cargo.toml
vendored
Normal file
35
third_party/rust/android_system_properties/Cargo.toml
vendored
Normal file
@ -0,0 +1,35 @@
|
||||
# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
|
||||
#
|
||||
# When uploading crates to the registry Cargo will automatically
|
||||
# "normalize" Cargo.toml files for maximal compatibility
|
||||
# with all versions of Cargo and also rewrite `path` dependencies
|
||||
# to registry (e.g., crates.io) dependencies.
|
||||
#
|
||||
# If you are reading this file be aware that the original Cargo.toml
|
||||
# will likely look very different (and much more reasonable).
|
||||
# See Cargo.toml.orig for the original contents.
|
||||
|
||||
[package]
|
||||
edition = "2018"
|
||||
name = "android_system_properties"
|
||||
version = "0.1.2"
|
||||
authors = ["Nicolas Silva <nical@fastmail.com>"]
|
||||
description = "Minimal Android system properties wrapper"
|
||||
homepage = "https://github.com/nical/android_system_properties"
|
||||
documentation = "https://docs.rs/android_system_properties"
|
||||
readme = "README.md"
|
||||
keywords = ["android"]
|
||||
license = "MIT/Apache-2.0"
|
||||
repository = "https://github.com/nical/android_system_properties"
|
||||
|
||||
[package.metadata.docs.rs]
|
||||
targets = [
|
||||
"arm-linux-androideabi",
|
||||
"armv7-linux-androideabi",
|
||||
"aarch64-linux-android",
|
||||
"i686-linux-android",
|
||||
"x86_64-unknown-linux-gnu",
|
||||
]
|
||||
|
||||
[dependencies.libc]
|
||||
version = "0.2.126"
|
13
third_party/rust/android_system_properties/LICENSE-APACHE
vendored
Normal file
13
third_party/rust/android_system_properties/LICENSE-APACHE
vendored
Normal file
@ -0,0 +1,13 @@
|
||||
Copyright 2016 Nicolas Silva
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
20
third_party/rust/android_system_properties/LICENSE-MIT
vendored
Normal file
20
third_party/rust/android_system_properties/LICENSE-MIT
vendored
Normal file
@ -0,0 +1,20 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2013 Nicolas Silva
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
37
third_party/rust/android_system_properties/README.md
vendored
Normal file
37
third_party/rust/android_system_properties/README.md
vendored
Normal file
@ -0,0 +1,37 @@
|
||||
# android_system_properties
|
||||
|
||||
A minimal rust wrapper over android system properties
|
||||
|
||||
|
||||
This crate is similar to the `android-properties` crate with the exception that
|
||||
the necessary Android libc symbols are loaded dynamically instead of linked
|
||||
statically. In practice this means that the same binary will work with old and
|
||||
new versions of Android, even though the API for reading system properties changed
|
||||
around Android L.
|
||||
|
||||
## Example
|
||||
|
||||
```rust
|
||||
use android_system_properties::AndroidSystemProperties;
|
||||
|
||||
let properties = AndroidSystemProperties::new();
|
||||
|
||||
if let Some(value) = properties.get("persist.sys.timezone") {
|
||||
println!("{}", value);
|
||||
}
|
||||
```
|
||||
|
||||
## Listing and setting properties
|
||||
|
||||
For the sake of simplicity this crate currently only contains what's needed by wgpu.
|
||||
The implementations for listing and setting properties can be added back if anyone needs
|
||||
them (let me know by filing an issue).
|
||||
|
||||
## License
|
||||
|
||||
Licensed under either of
|
||||
|
||||
* Apache License, Version 2.0 ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
|
||||
* MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)
|
||||
|
||||
at your option.
|
162
third_party/rust/android_system_properties/src/lib.rs
vendored
Normal file
162
third_party/rust/android_system_properties/src/lib.rs
vendored
Normal file
@ -0,0 +1,162 @@
|
||||
//! A thin rust wrapper for Andoird system properties.
|
||||
//!
|
||||
//! This crate is similar to the `android-properties` crate with the exception that
|
||||
//! the necessary Android libc symbols are loaded dynamically instead of linked
|
||||
//! statically. In practice this means that the same binary will work with old and
|
||||
//! new versions of Android, even though the API for reading system properties changed
|
||||
//! around Android L.
|
||||
use std::{
|
||||
ffi::{CStr, CString},
|
||||
os::raw::{c_char, c_int, c_void},
|
||||
};
|
||||
|
||||
#[cfg(target_os = "android")]
|
||||
use std::mem;
|
||||
|
||||
unsafe fn property_callback(payload: *mut String, _name: *const c_char, value: *const c_char, _serial: u32) {
|
||||
let cvalue = CStr::from_ptr(value);
|
||||
(*payload) = cvalue.to_str().unwrap().to_string();
|
||||
}
|
||||
|
||||
type Callback = unsafe fn(*mut String, *const c_char, *const c_char, u32);
|
||||
|
||||
type SystemPropertyGetFn = unsafe extern "C" fn(*const c_char, *mut c_char) -> c_int;
|
||||
type SystemPropertyFindFn = unsafe extern "C" fn(*const c_char) -> *const c_void;
|
||||
type SystemPropertyReadCallbackFn = unsafe extern "C" fn(*const c_void, Callback, *mut String) -> *const c_void;
|
||||
|
||||
#[derive(Debug)]
|
||||
/// An object that can retrieve android system properties.
|
||||
///
|
||||
/// ## Example
|
||||
///
|
||||
/// ```
|
||||
/// use android_system_properties::AndroidSystemProperties;
|
||||
///
|
||||
/// let properties = AndroidSystemProperties::new();
|
||||
///
|
||||
/// if let Some(value) = properties.get("persist.sys.timezone") {
|
||||
/// println!("{}", value);
|
||||
/// }
|
||||
/// ```
|
||||
pub struct AndroidSystemProperties {
|
||||
libc_so: *mut c_void,
|
||||
get_fn: Option<SystemPropertyGetFn>,
|
||||
find_fn: Option<SystemPropertyFindFn>,
|
||||
read_callback_fn: Option<SystemPropertyReadCallbackFn>,
|
||||
}
|
||||
|
||||
impl AndroidSystemProperties {
|
||||
#[cfg(not(target_os = "android"))]
|
||||
/// Create an entry point for accessing Android properties.
|
||||
pub fn new() -> Self {
|
||||
AndroidSystemProperties {
|
||||
libc_so: std::ptr::null_mut(),
|
||||
find_fn: None,
|
||||
read_callback_fn: None,
|
||||
get_fn: None,
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_os = "android")]
|
||||
/// Create an entry point for accessing Android properties.
|
||||
pub fn new() -> Self {
|
||||
let libc_name = CString::new("libc.so").unwrap();
|
||||
let libc_so = unsafe { libc::dlopen(libc_name.as_ptr(), libc::RTLD_NOLOAD) };
|
||||
|
||||
let mut properties = AndroidSystemProperties {
|
||||
libc_so,
|
||||
find_fn: None,
|
||||
read_callback_fn: None,
|
||||
get_fn: None,
|
||||
};
|
||||
|
||||
if libc_so.is_null() {
|
||||
return properties;
|
||||
}
|
||||
|
||||
|
||||
unsafe fn load_fn(libc_so: *mut c_void, name: &str) -> Option<*const c_void> {
|
||||
let cname = CString::new(name).unwrap();
|
||||
let fn_ptr = libc::dlsym(libc_so, cname.as_ptr());
|
||||
|
||||
if fn_ptr.is_null() {
|
||||
return None;
|
||||
}
|
||||
|
||||
Some(fn_ptr)
|
||||
}
|
||||
|
||||
unsafe {
|
||||
properties.read_callback_fn = load_fn(libc_so, "__system_property_read_callback")
|
||||
.map(|raw| mem::transmute::<*const c_void, SystemPropertyReadCallbackFn>(raw));
|
||||
|
||||
properties.find_fn = load_fn(libc_so, "__system_property_find")
|
||||
.map(|raw| mem::transmute::<*const c_void, SystemPropertyFindFn>(raw));
|
||||
|
||||
// Fallback for old versions of Android.
|
||||
if properties.read_callback_fn.is_none() || properties.find_fn.is_none() {
|
||||
properties.get_fn = load_fn(libc_so, "__system_property_get")
|
||||
.map(|raw| mem::transmute::<*const c_void, SystemPropertyGetFn>(raw));
|
||||
}
|
||||
}
|
||||
|
||||
properties
|
||||
}
|
||||
|
||||
/// Retrieve a system property.
|
||||
///
|
||||
/// Returns None if the operation fails.
|
||||
pub fn get(&self, name: &str) -> Option<String> {
|
||||
let cname = CString::new(name).unwrap();
|
||||
|
||||
// If available, use the recommended approach to accessing properties (Android L and onward).
|
||||
if let (Some(find_fn), Some(read_callback_fn)) = (self.find_fn, self.read_callback_fn) {
|
||||
let info = unsafe { (find_fn)(cname.as_ptr()) };
|
||||
|
||||
if info.is_null() {
|
||||
return None;
|
||||
}
|
||||
|
||||
let mut result = String::new();
|
||||
|
||||
unsafe {
|
||||
(read_callback_fn)(info, property_callback, &mut result);
|
||||
}
|
||||
|
||||
return Some(result);
|
||||
}
|
||||
|
||||
// Fall back to the older approach.
|
||||
if let Some(get_fn) = self.get_fn {
|
||||
// The constant is PROP_VALUE_MAX in Android's libc/include/sys/system_properties.h
|
||||
const PROPERTY_VALUE_MAX: usize = 92;
|
||||
let cvalue = CString::new(Vec::with_capacity(PROPERTY_VALUE_MAX)).unwrap();
|
||||
let raw = cvalue.into_raw();
|
||||
|
||||
let len = unsafe { (get_fn)(cname.as_ptr(), raw) };
|
||||
|
||||
let bytes = unsafe {
|
||||
let raw: *mut u8 = std::mem::transmute(raw); // Cast from *mut i8.
|
||||
Vec::from_raw_parts(raw, len as usize, PROPERTY_VALUE_MAX)
|
||||
};
|
||||
|
||||
if len > 0 {
|
||||
String::from_utf8(bytes).ok()
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for AndroidSystemProperties {
|
||||
fn drop(&mut self) {
|
||||
if !self.libc_so.is_null() {
|
||||
unsafe {
|
||||
libc::dlclose(self.libc_so);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
2
third_party/rust/naga/.cargo-checksum.json
vendored
2
third_party/rust/naga/.cargo-checksum.json
vendored
File diff suppressed because one or more lines are too long
@ -48,6 +48,11 @@ jobs:
|
||||
args: --all-features --workspace
|
||||
- name: Check snapshots
|
||||
run: git diff --exit-code -- tests/out
|
||||
- uses: actions-rs/cargo@v1
|
||||
name: Check benchmarks
|
||||
with:
|
||||
command: check
|
||||
args: --benches
|
||||
clippy:
|
||||
name: Clippy
|
||||
runs-on: ubuntu-latest
|
||||
@ -75,7 +80,6 @@ jobs:
|
||||
profile: minimal
|
||||
toolchain: stable
|
||||
override: true
|
||||
- run: rustup component add clippy
|
||||
- uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: doc
|
||||
|
@ -5,12 +5,28 @@ on:
|
||||
- 'tests/out/hlsl/*.hlsl'
|
||||
|
||||
jobs:
|
||||
validate-windows:
|
||||
name: HLSL
|
||||
validate-windows-dxc:
|
||||
name: HLSL via DXC
|
||||
runs-on: windows-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Add DirectXShaderCompiler
|
||||
uses: napokue/setup-dxc@v1.0.0
|
||||
- run: make validate-hlsl
|
||||
- run: make validate-hlsl-dxc
|
||||
shell: sh
|
||||
|
||||
validate-windows-fxc:
|
||||
name: HLSL via FXC
|
||||
runs-on: windows-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Add fxc bin to PATH
|
||||
run: |
|
||||
Get-Childitem -Path "C:\Program Files (x86)\Windows Kits\10\bin\**\x64\fxc.exe" `
|
||||
| Sort-Object -Property LastWriteTime -Descending `
|
||||
| Select-Object -First 1 `
|
||||
| Split-Path -Parent `
|
||||
| Out-File -FilePath $Env:GITHUB_PATH -Encoding utf8 -Append
|
||||
shell: powershell
|
||||
- run: make validate-hlsl-fxc
|
||||
shell: sh
|
312
third_party/rust/naga/CHANGELOG.md
vendored
312
third_party/rust/naga/CHANGELOG.md
vendored
@ -1,16 +1,288 @@
|
||||
# Change Log
|
||||
|
||||
## v0.9 (TBD)
|
||||
- WGSL:
|
||||
- semicolon after struct declaration are now optional
|
||||
- commas are used to separate struct members instead of semicolons
|
||||
- attributes are declared with `@attrib` instead of `[[attrib]]`
|
||||
- `stride` attribute is removed
|
||||
- block comments are supported
|
||||
- renames:
|
||||
- `findLsb` -> `firstTrailingBit`
|
||||
- `findMsb` -> `firstLeadingBit`
|
||||
- `smoothStep` -> `smoothstep`
|
||||
|
||||
- Fix minimal-versions of dependencies ([#1840](https://github.com/gfx-rs/naga/pull/1840)) **@teoxoy**
|
||||
- Update MSRV to 1.56 ([#1838](https://github.com/gfx-rs/naga/pull/1838)) **@teoxoy**
|
||||
|
||||
API
|
||||
|
||||
- Rename `TypeFlags` `INTERFACE`/`HOST_SHARED` to `IO_SHARED`/`HOST_SHAREABLE` ([#1872](https://github.com/gfx-rs/naga/pull/1872)) **@jimblandy**
|
||||
- Expose more error information ([#1827](https://github.com/gfx-rs/naga/pull/1827), [#1937](https://github.com/gfx-rs/naga/pull/1937)) **@jakobhellermann** **@nical** **@jimblandy**
|
||||
- Do not unconditionally make error output colorful ([#1707](https://github.com/gfx-rs/naga/pull/1707)) **@rhysd**
|
||||
- Rename `StorageClass` to `AddressSpace` ([#1699](https://github.com/gfx-rs/naga/pull/1699)) **@kvark**
|
||||
- Add a way to emit errors to a path ([#1640](https://github.com/gfx-rs/naga/pull/1640)) **@laptou**
|
||||
|
||||
CLI
|
||||
|
||||
- Add `bincode` representation ([#1729](https://github.com/gfx-rs/naga/pull/1729)) **@kvark**
|
||||
- Include file path in WGSL parse error ([#1708](https://github.com/gfx-rs/naga/pull/1708)) **@rhysd**
|
||||
- Add `--version` flag ([#1706](https://github.com/gfx-rs/naga/pull/1706)) **@rhysd**
|
||||
- Support reading input from stdin via `--stdin-file-path` ([#1701](https://github.com/gfx-rs/naga/pull/1701)) **@rhysd**
|
||||
- Use `panic = "abort"` ([#1597](https://github.com/gfx-rs/naga/pull/1597)) **@jrmuizel**
|
||||
|
||||
DOCS
|
||||
|
||||
- Standardize some docs ([#1660](https://github.com/gfx-rs/naga/pull/1660)) **@NoelTautges**
|
||||
- Document `TypeInner::BindingArray` ([#1859](https://github.com/gfx-rs/naga/pull/1859)) **@jimblandy**
|
||||
- Clarify accepted types for `Expression::AccessIndex` ([#1862](https://github.com/gfx-rs/naga/pull/1862)) **@NoelTautges**
|
||||
- Document `proc::layouter` ([#1693](https://github.com/gfx-rs/naga/pull/1693)) **@jimblandy**
|
||||
- Document Naga's promises around validation and panics ([#1828](https://github.com/gfx-rs/naga/pull/1828)) **@jimblandy**
|
||||
- `FunctionInfo` doc fixes ([#1726](https://github.com/gfx-rs/naga/pull/1726)) **@jimblandy**
|
||||
|
||||
VALIDATOR
|
||||
|
||||
- Forbid returning pointers and atomics from functions ([#911](https://github.com/gfx-rs/naga/pull/911)) **@jimblandy**
|
||||
- Let validation check for more unsupported builtins ([#1962](https://github.com/gfx-rs/naga/pull/1962)) **@jimblandy**
|
||||
- Fix `Capabilities::SAMPLER_NON_UNIFORM_INDEXING` bitflag ([#1915](https://github.com/gfx-rs/naga/pull/1915)) **@cwfitzgerald**
|
||||
- Properly check that user-defined IO uses IO-shareable types ([#912](https://github.com/gfx-rs/naga/pull/912)) **@jimblandy**
|
||||
- Validate `ValuePointer` exactly like a `Pointer` to a `Scalar` ([#1875](https://github.com/gfx-rs/naga/pull/1875)) **@jimblandy**
|
||||
- Reject empty structs ([#1826](https://github.com/gfx-rs/naga/pull/1826)) **@jimblandy**
|
||||
- Validate uniform address space layout constraints ([#1812](https://github.com/gfx-rs/naga/pull/1812)) **@teoxoy**
|
||||
- Improve `AddressSpace` related error messages ([#1710](https://github.com/gfx-rs/naga/pull/1710)) **@kvark**
|
||||
|
||||
WGSL-IN
|
||||
|
||||
Main breaking changes
|
||||
|
||||
- Commas to separate struct members (comma after last member is optional)
|
||||
- `struct S { a: f32; b: i32; }` -> `struct S { a: f32, b: i32 }`
|
||||
- Attribute syntax
|
||||
- `[[binding(0), group(0)]]` -> `@binding(0) @group(0)`
|
||||
- Entry point stage attributes
|
||||
- `@stage(vertex)` -> `@vertex`
|
||||
- `@stage(fragment)` -> `@fragment`
|
||||
- `@stage(compute)` -> `@compute`
|
||||
- Function renames
|
||||
- `smoothStep` -> `smoothstep`
|
||||
- `findLsb` -> `firstTrailingBit`
|
||||
- `findMsb` -> `firstLeadingBit`
|
||||
|
||||
Specification Changes (relavant changes have also been applied to the WGSL backend)
|
||||
|
||||
- Update number literal format ([#1863](https://github.com/gfx-rs/naga/pull/1863)) **@teoxoy**
|
||||
- Allow non-ascii characters in identifiers ([#1849](https://github.com/gfx-rs/naga/pull/1849)) **@teoxoy**
|
||||
- Update reserved keywords ([#1847](https://github.com/gfx-rs/naga/pull/1847), [#1870](https://github.com/gfx-rs/naga/pull/1870), [#1905](https://github.com/gfx-rs/naga/pull/1905)) **@teoxoy** **@Gordon-F**
|
||||
- Update entry point stage attributes ([#1833](https://github.com/gfx-rs/naga/pull/1833)) **@Gordon-F**
|
||||
- Make colon in case optional ([#1801](https://github.com/gfx-rs/naga/pull/1801)) **@Gordon-F**
|
||||
- Rename `smoothStep` to `smoothstep` ([#1800](https://github.com/gfx-rs/naga/pull/1800)) **@Gordon-F**
|
||||
- Make semicolon after struct declaration optional ([#1791](https://github.com/gfx-rs/naga/pull/1791)) **@stshine**
|
||||
- Use commas to separate struct members instead of semicolons ([#1773](https://github.com/gfx-rs/naga/pull/1773)) **@Gordon-F**
|
||||
- Rename `findLsb`/`findMsb` to `firstTrailingBit`/`firstLeadingBit` ([#1735](https://github.com/gfx-rs/naga/pull/1735)) **@kvark**
|
||||
- Make parenthesis optional for `if` and `switch` statements ([#1725](https://github.com/gfx-rs/naga/pull/1725)) **@Gordon-F**
|
||||
- Declare attribtues with `@attrib` instead of `[[attrib]]` ([#1676](https://github.com/gfx-rs/naga/pull/1676)) **@kvark**
|
||||
- Allow non-structure buffer types ([#1682](https://github.com/gfx-rs/naga/pull/1682)) **@kvark**
|
||||
- Remove `stride` attribute ([#1681](https://github.com/gfx-rs/naga/pull/1681)) **@kvark**
|
||||
|
||||
Improvements
|
||||
|
||||
- Implement `firstTrailingBit`/`firstLeadingBit` u32 overloads ([#1865](https://github.com/gfx-rs/naga/pull/1865)) **@teoxoy**
|
||||
- Add error for non-floating-point matrix ([#1917](https://github.com/gfx-rs/naga/pull/1917)) **@grovesNL**
|
||||
- Implement partial vector & matrix identity constructors ([#1916](https://github.com/gfx-rs/naga/pull/1916)) **@teoxoy**
|
||||
- Implement phony assignment ([#1866](https://github.com/gfx-rs/naga/pull/1866), [#1869](https://github.com/gfx-rs/naga/pull/1869)) **@teoxoy**
|
||||
- Fix being able to match `~=` as LogicalOperation ([#1849](https://github.com/gfx-rs/naga/pull/1849)) **@teoxoy**
|
||||
- Implement Binding Arrays ([#1845](https://github.com/gfx-rs/naga/pull/1845)) **@cwfitzgerald**
|
||||
- Implement unary vector operators ([#1820](https://github.com/gfx-rs/naga/pull/1820)) **@teoxoy**
|
||||
- Implement zero value constructors and constructors that infer their type from their parameters ([#1790](https://github.com/gfx-rs/naga/pull/1790)) **@teoxoy**
|
||||
- Implement invariant attribute ([#1789](https://github.com/gfx-rs/naga/pull/1789), [#1822](https://github.com/gfx-rs/naga/pull/1822)) **@teoxoy** **@jimblandy**
|
||||
- Implement increment and decrement statements ([#1788](https://github.com/gfx-rs/naga/pull/1788), [#1912](https://github.com/gfx-rs/naga/pull/1912)) **@teoxoy**
|
||||
- Implement `while` loop ([#1787](https://github.com/gfx-rs/naga/pull/1787)) **@teoxoy**
|
||||
- Fix array size on globals ([#1717](https://github.com/gfx-rs/naga/pull/1717)) **@jimblandy**
|
||||
- Implement integer vector overloads for `dot` function ([#1689](https://github.com/gfx-rs/naga/pull/1689)) **@francesco-cattoglio**
|
||||
- Implement block comments ([#1675](https://github.com/gfx-rs/naga/pull/1675)) **@kocsis1david**
|
||||
- Implement assignment binary operators ([#1662](https://github.com/gfx-rs/naga/pull/1662)) **@kvark**
|
||||
- Implement `radians`/`degrees` builtin functions ([#1627](https://github.com/gfx-rs/naga/pull/1627)) **@encounter**
|
||||
- Implement `findLsb`/`findMsb` builtin functions ([#1473](https://github.com/gfx-rs/naga/pull/1473)) **@fintelia**
|
||||
- Implement `textureGather`/`textureGatherCompare` builtin functions ([#1596](https://github.com/gfx-rs/naga/pull/1596)) **@kvark**
|
||||
|
||||
SPV-IN
|
||||
|
||||
- Implement `OpBitReverse` and `OpBitCount` ([#1954](https://github.com/gfx-rs/naga/pull/1954)) **@JCapucho**
|
||||
- Add `MultiView` to `SUPPORTED_CAPABILITIES` ([#1934](https://github.com/gfx-rs/naga/pull/1934)) **@expenses**
|
||||
- Translate `OpSMod` and `OpFMod` correctly ([#1867](https://github.com/gfx-rs/naga/pull/1867)) **@teoxoy**
|
||||
- Error on unsupported `MatrixStride` ([#1805](https://github.com/gfx-rs/naga/pull/1805)) **@teoxoy**
|
||||
- Align array stride for undecorated arrays ([#1724](https://github.com/gfx-rs/naga/pull/1724)) **@JCapucho**
|
||||
|
||||
GLSL-IN
|
||||
|
||||
- Fix matrix multiplication check ([#1953](https://github.com/gfx-rs/naga/pull/1953)) **@JCapucho**
|
||||
- Fix panic (stop emitter in conditional) ([#1952](https://github.com/gfx-rs/naga/pull/1952)) **@JCapucho**
|
||||
- Translate `mod` fn correctly ([#1867](https://github.com/gfx-rs/naga/pull/1867)) **@teoxoy**
|
||||
- Make the ternary operator behave as an if ([#1877](https://github.com/gfx-rs/naga/pull/1877)) **@JCapucho**
|
||||
- Add support for `clamp` function ([#1502](https://github.com/gfx-rs/naga/pull/1502)) **@sjinno**
|
||||
- Better errors for bad constant expression ([#1501](https://github.com/gfx-rs/naga/pull/1501)) **@sjinno**
|
||||
- Error on a `matCx2` used with the `std140` layout ([#1806](https://github.com/gfx-rs/naga/pull/1806)) **@teoxoy**
|
||||
- Allow nested accesses in lhs positions ([#1794](https://github.com/gfx-rs/naga/pull/1794)) **@JCapucho**
|
||||
- Use forced conversions for vector/matrix constructors ([#1796](https://github.com/gfx-rs/naga/pull/1796)) **@JCapucho**
|
||||
- Add support for `barrier` function ([#1793](https://github.com/gfx-rs/naga/pull/1793)) **@fintelia**
|
||||
- Fix panic (resume expression emit after `imageStore`) ([#1795](https://github.com/gfx-rs/naga/pull/1795)) **@JCapucho**
|
||||
- Allow multiple array specifiers ([#1780](https://github.com/gfx-rs/naga/pull/1780)) **@JCapucho**
|
||||
- Fix memory qualifiers being inverted ([#1779](https://github.com/gfx-rs/naga/pull/1779)) **@JCapucho**
|
||||
- Support arrays as input/output types ([#1759](https://github.com/gfx-rs/naga/pull/1759)) **@JCapucho**
|
||||
- Fix freestanding constructor parsing ([#1758](https://github.com/gfx-rs/naga/pull/1758)) **@JCapucho**
|
||||
- Fix matrix - scalar operations ([#1757](https://github.com/gfx-rs/naga/pull/1757)) **@JCapucho**
|
||||
- Fix matrix - matrix division ([#1757](https://github.com/gfx-rs/naga/pull/1757)) **@JCapucho**
|
||||
- Fix matrix comparisons ([#1757](https://github.com/gfx-rs/naga/pull/1757)) **@JCapucho**
|
||||
- Add support for `texelFetchOffset` ([#1746](https://github.com/gfx-rs/naga/pull/1746)) **@JCapucho**
|
||||
- Inject `sampler2DMSArray` builtins on use ([#1737](https://github.com/gfx-rs/naga/pull/1737)) **@JCapucho**
|
||||
- Inject `samplerCubeArray` builtins on use ([#1736](https://github.com/gfx-rs/naga/pull/1736)) **@JCapucho**
|
||||
- Add support for image builtin functions ([#1723](https://github.com/gfx-rs/naga/pull/1723)) **@JCapucho**
|
||||
- Add support for image declarations ([#1723](https://github.com/gfx-rs/naga/pull/1723)) **@JCapucho**
|
||||
- Texture builtins fixes ([#1719](https://github.com/gfx-rs/naga/pull/1719)) **@JCapucho**
|
||||
- Type qualifiers rework ([#1713](https://github.com/gfx-rs/naga/pull/1713)) **@JCapucho**
|
||||
- `texelFetch` accept multisampled textures ([#1715](https://github.com/gfx-rs/naga/pull/1715)) **@JCapucho**
|
||||
- Fix panic when culling nested block ([#1714](https://github.com/gfx-rs/naga/pull/1714)) **@JCapucho**
|
||||
- Fix composite constructors ([#1631](https://github.com/gfx-rs/naga/pull/1631)) **@JCapucho**
|
||||
- Fix using swizzle as out arguments ([#1632](https://github.com/gfx-rs/naga/pull/1632)) **@JCapucho**
|
||||
|
||||
SPV-OUT
|
||||
|
||||
- Implement `reverseBits` and `countOneBits` ([#1897](https://github.com/gfx-rs/naga/pull/1897)) **@hasali19**
|
||||
- Use `OpCopyObject` for matrix identity casts ([#1916](https://github.com/gfx-rs/naga/pull/1916)) **@teoxoy**
|
||||
- Use `OpCopyObject` for bool - bool conversion due to `OpBitcast` not being feasible for booleans ([#1916](https://github.com/gfx-rs/naga/pull/1916)) **@teoxoy**
|
||||
- Zero init variables in function and private address spaces ([#1871](https://github.com/gfx-rs/naga/pull/1871)) **@teoxoy**
|
||||
- Use `SRem` instead of `SMod` ([#1867](https://github.com/gfx-rs/naga/pull/1867)) **@teoxoy**
|
||||
- Add support for integer vector - scalar multiplication ([#1820](https://github.com/gfx-rs/naga/pull/1820)) **@teoxoy**
|
||||
- Add support for matrix addition and subtraction ([#1820](https://github.com/gfx-rs/naga/pull/1820)) **@teoxoy**
|
||||
- Emit required decorations on wrapper struct types ([#1815](https://github.com/gfx-rs/naga/pull/1815)) **@jimblandy**
|
||||
- Decorate array and struct type layouts unconditionally ([#1815](https://github.com/gfx-rs/naga/pull/1815)) **@jimblandy**
|
||||
- Fix wrong `MatrixStride` for `matCx2` and `mat2xR` ([#1781](https://github.com/gfx-rs/naga/pull/1781)) **@teoxoy**
|
||||
- Use `OpImageQuerySize` for MS images ([#1742](https://github.com/gfx-rs/naga/pull/1742)) **@JCapucho**
|
||||
|
||||
MSL-OUT
|
||||
|
||||
- Fix pointers to private or workgroup address spaces possibly being read only ([#1901](https://github.com/gfx-rs/naga/pull/1901)) **@teoxoy**
|
||||
- Zero init variables in function address space ([#1871](https://github.com/gfx-rs/naga/pull/1871)) **@teoxoy**
|
||||
- Make binding arrays play nice with bounds checks ([#1855](https://github.com/gfx-rs/naga/pull/1855)) **@cwfitzgerald**
|
||||
- Permit `invariant` qualifier on vertex shader outputs ([#1821](https://github.com/gfx-rs/naga/pull/1821)) **@jimblandy**
|
||||
- Fix packed `vec3` stores ([#1816](https://github.com/gfx-rs/naga/pull/1816)) **@teoxoy**
|
||||
- Actually test push constants to be used ([#1767](https://github.com/gfx-rs/naga/pull/1767)) **@kvark**
|
||||
- Properly rename entry point arguments for struct members ([#1766](https://github.com/gfx-rs/naga/pull/1766)) **@jimblandy**
|
||||
- Qualify read-only storage with const ([#1763](https://github.com/gfx-rs/naga/pull/1763)) **@kvark**
|
||||
- Fix not unary operator for integer scalars ([#1760](https://github.com/gfx-rs/naga/pull/1760)) **@vincentisambart**
|
||||
- Add bounds checks for `ImageLoad` and `ImageStore` ([#1730](https://github.com/gfx-rs/naga/pull/1730)) **@jimblandy**
|
||||
- Fix resource bindings for non-structures ([#1718](https://github.com/gfx-rs/naga/pull/1718)) **@kvark**
|
||||
- Always check whether _buffer_sizes arg is needed ([#1717](https://github.com/gfx-rs/naga/pull/1717)) **@jimblandy**
|
||||
- WGSL storage address space should always correspond to MSL device address space ([#1711](https://github.com/gfx-rs/naga/pull/1711)) **@wtholliday**
|
||||
- Mitigation for MSL atomic bounds check ([#1703](https://github.com/gfx-rs/naga/pull/1703)) **@glalonde**
|
||||
|
||||
HLSL-OUT
|
||||
|
||||
- Fix fallthrough in switch statements ([#1920](https://github.com/gfx-rs/naga/pull/1920)) **@teoxoy**
|
||||
- Fix missing break statements ([#1919](https://github.com/gfx-rs/naga/pull/1919)) **@teoxoy**
|
||||
- Fix `countOneBits` and `reverseBits` for signed integers ([#1928](https://github.com/gfx-rs/naga/pull/1928)) **@hasali19**
|
||||
- Fix array constructor return type ([#1914](https://github.com/gfx-rs/naga/pull/1914)) **@teoxoy**
|
||||
- Fix hlsl output for writes to scalar/vector storage buffer ([#1903](https://github.com/gfx-rs/naga/pull/1903)) **@hasali19**
|
||||
- Use `fmod` instead of `%` ([#1867](https://github.com/gfx-rs/naga/pull/1867)) **@teoxoy**
|
||||
- Use wrapped constructors when loading from storage address space ([#1893](https://github.com/gfx-rs/naga/pull/1893)) **@teoxoy**
|
||||
- Zero init struct constructor ([#1890](https://github.com/gfx-rs/naga/pull/1890)) **@teoxoy**
|
||||
- Flesh out matrix handling documentation ([#1850](https://github.com/gfx-rs/naga/pull/1850)) **@jimblandy**
|
||||
- Emit `row_major` qualifier on matrix uniform globals ([#1846](https://github.com/gfx-rs/naga/pull/1846)) **@jimblandy**
|
||||
- Fix bool splat ([#1820](https://github.com/gfx-rs/naga/pull/1820)) **@teoxoy**
|
||||
- Add more padding when necessary ([#1814](https://github.com/gfx-rs/naga/pull/1814)) **@teoxoy**
|
||||
- Support multidimensional arrays ([#1814](https://github.com/gfx-rs/naga/pull/1814)) **@teoxoy**
|
||||
- Don't output interpolation modifier if it's the default ([#1809](https://github.com/gfx-rs/naga/pull/1809)) **@NoelTautges**
|
||||
- Fix `matCx2` translation for uniform buffers ([#1802](https://github.com/gfx-rs/naga/pull/1802)) **@teoxoy**
|
||||
- Fix modifiers not being written in the vertex output and fragment input structs ([#1789](https://github.com/gfx-rs/naga/pull/1789)) **@teoxoy**
|
||||
- Fix matrix not being declared as transposed ([#1784](https://github.com/gfx-rs/naga/pull/1784)) **@teoxoy**
|
||||
- Insert padding between struct members ([#1786](https://github.com/gfx-rs/naga/pull/1786)) **@teoxoy**
|
||||
- Fix not unary operator for integer scalars ([#1760](https://github.com/gfx-rs/naga/pull/1760)) **@vincentisambart**
|
||||
|
||||
GLSL-OUT
|
||||
|
||||
- Fix type error for `countOneBits` implementation ([#1897](https://github.com/gfx-rs/naga/pull/1897)) **@hasali19**
|
||||
- Fix storage format for `Rgba8Unorm` ([#1955](https://github.com/gfx-rs/naga/pull/1955)) **@JCapucho**
|
||||
- Implement bounds checks for `ImageLoad` ([#1889](https://github.com/gfx-rs/naga/pull/1889)) **@JCapucho**
|
||||
- Fix feature search in expressions ([#1887](https://github.com/gfx-rs/naga/pull/1887)) **@JCapucho**
|
||||
- Emit globals of any type ([#1823](https://github.com/gfx-rs/naga/pull/1823)) **@jimblandy**
|
||||
- Add support for boolean vector `~`, `|` and `&` ops ([#1820](https://github.com/gfx-rs/naga/pull/1820)) **@teoxoy**
|
||||
- Fix array function arguments ([#1814](https://github.com/gfx-rs/naga/pull/1814)) **@teoxoy**
|
||||
- Write constant sized array type for uniform ([#1768](https://github.com/gfx-rs/naga/pull/1768)) **@hatoo**
|
||||
- Texture function fixes ([#1742](https://github.com/gfx-rs/naga/pull/1742)) **@JCapucho**
|
||||
- Push constants use anonymous uniforms ([#1683](https://github.com/gfx-rs/naga/pull/1683)) **@JCapucho**
|
||||
- Add support for push constant emulation ([#1672](https://github.com/gfx-rs/naga/pull/1672)) **@JCapucho**
|
||||
- Skip unsized types if unused ([#1649](https://github.com/gfx-rs/naga/pull/1649)) **@kvark**
|
||||
- Write struct and array initializers ([#1644](https://github.com/gfx-rs/naga/pull/1644)) **@JCapucho**
|
||||
|
||||
|
||||
## v0.8.5 (2022-01-25)
|
||||
|
||||
MSL-OUT
|
||||
|
||||
- Make VS-output positions invariant on even more systems ([#1697](https://github.com/gfx-rs/naga/pull/1697)) **@cwfitzgerald**
|
||||
- Improve support for point primitives ([#1696](https://github.com/gfx-rs/naga/pull/1696)) **@kvark**
|
||||
|
||||
|
||||
## v0.8.4 (2022-01-24)
|
||||
|
||||
MSL-OUT
|
||||
|
||||
- Make VS-output positions invariant if possible ([#1687](https://github.com/gfx-rs/naga/pull/1687)) **@kvark**
|
||||
|
||||
GLSL-OUT
|
||||
|
||||
- Fix `floatBitsToUint` spelling ([#1688](https://github.com/gfx-rs/naga/pull/1688)) **@cwfitzgerald**
|
||||
- Call proper memory barrier functions ([#1680](https://github.com/gfx-rs/naga/pull/1680)) **@francesco-cattoglio**
|
||||
|
||||
|
||||
## v0.8.3 (2022-01-20)
|
||||
|
||||
- Don't pin `indexmap` version ([#1666](https://github.com/gfx-rs/naga/pull/1666)) **@a1phyr**
|
||||
|
||||
MSL-OUT
|
||||
|
||||
- Fix support for point primitives ([#1674](https://github.com/gfx-rs/naga/pull/1674)) **@kvark**
|
||||
|
||||
GLSL-OUT
|
||||
|
||||
- Fix sampler association ([#1671](https://github.com/gfx-rs/naga/pull/1671)) **@JCapucho**
|
||||
|
||||
|
||||
## v0.8.2 (2022-01-11)
|
||||
|
||||
VALIDATOR
|
||||
|
||||
- Check structure resource types ([#1639](https://github.com/gfx-rs/naga/pull/1639)) **@kvark**
|
||||
|
||||
WGSL-IN
|
||||
|
||||
- Improve type mismatch errors ([#1658](https://github.com/gfx-rs/naga/pull/1658)) **@Gordon-F**
|
||||
|
||||
SPV-IN
|
||||
|
||||
- Implement more sign agnostic operations ([#1651](https://github.com/gfx-rs/naga/pull/1651), [#1650](https://github.com/gfx-rs/naga/pull/1650)) **@JCapucho**
|
||||
|
||||
SPV-OUT
|
||||
|
||||
- Fix modulo operator (use `OpFRem` instead of `OpFMod`) ([#1653](https://github.com/gfx-rs/naga/pull/1653)) **@JCapucho**
|
||||
|
||||
MSL-OUT
|
||||
|
||||
- Fix `texture1d` accesses ([#1647](https://github.com/gfx-rs/naga/pull/1647)) **@jimblandy**
|
||||
- Fix data packing functions ([#1637](https://github.com/gfx-rs/naga/pull/1637)) **@phoekz**
|
||||
|
||||
|
||||
## v0.8.1 (2021-12-29)
|
||||
|
||||
API
|
||||
|
||||
- Make `WithSpan` clonable ([#1620](https://github.com/gfx-rs/naga/pull/1620)) **@jakobhellermann**
|
||||
|
||||
MSL-OUT
|
||||
|
||||
- Fix packed vec access ([#1634](https://github.com/gfx-rs/naga/pull/1634)) **@kvark**
|
||||
- Fix packed float support ([#1630](https://github.com/gfx-rs/naga/pull/1630)) **@kvark**
|
||||
|
||||
HLSL-OUT
|
||||
|
||||
- Support arrays of matrices ([#1629](https://github.com/gfx-rs/naga/pull/1629)) **@kvark**
|
||||
- Use `mad` instead of `fma` function ([#1580](https://github.com/gfx-rs/naga/pull/1580)) **@parasyte**
|
||||
|
||||
GLSL-OUT
|
||||
|
||||
- Fix conflicting names for globals ([#1616](https://github.com/gfx-rs/naga/pull/1616)) **@Gordon-F**
|
||||
- Fix `fma` function ([#1580](https://github.com/gfx-rs/naga/pull/1580)) **@parasyte**
|
||||
|
||||
|
||||
## v0.8 (2021-12-18)
|
||||
- development release for wgpu-0.12
|
||||
@ -26,7 +298,7 @@
|
||||
- MSL-out:
|
||||
- full out-of-bounds checking
|
||||
|
||||
### v0.7.3 (2021-12-14)
|
||||
## v0.7.3 (2021-12-14)
|
||||
- API:
|
||||
- `view_index` builtin
|
||||
- GLSL-out:
|
||||
@ -34,7 +306,7 @@
|
||||
- SPV-out:
|
||||
- fix incorrect pack/unpack
|
||||
|
||||
### v0.7.2 (2021-12-01)
|
||||
## v0.7.2 (2021-12-01)
|
||||
- validator:
|
||||
- check stores for proper pointer class
|
||||
- HLSL-out:
|
||||
@ -50,7 +322,7 @@
|
||||
- GLSL-in:
|
||||
- don't panic on invalid integer operations
|
||||
|
||||
### v0.7.1 (2021-10-12)
|
||||
## v0.7.1 (2021-10-12)
|
||||
- implement casts from and to booleans in the backends
|
||||
|
||||
## v0.7 (2021-10-07)
|
||||
@ -83,7 +355,7 @@
|
||||
- option to emit point size
|
||||
- option to clamp output depth
|
||||
|
||||
### v0.6.3 (2021-09-08)
|
||||
## v0.6.3 (2021-09-08)
|
||||
- Reduced heap allocations when generating WGSL, HLSL, and GLSL
|
||||
- WGSL-in:
|
||||
- support module-scope `let` type inference
|
||||
@ -96,7 +368,7 @@
|
||||
- SPV-out:
|
||||
- allow working around Adreno issue with `OpName`
|
||||
|
||||
### v0.6.2 (2021-09-01)
|
||||
## v0.6.2 (2021-09-01)
|
||||
- SPV-out fixes:
|
||||
- requested capabilities for 1D and cube images, storage formats
|
||||
- handling `break` and `continue` in a `switch` statement
|
||||
@ -110,7 +382,7 @@
|
||||
- GLSL-in fixes:
|
||||
- avoid infinite loop on invalid statements
|
||||
|
||||
### v0.6.1 (2021-08-24)
|
||||
## v0.6.1 (2021-08-24)
|
||||
- HLSL-out fixes:
|
||||
- array arguments
|
||||
- pointers to array arguments
|
||||
@ -165,7 +437,7 @@
|
||||
- multisampling on GLES
|
||||
- WGSL is vastly improved and now usable
|
||||
|
||||
### v0.4.2 (2021-05-28)
|
||||
## v0.4.2 (2021-05-28)
|
||||
- SPIR-V frontend:
|
||||
- fix image stores
|
||||
- fix matrix stride check
|
||||
@ -175,7 +447,7 @@
|
||||
- support sample interpolation
|
||||
- write out swizzled vector accesses
|
||||
|
||||
### v0.4.1 (2021-05-14)
|
||||
## v0.4.1 (2021-05-14)
|
||||
- numerous additions and improvements to SPIR-V frontend:
|
||||
- int8, in16, int64
|
||||
- null constant initializers for structs and matrices
|
||||
@ -218,7 +490,7 @@
|
||||
- `convert` example is transformed into the default binary target named `naga`
|
||||
- lots of frontend and backend fixes
|
||||
|
||||
### v0.3.2 (2021-02-15)
|
||||
## v0.3.2 (2021-02-15)
|
||||
- fix logical expression types
|
||||
- fix _FragDepth_ semantics
|
||||
- spv-in:
|
||||
@ -227,7 +499,7 @@
|
||||
- add lots of missing math functions
|
||||
- implement discard
|
||||
|
||||
### v0.3.1 (2021-01-31)
|
||||
## v0.3.1 (2021-01-31)
|
||||
- wgsl:
|
||||
- support constant array sizes
|
||||
- spv-out:
|
||||
|
5
third_party/rust/naga/Cargo.toml
vendored
5
third_party/rust/naga/Cargo.toml
vendored
@ -67,7 +67,10 @@ unicode-xid = { version = "0.2.3", optional = true }
|
||||
bincode = "1"
|
||||
criterion = { version = "0.3", features = [] }
|
||||
diff = "0.1"
|
||||
ron = "0.7"
|
||||
# Require at least version 0.7.1 of ron, this version changed how floating points are
|
||||
# serialized by forcing them to always have the decimal part, this makes it backwards
|
||||
# incompatible with our tests because we do a syntatic diff and not a semantic one.
|
||||
ron = "~0.7.1"
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
spirv = { version = "0.2", features = ["deserialize"] }
|
||||
rspirv = "0.11"
|
||||
|
42
third_party/rust/naga/Makefile
vendored
42
third_party/rust/naga/Makefile
vendored
@ -1,4 +1,4 @@
|
||||
.PHONY: all clean validate-spv validate-msl validate-glsl validate-dot validate-wgsl validate-hlsl
|
||||
.PHONY: all clean validate-spv validate-msl validate-glsl validate-dot validate-wgsl validate-hlsl-dxc validate-hlsl-fxc
|
||||
.SECONDARY: boids.metal quad.metal
|
||||
SNAPSHOTS_BASE_IN=tests/in
|
||||
SNAPSHOTS_BASE_OUT=tests/out
|
||||
@ -69,10 +69,10 @@ validate-wgsl: $(SNAPSHOTS_BASE_OUT)/wgsl/*.wgsl
|
||||
cargo run $${file}; \
|
||||
done
|
||||
|
||||
validate-hlsl: SHELL:=/bin/bash # required because config files uses arrays
|
||||
validate-hlsl: $(SNAPSHOTS_BASE_OUT)/hlsl/*.hlsl
|
||||
validate-hlsl-dxc: SHELL:=/bin/bash # required because config files uses arrays
|
||||
validate-hlsl-dxc: $(SNAPSHOTS_BASE_OUT)/hlsl/*.hlsl
|
||||
@set -e && for file in $^ ; do \
|
||||
DXC_PARAMS="-Wno-parentheses-equality -Zi -Qembed_debug"; \
|
||||
DXC_PARAMS="-Wno-parentheses-equality -Zi -Qembed_debug -Od"; \
|
||||
echo "Validating" $${file#"$(SNAPSHOTS_BASE_OUT)/"}; \
|
||||
config="$$(dirname $${file})/$$(basename $${file}).config"; \
|
||||
. $${config}; \
|
||||
@ -93,3 +93,37 @@ validate-hlsl: $(SNAPSHOTS_BASE_OUT)/hlsl/*.hlsl
|
||||
done; \
|
||||
echo "======================"; \
|
||||
done
|
||||
|
||||
validate-hlsl-fxc: SHELL:=/bin/bash # required because config files uses arrays
|
||||
validate-hlsl-fxc: $(SNAPSHOTS_BASE_OUT)/hlsl/*.hlsl
|
||||
@set -e && for file in $^ ; do \
|
||||
FXC_PARAMS="-Zi -Od"; \
|
||||
echo "Validating" $${file#"$(SNAPSHOTS_BASE_OUT)/"}; \
|
||||
config="$$(dirname $${file})/$$(basename $${file}).config"; \
|
||||
. $${config}; \
|
||||
for (( i=0; i<$${#vertex[@]}; i++ )); do \
|
||||
name=`echo $${vertex[i]} | cut -d \: -f 1`; \
|
||||
profile=`echo $${vertex[i]} | cut -d \: -f 2`; \
|
||||
sm=`echo $${profile} | cut -d \_ -f 2`; \
|
||||
if (( sm < 6 )); then \
|
||||
(set -x; fxc $${file} -T $${profile} -E $${name} $${FXC_PARAMS} > /dev/null); \
|
||||
fi \
|
||||
done; \
|
||||
for (( i=0; i<$${#fragment[@]}; i++ )); do \
|
||||
name=`echo $${fragment[i]} | cut -d \: -f 1`; \
|
||||
profile=`echo $${fragment[i]} | cut -d \: -f 2`; \
|
||||
sm=`echo $${profile} | cut -d \_ -f 2`; \
|
||||
if (( sm < 6 )); then \
|
||||
(set -x; fxc $${file} -T $${profile} -E $${name} $${FXC_PARAMS} > /dev/null); \
|
||||
fi \
|
||||
done; \
|
||||
for (( i=0; i<$${#compute[@]}; i++ )); do \
|
||||
name=`echo $${compute[i]} | cut -d \: -f 1`; \
|
||||
profile=`echo $${compute[i]} | cut -d \: -f 2`; \
|
||||
sm=`echo $${profile} | cut -d \_ -f 2`; \
|
||||
if (( sm < 6 )); then \
|
||||
(set -x; fxc $${file} -T $${profile} -E $${name} $${FXC_PARAMS} > /dev/null); \
|
||||
fi \
|
||||
done; \
|
||||
echo "======================"; \
|
||||
done
|
||||
|
6
third_party/rust/naga/README.md
vendored
6
third_party/rust/naga/README.md
vendored
@ -15,7 +15,7 @@ Front-end | Status | Feature | Notes |
|
||||
--------------- | ------------------ | ------- | ----- |
|
||||
SPIR-V (binary) | :white_check_mark: | spv-in | |
|
||||
WGSL | :white_check_mark: | wgsl-in | Fully validated |
|
||||
GLSL | :ok: | glsl-in | GLSL 440+ |
|
||||
GLSL | :ok: | glsl-in | GLSL 440+ and Vulkan semantics only |
|
||||
|
||||
Back-end | Status | Feature | Notes |
|
||||
--------------- | ------------------ | -------- | ----- |
|
||||
@ -81,5 +81,7 @@ make validate-msl # for Metal shaders, requires XCode command-line tools install
|
||||
make validate-glsl # for OpenGL shaders, requires GLSLang installed
|
||||
make validate-dot # for dot files, requires GraphViz installed
|
||||
make validate-wgsl # for WGSL shaders
|
||||
make validate-hlsl # for HLSL shaders. Note: this Make target makes use of the "sh" shell. This is not the default shell in Windows.
|
||||
make validate-hlsl-dxc # for HLSL shaders via DXC
|
||||
make validate-hlsl-fxc # for HLSL shaders via FXC
|
||||
# Note: HLSL Make targets make use of the "sh" shell. This is not the default shell in Windows.
|
||||
```
|
||||
|
1
third_party/rust/naga/benches/criterion.rs
vendored
1
third_party/rust/naga/benches/criterion.rs
vendored
@ -255,6 +255,7 @@ fn backends(c: &mut Criterion) {
|
||||
info,
|
||||
&options,
|
||||
&pipeline_options,
|
||||
naga::proc::BoundsCheckPolicies::default(),
|
||||
) {
|
||||
Ok(mut writer) => {
|
||||
let _ = writer.write(); // can error if unsupported
|
||||
|
4
third_party/rust/naga/src/back/dot/mod.rs
vendored
4
third_party/rust/naga/src/back/dot/mod.rs
vendored
@ -81,11 +81,15 @@ impl StatementGraph {
|
||||
S::Loop {
|
||||
ref body,
|
||||
ref continuing,
|
||||
break_if,
|
||||
} => {
|
||||
let body_id = self.add(body);
|
||||
self.flow.push((id, body_id, "body"));
|
||||
let continuing_id = self.add(continuing);
|
||||
self.flow.push((body_id, continuing_id, "continuing"));
|
||||
if let Some(expr) = break_if {
|
||||
self.dependencies.push((id, expr, "break if"));
|
||||
}
|
||||
"Loop"
|
||||
}
|
||||
S::Return { value } => {
|
||||
|
83
third_party/rust/naga/src/back/glsl/features.rs
vendored
83
third_party/rust/naga/src/back/glsl/features.rs
vendored
@ -38,6 +38,10 @@ bitflags::bitflags! {
|
||||
const FMA = 1 << 18;
|
||||
/// Texture samples query
|
||||
const TEXTURE_SAMPLES = 1 << 19;
|
||||
/// Texture levels query
|
||||
const TEXTURE_LEVELS = 1 << 20;
|
||||
/// Image size query
|
||||
const IMAGE_SIZE = 1 << 21;
|
||||
}
|
||||
}
|
||||
|
||||
@ -104,9 +108,11 @@ impl FeaturesManager {
|
||||
check_feature!(DYNAMIC_ARRAY_SIZE, 430, 310);
|
||||
check_feature!(MULTI_VIEW, 140, 310);
|
||||
// Only available on glsl core, this means that opengl es can't query the number
|
||||
// of samples in a image and neither do bound checks on the sample argument
|
||||
// of texelFecth
|
||||
// of samples nor levels in a image and neither do bound checks on the sample nor
|
||||
// the level argument of texelFecth
|
||||
check_feature!(TEXTURE_SAMPLES, 150);
|
||||
check_feature!(TEXTURE_LEVELS, 130);
|
||||
check_feature!(IMAGE_SIZE, 430, 310);
|
||||
|
||||
// Return an error if there are missing features
|
||||
if missing.is_empty() {
|
||||
@ -223,6 +229,11 @@ impl FeaturesManager {
|
||||
)?;
|
||||
}
|
||||
|
||||
if self.0.contains(Features::TEXTURE_LEVELS) && version < Version::Desktop(430) {
|
||||
// https://www.khronos.org/registry/OpenGL/extensions/ARB/ARB_texture_query_levels.txt
|
||||
writeln!(out, "#extension GL_ARB_texture_query_levels : require")?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@ -376,27 +387,75 @@ impl<'a, W> Writer<'a, W> {
|
||||
}
|
||||
}
|
||||
|
||||
// Loop trough all expressions in both functions and entry points
|
||||
// We will need to pass some of the members to a closure, so we need
|
||||
// to separate them otherwise the borrow checker will complain, this
|
||||
// shouldn't be needed in rust 2021
|
||||
let &mut Self {
|
||||
module,
|
||||
info,
|
||||
ref mut features,
|
||||
entry_point,
|
||||
entry_point_idx,
|
||||
ref policies,
|
||||
..
|
||||
} = self;
|
||||
|
||||
// Loop trough all expressions in both functions and the entry point
|
||||
// to check for needed features
|
||||
for (_, expr) in self
|
||||
.module
|
||||
for (expressions, info) in module
|
||||
.functions
|
||||
.iter()
|
||||
.flat_map(|(_, f)| f.expressions.iter())
|
||||
.chain(self.entry_point.function.expressions.iter())
|
||||
.map(|(h, f)| (&f.expressions, &info[h]))
|
||||
.chain(std::iter::once((
|
||||
&entry_point.function.expressions,
|
||||
info.get_entry_point(entry_point_idx as usize),
|
||||
)))
|
||||
{
|
||||
match *expr {
|
||||
for (_, expr) in expressions.iter() {
|
||||
match *expr {
|
||||
// Check for fused multiply add use
|
||||
Expression::Math { fun, .. } if fun == MathFunction::Fma => {
|
||||
self.features.request(Features::FMA)
|
||||
features.request(Features::FMA)
|
||||
}
|
||||
// Check for samples query
|
||||
// Check for queries that neeed aditonal features
|
||||
Expression::ImageQuery {
|
||||
query: crate::ImageQuery::NumSamples,
|
||||
image,
|
||||
query,
|
||||
..
|
||||
} => self.features.request(Features::TEXTURE_SAMPLES),
|
||||
} => match query {
|
||||
// Storage images use `imageSize` which is only available
|
||||
// in glsl > 420
|
||||
//
|
||||
// layers queries are also implemented as size queries
|
||||
crate::ImageQuery::Size { .. } | crate::ImageQuery::NumLayers => {
|
||||
if let TypeInner::Image {
|
||||
class: crate::ImageClass::Storage { .. }, ..
|
||||
} = *info[image].ty.inner_with(&module.types) {
|
||||
features.request(Features::IMAGE_SIZE)
|
||||
}
|
||||
},
|
||||
crate::ImageQuery::NumLevels => features.request(Features::TEXTURE_LEVELS),
|
||||
crate::ImageQuery::NumSamples => features.request(Features::TEXTURE_SAMPLES),
|
||||
}
|
||||
,
|
||||
// Check for image loads that needs bound checking on the sample
|
||||
// or level argument since this requires a feature
|
||||
Expression::ImageLoad {
|
||||
sample, level, ..
|
||||
} => {
|
||||
if policies.image != crate::proc::BoundsCheckPolicy::Unchecked {
|
||||
if sample.is_some() {
|
||||
features.request(Features::TEXTURE_SAMPLES)
|
||||
}
|
||||
|
||||
if level.is_some() {
|
||||
features.request(Features::TEXTURE_LEVELS)
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.features.check_availability(self.options.version)
|
||||
|
665
third_party/rust/naga/src/back/glsl/mod.rs
vendored
665
third_party/rust/naga/src/back/glsl/mod.rs
vendored
@ -12,7 +12,6 @@ to output a [`Module`](crate::Module) into glsl
|
||||
- 420
|
||||
- 430
|
||||
- 450
|
||||
- 460
|
||||
|
||||
### ES
|
||||
- 300
|
||||
@ -69,6 +68,10 @@ pub const SUPPORTED_CORE_VERSIONS: &[u16] = &[330, 400, 410, 420, 430, 440, 450]
|
||||
/// List of supported `es` GLSL versions.
|
||||
pub const SUPPORTED_ES_VERSIONS: &[u16] = &[300, 310, 320];
|
||||
|
||||
/// The suffix of the variable that will hold the calculated clamped level
|
||||
/// of detail for bounds checking in `ImageLoad`
|
||||
const CLAMPED_LOD_SUFFIX: &str = "_clamped_lod";
|
||||
|
||||
/// Mapping between resources and bindings.
|
||||
pub type BindingMap = std::collections::BTreeMap<crate::ResourceBinding, u8>;
|
||||
|
||||
@ -375,6 +378,8 @@ pub struct Writer<'a, W> {
|
||||
out: W,
|
||||
/// User defined configuration to be used.
|
||||
options: &'a Options,
|
||||
/// The bound checking policies to be used
|
||||
policies: proc::BoundsCheckPolicies,
|
||||
|
||||
// Internal State
|
||||
/// Features manager used to store all the needed features and write them.
|
||||
@ -410,6 +415,7 @@ impl<'a, W: Write> Writer<'a, W> {
|
||||
info: &'a valid::ModuleInfo,
|
||||
options: &'a Options,
|
||||
pipeline_options: &'a PipelineOptions,
|
||||
policies: proc::BoundsCheckPolicies,
|
||||
) -> Result<Self, Error> {
|
||||
// Check if the requested version is supported
|
||||
if !options.version.is_supported() {
|
||||
@ -437,6 +443,8 @@ impl<'a, W: Write> Writer<'a, W> {
|
||||
info,
|
||||
out,
|
||||
options,
|
||||
policies,
|
||||
|
||||
namer,
|
||||
features: FeaturesManager::new(),
|
||||
names,
|
||||
@ -1518,20 +1526,30 @@ impl<'a, W: Write> Writer<'a, W> {
|
||||
arg: Handle<crate::Expression>,
|
||||
arg1: Handle<crate::Expression>,
|
||||
size: usize,
|
||||
ctx: &back::FunctionCtx<'_>,
|
||||
) -> BackendResult {
|
||||
// Write parantheses around the dot product expression to prevent operators
|
||||
// with different precedences from applying earlier.
|
||||
write!(self.out, "(")?;
|
||||
|
||||
let arg0_name = &self.named_expressions[&arg];
|
||||
let arg1_name = &self.named_expressions[&arg1];
|
||||
|
||||
// This will print an extra '+' at the beginning but that is fine in glsl
|
||||
// Cycle trough all the components of the vector
|
||||
for index in 0..size {
|
||||
let component = back::COMPONENTS[index];
|
||||
write!(
|
||||
self.out,
|
||||
" + {}.{} * {}.{}",
|
||||
arg0_name, component, arg1_name, component
|
||||
)?;
|
||||
// Write the addition to the previous product
|
||||
// This will print an extra '+' at the beginning but that is fine in glsl
|
||||
write!(self.out, " + ")?;
|
||||
// Write the first vector expression, this expression is marked to be
|
||||
// cached so unless it can't be cached (for example, it's a Constant)
|
||||
// it shouldn't produce large expressions.
|
||||
self.write_expr(arg, ctx)?;
|
||||
// Access the current component on the first vector
|
||||
write!(self.out, ".{} * ", component)?;
|
||||
// Write the second vector expression, this expression is marked to be
|
||||
// cached so unless it can't be cached (for example, it's a Constant)
|
||||
// it shouldn't produce large expressions.
|
||||
self.write_expr(arg1, ctx)?;
|
||||
// Access the current component on the second vector
|
||||
write!(self.out, ".{}", component)?;
|
||||
}
|
||||
|
||||
write!(self.out, ")")?;
|
||||
@ -1635,6 +1653,27 @@ impl<'a, W: Write> Writer<'a, W> {
|
||||
None
|
||||
};
|
||||
|
||||
// If we are going to write an `ImageLoad` next and the target image
|
||||
// is sampled and we are using the `Restrict` policy for bounds
|
||||
// checking images we need to write a local holding the clamped lod.
|
||||
if let crate::Expression::ImageLoad {
|
||||
image,
|
||||
level: Some(level_expr),
|
||||
..
|
||||
} = ctx.expressions[handle]
|
||||
{
|
||||
if let TypeInner::Image {
|
||||
class: crate::ImageClass::Sampled { .. },
|
||||
..
|
||||
} = *ctx.info[image].ty.inner_with(&self.module.types)
|
||||
{
|
||||
if let proc::BoundsCheckPolicy::Restrict = self.policies.image {
|
||||
write!(self.out, "{}", level)?;
|
||||
self.write_clamped_lod(ctx, handle, image, level_expr)?
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(name) = expr_name {
|
||||
write!(self.out, "{}", level)?;
|
||||
self.write_named_expr(handle, name, ctx)?;
|
||||
@ -1761,16 +1800,26 @@ impl<'a, W: Write> Writer<'a, W> {
|
||||
Statement::Loop {
|
||||
ref body,
|
||||
ref continuing,
|
||||
break_if,
|
||||
} => {
|
||||
if !continuing.is_empty() {
|
||||
if !continuing.is_empty() || break_if.is_some() {
|
||||
let gate_name = self.namer.call("loop_init");
|
||||
writeln!(self.out, "{}bool {} = true;", level, gate_name)?;
|
||||
writeln!(self.out, "{}while(true) {{", level)?;
|
||||
writeln!(self.out, "{}if (!{}) {{", level.next(), gate_name)?;
|
||||
let l2 = level.next();
|
||||
let l3 = l2.next();
|
||||
writeln!(self.out, "{}if (!{}) {{", l2, gate_name)?;
|
||||
for sta in continuing {
|
||||
self.write_stmt(sta, ctx, level.next())?;
|
||||
self.write_stmt(sta, ctx, l3)?;
|
||||
}
|
||||
writeln!(self.out, "{}}}", level.next())?;
|
||||
if let Some(condition) = break_if {
|
||||
write!(self.out, "{}if (", l3)?;
|
||||
self.write_expr(condition, ctx)?;
|
||||
writeln!(self.out, ") {{")?;
|
||||
writeln!(self.out, "{}break;", l3.next())?;
|
||||
writeln!(self.out, "{}}}", l3)?;
|
||||
}
|
||||
writeln!(self.out, "{}}}", l2)?;
|
||||
writeln!(self.out, "{}{} = false;", level.next(), gate_name)?;
|
||||
} else {
|
||||
writeln!(self.out, "{}while(true) {{", level)?;
|
||||
@ -1933,19 +1982,7 @@ impl<'a, W: Write> Writer<'a, W> {
|
||||
value,
|
||||
} => {
|
||||
write!(self.out, "{}", level)?;
|
||||
// This will only panic if the module is invalid
|
||||
let dim = match *ctx.info[image].ty.inner_with(&self.module.types) {
|
||||
TypeInner::Image { dim, .. } => dim,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
write!(self.out, "imageStore(")?;
|
||||
self.write_expr(image, ctx)?;
|
||||
write!(self.out, ", ")?;
|
||||
self.write_texture_coordinates(coordinate, array_index, dim, ctx)?;
|
||||
write!(self.out, ", ")?;
|
||||
self.write_expr(value, ctx)?;
|
||||
writeln!(self.out, ");")?;
|
||||
self.write_image_store(ctx, image, coordinate, array_index, value)?
|
||||
}
|
||||
// A `Call` is written `name(arguments)` where `arguments` is a comma separated expressions list
|
||||
Statement::Call {
|
||||
@ -2320,51 +2357,13 @@ impl<'a, W: Write> Writer<'a, W> {
|
||||
// End the function
|
||||
write!(self.out, ")")?
|
||||
}
|
||||
// `ImageLoad` is also a bit complicated.
|
||||
// There are two functions one for sampled
|
||||
// images another for storage images, the former uses `texelFetch` and the latter uses
|
||||
// `imageLoad`.
|
||||
// Furthermore we have `index` which is always `Some` for sampled images
|
||||
// and `None` for storage images, so we end up with two functions:
|
||||
// `texelFetch(image, coordinate, index)` - for sampled images
|
||||
// `imageLoad(image, coordinate)` - for storage images
|
||||
Expression::ImageLoad {
|
||||
image,
|
||||
coordinate,
|
||||
array_index,
|
||||
sample,
|
||||
level,
|
||||
} => {
|
||||
// This will only panic if the module is invalid
|
||||
let (dim, class) = match *ctx.info[image].ty.inner_with(&self.module.types) {
|
||||
TypeInner::Image {
|
||||
dim,
|
||||
arrayed: _,
|
||||
class,
|
||||
} => (dim, class),
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
let fun_name = match class {
|
||||
crate::ImageClass::Sampled { .. } => "texelFetch",
|
||||
crate::ImageClass::Storage { .. } => "imageLoad",
|
||||
// TODO: Is there even a function for this?
|
||||
crate::ImageClass::Depth { multi: _ } => {
|
||||
return Err(Error::Custom("TODO: depth sample loads".to_string()))
|
||||
}
|
||||
};
|
||||
|
||||
write!(self.out, "{}(", fun_name)?;
|
||||
self.write_expr(image, ctx)?;
|
||||
write!(self.out, ", ")?;
|
||||
self.write_texture_coordinates(coordinate, array_index, dim, ctx)?;
|
||||
|
||||
if let Some(sample_or_level) = sample.or(level) {
|
||||
write!(self.out, ", ")?;
|
||||
self.write_expr(sample_or_level, ctx)?;
|
||||
}
|
||||
write!(self.out, ")")?;
|
||||
}
|
||||
} => self.write_image_load(expr, ctx, image, coordinate, array_index, sample, level)?,
|
||||
// Query translates into one of the:
|
||||
// - textureSize/imageSize
|
||||
// - textureQueryLevels
|
||||
@ -2747,7 +2746,7 @@ impl<'a, W: Write> Writer<'a, W> {
|
||||
..
|
||||
} => "dot",
|
||||
crate::TypeInner::Vector { size, .. } => {
|
||||
return self.write_dot_product(arg, arg1.unwrap(), size as usize)
|
||||
return self.write_dot_product(arg, arg1.unwrap(), size as usize, ctx)
|
||||
}
|
||||
_ => unreachable!(
|
||||
"Correct TypeInner for dot product should be already validated"
|
||||
@ -2819,32 +2818,59 @@ impl<'a, W: Write> Writer<'a, W> {
|
||||
let extract_bits = fun == Mf::ExtractBits;
|
||||
let insert_bits = fun == Mf::InsertBits;
|
||||
|
||||
// we might need to cast to unsigned integers since
|
||||
// GLSL's findLSB / findMSB always return signed integers
|
||||
let need_extra_paren = {
|
||||
(fun == Mf::FindLsb || fun == Mf::FindMsb || fun == Mf::CountOneBits)
|
||||
&& match *ctx.info[arg].ty.inner_with(&self.module.types) {
|
||||
crate::TypeInner::Scalar {
|
||||
kind: crate::ScalarKind::Uint,
|
||||
..
|
||||
} => {
|
||||
write!(self.out, "uint(")?;
|
||||
true
|
||||
}
|
||||
crate::TypeInner::Vector {
|
||||
kind: crate::ScalarKind::Uint,
|
||||
size,
|
||||
..
|
||||
} => {
|
||||
write!(self.out, "uvec{}(", size as u8)?;
|
||||
true
|
||||
}
|
||||
_ => false,
|
||||
}
|
||||
// Some GLSL functions always return signed integers (like findMSB),
|
||||
// so they need to be cast to uint if the argument is also an uint.
|
||||
let ret_might_need_int_to_uint =
|
||||
matches!(fun, Mf::FindLsb | Mf::FindMsb | Mf::CountOneBits | Mf::Abs);
|
||||
|
||||
// Some GLSL functions only accept signed integers (like abs),
|
||||
// so they need their argument cast from uint to int.
|
||||
let arg_might_need_uint_to_int = matches!(fun, Mf::Abs);
|
||||
|
||||
// Check if the argument is an unsigned integer and return the vector size
|
||||
// in case it's a vector
|
||||
let maybe_uint_size = match *ctx.info[arg].ty.inner_with(&self.module.types) {
|
||||
crate::TypeInner::Scalar {
|
||||
kind: crate::ScalarKind::Uint,
|
||||
..
|
||||
} => Some(None),
|
||||
crate::TypeInner::Vector {
|
||||
kind: crate::ScalarKind::Uint,
|
||||
size,
|
||||
..
|
||||
} => Some(Some(size)),
|
||||
_ => None,
|
||||
};
|
||||
|
||||
// Cast to uint if the function needs it
|
||||
if ret_might_need_int_to_uint {
|
||||
if let Some(maybe_size) = maybe_uint_size {
|
||||
match maybe_size {
|
||||
Some(size) => write!(self.out, "uvec{}(", size as u8)?,
|
||||
None => write!(self.out, "uint(")?,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
write!(self.out, "{}(", fun_name)?;
|
||||
|
||||
// Cast to int if the function needs it
|
||||
if arg_might_need_uint_to_int {
|
||||
if let Some(maybe_size) = maybe_uint_size {
|
||||
match maybe_size {
|
||||
Some(size) => write!(self.out, "ivec{}(", size as u8)?,
|
||||
None => write!(self.out, "int(")?,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self.write_expr(arg, ctx)?;
|
||||
|
||||
// Close the cast from uint to int
|
||||
if arg_might_need_uint_to_int && maybe_uint_size.is_some() {
|
||||
write!(self.out, ")")?
|
||||
}
|
||||
|
||||
if let Some(arg) = arg1 {
|
||||
write!(self.out, ", ")?;
|
||||
if extract_bits {
|
||||
@ -2877,7 +2903,8 @@ impl<'a, W: Write> Writer<'a, W> {
|
||||
}
|
||||
write!(self.out, ")")?;
|
||||
|
||||
if need_extra_paren {
|
||||
// Close the cast from int to uint
|
||||
if ret_might_need_int_to_uint && maybe_uint_size.is_some() {
|
||||
write!(self.out, ")")?
|
||||
}
|
||||
}
|
||||
@ -2913,38 +2940,50 @@ impl<'a, W: Write> Writer<'a, W> {
|
||||
None => {
|
||||
use crate::ScalarKind as Sk;
|
||||
|
||||
let source_kind = inner.scalar_kind().unwrap();
|
||||
let conv_op = match (source_kind, target_kind) {
|
||||
(Sk::Float, Sk::Sint) => "floatBitsToInt",
|
||||
(Sk::Float, Sk::Uint) => "floatBitsToUint",
|
||||
(Sk::Sint, Sk::Float) => "intBitsToFloat",
|
||||
(Sk::Uint, Sk::Float) => "uintBitsToFloat",
|
||||
// There is no way to bitcast between Uint/Sint in glsl. Use constructor conversion
|
||||
(Sk::Uint, Sk::Sint) => "int",
|
||||
(Sk::Sint, Sk::Uint) => "uint",
|
||||
|
||||
(Sk::Bool, Sk::Sint) => "int",
|
||||
(Sk::Bool, Sk::Uint) => "uint",
|
||||
(Sk::Bool, Sk::Float) => "float",
|
||||
|
||||
(Sk::Sint, Sk::Bool) => "bool",
|
||||
(Sk::Uint, Sk::Bool) => "bool",
|
||||
(Sk::Float, Sk::Bool) => "bool",
|
||||
|
||||
// No conversion needed
|
||||
(Sk::Sint, Sk::Sint) => "",
|
||||
(Sk::Uint, Sk::Uint) => "",
|
||||
(Sk::Float, Sk::Float) => "",
|
||||
(Sk::Bool, Sk::Bool) => "",
|
||||
let target_vector_type = match *inner {
|
||||
TypeInner::Vector { size, width, .. } => Some(TypeInner::Vector {
|
||||
size,
|
||||
width,
|
||||
kind: target_kind,
|
||||
}),
|
||||
_ => None,
|
||||
};
|
||||
write!(self.out, "{}", conv_op)?;
|
||||
if !conv_op.is_empty() {
|
||||
write!(self.out, "(")?;
|
||||
}
|
||||
|
||||
let source_kind = inner.scalar_kind().unwrap();
|
||||
|
||||
match (source_kind, target_kind, target_vector_type) {
|
||||
// No conversion needed
|
||||
(Sk::Sint, Sk::Sint, _)
|
||||
| (Sk::Uint, Sk::Uint, _)
|
||||
| (Sk::Float, Sk::Float, _)
|
||||
| (Sk::Bool, Sk::Bool, _) => {
|
||||
self.write_expr(expr, ctx)?;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
// Cast to/from floats
|
||||
(Sk::Float, Sk::Sint, _) => write!(self.out, "floatBitsToInt")?,
|
||||
(Sk::Float, Sk::Uint, _) => write!(self.out, "floatBitsToUint")?,
|
||||
(Sk::Sint, Sk::Float, _) => write!(self.out, "intBitsToFloat")?,
|
||||
(Sk::Uint, Sk::Float, _) => write!(self.out, "uintBitsToFloat")?,
|
||||
|
||||
// Cast between vector types
|
||||
(_, _, Some(vector)) => {
|
||||
self.write_value_type(&vector)?;
|
||||
}
|
||||
|
||||
// There is no way to bitcast between Uint/Sint in glsl. Use constructor conversion
|
||||
(Sk::Uint | Sk::Bool, Sk::Sint, None) => write!(self.out, "int")?,
|
||||
(Sk::Sint | Sk::Bool, Sk::Uint, None) => write!(self.out, "uint")?,
|
||||
(Sk::Bool, Sk::Float, None) => write!(self.out, "float")?,
|
||||
(Sk::Sint | Sk::Uint | Sk::Float, Sk::Bool, None) => {
|
||||
write!(self.out, "bool")?
|
||||
}
|
||||
};
|
||||
|
||||
write!(self.out, "(")?;
|
||||
self.write_expr(expr, ctx)?;
|
||||
if !conv_op.is_empty() {
|
||||
write!(self.out, ")")?
|
||||
}
|
||||
write!(self.out, ")")?;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2961,25 +3000,71 @@ impl<'a, W: Write> Writer<'a, W> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn write_texture_coordinates(
|
||||
/// Helper function to write the local holding the clamped lod
|
||||
fn write_clamped_lod(
|
||||
&mut self,
|
||||
ctx: &back::FunctionCtx,
|
||||
expr: Handle<crate::Expression>,
|
||||
image: Handle<crate::Expression>,
|
||||
level_expr: Handle<crate::Expression>,
|
||||
) -> Result<(), Error> {
|
||||
// Define our local and start a call to `clamp`
|
||||
write!(
|
||||
self.out,
|
||||
"int {}{}{} = clamp(",
|
||||
back::BAKE_PREFIX,
|
||||
expr.index(),
|
||||
CLAMPED_LOD_SUFFIX
|
||||
)?;
|
||||
// Write the lod that will be clamped
|
||||
self.write_expr(level_expr, ctx)?;
|
||||
// Set the min value to 0 and start a call to `textureQueryLevels` to get
|
||||
// the maximum value
|
||||
write!(self.out, ", 0, textureQueryLevels(")?;
|
||||
// Write the target image as an argument to `textureQueryLevels`
|
||||
self.write_expr(image, ctx)?;
|
||||
// Close the call to `textureQueryLevels` subtract 1 from it since
|
||||
// the lod argument is 0 based, close the `clamp` call and end the
|
||||
// local declaration statement.
|
||||
writeln!(self.out, ") - 1);")?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// Helper method used to retrieve how many elements a coordinate vector
|
||||
// for the images operations need.
|
||||
fn get_coordinate_vector_size(&self, dim: crate::ImageDimension, arrayed: bool) -> u8 {
|
||||
// openGL es doesn't have 1D images so we need workaround it
|
||||
let tex_1d_hack = dim == crate::ImageDimension::D1 && self.options.version.is_es();
|
||||
// Get how many components the coordinate vector needs for the dimensions only
|
||||
let tex_coord_size = match dim {
|
||||
crate::ImageDimension::D1 => 1,
|
||||
crate::ImageDimension::D2 => 2,
|
||||
crate::ImageDimension::D3 => 3,
|
||||
crate::ImageDimension::Cube => 2,
|
||||
};
|
||||
// Calculate the true size of the coordinate vector by adding 1 for arrayed images
|
||||
// and another 1 if we need to workaround 1D images by making them 2D
|
||||
tex_coord_size + tex_1d_hack as u8 + arrayed as u8
|
||||
}
|
||||
|
||||
/// Helper method to write the coordinate vector for image operations
|
||||
fn write_texture_coord(
|
||||
&mut self,
|
||||
ctx: &back::FunctionCtx,
|
||||
vector_size: u8,
|
||||
coordinate: Handle<crate::Expression>,
|
||||
array_index: Option<Handle<crate::Expression>>,
|
||||
dim: crate::ImageDimension,
|
||||
ctx: &back::FunctionCtx,
|
||||
// Emulate 1D images as 2D for profiles that don't support it (glsl es)
|
||||
tex_1d_hack: bool,
|
||||
) -> Result<(), Error> {
|
||||
use crate::ImageDimension as IDim;
|
||||
|
||||
let tex_1d_hack = dim == IDim::D1 && self.options.version.is_es();
|
||||
match array_index {
|
||||
// If the image needs an array indice we need to add it to the end of our
|
||||
// coordinate vector, to do so we will use the `ivec(ivec, scalar)`
|
||||
// constructor notation (NOTE: the inner `ivec` can also be a scalar, this
|
||||
// is important for 1D arrayed images).
|
||||
Some(layer_expr) => {
|
||||
let tex_coord_size = match dim {
|
||||
IDim::D1 => 2,
|
||||
IDim::D2 => 3,
|
||||
IDim::D3 => 4,
|
||||
IDim::Cube => 4,
|
||||
};
|
||||
write!(self.out, "ivec{}(", tex_coord_size + tex_1d_hack as u8)?;
|
||||
write!(self.out, "ivec{}(", vector_size)?;
|
||||
self.write_expr(coordinate, ctx)?;
|
||||
write!(self.out, ", ")?;
|
||||
// If we are replacing sampler1D with sampler2D we also need
|
||||
@ -2990,16 +3075,326 @@ impl<'a, W: Write> Writer<'a, W> {
|
||||
self.write_expr(layer_expr, ctx)?;
|
||||
write!(self.out, ")")?;
|
||||
}
|
||||
// Otherwise write just the expression (and the 1D hack if needed)
|
||||
None => {
|
||||
if tex_1d_hack {
|
||||
write!(self.out, "ivec2(")?;
|
||||
}
|
||||
self.write_expr(coordinate, ctx)?;
|
||||
if tex_1d_hack {
|
||||
write!(self.out, ", 0.0)")?;
|
||||
write!(self.out, ", 0)")?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Helper method to write the `ImageStore` statement
|
||||
fn write_image_store(
|
||||
&mut self,
|
||||
ctx: &back::FunctionCtx,
|
||||
image: Handle<crate::Expression>,
|
||||
coordinate: Handle<crate::Expression>,
|
||||
array_index: Option<Handle<crate::Expression>>,
|
||||
value: Handle<crate::Expression>,
|
||||
) -> Result<(), Error> {
|
||||
use crate::ImageDimension as IDim;
|
||||
|
||||
// NOTE: openGL requires that `imageStore`s have no effets when the texel is invalid
|
||||
// so we don't need to generate bounds checks (OpenGL 4.2 Core §3.9.20)
|
||||
|
||||
// This will only panic if the module is invalid
|
||||
let dim = match *ctx.info[image].ty.inner_with(&self.module.types) {
|
||||
TypeInner::Image { dim, .. } => dim,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
// Begin our call to `imageStore`
|
||||
write!(self.out, "imageStore(")?;
|
||||
self.write_expr(image, ctx)?;
|
||||
// Separate the image argument from the coordinates
|
||||
write!(self.out, ", ")?;
|
||||
|
||||
// openGL es doesn't have 1D images so we need workaround it
|
||||
let tex_1d_hack = dim == IDim::D1 && self.options.version.is_es();
|
||||
// Write the coordinate vector
|
||||
self.write_texture_coord(
|
||||
ctx,
|
||||
// Get the size of the coordinate vector
|
||||
self.get_coordinate_vector_size(dim, array_index.is_some()),
|
||||
coordinate,
|
||||
array_index,
|
||||
tex_1d_hack,
|
||||
)?;
|
||||
|
||||
// Separate the coordinate from the value to write and write the expression
|
||||
// of the value to write.
|
||||
write!(self.out, ", ")?;
|
||||
self.write_expr(value, ctx)?;
|
||||
// End the call to `imageStore` and the statement.
|
||||
writeln!(self.out, ");")?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Helper method for writing an `ImageLoad` expression.
|
||||
#[allow(clippy::too_many_arguments)]
|
||||
fn write_image_load(
|
||||
&mut self,
|
||||
handle: Handle<crate::Expression>,
|
||||
ctx: &back::FunctionCtx,
|
||||
image: Handle<crate::Expression>,
|
||||
coordinate: Handle<crate::Expression>,
|
||||
array_index: Option<Handle<crate::Expression>>,
|
||||
sample: Option<Handle<crate::Expression>>,
|
||||
level: Option<Handle<crate::Expression>>,
|
||||
) -> Result<(), Error> {
|
||||
use crate::ImageDimension as IDim;
|
||||
|
||||
// `ImageLoad` is a bit complicated.
|
||||
// There are two functions one for sampled
|
||||
// images another for storage images, the former uses `texelFetch` and the
|
||||
// latter uses `imageLoad`.
|
||||
//
|
||||
// Furthermore we have `level` which is always `Some` for sampled images
|
||||
// and `None` for storage images, so we end up with two functions:
|
||||
// - `texelFetch(image, coordinate, level)` for sampled images
|
||||
// - `imageLoad(image, coordinate)` for storage images
|
||||
//
|
||||
// Finally we also have to consider bounds checking, for storage images
|
||||
// this is easy since openGL requires that invalid texels always return
|
||||
// 0, for sampled images we need to either verify that all arguments are
|
||||
// in bounds (`ReadZeroSkipWrite`) or make them a valid texel (`Restrict`).
|
||||
|
||||
// This will only panic if the module is invalid
|
||||
let (dim, class) = match *ctx.info[image].ty.inner_with(&self.module.types) {
|
||||
TypeInner::Image {
|
||||
dim,
|
||||
arrayed: _,
|
||||
class,
|
||||
} => (dim, class),
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
// Get the name of the function to be used for the load operation
|
||||
// and the policy to be used with it.
|
||||
let (fun_name, policy) = match class {
|
||||
// Sampled images inherit the policy from the user passed policies
|
||||
crate::ImageClass::Sampled { .. } => ("texelFetch", self.policies.image),
|
||||
crate::ImageClass::Storage { .. } => {
|
||||
// OpenGL 4.2 Core §3.9.20 defines that out of bounds texels in `imageLoad`s
|
||||
// always return zero values so we don't need to generate bounds checks
|
||||
("imageLoad", proc::BoundsCheckPolicy::Unchecked)
|
||||
}
|
||||
// TODO: Is there even a function for this?
|
||||
crate::ImageClass::Depth { multi: _ } => {
|
||||
return Err(Error::Custom(
|
||||
"WGSL `textureLoad` from depth textures is not supported in GLSL".to_string(),
|
||||
))
|
||||
}
|
||||
};
|
||||
|
||||
// openGL es doesn't have 1D images so we need workaround it
|
||||
let tex_1d_hack = dim == IDim::D1 && self.options.version.is_es();
|
||||
// Get the size of the coordinate vector
|
||||
let vector_size = self.get_coordinate_vector_size(dim, array_index.is_some());
|
||||
|
||||
if let proc::BoundsCheckPolicy::ReadZeroSkipWrite = policy {
|
||||
// To write the bounds checks for `ReadZeroSkipWrite` we will use a
|
||||
// ternary operator since we are in the middle of an expression and
|
||||
// need to return a value.
|
||||
//
|
||||
// NOTE: glsl does short circuit when evaluating logical
|
||||
// expressions so we can be sure that after we test a
|
||||
// condition it will be true for the next ones
|
||||
|
||||
// Write parantheses around the ternary operator to prevent problems with
|
||||
// expressions emitted before or after it having more precedence
|
||||
write!(self.out, "(",)?;
|
||||
|
||||
// The lod check needs to precede the size check since we need
|
||||
// to use the lod to get the size of the image at that level.
|
||||
if let Some(level_expr) = level {
|
||||
self.write_expr(level_expr, ctx)?;
|
||||
write!(self.out, " < textureQueryLevels(",)?;
|
||||
self.write_expr(image, ctx)?;
|
||||
// Chain the next check
|
||||
write!(self.out, ") && ")?;
|
||||
}
|
||||
|
||||
// Check that the sample arguments doesn't exceed the number of samples
|
||||
if let Some(sample_expr) = sample {
|
||||
self.write_expr(sample_expr, ctx)?;
|
||||
write!(self.out, " < textureSamples(",)?;
|
||||
self.write_expr(image, ctx)?;
|
||||
// Chain the next check
|
||||
write!(self.out, ") && ")?;
|
||||
}
|
||||
|
||||
// We now need to write the size checks for the coordinates and array index
|
||||
// first we write the comparation function in case the image is 1D non arrayed
|
||||
// (and no 1D to 2D hack was needed) we are comparing scalars so the less than
|
||||
// operator will suffice, but otherwise we'll be comparing two vectors so we'll
|
||||
// need to use the `lessThan` function but it returns a vector of booleans (one
|
||||
// for each comparison) so we need to fold it all in one scalar boolean, since
|
||||
// we want all comparisons to pass we use the `all` function which will only
|
||||
// return `true` if all the elements of the boolean vector are also `true`.
|
||||
//
|
||||
// So we'll end with one of the following forms
|
||||
// - `coord < textureSize(image, lod)` for 1D images
|
||||
// - `all(lessThan(coord, textureSize(image, lod)))` for normal images
|
||||
// - `all(lessThan(ivec(coord, array_index), textureSize(image, lod)))`
|
||||
// for arrayed images
|
||||
// - `all(lessThan(coord, textureSize(image)))` for multi sampled images
|
||||
|
||||
if vector_size != 1 {
|
||||
write!(self.out, "all(lessThan(")?;
|
||||
}
|
||||
|
||||
// Write the coordinate vector
|
||||
self.write_texture_coord(ctx, vector_size, coordinate, array_index, tex_1d_hack)?;
|
||||
|
||||
if vector_size != 1 {
|
||||
// If we used the `lessThan` function we need to separate the
|
||||
// coordinates from the image size.
|
||||
write!(self.out, ", ")?;
|
||||
} else {
|
||||
// If we didn't use it (ie. 1D images) we perform the comparsion
|
||||
// using the less than operator.
|
||||
write!(self.out, " < ")?;
|
||||
}
|
||||
|
||||
// Call `textureSize` to get our image size
|
||||
write!(self.out, "textureSize(")?;
|
||||
self.write_expr(image, ctx)?;
|
||||
// `textureSize` uses the lod as a second argument for mipmapped images
|
||||
if let Some(level_expr) = level {
|
||||
// Separate the image from the lod
|
||||
write!(self.out, ", ")?;
|
||||
self.write_expr(level_expr, ctx)?;
|
||||
}
|
||||
// Close the `textureSize` call
|
||||
write!(self.out, ")")?;
|
||||
|
||||
if vector_size != 1 {
|
||||
// Close the `all` and `lessThan` calls
|
||||
write!(self.out, "))")?;
|
||||
}
|
||||
|
||||
// Finally end the condition part of the ternary operator
|
||||
write!(self.out, " ? ")?;
|
||||
}
|
||||
|
||||
// Begin the call to the function used to load the texel
|
||||
write!(self.out, "{}(", fun_name)?;
|
||||
self.write_expr(image, ctx)?;
|
||||
write!(self.out, ", ")?;
|
||||
|
||||
// If we are using `Restrict` bounds checking we need to pass valid texel
|
||||
// coordinates, to do so we use the `clamp` function to get a value between
|
||||
// 0 and the image size - 1 (indexing begins at 0)
|
||||
if let proc::BoundsCheckPolicy::Restrict = policy {
|
||||
write!(self.out, "clamp(")?;
|
||||
}
|
||||
|
||||
// Write the coordinate vector
|
||||
self.write_texture_coord(ctx, vector_size, coordinate, array_index, tex_1d_hack)?;
|
||||
|
||||
// If we are using `Restrict` bounds checking we need to write the rest of the
|
||||
// clamp we initiated before writing the coordinates.
|
||||
if let proc::BoundsCheckPolicy::Restrict = policy {
|
||||
// Write the min value 0
|
||||
if vector_size == 1 {
|
||||
write!(self.out, ", 0")?;
|
||||
} else {
|
||||
write!(self.out, ", ivec{}(0)", vector_size)?;
|
||||
}
|
||||
// Start the `textureSize` call to use as the max value.
|
||||
write!(self.out, ", textureSize(")?;
|
||||
self.write_expr(image, ctx)?;
|
||||
// If the image is mipmapped we need to add the lod argument to the
|
||||
// `textureSize` call, but this needs to be the clamped lod, this should
|
||||
// have been generated earlier and put in a local.
|
||||
if class.is_mipmapped() {
|
||||
write!(
|
||||
self.out,
|
||||
", {}{}{}",
|
||||
back::BAKE_PREFIX,
|
||||
handle.index(),
|
||||
CLAMPED_LOD_SUFFIX
|
||||
)?;
|
||||
}
|
||||
// Close the `textureSize` call
|
||||
write!(self.out, ")")?;
|
||||
|
||||
// Subtract 1 from the `textureSize` call since the coordinates are zero based.
|
||||
if vector_size == 1 {
|
||||
write!(self.out, " - 1")?;
|
||||
} else {
|
||||
write!(self.out, " - ivec{}(1)", vector_size)?;
|
||||
}
|
||||
|
||||
// Close the `clamp` call
|
||||
write!(self.out, ")")?;
|
||||
|
||||
// Add the clamped lod (if present) as the second argument to the
|
||||
// image load function.
|
||||
if level.is_some() {
|
||||
write!(
|
||||
self.out,
|
||||
", {}{}{}",
|
||||
back::BAKE_PREFIX,
|
||||
handle.index(),
|
||||
CLAMPED_LOD_SUFFIX
|
||||
)?;
|
||||
}
|
||||
|
||||
// If a sample argument is needed we need to clamp it between 0 and
|
||||
// the number of samples the image has.
|
||||
if let Some(sample_expr) = sample {
|
||||
write!(self.out, ", clamp(")?;
|
||||
self.write_expr(sample_expr, ctx)?;
|
||||
// Set the min value to 0 and start the call to `textureSamples`
|
||||
write!(self.out, ", 0, textureSamples(")?;
|
||||
self.write_expr(image, ctx)?;
|
||||
// Close the `textureSamples` call, subtract 1 from it since the sample
|
||||
// argument is zero based, and close the `clamp` call
|
||||
writeln!(self.out, ") - 1)")?;
|
||||
}
|
||||
} else if let Some(sample_or_level) = sample.or(level) {
|
||||
// If no bounds checking is need just add the sample or level argument
|
||||
// after the coordinates
|
||||
write!(self.out, ", ")?;
|
||||
self.write_expr(sample_or_level, ctx)?;
|
||||
}
|
||||
|
||||
// Close the image load function.
|
||||
write!(self.out, ")")?;
|
||||
|
||||
// If we were using the `ReadZeroSkipWrite` policy we need to end the first branch
|
||||
// (which is taken if the condition is `true`) with a colon (`:`) and write the
|
||||
// second branch which is just a 0 value.
|
||||
if let proc::BoundsCheckPolicy::ReadZeroSkipWrite = policy {
|
||||
// Get the kind of the output value.
|
||||
let kind = match class {
|
||||
// Only sampled images can reach here since storage images
|
||||
// don't need bounds checks and depth images aren't implmented
|
||||
crate::ImageClass::Sampled { kind, .. } => kind,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
// End the first branch
|
||||
write!(self.out, " : ")?;
|
||||
// Write the 0 value
|
||||
write!(self.out, "{}vec4(", glsl_scalar(kind, 4)?.prefix,)?;
|
||||
self.write_zero_init_scalar(kind)?;
|
||||
// Close the zero value constructor
|
||||
write!(self.out, ")")?;
|
||||
// Close the parantheses surrounding our ternary
|
||||
write!(self.out, ")")?;
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@ -3347,7 +3742,7 @@ const fn glsl_storage_format(format: crate::StorageFormat) -> &'static str {
|
||||
Sf::Rg16Uint => "rg16ui",
|
||||
Sf::Rg16Sint => "rg16i",
|
||||
Sf::Rg16Float => "rg16f",
|
||||
Sf::Rgba8Unorm => "rgba8ui",
|
||||
Sf::Rgba8Unorm => "rgba8",
|
||||
Sf::Rgba8Snorm => "rgba8_snorm",
|
||||
Sf::Rgba8Uint => "rgba8ui",
|
||||
Sf::Rgba8Sint => "rgba8i",
|
||||
|
5
third_party/rust/naga/src/back/hlsl/conv.rs
vendored
5
third_party/rust/naga/src/back/hlsl/conv.rs
vendored
@ -1,5 +1,7 @@
|
||||
use std::borrow::Cow;
|
||||
|
||||
use crate::proc::Alignment;
|
||||
|
||||
use super::Error;
|
||||
|
||||
impl crate::ScalarKind {
|
||||
@ -49,8 +51,7 @@ impl crate::TypeInner {
|
||||
rows,
|
||||
width,
|
||||
} => {
|
||||
let aligned_rows = if rows > crate::VectorSize::Bi { 4 } else { 2 };
|
||||
let stride = aligned_rows * width as u32;
|
||||
let stride = Alignment::from(rows) * width as u32;
|
||||
let last_row_size = rows as u32 * width as u32;
|
||||
((columns as u32 - 1) * stride) + last_row_size
|
||||
}
|
||||
|
158
third_party/rust/naga/src/back/hlsl/help.rs
vendored
158
third_party/rust/naga/src/back/hlsl/help.rs
vendored
@ -54,6 +54,11 @@ pub(super) struct WrappedStructMatrixAccess {
|
||||
pub(super) index: u32,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, Hash, Eq, Ord, PartialEq, PartialOrd)]
|
||||
pub(super) struct WrappedMatCx2 {
|
||||
pub(super) columns: crate::VectorSize,
|
||||
}
|
||||
|
||||
/// HLSL backend requires its own `ImageQuery` enum.
|
||||
///
|
||||
/// It is used inside `WrappedImageQuery` and should be unique per ImageQuery function.
|
||||
@ -461,12 +466,36 @@ impl<'a, W: Write> super::Writer<'a, W> {
|
||||
)?;
|
||||
}
|
||||
}
|
||||
_ => {
|
||||
writeln!(
|
||||
self.out,
|
||||
"{}{}.{} = {}{};",
|
||||
INDENT, RETURN_VARIABLE_NAME, field_name, ARGUMENT_VARIABLE_NAME, i,
|
||||
)?;
|
||||
ref other => {
|
||||
// We cast arrays of native HLSL `floatCx2`s to arrays of `matCx2`s
|
||||
// (where the inner matrix is represented by a struct with C `float2` members).
|
||||
// See the module-level block comment in mod.rs for details.
|
||||
if let Some(super::writer::MatrixType {
|
||||
columns,
|
||||
rows: crate::VectorSize::Bi,
|
||||
width: 4,
|
||||
}) = super::writer::get_inner_matrix_data(module, member.ty)
|
||||
{
|
||||
write!(
|
||||
self.out,
|
||||
"{}{}.{} = (__mat{}x2",
|
||||
INDENT, RETURN_VARIABLE_NAME, field_name, columns as u8
|
||||
)?;
|
||||
if let crate::TypeInner::Array { base, size, .. } = *other {
|
||||
self.write_array_size(module, base, size)?;
|
||||
}
|
||||
writeln!(self.out, "){}{};", ARGUMENT_VARIABLE_NAME, i,)?;
|
||||
} else {
|
||||
writeln!(
|
||||
self.out,
|
||||
"{}{}.{} = {}{};",
|
||||
INDENT,
|
||||
RETURN_VARIABLE_NAME,
|
||||
field_name,
|
||||
ARGUMENT_VARIABLE_NAME,
|
||||
i,
|
||||
)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -715,7 +744,7 @@ impl<'a, W: Write> super::Writer<'a, W> {
|
||||
for i in 0..columns as u8 {
|
||||
writeln!(
|
||||
self.out,
|
||||
"{}case {}: {}.{}_{} = {};",
|
||||
"{}case {}: {{ {}.{}_{} = {}; break; }}",
|
||||
INDENT,
|
||||
i,
|
||||
STRUCT_ARGUMENT_VARIABLE_NAME,
|
||||
@ -809,7 +838,7 @@ impl<'a, W: Write> super::Writer<'a, W> {
|
||||
for i in 0..columns as u8 {
|
||||
writeln!(
|
||||
self.out,
|
||||
"{}case {}: {}.{}_{}[{}] = {};",
|
||||
"{}case {}: {{ {}.{}_{}[{}] = {}; break; }}",
|
||||
INDENT,
|
||||
i,
|
||||
STRUCT_ARGUMENT_VARIABLE_NAME,
|
||||
@ -1050,4 +1079,117 @@ impl<'a, W: Write> super::Writer<'a, W> {
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn write_mat_cx2_typedef_and_functions(
|
||||
&mut self,
|
||||
WrappedMatCx2 { columns }: WrappedMatCx2,
|
||||
) -> BackendResult {
|
||||
use crate::back::INDENT;
|
||||
|
||||
// typedef
|
||||
write!(self.out, "typedef struct {{ ")?;
|
||||
for i in 0..columns as u8 {
|
||||
write!(self.out, "float2 _{}; ", i)?;
|
||||
}
|
||||
writeln!(self.out, "}} __mat{}x2;", columns as u8)?;
|
||||
|
||||
// __get_col_of_mat
|
||||
writeln!(
|
||||
self.out,
|
||||
"float2 __get_col_of_mat{}x2(__mat{}x2 mat, uint idx) {{",
|
||||
columns as u8, columns as u8
|
||||
)?;
|
||||
writeln!(self.out, "{}switch(idx) {{", INDENT)?;
|
||||
for i in 0..columns as u8 {
|
||||
writeln!(self.out, "{}case {}: {{ return mat._{}; }}", INDENT, i, i)?;
|
||||
}
|
||||
writeln!(self.out, "{}default: {{ return (float2)0; }}", INDENT)?;
|
||||
writeln!(self.out, "{}}}", INDENT)?;
|
||||
writeln!(self.out, "}}")?;
|
||||
|
||||
// __set_col_of_mat
|
||||
writeln!(
|
||||
self.out,
|
||||
"void __set_col_of_mat{}x2(__mat{}x2 mat, uint idx, float2 value) {{",
|
||||
columns as u8, columns as u8
|
||||
)?;
|
||||
writeln!(self.out, "{}switch(idx) {{", INDENT)?;
|
||||
for i in 0..columns as u8 {
|
||||
writeln!(
|
||||
self.out,
|
||||
"{}case {}: {{ mat._{} = value; break; }}",
|
||||
INDENT, i, i
|
||||
)?;
|
||||
}
|
||||
writeln!(self.out, "{}}}", INDENT)?;
|
||||
writeln!(self.out, "}}")?;
|
||||
|
||||
// __set_el_of_mat
|
||||
writeln!(
|
||||
self.out,
|
||||
"void __set_el_of_mat{}x2(__mat{}x2 mat, uint idx, uint vec_idx, float value) {{",
|
||||
columns as u8, columns as u8
|
||||
)?;
|
||||
writeln!(self.out, "{}switch(idx) {{", INDENT)?;
|
||||
for i in 0..columns as u8 {
|
||||
writeln!(
|
||||
self.out,
|
||||
"{}case {}: {{ mat._{}[vec_idx] = value; break; }}",
|
||||
INDENT, i, i
|
||||
)?;
|
||||
}
|
||||
writeln!(self.out, "{}}}", INDENT)?;
|
||||
writeln!(self.out, "}}")?;
|
||||
|
||||
writeln!(self.out)?;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(super) fn write_all_mat_cx2_typedefs_and_functions(
|
||||
&mut self,
|
||||
module: &crate::Module,
|
||||
) -> BackendResult {
|
||||
for (handle, _) in module.global_variables.iter() {
|
||||
let global = &module.global_variables[handle];
|
||||
|
||||
if global.space == crate::AddressSpace::Uniform {
|
||||
if let Some(super::writer::MatrixType {
|
||||
columns,
|
||||
rows: crate::VectorSize::Bi,
|
||||
width: 4,
|
||||
}) = super::writer::get_inner_matrix_data(module, global.ty)
|
||||
{
|
||||
let entry = WrappedMatCx2 { columns };
|
||||
if !self.wrapped.mat_cx2s.contains(&entry) {
|
||||
self.write_mat_cx2_typedef_and_functions(entry)?;
|
||||
self.wrapped.mat_cx2s.insert(entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (_, ty) in module.types.iter() {
|
||||
if let crate::TypeInner::Struct { ref members, .. } = ty.inner {
|
||||
for member in members.iter() {
|
||||
if let crate::TypeInner::Array { .. } = module.types[member.ty].inner {
|
||||
if let Some(super::writer::MatrixType {
|
||||
columns,
|
||||
rows: crate::VectorSize::Bi,
|
||||
width: 4,
|
||||
}) = super::writer::get_inner_matrix_data(module, member.ty)
|
||||
{
|
||||
let entry = WrappedMatCx2 { columns };
|
||||
if !self.wrapped.mat_cx2s.contains(&entry) {
|
||||
self.write_mat_cx2_typedef_and_functions(entry)?;
|
||||
self.wrapped.mat_cx2s.insert(entry);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
10
third_party/rust/naga/src/back/hlsl/mod.rs
vendored
10
third_party/rust/naga/src/back/hlsl/mod.rs
vendored
@ -57,8 +57,8 @@ that the columns of a `matKx2<f32>` need only be [aligned as required
|
||||
for `vec2<f32>`][ilov], which is [eight-byte alignment][8bb].
|
||||
|
||||
To compensate for this, any time a `matKx2<f32>` appears in a WGSL
|
||||
`uniform` variable, whether directly as the variable's type or as a
|
||||
struct member, we actually emit `K` separate `float2` members, and
|
||||
`uniform` variable, whether directly as the variable's type or as part
|
||||
of a struct/array, we actually emit `K` separate `float2` members, and
|
||||
assemble/disassemble the matrix from its columns (in WGSL; rows in
|
||||
HLSL) upon load and store.
|
||||
|
||||
@ -92,14 +92,10 @@ float3x2 GetMatmOnBaz(Baz obj) {
|
||||
We also emit an analogous `Set` function, as well as functions for
|
||||
accessing individual columns by dynamic index.
|
||||
|
||||
At present, we do not generate correct HLSL when `matCx2<f32>` us used
|
||||
directly as the type of a WGSL `uniform` global ([#1837]).
|
||||
|
||||
[hlsl]: https://docs.microsoft.com/en-us/windows/win32/direct3dhlsl/dx-graphics-hlsl
|
||||
[ilov]: https://gpuweb.github.io/gpuweb/wgsl/#internal-value-layout
|
||||
[16bb]: https://github.com/microsoft/DirectXShaderCompiler/wiki/Buffer-Packing#constant-buffer-packing
|
||||
[8bb]: https://gpuweb.github.io/gpuweb/wgsl/#alignment-and-size
|
||||
[#1837]: https://github.com/gfx-rs/naga/issues/1837
|
||||
*/
|
||||
|
||||
mod conv;
|
||||
@ -253,6 +249,7 @@ struct Wrapped {
|
||||
image_queries: crate::FastHashSet<help::WrappedImageQuery>,
|
||||
constructors: crate::FastHashSet<help::WrappedConstructor>,
|
||||
struct_matrix_access: crate::FastHashSet<help::WrappedStructMatrixAccess>,
|
||||
mat_cx2s: crate::FastHashSet<help::WrappedMatCx2>,
|
||||
}
|
||||
|
||||
impl Wrapped {
|
||||
@ -261,6 +258,7 @@ impl Wrapped {
|
||||
self.image_queries.clear();
|
||||
self.constructors.clear();
|
||||
self.struct_matrix_access.clear();
|
||||
self.mat_cx2s.clear();
|
||||
}
|
||||
}
|
||||
|
||||
|
21
third_party/rust/naga/src/back/hlsl/storage.rs
vendored
21
third_party/rust/naga/src/back/hlsl/storage.rs
vendored
@ -6,7 +6,7 @@ HLSL backend uses byte address buffers for all storage buffers in IR.
|
||||
|
||||
use super::{super::FunctionCtx, BackendResult, Error};
|
||||
use crate::{
|
||||
proc::{NameKey, TypeResolution},
|
||||
proc::{Alignment, NameKey, TypeResolution},
|
||||
Handle,
|
||||
};
|
||||
|
||||
@ -130,11 +130,7 @@ impl<W: fmt::Write> super::Writer<'_, W> {
|
||||
)?;
|
||||
|
||||
// Note: Matrices containing vec3s, due to padding, act like they contain vec4s.
|
||||
let padded_rows = match rows {
|
||||
crate::VectorSize::Tri => 4,
|
||||
rows => rows as u32,
|
||||
};
|
||||
let row_stride = width as u32 * padded_rows;
|
||||
let row_stride = Alignment::from(rows) * width as u32;
|
||||
let iter = (0..columns as u32).map(|i| {
|
||||
let ty_inner = crate::TypeInner::Vector {
|
||||
size: rows,
|
||||
@ -277,11 +273,7 @@ impl<W: fmt::Write> super::Writer<'_, W> {
|
||||
writeln!(self.out, ";")?;
|
||||
|
||||
// Note: Matrices containing vec3s, due to padding, act like they contain vec4s.
|
||||
let padded_rows = match rows {
|
||||
crate::VectorSize::Tri => 4,
|
||||
rows => rows as u32,
|
||||
};
|
||||
let row_stride = width as u32 * padded_rows;
|
||||
let row_stride = Alignment::from(rows) * width as u32;
|
||||
|
||||
// then iterate the stores
|
||||
for i in 0..columns as u32 {
|
||||
@ -409,12 +401,7 @@ impl<W: fmt::Write> super::Writer<'_, W> {
|
||||
stride: width as u32,
|
||||
},
|
||||
crate::TypeInner::Matrix { columns, width, .. } => Parent::Array {
|
||||
stride: width as u32
|
||||
* if columns > crate::VectorSize::Bi {
|
||||
4
|
||||
} else {
|
||||
2
|
||||
},
|
||||
stride: Alignment::from(columns) * width as u32,
|
||||
},
|
||||
_ => unreachable!(),
|
||||
},
|
||||
|
574
third_party/rust/naga/src/back/hlsl/writer.rs
vendored
574
third_party/rust/naga/src/back/hlsl/writer.rs
vendored
@ -150,6 +150,8 @@ impl<'a, W: fmt::Write> super::Writer<'a, W> {
|
||||
.map(|ep| (ep.stage, ep.function.result.clone()))
|
||||
.collect::<Vec<(ShaderStage, Option<crate::FunctionResult>)>>();
|
||||
|
||||
self.write_all_mat_cx2_typedefs_and_functions(module)?;
|
||||
|
||||
// Write all structs
|
||||
for (handle, ty) in module.types.iter() {
|
||||
if let TypeInner::Struct { ref members, span } = ty.inner {
|
||||
@ -661,19 +663,41 @@ impl<'a, W: fmt::Write> super::Writer<'a, W> {
|
||||
|
||||
if global.space == crate::AddressSpace::Uniform {
|
||||
write!(self.out, " {{ ")?;
|
||||
// Even though Naga IR matrices are column-major, we must describe
|
||||
// matrices passed from the CPU as being in row-major order. See
|
||||
// the module-level comments for details.
|
||||
if let TypeInner::Matrix { .. } = module.types[global.ty].inner {
|
||||
write!(self.out, "row_major ")?;
|
||||
|
||||
let matrix_data = get_inner_matrix_data(module, global.ty);
|
||||
|
||||
// We treat matrices of the form `matCx2` as a sequence of C `vec2`s.
|
||||
// See the module-level block comment in mod.rs for details.
|
||||
if let Some(MatrixType {
|
||||
columns,
|
||||
rows: crate::VectorSize::Bi,
|
||||
width: 4,
|
||||
}) = matrix_data
|
||||
{
|
||||
write!(
|
||||
self.out,
|
||||
"__mat{}x2 {}",
|
||||
columns as u8,
|
||||
&self.names[&NameKey::GlobalVariable(handle)]
|
||||
)?;
|
||||
} else {
|
||||
// Even though Naga IR matrices are column-major, we must describe
|
||||
// matrices passed from the CPU as being in row-major order.
|
||||
// See the module-level block comment in mod.rs for details.
|
||||
if matrix_data.is_some() {
|
||||
write!(self.out, "row_major ")?;
|
||||
}
|
||||
|
||||
self.write_type(module, global.ty)?;
|
||||
let sub_name = &self.names[&NameKey::GlobalVariable(handle)];
|
||||
write!(self.out, " {}", sub_name)?;
|
||||
}
|
||||
self.write_type(module, global.ty)?;
|
||||
let sub_name = &self.names[&NameKey::GlobalVariable(handle)];
|
||||
write!(self.out, " {}", sub_name)?;
|
||||
|
||||
// need to write the array size if the type was emitted with `write_type`
|
||||
if let TypeInner::Array { base, size, .. } = module.types[global.ty].inner {
|
||||
self.write_array_size(module, base, size)?;
|
||||
}
|
||||
|
||||
writeln!(self.out, "; }}")?;
|
||||
} else {
|
||||
writeln!(self.out, ";")?;
|
||||
@ -801,16 +825,31 @@ impl<'a, W: fmt::Write> super::Writer<'a, W> {
|
||||
write!(self.out, "{}", back::INDENT)?;
|
||||
|
||||
match module.types[member.ty].inner {
|
||||
TypeInner::Array {
|
||||
base,
|
||||
size,
|
||||
stride: _,
|
||||
} => {
|
||||
TypeInner::Array { base, size, .. } => {
|
||||
// HLSL arrays are written as `type name[size]`
|
||||
if let TypeInner::Matrix { .. } = module.types[base].inner {
|
||||
write!(self.out, "row_major ")?;
|
||||
|
||||
let matrix_data = get_inner_matrix_data(module, member.ty);
|
||||
|
||||
// We treat matrices of the form `matCx2` as a sequence of C `vec2`s.
|
||||
// See the module-level block comment in mod.rs for details.
|
||||
if let Some(MatrixType {
|
||||
columns,
|
||||
rows: crate::VectorSize::Bi,
|
||||
width: 4,
|
||||
}) = matrix_data
|
||||
{
|
||||
write!(self.out, "__mat{}x2", columns as u8)?;
|
||||
} else {
|
||||
// Even though Naga IR matrices are column-major, we must describe
|
||||
// matrices passed from the CPU as being in row-major order.
|
||||
// See the module-level block comment in mod.rs for details.
|
||||
if matrix_data.is_some() {
|
||||
write!(self.out, "row_major ")?;
|
||||
}
|
||||
|
||||
self.write_type(module, base)?;
|
||||
}
|
||||
self.write_type(module, base)?;
|
||||
|
||||
// Write `name`
|
||||
write!(
|
||||
self.out,
|
||||
@ -820,8 +859,8 @@ impl<'a, W: fmt::Write> super::Writer<'a, W> {
|
||||
// Write [size]
|
||||
self.write_array_size(module, base, size)?;
|
||||
}
|
||||
// We treat matrices of the form `matCx2` as a sequence of C `vec2`s
|
||||
// (see top level module docs for details).
|
||||
// We treat matrices of the form `matCx2` as a sequence of C `vec2`s.
|
||||
// See the module-level block comment in mod.rs for details.
|
||||
TypeInner::Matrix {
|
||||
rows,
|
||||
columns,
|
||||
@ -848,6 +887,9 @@ impl<'a, W: fmt::Write> super::Writer<'a, W> {
|
||||
self.write_modifier(binding)?;
|
||||
}
|
||||
|
||||
// Even though Naga IR matrices are column-major, we must describe
|
||||
// matrices passed from the CPU as being in row-major order.
|
||||
// See the module-level block comment in mod.rs for details.
|
||||
if let TypeInner::Matrix { .. } = module.types[member.ty].inner {
|
||||
write!(self.out, "row_major ")?;
|
||||
}
|
||||
@ -1285,17 +1327,6 @@ impl<'a, W: fmt::Write> super::Writer<'a, W> {
|
||||
}
|
||||
Statement::Store { pointer, value } => {
|
||||
let ty_inner = func_ctx.info[pointer].ty.inner_with(&module.types);
|
||||
let array_info = match *ty_inner {
|
||||
TypeInner::Pointer { base, .. } => match module.types[base].inner {
|
||||
crate::TypeInner::Array {
|
||||
size: crate::ArraySize::Constant(ch),
|
||||
..
|
||||
} => Some((ch, base)),
|
||||
_ => None,
|
||||
},
|
||||
_ => None,
|
||||
};
|
||||
|
||||
if let Some(crate::AddressSpace::Storage { .. }) = ty_inner.pointer_space() {
|
||||
let var_handle = self.fill_access_chain(module, pointer, func_ctx)?;
|
||||
self.write_storage_store(
|
||||
@ -1305,26 +1336,9 @@ impl<'a, W: fmt::Write> super::Writer<'a, W> {
|
||||
func_ctx,
|
||||
level,
|
||||
)?;
|
||||
} else if let Some((const_handle, base_ty)) = array_info {
|
||||
let size = module.constants[const_handle].to_array_length().unwrap();
|
||||
writeln!(self.out, "{}{{", level)?;
|
||||
write!(self.out, "{}", level.next())?;
|
||||
self.write_type(module, base_ty)?;
|
||||
write!(self.out, " _result[{}]=", size)?;
|
||||
self.write_expr(module, value, func_ctx)?;
|
||||
writeln!(self.out, ";")?;
|
||||
write!(
|
||||
self.out,
|
||||
"{}for(int _i=0; _i<{}; ++_i) ",
|
||||
level.next(),
|
||||
size
|
||||
)?;
|
||||
self.write_expr(module, pointer, func_ctx)?;
|
||||
writeln!(self.out, "[_i] = _result[_i];")?;
|
||||
writeln!(self.out, "{}}}", level)?;
|
||||
} else {
|
||||
// We treat matrices of the form `matCx2` as a sequence of C `vec2`s
|
||||
// (see top level module docs for details).
|
||||
// We treat matrices of the form `matCx2` as a sequence of C `vec2`s.
|
||||
// See the module-level block comment in mod.rs for details.
|
||||
//
|
||||
// We handle matrix Stores here directly (including sub accesses for Vectors and Scalars).
|
||||
// Loads are handled by `Expression::AccessIndex` (since sub accesses work fine for Loads).
|
||||
@ -1487,28 +1501,159 @@ impl<'a, W: fmt::Write> super::Writer<'a, W> {
|
||||
writeln!(self.out, ");")?;
|
||||
}
|
||||
} else {
|
||||
self.write_expr(module, pointer, func_ctx)?;
|
||||
write!(self.out, " = ")?;
|
||||
self.write_expr(module, value, func_ctx)?;
|
||||
writeln!(self.out, ";")?
|
||||
// We handle `Store`s to __matCx2 column vectors and scalar elements via
|
||||
// the previously injected functions __set_col_of_matCx2 / __set_el_of_matCx2.
|
||||
struct MatrixData {
|
||||
columns: crate::VectorSize,
|
||||
base: Handle<crate::Expression>,
|
||||
}
|
||||
|
||||
enum Index {
|
||||
Expression(Handle<crate::Expression>),
|
||||
Static(u32),
|
||||
}
|
||||
|
||||
let mut matrix = None;
|
||||
let mut vector = None;
|
||||
let mut scalar = None;
|
||||
|
||||
let mut current_expr = pointer;
|
||||
for _ in 0..3 {
|
||||
let resolved = func_ctx.info[current_expr].ty.inner_with(&module.types);
|
||||
match (resolved, &func_ctx.expressions[current_expr]) {
|
||||
(
|
||||
&TypeInner::ValuePointer {
|
||||
size: Some(crate::VectorSize::Bi),
|
||||
..
|
||||
},
|
||||
&crate::Expression::Access { base, index },
|
||||
) => {
|
||||
vector = Some(index);
|
||||
current_expr = base;
|
||||
}
|
||||
(
|
||||
&TypeInner::ValuePointer { size: None, .. },
|
||||
&crate::Expression::Access { base, index },
|
||||
) => {
|
||||
scalar = Some(Index::Expression(index));
|
||||
current_expr = base;
|
||||
}
|
||||
(
|
||||
&TypeInner::ValuePointer { size: None, .. },
|
||||
&crate::Expression::AccessIndex { base, index },
|
||||
) => {
|
||||
scalar = Some(Index::Static(index));
|
||||
current_expr = base;
|
||||
}
|
||||
_ => {
|
||||
if let Some(MatrixType {
|
||||
columns,
|
||||
rows: crate::VectorSize::Bi,
|
||||
width: 4,
|
||||
}) = get_inner_matrix_of_struct_array_member(
|
||||
module,
|
||||
current_expr,
|
||||
func_ctx,
|
||||
true,
|
||||
) {
|
||||
matrix = Some(MatrixData {
|
||||
columns,
|
||||
base: current_expr,
|
||||
});
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let (Some(MatrixData { columns, base }), Some(vec_index)) =
|
||||
(matrix, vector)
|
||||
{
|
||||
if scalar.is_some() {
|
||||
write!(self.out, "__set_el_of_mat{}x2", columns as u8)?;
|
||||
} else {
|
||||
write!(self.out, "__set_col_of_mat{}x2", columns as u8)?;
|
||||
}
|
||||
write!(self.out, "(")?;
|
||||
self.write_expr(module, base, func_ctx)?;
|
||||
write!(self.out, ", ")?;
|
||||
self.write_expr(module, vec_index, func_ctx)?;
|
||||
|
||||
if let Some(scalar_index) = scalar {
|
||||
write!(self.out, ", ")?;
|
||||
match scalar_index {
|
||||
Index::Static(index) => {
|
||||
write!(self.out, "{}", index)?;
|
||||
}
|
||||
Index::Expression(index) => {
|
||||
self.write_expr(module, index, func_ctx)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
write!(self.out, ", ")?;
|
||||
self.write_expr(module, value, func_ctx)?;
|
||||
|
||||
writeln!(self.out, ");")?;
|
||||
} else {
|
||||
self.write_expr(module, pointer, func_ctx)?;
|
||||
write!(self.out, " = ")?;
|
||||
|
||||
// We cast the RHS of this store in cases where the LHS
|
||||
// is a struct member with type:
|
||||
// - matCx2 or
|
||||
// - a (possibly nested) array of matCx2's
|
||||
if let Some(MatrixType {
|
||||
columns,
|
||||
rows: crate::VectorSize::Bi,
|
||||
width: 4,
|
||||
}) = get_inner_matrix_of_struct_array_member(
|
||||
module, pointer, func_ctx, false,
|
||||
) {
|
||||
let mut resolved =
|
||||
func_ctx.info[pointer].ty.inner_with(&module.types);
|
||||
if let TypeInner::Pointer { base, .. } = *resolved {
|
||||
resolved = &module.types[base].inner;
|
||||
}
|
||||
|
||||
write!(self.out, "(__mat{}x2", columns as u8)?;
|
||||
if let TypeInner::Array { base, size, .. } = *resolved {
|
||||
self.write_array_size(module, base, size)?;
|
||||
}
|
||||
write!(self.out, ")")?;
|
||||
}
|
||||
|
||||
self.write_expr(module, value, func_ctx)?;
|
||||
writeln!(self.out, ";")?
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Statement::Loop {
|
||||
ref body,
|
||||
ref continuing,
|
||||
break_if,
|
||||
} => {
|
||||
let l2 = level.next();
|
||||
if !continuing.is_empty() {
|
||||
if !continuing.is_empty() || break_if.is_some() {
|
||||
let gate_name = self.namer.call("loop_init");
|
||||
writeln!(self.out, "{}bool {} = true;", level, gate_name)?;
|
||||
writeln!(self.out, "{}while(true) {{", level)?;
|
||||
writeln!(self.out, "{}if (!{}) {{", l2, gate_name)?;
|
||||
let l3 = l2.next();
|
||||
for sta in continuing.iter() {
|
||||
self.write_stmt(module, sta, func_ctx, l2)?;
|
||||
self.write_stmt(module, sta, func_ctx, l3)?;
|
||||
}
|
||||
writeln!(self.out, "{}}}", level.next())?;
|
||||
writeln!(self.out, "{}{} = false;", level.next(), gate_name)?;
|
||||
if let Some(condition) = break_if {
|
||||
write!(self.out, "{}if (", l3)?;
|
||||
self.write_expr(module, condition, func_ctx)?;
|
||||
writeln!(self.out, ") {{")?;
|
||||
writeln!(self.out, "{}break;", l3.next())?;
|
||||
writeln!(self.out, "{}}}", l3)?;
|
||||
}
|
||||
writeln!(self.out, "{}}}", l2)?;
|
||||
writeln!(self.out, "{}{} = false;", l2, gate_name)?;
|
||||
} else {
|
||||
writeln!(self.out, "{}while(true) {{", level)?;
|
||||
}
|
||||
@ -1651,7 +1796,7 @@ impl<'a, W: fmt::Write> super::Writer<'a, W> {
|
||||
let indent_level_1 = level.next();
|
||||
let indent_level_2 = indent_level_1.next();
|
||||
|
||||
for case in cases {
|
||||
for (i, case) in cases.iter().enumerate() {
|
||||
match case.value {
|
||||
crate::SwitchValue::Integer(value) => writeln!(
|
||||
self.out,
|
||||
@ -1663,25 +1808,35 @@ impl<'a, W: fmt::Write> super::Writer<'a, W> {
|
||||
}
|
||||
}
|
||||
|
||||
// FXC doesn't support fallthrough so we duplicate the body of the following case blocks
|
||||
if case.fall_through {
|
||||
// Generate each fallthrough case statement in a new block. This is done to
|
||||
// prevent symbol collision of variables declared in these cases statements.
|
||||
writeln!(self.out, "{}/* fallthrough */", indent_level_2)?;
|
||||
writeln!(self.out, "{}{{", indent_level_2)?;
|
||||
}
|
||||
for sta in case.body.iter() {
|
||||
self.write_stmt(
|
||||
module,
|
||||
sta,
|
||||
func_ctx,
|
||||
back::Level(indent_level_2.0 + usize::from(case.fall_through)),
|
||||
)?;
|
||||
}
|
||||
let curr_len = i + 1;
|
||||
let end_case_idx = curr_len
|
||||
+ cases
|
||||
.iter()
|
||||
.skip(curr_len)
|
||||
.position(|case| !case.fall_through)
|
||||
.unwrap();
|
||||
let indent_level_3 = indent_level_2.next();
|
||||
for case in &cases[i..=end_case_idx] {
|
||||
writeln!(self.out, "{}{{", indent_level_2)?;
|
||||
for sta in case.body.iter() {
|
||||
self.write_stmt(module, sta, func_ctx, indent_level_3)?;
|
||||
}
|
||||
writeln!(self.out, "{}}}", indent_level_2)?;
|
||||
}
|
||||
|
||||
if case.fall_through {
|
||||
writeln!(self.out, "{}}}", indent_level_2)?;
|
||||
} else if case.body.last().map_or(true, |s| !s.is_terminator()) {
|
||||
writeln!(self.out, "{}break;", indent_level_2)?;
|
||||
let last_case = &cases[end_case_idx];
|
||||
if last_case.body.last().map_or(true, |s| !s.is_terminator()) {
|
||||
writeln!(self.out, "{}break;", indent_level_2)?;
|
||||
}
|
||||
} else {
|
||||
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()) {
|
||||
writeln!(self.out, "{}break;", indent_level_2)?;
|
||||
}
|
||||
}
|
||||
|
||||
writeln!(self.out, "{}}}", indent_level_1)?;
|
||||
@ -1844,6 +1999,26 @@ impl<'a, W: fmt::Write> super::Writer<'a, W> {
|
||||
{
|
||||
// do nothing, the chain is written on `Load`/`Store`
|
||||
} else {
|
||||
// We use the function __get_col_of_matCx2 here in cases
|
||||
// where `base`s type resolves to a matCx2 and is part of a
|
||||
// struct member with type of (possibly nested) array of matCx2's.
|
||||
//
|
||||
// Note that this only works for `Load`s and we handle
|
||||
// `Store`s differently in `Statement::Store`.
|
||||
if let Some(MatrixType {
|
||||
columns,
|
||||
rows: crate::VectorSize::Bi,
|
||||
width: 4,
|
||||
}) = get_inner_matrix_of_struct_array_member(module, base, func_ctx, true)
|
||||
{
|
||||
write!(self.out, "__get_col_of_mat{}x2(", columns as u8)?;
|
||||
self.write_expr(module, base, func_ctx)?;
|
||||
write!(self.out, ", ")?;
|
||||
self.write_expr(module, index, func_ctx)?;
|
||||
write!(self.out, ")")?;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let base_ty_res = &func_ctx.info[base].ty;
|
||||
let resolved = base_ty_res.inner_with(&module.types);
|
||||
|
||||
@ -1876,18 +2051,64 @@ impl<'a, W: fmt::Write> super::Writer<'a, W> {
|
||||
{
|
||||
// do nothing, the chain is written on `Load`/`Store`
|
||||
} else {
|
||||
fn write_access<W: fmt::Write>(
|
||||
writer: &mut super::Writer<'_, W>,
|
||||
resolved: &TypeInner,
|
||||
base_ty_handle: Option<Handle<crate::Type>>,
|
||||
index: u32,
|
||||
) -> BackendResult {
|
||||
match *resolved {
|
||||
TypeInner::Vector { .. } => {
|
||||
// Write vector access as a swizzle
|
||||
write!(writer.out, ".{}", back::COMPONENTS[index as usize])?
|
||||
}
|
||||
TypeInner::Matrix { .. }
|
||||
| TypeInner::Array { .. }
|
||||
| TypeInner::BindingArray { .. }
|
||||
| TypeInner::ValuePointer { .. } => write!(writer.out, "[{}]", index)?,
|
||||
TypeInner::Struct { .. } => {
|
||||
// This will never panic in case the type is a `Struct`, this is not true
|
||||
// for other types so we can only check while inside this match arm
|
||||
let ty = base_ty_handle.unwrap();
|
||||
|
||||
write!(
|
||||
writer.out,
|
||||
".{}",
|
||||
&writer.names[&NameKey::StructMember(ty, index)]
|
||||
)?
|
||||
}
|
||||
ref other => {
|
||||
return Err(Error::Custom(format!("Cannot index {:?}", other)))
|
||||
}
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// We write the matrix column access in a special way since
|
||||
// the type of `base` is our special __matCx2 struct.
|
||||
if let Some(MatrixType {
|
||||
rows: crate::VectorSize::Bi,
|
||||
width: 4,
|
||||
..
|
||||
}) = get_inner_matrix_of_struct_array_member(module, base, func_ctx, true)
|
||||
{
|
||||
self.write_expr(module, base, func_ctx)?;
|
||||
write!(self.out, "._{}", index)?;
|
||||
return Ok(());
|
||||
}
|
||||
|
||||
let base_ty_res = &func_ctx.info[base].ty;
|
||||
let mut resolved = base_ty_res.inner_with(&module.types);
|
||||
let base_ty_handle = match *resolved {
|
||||
TypeInner::Pointer { base, space: _ } => {
|
||||
TypeInner::Pointer { base, .. } => {
|
||||
resolved = &module.types[base].inner;
|
||||
Some(base)
|
||||
}
|
||||
_ => base_ty_res.handle(),
|
||||
};
|
||||
|
||||
// We treat matrices of the form `matCx2` as a sequence of C `vec2`s
|
||||
// (see top level module docs for details).
|
||||
// We treat matrices of the form `matCx2` as a sequence of C `vec2`s.
|
||||
// See the module-level block comment in mod.rs for details.
|
||||
//
|
||||
// We handle matrix reconstruction here for Loads.
|
||||
// Stores are handled directly by `Statement::Store`.
|
||||
@ -1910,34 +2131,10 @@ impl<'a, W: fmt::Write> super::Writer<'a, W> {
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
self.write_expr(module, base, func_ctx)?;
|
||||
|
||||
match *resolved {
|
||||
TypeInner::Vector { .. } => {
|
||||
// Write vector access as a swizzle
|
||||
write!(self.out, ".{}", back::COMPONENTS[index as usize])?
|
||||
}
|
||||
TypeInner::Matrix { .. }
|
||||
| TypeInner::Array { .. }
|
||||
| TypeInner::BindingArray { .. }
|
||||
| TypeInner::ValuePointer { .. } => write!(self.out, "[{}]", index)?,
|
||||
TypeInner::Struct { .. } => {
|
||||
// This will never panic in case the type is a `Struct`, this is not true
|
||||
// for other types so we can only check while inside this match arm
|
||||
let ty = base_ty_handle.unwrap();
|
||||
|
||||
write!(
|
||||
self.out,
|
||||
".{}",
|
||||
&self.names[&NameKey::StructMember(ty, index)]
|
||||
)?
|
||||
}
|
||||
ref other => {
|
||||
return Err(Error::Custom(format!("Cannot index {:?}", other)))
|
||||
}
|
||||
}
|
||||
write_access(self, resolved, base_ty_handle, index)?;
|
||||
}
|
||||
}
|
||||
Expression::FunctionArgument(pos) => {
|
||||
@ -2108,7 +2305,42 @@ impl<'a, W: fmt::Write> super::Writer<'a, W> {
|
||||
self.write_storage_load(module, var_handle, result_ty, func_ctx)?;
|
||||
}
|
||||
_ => {
|
||||
let mut close_paren = false;
|
||||
|
||||
// We cast the value loaded to a native HLSL floatCx2
|
||||
// in cases where it is of type:
|
||||
// - __matCx2 or
|
||||
// - a (possibly nested) array of __matCx2's
|
||||
if let Some(MatrixType {
|
||||
rows: crate::VectorSize::Bi,
|
||||
width: 4,
|
||||
..
|
||||
}) = get_inner_matrix_of_struct_array_member(
|
||||
module, pointer, func_ctx, false,
|
||||
)
|
||||
.or_else(|| get_inner_matrix_of_global_uniform(module, pointer, func_ctx))
|
||||
{
|
||||
let mut resolved = func_ctx.info[pointer].ty.inner_with(&module.types);
|
||||
if let TypeInner::Pointer { base, .. } = *resolved {
|
||||
resolved = &module.types[base].inner;
|
||||
}
|
||||
|
||||
write!(self.out, "((")?;
|
||||
if let TypeInner::Array { base, size, .. } = *resolved {
|
||||
self.write_type(module, base)?;
|
||||
self.write_array_size(module, base, size)?;
|
||||
} else {
|
||||
self.write_value_type(module, resolved)?;
|
||||
}
|
||||
write!(self.out, ")")?;
|
||||
close_paren = true;
|
||||
}
|
||||
|
||||
self.write_expr(module, pointer, func_ctx)?;
|
||||
|
||||
if close_paren {
|
||||
write!(self.out, ")")?;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2567,3 +2799,139 @@ impl<'a, W: fmt::Write> super::Writer<'a, W> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) struct MatrixType {
|
||||
pub(super) columns: crate::VectorSize,
|
||||
pub(super) rows: crate::VectorSize,
|
||||
pub(super) width: crate::Bytes,
|
||||
}
|
||||
|
||||
pub(super) fn get_inner_matrix_data(
|
||||
module: &Module,
|
||||
handle: Handle<crate::Type>,
|
||||
) -> Option<MatrixType> {
|
||||
match module.types[handle].inner {
|
||||
TypeInner::Matrix {
|
||||
columns,
|
||||
rows,
|
||||
width,
|
||||
} => Some(MatrixType {
|
||||
columns,
|
||||
rows,
|
||||
width,
|
||||
}),
|
||||
TypeInner::Array { base, .. } => get_inner_matrix_data(module, base),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the matrix data if the access chain starting at `base`:
|
||||
/// - starts with an expression with resolved type of [`TypeInner::Matrix`] if `direct = true`
|
||||
/// - contains one or more expressions with resolved type of [`TypeInner::Array`] of [`TypeInner::Matrix`]
|
||||
/// - ends at an expression with resolved type of [`TypeInner::Struct`]
|
||||
pub(super) fn get_inner_matrix_of_struct_array_member(
|
||||
module: &Module,
|
||||
base: Handle<crate::Expression>,
|
||||
func_ctx: &back::FunctionCtx<'_>,
|
||||
direct: bool,
|
||||
) -> Option<MatrixType> {
|
||||
let mut mat_data = None;
|
||||
let mut array_base = None;
|
||||
|
||||
let mut current_base = base;
|
||||
loop {
|
||||
let mut resolved = func_ctx.info[current_base].ty.inner_with(&module.types);
|
||||
if let TypeInner::Pointer { base, .. } = *resolved {
|
||||
resolved = &module.types[base].inner;
|
||||
};
|
||||
|
||||
match *resolved {
|
||||
TypeInner::Matrix {
|
||||
columns,
|
||||
rows,
|
||||
width,
|
||||
} => {
|
||||
mat_data = Some(MatrixType {
|
||||
columns,
|
||||
rows,
|
||||
width,
|
||||
})
|
||||
}
|
||||
TypeInner::Array { base, .. } => {
|
||||
array_base = Some(base);
|
||||
}
|
||||
TypeInner::Struct { .. } => {
|
||||
if let Some(array_base) = array_base {
|
||||
if direct {
|
||||
return mat_data;
|
||||
} else {
|
||||
return get_inner_matrix_data(module, array_base);
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
_ => break,
|
||||
}
|
||||
|
||||
current_base = match func_ctx.expressions[current_base] {
|
||||
crate::Expression::Access { base, .. } => base,
|
||||
crate::Expression::AccessIndex { base, .. } => base,
|
||||
_ => break,
|
||||
};
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
/// Returns the matrix data if the access chain starting at `base`:
|
||||
/// - starts with an expression with resolved type of [`TypeInner::Matrix`]
|
||||
/// - contains zero or more expressions with resolved type of [`TypeInner::Array`] of [`TypeInner::Matrix`]
|
||||
/// - ends with an [`Expression::GlobalVariable`](crate::Expression::GlobalVariable) in [`AddressSpace::Uniform`](crate::AddressSpace::Uniform)
|
||||
fn get_inner_matrix_of_global_uniform(
|
||||
module: &Module,
|
||||
base: Handle<crate::Expression>,
|
||||
func_ctx: &back::FunctionCtx<'_>,
|
||||
) -> Option<MatrixType> {
|
||||
let mut mat_data = None;
|
||||
let mut array_base = None;
|
||||
|
||||
let mut current_base = base;
|
||||
loop {
|
||||
let mut resolved = func_ctx.info[current_base].ty.inner_with(&module.types);
|
||||
if let TypeInner::Pointer { base, .. } = *resolved {
|
||||
resolved = &module.types[base].inner;
|
||||
};
|
||||
|
||||
match *resolved {
|
||||
TypeInner::Matrix {
|
||||
columns,
|
||||
rows,
|
||||
width,
|
||||
} => {
|
||||
mat_data = Some(MatrixType {
|
||||
columns,
|
||||
rows,
|
||||
width,
|
||||
})
|
||||
}
|
||||
TypeInner::Array { base, .. } => {
|
||||
array_base = Some(base);
|
||||
}
|
||||
_ => break,
|
||||
}
|
||||
|
||||
current_base = match func_ctx.expressions[current_base] {
|
||||
crate::Expression::Access { base, .. } => base,
|
||||
crate::Expression::AccessIndex { base, .. } => base,
|
||||
crate::Expression::GlobalVariable(handle)
|
||||
if module.global_variables[handle].space == crate::AddressSpace::Uniform =>
|
||||
{
|
||||
return mat_data.or_else(|| {
|
||||
array_base.and_then(|array_base| get_inner_matrix_data(module, array_base))
|
||||
})
|
||||
}
|
||||
_ => break,
|
||||
};
|
||||
}
|
||||
None
|
||||
}
|
||||
|
51
third_party/rust/naga/src/back/msl/writer.rs
vendored
51
third_party/rust/naga/src/back/msl/writer.rs
vendored
@ -364,6 +364,8 @@ pub struct Writer<W> {
|
||||
put_expression_stack_pointers: FastHashSet<*const ()>,
|
||||
#[cfg(test)]
|
||||
put_block_stack_pointers: FastHashSet<*const ()>,
|
||||
/// Set of (struct type, struct field index) denoting which fields require
|
||||
/// padding inserted **before** them (i.e. between fields at index - 1 and index)
|
||||
struct_member_pads: FastHashSet<(Handle<crate::Type>, u32)>,
|
||||
}
|
||||
|
||||
@ -647,6 +649,8 @@ impl<W: Write> Writer<W> {
|
||||
}
|
||||
|
||||
/// Finishes writing and returns the output.
|
||||
// See https://github.com/rust-lang/rust-clippy/issues/4979.
|
||||
#[allow(clippy::missing_const_for_fn)]
|
||||
pub fn finish(self) -> W {
|
||||
self.out
|
||||
}
|
||||
@ -1221,20 +1225,30 @@ impl<W: Write> Writer<W> {
|
||||
arg: Handle<crate::Expression>,
|
||||
arg1: Handle<crate::Expression>,
|
||||
size: usize,
|
||||
context: &ExpressionContext,
|
||||
) -> BackendResult {
|
||||
// Write parantheses around the dot product expression to prevent operators
|
||||
// with different precedences from applying earlier.
|
||||
write!(self.out, "(")?;
|
||||
|
||||
let arg0_name = &self.named_expressions[&arg];
|
||||
let arg1_name = &self.named_expressions[&arg1];
|
||||
|
||||
// This will print an extra '+' at the beginning but that is fine in msl
|
||||
// Cycle trough all the components of the vector
|
||||
for index in 0..size {
|
||||
let component = back::COMPONENTS[index];
|
||||
write!(
|
||||
self.out,
|
||||
" + {}.{} * {}.{}",
|
||||
arg0_name, component, arg1_name, component
|
||||
)?;
|
||||
// Write the addition to the previous product
|
||||
// This will print an extra '+' at the beginning but that is fine in msl
|
||||
write!(self.out, " + ")?;
|
||||
// Write the first vector expression, this expression is marked to be
|
||||
// cached so unless it can't be cached (for example, it's a Constant)
|
||||
// it shouldn't produce large expressions.
|
||||
self.put_expression(arg, context, true)?;
|
||||
// Access the current component on the first vector
|
||||
write!(self.out, ".{} * ", component)?;
|
||||
// Write the second vector expression, this expression is marked to be
|
||||
// cached so unless it can't be cached (for example, it's a Constant)
|
||||
// it shouldn't produce large expressions.
|
||||
self.put_expression(arg1, context, true)?;
|
||||
// Access the current component on the second vector
|
||||
write!(self.out, ".{}", component)?;
|
||||
}
|
||||
|
||||
write!(self.out, ")")?;
|
||||
@ -1652,7 +1666,7 @@ impl<W: Write> Writer<W> {
|
||||
..
|
||||
} => "dot",
|
||||
crate::TypeInner::Vector { size, .. } => {
|
||||
return self.put_dot_product(arg, arg1.unwrap(), size as usize)
|
||||
return self.put_dot_product(arg, arg1.unwrap(), size as usize, context)
|
||||
}
|
||||
_ => unreachable!(
|
||||
"Correct TypeInner for dot product should be already validated"
|
||||
@ -2538,14 +2552,23 @@ impl<W: Write> Writer<W> {
|
||||
crate::Statement::Loop {
|
||||
ref body,
|
||||
ref continuing,
|
||||
break_if,
|
||||
} => {
|
||||
if !continuing.is_empty() {
|
||||
if !continuing.is_empty() || break_if.is_some() {
|
||||
let gate_name = self.namer.call("loop_init");
|
||||
writeln!(self.out, "{}bool {} = true;", level, gate_name)?;
|
||||
writeln!(self.out, "{}while(true) {{", level)?;
|
||||
let lif = level.next();
|
||||
let lcontinuing = lif.next();
|
||||
writeln!(self.out, "{}if (!{}) {{", lif, gate_name)?;
|
||||
self.put_block(lif.next(), continuing, context)?;
|
||||
self.put_block(lcontinuing, continuing, context)?;
|
||||
if let Some(condition) = break_if {
|
||||
write!(self.out, "{}if (", lcontinuing)?;
|
||||
self.put_expression(condition, &context.expression, true)?;
|
||||
writeln!(self.out, ") {{")?;
|
||||
writeln!(self.out, "{}break;", lcontinuing.next())?;
|
||||
writeln!(self.out, "{}}}", lcontinuing)?;
|
||||
}
|
||||
writeln!(self.out, "{}}}", lif)?;
|
||||
writeln!(self.out, "{}{} = false;", lif, gate_name)?;
|
||||
} else {
|
||||
@ -3080,6 +3103,10 @@ impl<W: Write> Writer<W> {
|
||||
};
|
||||
write!(self.out, "constant {} {} = {{", ty_name, name,)?;
|
||||
for (i, &sub_handle) in components.iter().enumerate() {
|
||||
// insert padding initialization, if needed
|
||||
if self.struct_member_pads.contains(&(ty, i as u32)) {
|
||||
write!(self.out, ", {{}}")?;
|
||||
}
|
||||
let separator = if i != 0 { ", " } else { "" };
|
||||
let coco = ConstantContext {
|
||||
handle: sub_handle,
|
||||
|
96
third_party/rust/naga/src/back/spv/block.rs
vendored
96
third_party/rust/naga/src/back/spv/block.rs
vendored
@ -37,6 +37,28 @@ enum ExpressionPointer {
|
||||
},
|
||||
}
|
||||
|
||||
/// The termination statement to be added to the end of the block
|
||||
pub enum BlockExit {
|
||||
/// Generates an OpReturn (void return)
|
||||
Return,
|
||||
/// Generates an OpBranch to the specified block
|
||||
Branch {
|
||||
/// The branch target block
|
||||
target: Word,
|
||||
},
|
||||
/// Translates a loop `break if` into an `OpBranchConditional` to the
|
||||
/// merge block if true (the merge block is passed through [`LoopContext::break_id`]
|
||||
/// or else to the loop header (passed through [`preamble_id`])
|
||||
///
|
||||
/// [`preamble_id`]: Self::BreakIf::preamble_id
|
||||
BreakIf {
|
||||
/// The condition of the `break if`
|
||||
condition: Handle<crate::Expression>,
|
||||
/// The loop header block id
|
||||
preamble_id: Word,
|
||||
},
|
||||
}
|
||||
|
||||
impl Writer {
|
||||
// Flip Y coordinate to adjust for coordinate space difference
|
||||
// between SPIR-V and our IR.
|
||||
@ -1491,7 +1513,7 @@ impl<'w> BlockContext<'w> {
|
||||
&mut self,
|
||||
label_id: Word,
|
||||
statements: &[crate::Statement],
|
||||
exit_id: Option<Word>,
|
||||
exit: BlockExit,
|
||||
loop_context: LoopContext,
|
||||
) -> Result<(), Error> {
|
||||
let mut block = Block::new(label_id);
|
||||
@ -1508,7 +1530,12 @@ impl<'w> BlockContext<'w> {
|
||||
self.function.consume(block, Instruction::branch(scope_id));
|
||||
|
||||
let merge_id = self.gen_id();
|
||||
self.write_block(scope_id, block_statements, Some(merge_id), loop_context)?;
|
||||
self.write_block(
|
||||
scope_id,
|
||||
block_statements,
|
||||
BlockExit::Branch { target: merge_id },
|
||||
loop_context,
|
||||
)?;
|
||||
|
||||
block = Block::new(merge_id);
|
||||
}
|
||||
@ -1546,10 +1573,20 @@ impl<'w> BlockContext<'w> {
|
||||
);
|
||||
|
||||
if let Some(block_id) = accept_id {
|
||||
self.write_block(block_id, accept, Some(merge_id), loop_context)?;
|
||||
self.write_block(
|
||||
block_id,
|
||||
accept,
|
||||
BlockExit::Branch { target: merge_id },
|
||||
loop_context,
|
||||
)?;
|
||||
}
|
||||
if let Some(block_id) = reject_id {
|
||||
self.write_block(block_id, reject, Some(merge_id), loop_context)?;
|
||||
self.write_block(
|
||||
block_id,
|
||||
reject,
|
||||
BlockExit::Branch { target: merge_id },
|
||||
loop_context,
|
||||
)?;
|
||||
}
|
||||
|
||||
block = Block::new(merge_id);
|
||||
@ -1611,7 +1648,9 @@ impl<'w> BlockContext<'w> {
|
||||
self.write_block(
|
||||
*label_id,
|
||||
&case.body,
|
||||
Some(case_finish_id),
|
||||
BlockExit::Branch {
|
||||
target: case_finish_id,
|
||||
},
|
||||
inner_context,
|
||||
)?;
|
||||
}
|
||||
@ -1619,7 +1658,12 @@ 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, &[], Some(merge_id), inner_context)?;
|
||||
self.write_block(
|
||||
default_id,
|
||||
&[],
|
||||
BlockExit::Branch { target: merge_id },
|
||||
inner_context,
|
||||
)?;
|
||||
}
|
||||
|
||||
block = Block::new(merge_id);
|
||||
@ -1627,6 +1671,7 @@ impl<'w> BlockContext<'w> {
|
||||
crate::Statement::Loop {
|
||||
ref body,
|
||||
ref continuing,
|
||||
break_if,
|
||||
} => {
|
||||
let preamble_id = self.gen_id();
|
||||
self.function
|
||||
@ -1649,17 +1694,29 @@ impl<'w> BlockContext<'w> {
|
||||
self.write_block(
|
||||
body_id,
|
||||
body,
|
||||
Some(continuing_id),
|
||||
BlockExit::Branch {
|
||||
target: continuing_id,
|
||||
},
|
||||
LoopContext {
|
||||
continuing_id: Some(continuing_id),
|
||||
break_id: Some(merge_id),
|
||||
},
|
||||
)?;
|
||||
|
||||
let exit = match break_if {
|
||||
Some(condition) => BlockExit::BreakIf {
|
||||
condition,
|
||||
preamble_id,
|
||||
},
|
||||
None => BlockExit::Branch {
|
||||
target: preamble_id,
|
||||
},
|
||||
};
|
||||
|
||||
self.write_block(
|
||||
continuing_id,
|
||||
continuing,
|
||||
Some(preamble_id),
|
||||
exit,
|
||||
LoopContext {
|
||||
continuing_id: None,
|
||||
break_id: Some(merge_id),
|
||||
@ -1955,12 +2012,10 @@ impl<'w> BlockContext<'w> {
|
||||
}
|
||||
}
|
||||
|
||||
let termination = match exit_id {
|
||||
Some(id) => Instruction::branch(id),
|
||||
// This can happen if the last branch had all the paths
|
||||
// leading out of the graph (i.e. returning).
|
||||
// Or it may be the end of the self.function.
|
||||
None => match self.ir_function.result {
|
||||
let termination = match exit {
|
||||
// We're generating code for the top-level Block of the function, so we
|
||||
// need to end it with some kind of return instruction.
|
||||
BlockExit::Return => match self.ir_function.result {
|
||||
Some(ref result) if self.function.entry_point_context.is_none() => {
|
||||
let type_id = self.get_type_id(LookupType::Handle(result.ty));
|
||||
let null_id = self.writer.write_constant_null(type_id);
|
||||
@ -1968,6 +2023,19 @@ impl<'w> BlockContext<'w> {
|
||||
}
|
||||
_ => Instruction::return_void(),
|
||||
},
|
||||
BlockExit::Branch { target } => Instruction::branch(target),
|
||||
BlockExit::BreakIf {
|
||||
condition,
|
||||
preamble_id,
|
||||
} => {
|
||||
let condition_id = self.cached[condition];
|
||||
|
||||
Instruction::branch_conditional(
|
||||
condition_id,
|
||||
loop_context.break_id.unwrap(),
|
||||
preamble_id,
|
||||
)
|
||||
}
|
||||
};
|
||||
|
||||
self.function.consume(block, termination);
|
||||
|
16
third_party/rust/naga/src/back/spv/writer.rs
vendored
16
third_party/rust/naga/src/back/spv/writer.rs
vendored
@ -8,7 +8,7 @@ use super::{
|
||||
use crate::{
|
||||
arena::{Handle, UniqueArena},
|
||||
back::spv::BindingInfo,
|
||||
proc::TypeResolution,
|
||||
proc::{Alignment, TypeResolution},
|
||||
valid::{FunctionInfo, ModuleInfo},
|
||||
};
|
||||
use spirv::Word;
|
||||
@ -574,7 +574,12 @@ impl Writer {
|
||||
context
|
||||
.function
|
||||
.consume(prelude, Instruction::branch(main_id));
|
||||
context.write_block(main_id, &ir_function.body, None, LoopContext::default())?;
|
||||
context.write_block(
|
||||
main_id,
|
||||
&ir_function.body,
|
||||
super::block::BlockExit::Return,
|
||||
LoopContext::default(),
|
||||
)?;
|
||||
|
||||
// Consume the `BlockContext`, ending its borrows and letting the
|
||||
// `Writer` steal back its cached expression table and temp_list.
|
||||
@ -1379,10 +1384,7 @@ impl Writer {
|
||||
width,
|
||||
} = *member_array_subty_inner
|
||||
{
|
||||
let byte_stride = match rows {
|
||||
crate::VectorSize::Bi => 2 * width,
|
||||
crate::VectorSize::Tri | crate::VectorSize::Quad => 4 * width,
|
||||
};
|
||||
let byte_stride = Alignment::from(rows) * width as u32;
|
||||
self.annotations.push(Instruction::member_decorate(
|
||||
struct_id,
|
||||
index as u32,
|
||||
@ -1393,7 +1395,7 @@ impl Writer {
|
||||
struct_id,
|
||||
index as u32,
|
||||
Decoration::MatrixStride,
|
||||
&[byte_stride as u32],
|
||||
&[byte_stride],
|
||||
));
|
||||
}
|
||||
|
||||
|
20
third_party/rust/naga/src/back/wgsl/writer.rs
vendored
20
third_party/rust/naga/src/back/wgsl/writer.rs
vendored
@ -908,6 +908,7 @@ impl<W: Write> Writer<W> {
|
||||
Statement::Loop {
|
||||
ref body,
|
||||
ref continuing,
|
||||
break_if,
|
||||
} => {
|
||||
write!(self.out, "{}", level)?;
|
||||
writeln!(self.out, "loop {{")?;
|
||||
@ -917,11 +918,26 @@ impl<W: Write> Writer<W> {
|
||||
self.write_stmt(module, sta, func_ctx, l2)?;
|
||||
}
|
||||
|
||||
if !continuing.is_empty() {
|
||||
// The continuing is optional so we don't need to write it if
|
||||
// it is empty, but the `break if` counts as a continuing statement
|
||||
// so even if `continuing` is empty we must generate it if a
|
||||
// `break if` exists
|
||||
if !continuing.is_empty() || break_if.is_some() {
|
||||
writeln!(self.out, "{}continuing {{", l2)?;
|
||||
for sta in continuing.iter() {
|
||||
self.write_stmt(module, sta, func_ctx, l2.next())?;
|
||||
}
|
||||
|
||||
// The `break if` is always the last
|
||||
// statement of the `continuing` block
|
||||
if let Some(condition) = break_if {
|
||||
// The trailing space is important
|
||||
write!(self.out, "{}break if ", l2.next())?;
|
||||
self.write_expr(module, condition, func_ctx)?;
|
||||
// Close the `break if` statement
|
||||
writeln!(self.out, ";")?;
|
||||
}
|
||||
|
||||
writeln!(self.out, "{}}}", l2)?;
|
||||
}
|
||||
|
||||
@ -1859,6 +1875,8 @@ impl<W: Write> Writer<W> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// See https://github.com/rust-lang/rust-clippy/issues/4979.
|
||||
#[allow(clippy::missing_const_for_fn)]
|
||||
pub fn finish(self) -> W {
|
||||
self.out
|
||||
}
|
||||
|
42
third_party/rust/naga/src/front/glsl/builtins.rs
vendored
42
third_party/rust/naga/src/front/glsl/builtins.rs
vendored
@ -1556,7 +1556,7 @@ fn inject_common_builtin(
|
||||
};
|
||||
declaration.overloads.push(module.add_builtin(
|
||||
vec![ty(), ty(), base_ty()],
|
||||
MacroCall::MathFunction(MathFunction::SmoothStep),
|
||||
MacroCall::SmoothStep { splatted: size },
|
||||
))
|
||||
}
|
||||
}
|
||||
@ -1604,6 +1604,12 @@ pub enum MacroCall {
|
||||
BitCast(Sk),
|
||||
Derivate(DerivativeAxis),
|
||||
Barrier,
|
||||
/// SmoothStep needs a separate variant because it might need it's inputs
|
||||
/// to be splatted depending on the overload
|
||||
SmoothStep {
|
||||
/// The size of the splat operation if some
|
||||
splatted: Option<VectorSize>,
|
||||
},
|
||||
}
|
||||
|
||||
impl MacroCall {
|
||||
@ -2072,6 +2078,22 @@ impl MacroCall {
|
||||
body.push(crate::Statement::Barrier(crate::Barrier::all()), meta);
|
||||
return Ok(None);
|
||||
}
|
||||
MacroCall::SmoothStep { splatted } => {
|
||||
ctx.implicit_splat(parser, &mut args[0], meta, splatted)?;
|
||||
ctx.implicit_splat(parser, &mut args[1], meta, splatted)?;
|
||||
|
||||
ctx.add_expression(
|
||||
Expression::Math {
|
||||
fun: MathFunction::SmoothStep,
|
||||
arg: args[0],
|
||||
arg1: args.get(1).copied(),
|
||||
arg2: args.get(2).copied(),
|
||||
arg3: None,
|
||||
},
|
||||
Span::default(),
|
||||
body,
|
||||
)
|
||||
}
|
||||
}))
|
||||
}
|
||||
}
|
||||
@ -2237,20 +2259,26 @@ pub fn sampled_to_depth(
|
||||
meta: Span,
|
||||
errors: &mut Vec<Error>,
|
||||
) {
|
||||
// Get the a mutable type handle of the underlying image storage
|
||||
let ty = match ctx[image] {
|
||||
Expression::GlobalVariable(handle) => &mut module.global_variables.get_mut(handle).ty,
|
||||
Expression::FunctionArgument(i) => {
|
||||
// Mark the function argument as carrying a depth texture
|
||||
ctx.parameters_info[i as usize].depth = true;
|
||||
// NOTE: We need to later also change the parameter type
|
||||
&mut ctx.arguments[i as usize].ty
|
||||
}
|
||||
_ => {
|
||||
// Only globals and function arguments are allowed to carry an image
|
||||
return errors.push(Error {
|
||||
kind: ErrorKind::SemanticError("Not a valid texture expression".into()),
|
||||
meta,
|
||||
})
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
match module.types[*ty].inner {
|
||||
// Update the image class to depth in case it already isn't
|
||||
TypeInner::Image {
|
||||
class,
|
||||
dim,
|
||||
@ -2270,6 +2298,7 @@ pub fn sampled_to_depth(
|
||||
)
|
||||
}
|
||||
ImageClass::Depth { .. } => {}
|
||||
// Other image classes aren't allowed to be transformed to depth
|
||||
_ => errors.push(Error {
|
||||
kind: ErrorKind::SemanticError("Not a texture".into()),
|
||||
meta,
|
||||
@ -2280,6 +2309,15 @@ pub fn sampled_to_depth(
|
||||
meta,
|
||||
}),
|
||||
};
|
||||
|
||||
// Copy the handle to allow borrowing the `ctx` again
|
||||
let ty = *ty;
|
||||
|
||||
// If the image was passed trough a function argument we also need to change
|
||||
// the corresponding parameter
|
||||
if let Expression::FunctionArgument(i) = ctx[image] {
|
||||
ctx.parameters[i as usize] = ty;
|
||||
}
|
||||
}
|
||||
|
||||
bitflags::bitflags! {
|
||||
|
15
third_party/rust/naga/src/front/glsl/context.rs
vendored
15
third_party/rust/naga/src/front/glsl/context.rs
vendored
@ -617,11 +617,14 @@ impl Context {
|
||||
width: right_width,
|
||||
},
|
||||
) => {
|
||||
let dimensions_ok = if op == BinaryOperator::Multiply {
|
||||
left_columns == right_rows
|
||||
} else {
|
||||
left_columns == right_columns && left_rows == right_rows
|
||||
};
|
||||
|
||||
// Check that the two arguments have the same dimensions
|
||||
if left_columns != right_columns
|
||||
|| left_rows != right_rows
|
||||
|| left_width != right_width
|
||||
{
|
||||
if !dimensions_ok || left_width != right_width {
|
||||
parser.errors.push(Error {
|
||||
kind: ErrorKind::SemanticError(
|
||||
format!(
|
||||
@ -1174,6 +1177,10 @@ impl Context {
|
||||
self.emit_end(&mut reject_body);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Technically there's nothing to flush but later we will need to
|
||||
// add some expressions that must not be emitted.
|
||||
self.emit_end(body)
|
||||
}
|
||||
|
||||
// We need to get the type of the resulting expression to create the local,
|
||||
|
@ -671,6 +671,13 @@ impl Parser {
|
||||
let overload_param_ty = &self.module.types[*overload_parameter].inner;
|
||||
let call_arg_ty = self.resolve_type(ctx, call_argument.0, call_argument.1)?;
|
||||
|
||||
log::trace!(
|
||||
"Testing parameter {}\n\tOverload = {:?}\n\tCall = {:?}",
|
||||
i,
|
||||
overload_param_ty,
|
||||
call_arg_ty
|
||||
);
|
||||
|
||||
// Storage images cannot be directly compared since while the access is part of the
|
||||
// type in naga's IR, in glsl they are a qualifier and don't enter in the match as
|
||||
// long as the access needed is satisfied.
|
||||
|
44
third_party/rust/naga/src/front/glsl/offset.rs
vendored
44
third_party/rust/naga/src/front/glsl/offset.rs
vendored
@ -16,7 +16,7 @@ use super::{
|
||||
error::{Error, ErrorKind},
|
||||
Span,
|
||||
};
|
||||
use crate::{front::align_up, Arena, Constant, Handle, Type, TypeInner, UniqueArena};
|
||||
use crate::{proc::Alignment, Arena, Constant, Handle, Type, TypeInner, UniqueArena};
|
||||
|
||||
/// Struct with information needed for defining a struct member.
|
||||
///
|
||||
@ -28,7 +28,7 @@ pub struct TypeAlignSpan {
|
||||
/// with a different stride set.
|
||||
pub ty: Handle<Type>,
|
||||
/// The alignment required by the type.
|
||||
pub align: u32,
|
||||
pub align: Alignment,
|
||||
/// The size of the type.
|
||||
pub span: u32,
|
||||
}
|
||||
@ -54,15 +54,15 @@ pub fn calculate_offset(
|
||||
let (align, span) = match types[ty].inner {
|
||||
// 1. If the member is a scalar consuming N basic machine units,
|
||||
// the base alignment is N.
|
||||
TypeInner::Scalar { width, .. } => (width as u32, width as u32),
|
||||
TypeInner::Scalar { width, .. } => (Alignment::from_width(width), width as u32),
|
||||
// 2. If the member is a two- or four-component vector with components
|
||||
// consuming N basic machine units, the base alignment is 2N or 4N, respectively.
|
||||
// 3. If the member is a three-component vector with components consuming N
|
||||
// basic machine units, the base alignment is 4N.
|
||||
TypeInner::Vector { size, width, .. } => match size {
|
||||
crate::VectorSize::Tri => (4 * width as u32, 3 * width as u32),
|
||||
_ => (size as u32 * width as u32, size as u32 * width as u32),
|
||||
},
|
||||
TypeInner::Vector { size, width, .. } => (
|
||||
Alignment::from(size) * Alignment::from_width(width),
|
||||
size as u32 * width as u32,
|
||||
),
|
||||
// 4. If the member is an array of scalars or vectors, the base alignment and array
|
||||
// stride are set to match the base alignment of a single array element, according
|
||||
// to rules (1), (2), and (3), and rounded up to the base alignment of a vec4.
|
||||
@ -71,14 +71,14 @@ pub fn calculate_offset(
|
||||
let info = calculate_offset(base, meta, layout, types, constants, errors);
|
||||
|
||||
let name = types[ty].name.clone();
|
||||
let mut align = info.align;
|
||||
let mut stride = (align).max(info.span);
|
||||
|
||||
// See comment at the beginning of the function
|
||||
if StructLayout::Std430 != layout {
|
||||
stride = align_up(stride, 16);
|
||||
align = align_up(align, 16);
|
||||
}
|
||||
let (align, stride) = if StructLayout::Std430 == layout {
|
||||
(info.align, info.align.round_up(info.span))
|
||||
} else {
|
||||
let align = info.align.max(Alignment::MIN_UNIFORM);
|
||||
(align, align.round_up(info.span))
|
||||
};
|
||||
|
||||
let span = match size {
|
||||
crate::ArraySize::Constant(s) => {
|
||||
@ -111,14 +111,11 @@ pub fn calculate_offset(
|
||||
rows,
|
||||
width,
|
||||
} => {
|
||||
let mut align = match rows {
|
||||
crate::VectorSize::Tri => (4 * width as u32),
|
||||
_ => (rows as u32 * width as u32),
|
||||
};
|
||||
let mut align = Alignment::from(rows) * Alignment::from_width(width);
|
||||
|
||||
// See comment at the beginning of the function
|
||||
if StructLayout::Std430 != layout {
|
||||
align = align_up(align, 16);
|
||||
align = align.max(Alignment::MIN_UNIFORM);
|
||||
}
|
||||
|
||||
// See comment on the error kind
|
||||
@ -133,15 +130,16 @@ pub fn calculate_offset(
|
||||
}
|
||||
TypeInner::Struct { ref members, .. } => {
|
||||
let mut span = 0;
|
||||
let mut align = 0;
|
||||
let mut align = Alignment::ONE;
|
||||
let mut members = members.clone();
|
||||
let name = types[ty].name.clone();
|
||||
|
||||
for member in members.iter_mut() {
|
||||
let info = calculate_offset(member.ty, meta, layout, types, constants, errors);
|
||||
|
||||
span = align_up(span, info.align);
|
||||
align = align.max(info.align);
|
||||
let member_alignment = info.align;
|
||||
span = member_alignment.round_up(span);
|
||||
align = member_alignment.max(align);
|
||||
|
||||
member.ty = info.ty;
|
||||
member.offset = span;
|
||||
@ -149,7 +147,7 @@ pub fn calculate_offset(
|
||||
span += info.span;
|
||||
}
|
||||
|
||||
span = align_up(span, align);
|
||||
span = align.round_up(span);
|
||||
|
||||
let ty_span = types.get_span(ty);
|
||||
ty = types.insert(
|
||||
@ -167,7 +165,7 @@ pub fn calculate_offset(
|
||||
kind: ErrorKind::SemanticError("Invalid struct member type".into()),
|
||||
meta,
|
||||
});
|
||||
(1, 0)
|
||||
(Alignment::ONE, 0)
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -12,6 +12,7 @@ use crate::{
|
||||
variables::{GlobalOrConstant, VarDeclaration},
|
||||
Error, ErrorKind, Parser, Span,
|
||||
},
|
||||
proc::Alignment,
|
||||
AddressSpace, Block, Expression, FunctionResult, Handle, ScalarKind, Statement, StructMember,
|
||||
Type, TypeInner,
|
||||
};
|
||||
@ -570,7 +571,7 @@ impl<'source> ParsingContext<'source> {
|
||||
layout: StructLayout,
|
||||
) -> Result<u32> {
|
||||
let mut span = 0;
|
||||
let mut align = 0;
|
||||
let mut align = Alignment::ONE;
|
||||
|
||||
loop {
|
||||
// TODO: type_qualifier
|
||||
@ -593,8 +594,9 @@ impl<'source> ParsingContext<'source> {
|
||||
&mut parser.errors,
|
||||
);
|
||||
|
||||
span = crate::front::align_up(span, info.align);
|
||||
align = align.max(info.align);
|
||||
let member_alignment = info.align;
|
||||
span = member_alignment.round_up(span);
|
||||
align = member_alignment.max(align);
|
||||
|
||||
members.push(StructMember {
|
||||
name: Some(name),
|
||||
@ -610,7 +612,7 @@ impl<'source> ParsingContext<'source> {
|
||||
}
|
||||
}
|
||||
|
||||
span = crate::front::align_up(span, align);
|
||||
span = align.round_up(span);
|
||||
|
||||
Ok(span)
|
||||
}
|
||||
|
@ -177,6 +177,8 @@ impl<'source> ParsingContext<'source> {
|
||||
ctx.emit_restart(body);
|
||||
|
||||
let mut cases = Vec::new();
|
||||
// Track if any default case is present in the switch statement.
|
||||
let mut default_present = false;
|
||||
|
||||
self.expect(parser, TokenValue::LeftBrace)?;
|
||||
loop {
|
||||
@ -215,6 +217,7 @@ impl<'source> ParsingContext<'source> {
|
||||
}
|
||||
TokenValue::Default => {
|
||||
self.bump(parser)?;
|
||||
default_present = true;
|
||||
crate::SwitchValue::Default
|
||||
}
|
||||
TokenValue::RightBrace => {
|
||||
@ -273,6 +276,40 @@ impl<'source> ParsingContext<'source> {
|
||||
|
||||
meta.subsume(end_meta);
|
||||
|
||||
// NOTE: do not unwrap here since a switch statement isn't required
|
||||
// to have any cases.
|
||||
if let Some(case) = cases.last_mut() {
|
||||
// GLSL requires that the last case not be empty, so we check
|
||||
// that here and produce an error otherwise (fall_trough must
|
||||
// also be checked because `break`s count as statements but
|
||||
// they aren't added to the body)
|
||||
if case.body.is_empty() && case.fall_through {
|
||||
parser.errors.push(Error {
|
||||
kind: ErrorKind::SemanticError(
|
||||
"last case/default label must be followed by statements".into(),
|
||||
),
|
||||
meta,
|
||||
})
|
||||
}
|
||||
|
||||
// GLSL allows the last case to not have any `break` statement,
|
||||
// this would mark it as fall trough but naga's IR requires that
|
||||
// the last case must not be fall trough, so we mark need to mark
|
||||
// the last case as not fall trough always.
|
||||
case.fall_through = false;
|
||||
}
|
||||
|
||||
// Add an empty default case in case non was present, this is needed because
|
||||
// naga's IR requires that all switch statements must have a default case but
|
||||
// GLSL doesn't require that, so we might need to add an empty default case.
|
||||
if !default_present {
|
||||
cases.push(SwitchCase {
|
||||
value: crate::SwitchValue::Default,
|
||||
body: Block::new(),
|
||||
fall_through: false,
|
||||
})
|
||||
}
|
||||
|
||||
body.push(Statement::Switch { selector, cases }, meta);
|
||||
|
||||
meta
|
||||
@ -321,6 +358,7 @@ impl<'source> ParsingContext<'source> {
|
||||
Statement::Loop {
|
||||
body: loop_body,
|
||||
continuing: Block::new(),
|
||||
break_if: None,
|
||||
},
|
||||
meta,
|
||||
);
|
||||
@ -374,6 +412,7 @@ impl<'source> ParsingContext<'source> {
|
||||
Statement::Loop {
|
||||
body: loop_body,
|
||||
continuing: Block::new(),
|
||||
break_if: None,
|
||||
},
|
||||
meta,
|
||||
);
|
||||
@ -476,6 +515,7 @@ impl<'source> ParsingContext<'source> {
|
||||
Statement::Loop {
|
||||
body: block,
|
||||
continuing,
|
||||
break_if: None,
|
||||
},
|
||||
meta,
|
||||
);
|
||||
|
17
third_party/rust/naga/src/front/mod.rs
vendored
17
third_party/rust/naga/src/front/mod.rs
vendored
@ -133,20 +133,3 @@ impl ops::Index<Handle<crate::Expression>> for Typifier {
|
||||
&self.resolutions[handle.index()]
|
||||
}
|
||||
}
|
||||
|
||||
/// Helper function used for aligning `value` to the next multiple of `align`
|
||||
///
|
||||
/// # Notes:
|
||||
/// - `align` must be a power of two.
|
||||
/// - The returned value will be greater or equal to `value`.
|
||||
/// # Examples:
|
||||
/// ```ignore
|
||||
/// assert_eq!(0, align_up(0, 16));
|
||||
/// assert_eq!(16, align_up(1, 16));
|
||||
/// assert_eq!(16, align_up(16, 16));
|
||||
/// assert_eq!(334, align_up(333, 2));
|
||||
/// assert_eq!(384, align_up(257, 128));
|
||||
/// ```
|
||||
pub const fn align_up(value: u32, align: u32) -> u32 {
|
||||
((value.wrapping_sub(1)) & !(align - 1)).wrapping_add(align)
|
||||
}
|
||||
|
@ -556,7 +556,11 @@ impl<'function> BlockContext<'function> {
|
||||
let continuing = lower_impl(blocks, bodies, continuing);
|
||||
|
||||
block.push(
|
||||
crate::Statement::Loop { body, continuing },
|
||||
crate::Statement::Loop {
|
||||
body,
|
||||
continuing,
|
||||
break_if: None,
|
||||
},
|
||||
crate::Span::default(),
|
||||
)
|
||||
}
|
||||
|
47
third_party/rust/naga/src/front/spv/mod.rs
vendored
47
third_party/rust/naga/src/front/spv/mod.rs
vendored
@ -39,7 +39,7 @@ use function::*;
|
||||
|
||||
use crate::{
|
||||
arena::{Arena, Handle, UniqueArena},
|
||||
proc::Layouter,
|
||||
proc::{Alignment, Layouter},
|
||||
FastHashMap, FastHashSet,
|
||||
};
|
||||
|
||||
@ -71,6 +71,7 @@ pub const SUPPORTED_CAPABILITIES: &[spirv::Capability] = &[
|
||||
spirv::Capability::Float16,
|
||||
spirv::Capability::Float64,
|
||||
spirv::Capability::Geometry,
|
||||
spirv::Capability::MultiView,
|
||||
// tricky ones
|
||||
spirv::Capability::UniformBufferArrayDynamicIndexing,
|
||||
spirv::Capability::StorageBufferArrayDynamicIndexing,
|
||||
@ -305,7 +306,8 @@ struct LookupExpression {
|
||||
///
|
||||
/// Note that, while a SPIR-V result id can be used in any block dominated
|
||||
/// by its definition, a Naga `Expression` is only in scope for the rest of
|
||||
/// its subtree. `Parser::get_expr_handle` takes care of
|
||||
/// its subtree. `Parser::get_expr_handle` takes care of spilling the result
|
||||
/// to a `LocalVariable` which can then be used anywhere.
|
||||
handle: Handle<crate::Expression>,
|
||||
|
||||
/// The SPIR-V type of this result.
|
||||
@ -2391,6 +2393,34 @@ impl<I: Iterator<Item = u32>> Parser<I> {
|
||||
},
|
||||
);
|
||||
}
|
||||
Op::BitReverse | Op::BitCount => {
|
||||
inst.expect(4)?;
|
||||
|
||||
let result_type_id = self.next()?;
|
||||
let result_id = self.next()?;
|
||||
let base_id = self.next()?;
|
||||
let base_lexp = self.lookup_expression.lookup(base_id)?;
|
||||
let base_handle = get_expr_handle!(base_id, base_lexp);
|
||||
let expr = crate::Expression::Math {
|
||||
fun: match inst.op {
|
||||
Op::BitReverse => crate::MathFunction::ReverseBits,
|
||||
Op::BitCount => crate::MathFunction::CountOneBits,
|
||||
_ => unreachable!(),
|
||||
},
|
||||
arg: base_handle,
|
||||
arg1: None,
|
||||
arg2: None,
|
||||
arg3: None,
|
||||
};
|
||||
self.lookup_expression.insert(
|
||||
result_id,
|
||||
LookupExpression {
|
||||
handle: ctx.expressions.append(expr, span),
|
||||
type_id: result_type_id,
|
||||
block_id,
|
||||
},
|
||||
);
|
||||
}
|
||||
Op::OuterProduct => {
|
||||
inst.expect(5)?;
|
||||
|
||||
@ -3535,6 +3565,7 @@ impl<I: Iterator<Item = u32>> Parser<I> {
|
||||
S::Loop {
|
||||
ref mut body,
|
||||
ref mut continuing,
|
||||
break_if: _,
|
||||
} => {
|
||||
self.patch_statements(body, expressions, fun_parameter_sampling)?;
|
||||
self.patch_statements(continuing, expressions, fun_parameter_sampling)?;
|
||||
@ -4347,7 +4378,7 @@ impl<I: Iterator<Item = u32>> Parser<I> {
|
||||
let mut member_lookups = Vec::with_capacity(members.capacity());
|
||||
let mut storage_access = crate::StorageAccess::empty();
|
||||
let mut span = 0;
|
||||
let mut alignment = 1;
|
||||
let mut alignment = Alignment::ONE;
|
||||
for i in 0..u32::from(inst.wc) - 2 {
|
||||
let type_id = self.next()?;
|
||||
let ty = self.lookup_type.lookup(type_id)?.handle;
|
||||
@ -4363,8 +4394,9 @@ impl<I: Iterator<Item = u32>> Parser<I> {
|
||||
row_major: decor.matrix_major == Some(Majority::Row),
|
||||
});
|
||||
|
||||
span = crate::front::align_up(span, self.layouter[ty].alignment.get());
|
||||
alignment = self.layouter[ty].alignment.get().max(alignment);
|
||||
let member_alignment = self.layouter[ty].alignment;
|
||||
span = member_alignment.round_up(span);
|
||||
alignment = member_alignment.max(alignment);
|
||||
|
||||
let mut binding = decor.io_binding().ok();
|
||||
if let Some(offset) = decor.offset {
|
||||
@ -4382,8 +4414,7 @@ impl<I: Iterator<Item = u32>> Parser<I> {
|
||||
} = *inner
|
||||
{
|
||||
if let Some(stride) = decor.matrix_stride {
|
||||
let aligned_rows = if rows > crate::VectorSize::Bi { 4 } else { 2 };
|
||||
let expected_stride = aligned_rows * width as u32;
|
||||
let expected_stride = Alignment::from(rows) * width as u32;
|
||||
if stride.get() != expected_stride {
|
||||
return Err(Error::UnsupportedMatrixStride {
|
||||
stride: stride.get(),
|
||||
@ -4406,7 +4437,7 @@ impl<I: Iterator<Item = u32>> Parser<I> {
|
||||
});
|
||||
}
|
||||
|
||||
span = crate::front::align_up(span, alignment);
|
||||
span = alignment.round_up(span);
|
||||
|
||||
let inner = crate::TypeInner::Struct { span, members };
|
||||
|
||||
|
@ -196,9 +196,7 @@ fn parse_constructor_type<'a>(
|
||||
}
|
||||
(Token::Paren('<'), ConstructorType::PartialArray) => {
|
||||
lexer.expect_generic_paren('<')?;
|
||||
let base = parser
|
||||
.parse_type_decl(lexer, None, type_arena, const_arena)?
|
||||
.0;
|
||||
let base = parser.parse_type_decl(lexer, None, type_arena, const_arena)?;
|
||||
let size = if lexer.skip(Token::Separator(',')) {
|
||||
let const_handle = parser.parse_const_expression(lexer, type_arena, const_arena)?;
|
||||
ArraySize::Constant(const_handle)
|
||||
|
463
third_party/rust/naga/src/front/wgsl/lexer.rs
vendored
463
third_party/rust/naga/src/front/wgsl/lexer.rs
vendored
@ -1,269 +1,10 @@
|
||||
use super::{conv, Error, ExpectedToken, NumberType, Span, Token, TokenSpan};
|
||||
use super::{conv, number::consume_number, Error, ExpectedToken, Span, Token, TokenSpan};
|
||||
|
||||
fn consume_any(input: &str, what: impl Fn(char) -> bool) -> (&str, &str) {
|
||||
let pos = input.find(|c| !what(c)).unwrap_or(input.len());
|
||||
input.split_at(pos)
|
||||
}
|
||||
|
||||
/// Tries to skip a given prefix in the input string.
|
||||
/// Returns whether the prefix was present and could therefore be skipped,
|
||||
/// the remaining str and the number of *bytes* skipped.
|
||||
pub fn try_skip_prefix<'a, 'b>(input: &'a str, prefix: &'b str) -> (bool, &'a str, usize) {
|
||||
if let Some(rem) = input.strip_prefix(prefix) {
|
||||
(true, rem, prefix.len())
|
||||
} else {
|
||||
(false, input, 0)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, PartialEq, Eq)]
|
||||
enum NLDigitState {
|
||||
Nothing,
|
||||
LeadingZero,
|
||||
DigitBeforeDot,
|
||||
OnlyDot,
|
||||
DigitsThenDot,
|
||||
DigitAfterDot,
|
||||
Exponent,
|
||||
SignAfterExponent,
|
||||
DigitAfterExponent,
|
||||
}
|
||||
|
||||
struct NumberLexerState {
|
||||
_minus: bool,
|
||||
hex: bool,
|
||||
leading_zeros: usize,
|
||||
digit_state: NLDigitState,
|
||||
uint_suffix: bool,
|
||||
}
|
||||
|
||||
impl NumberLexerState {
|
||||
// TODO: add proper error reporting, possibly through try_into_token function returning Result
|
||||
|
||||
pub fn _is_valid_number(&self) -> bool {
|
||||
match *self {
|
||||
Self {
|
||||
_minus: false, // No negative zero for integers.
|
||||
hex,
|
||||
leading_zeros,
|
||||
digit_state: NLDigitState::LeadingZero,
|
||||
..
|
||||
} => hex || leading_zeros == 1, // No leading zeros allowed in non-hex integers, "0" is always allowed.
|
||||
Self {
|
||||
_minus: minus,
|
||||
hex,
|
||||
leading_zeros,
|
||||
digit_state: NLDigitState::DigitBeforeDot,
|
||||
uint_suffix,
|
||||
} => {
|
||||
(hex || leading_zeros == 0) // No leading zeros allowed in non-hex integers.
|
||||
// In this state the number has non-zero digits,
|
||||
// i.e. it is not just "0".
|
||||
&& (minus ^ uint_suffix) // Either a negative number, or and unsigned integer, not both.
|
||||
}
|
||||
_ => self.is_float(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_float(&self) -> bool {
|
||||
!self.uint_suffix
|
||||
&& (self.digit_state == NLDigitState::DigitsThenDot
|
||||
|| self.digit_state == NLDigitState::DigitAfterDot
|
||||
|| self.digit_state == NLDigitState::DigitAfterExponent)
|
||||
}
|
||||
}
|
||||
|
||||
fn consume_number(input: &str) -> (Token, &str) {
|
||||
let (minus, working_substr, minus_offset) = try_skip_prefix(input, "-");
|
||||
|
||||
let (hex, working_substr, hex_offset) = try_skip_prefix(working_substr, "0x");
|
||||
|
||||
let mut state = NumberLexerState {
|
||||
_minus: minus,
|
||||
hex,
|
||||
leading_zeros: 0,
|
||||
digit_state: NLDigitState::Nothing,
|
||||
uint_suffix: false,
|
||||
};
|
||||
|
||||
let mut what = |c| {
|
||||
match state {
|
||||
NumberLexerState {
|
||||
uint_suffix: true, ..
|
||||
} => return false, // Scanning is done once we've reached a type suffix.
|
||||
NumberLexerState {
|
||||
hex,
|
||||
digit_state: NLDigitState::Nothing,
|
||||
..
|
||||
} => match c {
|
||||
'0' => {
|
||||
state.digit_state = NLDigitState::LeadingZero;
|
||||
state.leading_zeros += 1;
|
||||
}
|
||||
'1'..='9' => {
|
||||
state.digit_state = NLDigitState::DigitBeforeDot;
|
||||
}
|
||||
'a'..='f' | 'A'..='F' if hex => {
|
||||
state.digit_state = NLDigitState::DigitBeforeDot;
|
||||
}
|
||||
'.' => {
|
||||
state.digit_state = NLDigitState::OnlyDot;
|
||||
}
|
||||
_ => return false,
|
||||
},
|
||||
|
||||
NumberLexerState {
|
||||
hex,
|
||||
digit_state: NLDigitState::LeadingZero,
|
||||
..
|
||||
} => match c {
|
||||
'0' => {
|
||||
// We stay in NLDigitState::LeadingZero.
|
||||
state.leading_zeros += 1;
|
||||
}
|
||||
'1'..='9' => {
|
||||
state.digit_state = NLDigitState::DigitBeforeDot;
|
||||
}
|
||||
'a'..='f' | 'A'..='F' if hex => {
|
||||
state.digit_state = NLDigitState::DigitBeforeDot;
|
||||
}
|
||||
'.' => {
|
||||
state.digit_state = NLDigitState::DigitsThenDot;
|
||||
}
|
||||
'e' | 'E' if !hex => {
|
||||
state.digit_state = NLDigitState::Exponent;
|
||||
}
|
||||
'p' | 'P' if hex => {
|
||||
state.digit_state = NLDigitState::Exponent;
|
||||
}
|
||||
'u' => {
|
||||
// We stay in NLDigitState::LeadingZero.
|
||||
state.uint_suffix = true;
|
||||
}
|
||||
_ => return false,
|
||||
},
|
||||
|
||||
NumberLexerState {
|
||||
hex,
|
||||
digit_state: NLDigitState::DigitBeforeDot,
|
||||
..
|
||||
} => match c {
|
||||
'0'..='9' => {
|
||||
// We stay in NLDigitState::DigitBeforeDot.
|
||||
}
|
||||
'a'..='f' | 'A'..='F' if hex => {
|
||||
// We stay in NLDigitState::DigitBeforeDot.
|
||||
}
|
||||
'.' => {
|
||||
state.digit_state = NLDigitState::DigitsThenDot;
|
||||
}
|
||||
'e' | 'E' if !hex => {
|
||||
state.digit_state = NLDigitState::Exponent;
|
||||
}
|
||||
'p' | 'P' if hex => {
|
||||
state.digit_state = NLDigitState::Exponent;
|
||||
}
|
||||
'u' => {
|
||||
// We stay in NLDigitState::DigitBeforeDot.
|
||||
state.uint_suffix = true;
|
||||
}
|
||||
_ => return false,
|
||||
},
|
||||
|
||||
NumberLexerState {
|
||||
hex,
|
||||
digit_state: NLDigitState::OnlyDot,
|
||||
..
|
||||
} => match c {
|
||||
'0'..='9' => {
|
||||
state.digit_state = NLDigitState::DigitAfterDot;
|
||||
}
|
||||
'a'..='f' | 'A'..='F' if hex => {
|
||||
state.digit_state = NLDigitState::DigitAfterDot;
|
||||
}
|
||||
_ => return false,
|
||||
},
|
||||
|
||||
NumberLexerState {
|
||||
hex,
|
||||
digit_state: NLDigitState::DigitsThenDot | NLDigitState::DigitAfterDot,
|
||||
..
|
||||
} => match c {
|
||||
'0'..='9' => {
|
||||
state.digit_state = NLDigitState::DigitAfterDot;
|
||||
}
|
||||
'a'..='f' | 'A'..='F' if hex => {
|
||||
state.digit_state = NLDigitState::DigitAfterDot;
|
||||
}
|
||||
'e' | 'E' if !hex => {
|
||||
state.digit_state = NLDigitState::Exponent;
|
||||
}
|
||||
'p' | 'P' if hex => {
|
||||
state.digit_state = NLDigitState::Exponent;
|
||||
}
|
||||
_ => return false,
|
||||
},
|
||||
|
||||
NumberLexerState {
|
||||
digit_state: NLDigitState::Exponent,
|
||||
..
|
||||
} => match c {
|
||||
'0'..='9' => {
|
||||
state.digit_state = NLDigitState::DigitAfterExponent;
|
||||
}
|
||||
'-' | '+' => {
|
||||
state.digit_state = NLDigitState::SignAfterExponent;
|
||||
}
|
||||
_ => return false,
|
||||
},
|
||||
|
||||
NumberLexerState {
|
||||
digit_state: NLDigitState::SignAfterExponent | NLDigitState::DigitAfterExponent,
|
||||
..
|
||||
} => match c {
|
||||
'0'..='9' => {
|
||||
state.digit_state = NLDigitState::DigitAfterExponent;
|
||||
}
|
||||
_ => return false,
|
||||
},
|
||||
}
|
||||
|
||||
// No match branch has rejected this yet, so we are still in a number literal
|
||||
true
|
||||
};
|
||||
|
||||
let pos = working_substr
|
||||
.find(|c| !what(c))
|
||||
.unwrap_or(working_substr.len());
|
||||
let (value, rest) = input.split_at(pos + minus_offset + hex_offset);
|
||||
|
||||
// NOTE: This code can use string slicing,
|
||||
// because number literals are exclusively ASCII.
|
||||
// This means all relevant characters fit into one byte
|
||||
// and using string slicing (which slices UTF-8 bytes) works for us.
|
||||
|
||||
// TODO: A syntax error can already be recognized here, possibly report it at this stage.
|
||||
|
||||
// Return possibly knowably incorrect (given !state.is_valid_number()) token for now.
|
||||
(
|
||||
Token::Number {
|
||||
value: if state.uint_suffix {
|
||||
&value[..value.len() - 1]
|
||||
} else {
|
||||
value
|
||||
},
|
||||
ty: if state.uint_suffix {
|
||||
NumberType::Uint
|
||||
} else if state.is_float() {
|
||||
NumberType::Float
|
||||
} else {
|
||||
NumberType::Sint
|
||||
},
|
||||
},
|
||||
rest,
|
||||
)
|
||||
}
|
||||
|
||||
fn consume_token(input: &str, generic: bool) -> (Token<'_>, &str) {
|
||||
let mut chars = input.chars();
|
||||
let cur = match chars.next() {
|
||||
@ -632,6 +373,9 @@ impl<'a> Lexer<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
use super::{number::Number, NumberError};
|
||||
|
||||
#[cfg(test)]
|
||||
fn sub_test(source: &str, expected_tokens: &[Token]) {
|
||||
let mut lex = Lexer::new(source);
|
||||
@ -641,41 +385,195 @@ fn sub_test(source: &str, expected_tokens: &[Token]) {
|
||||
assert_eq!(lex.next().0, Token::End);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_numbers() {
|
||||
// WGSL spec examples //
|
||||
|
||||
// decimal integer
|
||||
sub_test(
|
||||
"0x123 0X123u 1u 123 0 0i 0x3f",
|
||||
&[
|
||||
Token::Number(Ok(Number::I32(291))),
|
||||
Token::Number(Ok(Number::U32(291))),
|
||||
Token::Number(Ok(Number::U32(1))),
|
||||
Token::Number(Ok(Number::I32(123))),
|
||||
Token::Number(Ok(Number::I32(0))),
|
||||
Token::Number(Ok(Number::I32(0))),
|
||||
Token::Number(Ok(Number::I32(63))),
|
||||
],
|
||||
);
|
||||
// decimal floating point
|
||||
sub_test(
|
||||
"0.e+4f 01. .01 12.34 .0f 0h 1e-3 0xa.fp+2 0x1P+4f 0X.3 0x3p+2h 0X1.fp-4 0x3.2p+2h",
|
||||
&[
|
||||
Token::Number(Ok(Number::F32(0.))),
|
||||
Token::Number(Ok(Number::F32(1.))),
|
||||
Token::Number(Ok(Number::F32(0.01))),
|
||||
Token::Number(Ok(Number::F32(12.34))),
|
||||
Token::Number(Ok(Number::F32(0.))),
|
||||
Token::Number(Err(NumberError::UnimplementedF16)),
|
||||
Token::Number(Ok(Number::F32(0.001))),
|
||||
Token::Number(Ok(Number::F32(43.75))),
|
||||
Token::Number(Ok(Number::F32(16.))),
|
||||
Token::Number(Ok(Number::F32(0.1875))),
|
||||
Token::Number(Err(NumberError::UnimplementedF16)),
|
||||
Token::Number(Ok(Number::F32(0.12109375))),
|
||||
Token::Number(Err(NumberError::UnimplementedF16)),
|
||||
],
|
||||
);
|
||||
|
||||
// MIN / MAX //
|
||||
|
||||
// min / max decimal signed integer
|
||||
sub_test(
|
||||
"-2147483648i 2147483647i -2147483649i 2147483648i",
|
||||
&[
|
||||
Token::Number(Ok(Number::I32(i32::MIN))),
|
||||
Token::Number(Ok(Number::I32(i32::MAX))),
|
||||
Token::Number(Err(NumberError::NotRepresentable)),
|
||||
Token::Number(Err(NumberError::NotRepresentable)),
|
||||
],
|
||||
);
|
||||
// min / max decimal unsigned integer
|
||||
sub_test(
|
||||
"0u 4294967295u -1u 4294967296u",
|
||||
&[
|
||||
Token::Number(Ok(Number::U32(u32::MIN))),
|
||||
Token::Number(Ok(Number::U32(u32::MAX))),
|
||||
Token::Number(Err(NumberError::NotRepresentable)),
|
||||
Token::Number(Err(NumberError::NotRepresentable)),
|
||||
],
|
||||
);
|
||||
|
||||
// min / max hexadecimal signed integer
|
||||
sub_test(
|
||||
"-0x80000000i 0x7FFFFFFFi -0x80000001i 0x80000000i",
|
||||
&[
|
||||
Token::Number(Ok(Number::I32(i32::MIN))),
|
||||
Token::Number(Ok(Number::I32(i32::MAX))),
|
||||
Token::Number(Err(NumberError::NotRepresentable)),
|
||||
Token::Number(Err(NumberError::NotRepresentable)),
|
||||
],
|
||||
);
|
||||
// min / max hexadecimal unsigned integer
|
||||
sub_test(
|
||||
"0x0u 0xFFFFFFFFu -0x1u 0x100000000u",
|
||||
&[
|
||||
Token::Number(Ok(Number::U32(u32::MIN))),
|
||||
Token::Number(Ok(Number::U32(u32::MAX))),
|
||||
Token::Number(Err(NumberError::NotRepresentable)),
|
||||
Token::Number(Err(NumberError::NotRepresentable)),
|
||||
],
|
||||
);
|
||||
|
||||
/// ≈ 2^-126 * 2^−23 (= 2^−149)
|
||||
const SMALLEST_POSITIVE_SUBNORMAL_F32: f32 = 1e-45;
|
||||
/// ≈ 2^-126 * (1 − 2^−23)
|
||||
const LARGEST_SUBNORMAL_F32: f32 = 1.1754942e-38;
|
||||
/// ≈ 2^-126
|
||||
const SMALLEST_POSITIVE_NORMAL_F32: f32 = f32::MIN_POSITIVE;
|
||||
/// ≈ 1 − 2^−24
|
||||
const LARGEST_F32_LESS_THAN_ONE: f32 = 0.99999994;
|
||||
/// ≈ 1 + 2^−23
|
||||
const SMALLEST_F32_LARGER_THAN_ONE: f32 = 1.0000001;
|
||||
/// ≈ -(2^127 * (2 − 2^−23))
|
||||
const SMALLEST_NORMAL_F32: f32 = f32::MIN;
|
||||
/// ≈ 2^127 * (2 − 2^−23)
|
||||
const LARGEST_NORMAL_F32: f32 = f32::MAX;
|
||||
|
||||
// decimal floating point
|
||||
sub_test(
|
||||
"1e-45f 1.1754942e-38f 1.17549435e-38f 0.99999994f 1.0000001f -3.40282347e+38f 3.40282347e+38f",
|
||||
&[
|
||||
Token::Number(Ok(Number::F32(
|
||||
SMALLEST_POSITIVE_SUBNORMAL_F32,
|
||||
))),
|
||||
Token::Number(Ok(Number::F32(
|
||||
LARGEST_SUBNORMAL_F32,
|
||||
))),
|
||||
Token::Number(Ok(Number::F32(
|
||||
SMALLEST_POSITIVE_NORMAL_F32,
|
||||
))),
|
||||
Token::Number(Ok(Number::F32(
|
||||
LARGEST_F32_LESS_THAN_ONE,
|
||||
))),
|
||||
Token::Number(Ok(Number::F32(
|
||||
SMALLEST_F32_LARGER_THAN_ONE,
|
||||
))),
|
||||
Token::Number(Ok(Number::F32(
|
||||
SMALLEST_NORMAL_F32,
|
||||
))),
|
||||
Token::Number(Ok(Number::F32(
|
||||
LARGEST_NORMAL_F32,
|
||||
))),
|
||||
],
|
||||
);
|
||||
sub_test(
|
||||
"-3.40282367e+38f 3.40282367e+38f",
|
||||
&[
|
||||
Token::Number(Err(NumberError::NotRepresentable)), // ≈ -2^128
|
||||
Token::Number(Err(NumberError::NotRepresentable)), // ≈ 2^128
|
||||
],
|
||||
);
|
||||
|
||||
// hexadecimal floating point
|
||||
sub_test(
|
||||
"0x1p-149f 0x7FFFFFp-149f 0x1p-126f 0xFFFFFFp-24f 0x800001p-23f -0xFFFFFFp+104f 0xFFFFFFp+104f",
|
||||
&[
|
||||
Token::Number(Ok(Number::F32(
|
||||
SMALLEST_POSITIVE_SUBNORMAL_F32,
|
||||
))),
|
||||
Token::Number(Ok(Number::F32(
|
||||
LARGEST_SUBNORMAL_F32,
|
||||
))),
|
||||
Token::Number(Ok(Number::F32(
|
||||
SMALLEST_POSITIVE_NORMAL_F32,
|
||||
))),
|
||||
Token::Number(Ok(Number::F32(
|
||||
LARGEST_F32_LESS_THAN_ONE,
|
||||
))),
|
||||
Token::Number(Ok(Number::F32(
|
||||
SMALLEST_F32_LARGER_THAN_ONE,
|
||||
))),
|
||||
Token::Number(Ok(Number::F32(
|
||||
SMALLEST_NORMAL_F32,
|
||||
))),
|
||||
Token::Number(Ok(Number::F32(
|
||||
LARGEST_NORMAL_F32,
|
||||
))),
|
||||
],
|
||||
);
|
||||
sub_test(
|
||||
"-0x1p128f 0x1p128f 0x1.000001p0f",
|
||||
&[
|
||||
Token::Number(Err(NumberError::NotRepresentable)), // = -2^128
|
||||
Token::Number(Err(NumberError::NotRepresentable)), // = 2^128
|
||||
Token::Number(Err(NumberError::NotRepresentable)),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_tokens() {
|
||||
sub_test("id123_OK", &[Token::Word("id123_OK")]);
|
||||
sub_test(
|
||||
"92No",
|
||||
&[
|
||||
Token::Number {
|
||||
value: "92",
|
||||
ty: NumberType::Sint,
|
||||
},
|
||||
Token::Word("No"),
|
||||
],
|
||||
&[Token::Number(Ok(Number::I32(92))), Token::Word("No")],
|
||||
);
|
||||
sub_test(
|
||||
"2u3o",
|
||||
&[
|
||||
Token::Number {
|
||||
value: "2",
|
||||
ty: NumberType::Uint,
|
||||
},
|
||||
Token::Number {
|
||||
value: "3",
|
||||
ty: NumberType::Sint,
|
||||
},
|
||||
Token::Number(Ok(Number::U32(2))),
|
||||
Token::Number(Ok(Number::I32(3))),
|
||||
Token::Word("o"),
|
||||
],
|
||||
);
|
||||
sub_test(
|
||||
"2.4f44po",
|
||||
&[
|
||||
Token::Number {
|
||||
value: "2.4",
|
||||
ty: NumberType::Float,
|
||||
},
|
||||
Token::Word("f44po"),
|
||||
Token::Number(Ok(Number::F32(2.4))),
|
||||
Token::Number(Ok(Number::I32(44))),
|
||||
Token::Word("po"),
|
||||
],
|
||||
);
|
||||
sub_test(
|
||||
@ -715,10 +613,7 @@ fn test_variable_decl() {
|
||||
Token::Attribute,
|
||||
Token::Word("group"),
|
||||
Token::Paren('('),
|
||||
Token::Number {
|
||||
value: "0",
|
||||
ty: NumberType::Sint,
|
||||
},
|
||||
Token::Number(Ok(Number::I32(0))),
|
||||
Token::Paren(')'),
|
||||
Token::Word("var"),
|
||||
Token::Paren('<'),
|
||||
|
506
third_party/rust/naga/src/front/wgsl/mod.rs
vendored
506
third_party/rust/naga/src/front/wgsl/mod.rs
vendored
@ -7,7 +7,7 @@ Frontend for [WGSL][wgsl] (WebGPU Shading Language).
|
||||
mod construction;
|
||||
mod conv;
|
||||
mod lexer;
|
||||
mod number_literals;
|
||||
mod number;
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
||||
@ -16,31 +16,24 @@ use crate::{
|
||||
proc::{
|
||||
ensure_block_returns, Alignment, Layouter, ResolveContext, ResolveError, TypeResolution,
|
||||
},
|
||||
span::SourceLocation,
|
||||
span::Span as NagaSpan,
|
||||
Bytes, ConstantInner, FastHashMap, ScalarValue,
|
||||
ConstantInner, FastHashMap, ScalarValue,
|
||||
};
|
||||
|
||||
use self::{
|
||||
lexer::Lexer,
|
||||
number_literals::{
|
||||
get_f32_literal, get_i32_literal, get_u32_literal, parse_generic_non_negative_int_literal,
|
||||
parse_non_negative_sint_literal,
|
||||
},
|
||||
};
|
||||
use self::{lexer::Lexer, number::Number};
|
||||
use codespan_reporting::{
|
||||
diagnostic::{Diagnostic, Label},
|
||||
files::{Files, SimpleFile},
|
||||
files::SimpleFile,
|
||||
term::{
|
||||
self,
|
||||
termcolor::{ColorChoice, ColorSpec, StandardStream, WriteColor},
|
||||
},
|
||||
};
|
||||
use hexf_parse::ParseHexfError;
|
||||
use std::{
|
||||
borrow::Cow,
|
||||
convert::TryFrom,
|
||||
io::{self, Write},
|
||||
num::{NonZeroU32, ParseFloatError, ParseIntError},
|
||||
ops,
|
||||
};
|
||||
use thiserror::Error;
|
||||
@ -48,19 +41,12 @@ use thiserror::Error;
|
||||
type Span = ops::Range<usize>;
|
||||
type TokenSpan<'a> = (Token<'a>, Span);
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||
pub enum NumberType {
|
||||
Sint,
|
||||
Uint,
|
||||
Float,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||
pub enum Token<'a> {
|
||||
Separator(char),
|
||||
Paren(char),
|
||||
Attribute,
|
||||
Number { value: &'a str, ty: NumberType },
|
||||
Number(Result<Number, NumberError>),
|
||||
Word(&'a str),
|
||||
Operation(char),
|
||||
LogicalOperation(char),
|
||||
@ -74,14 +60,18 @@ pub enum Token<'a> {
|
||||
End,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||
pub enum NumberType {
|
||||
I32,
|
||||
U32,
|
||||
F32,
|
||||
}
|
||||
|
||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||
pub enum ExpectedToken<'a> {
|
||||
Token(Token<'a>),
|
||||
Identifier,
|
||||
Number {
|
||||
ty: Option<NumberType>,
|
||||
width: Option<Bytes>,
|
||||
},
|
||||
Number(NumberType),
|
||||
Integer,
|
||||
Constant,
|
||||
/// Expected: constant, parenthesized expression, identifier
|
||||
@ -100,36 +90,25 @@ pub enum ExpectedToken<'a> {
|
||||
GlobalItem,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Error)]
|
||||
pub enum BadIntError {
|
||||
#[error(transparent)]
|
||||
ParseIntError(#[from] ParseIntError),
|
||||
#[error("non-hex negative zero integer literals are not allowed")]
|
||||
NegativeZero,
|
||||
#[error("leading zeros for non-hex integer literals are not allowed")]
|
||||
LeadingZeros,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Error)]
|
||||
pub enum BadFloatError {
|
||||
#[error(transparent)]
|
||||
ParseFloatError(#[from] ParseFloatError),
|
||||
#[error(transparent)]
|
||||
ParseHexfError(#[from] ParseHexfError),
|
||||
#[derive(Clone, Copy, Debug, Error, PartialEq)]
|
||||
pub enum NumberError {
|
||||
#[error("invalid numeric literal format")]
|
||||
Invalid,
|
||||
#[error("numeric literal not representable by target type")]
|
||||
NotRepresentable,
|
||||
#[error("unimplemented f16 type")]
|
||||
UnimplementedF16,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum Error<'a> {
|
||||
Unexpected(TokenSpan<'a>, ExpectedToken<'a>),
|
||||
UnexpectedComponents(Span),
|
||||
BadU32(Span, BadIntError),
|
||||
BadI32(Span, BadIntError),
|
||||
BadNumber(Span, NumberError),
|
||||
/// A negative signed integer literal where both signed and unsigned,
|
||||
/// but only non-negative literals are allowed.
|
||||
NegativeInt(Span),
|
||||
BadFloat(Span, BadFloatError),
|
||||
BadU32Constant(Span),
|
||||
BadScalarWidth(Span, Bytes),
|
||||
BadMatrixScalarKind(Span, crate::ScalarKind, u8),
|
||||
BadAccessor(Span),
|
||||
BadTexture(Span),
|
||||
@ -146,7 +125,9 @@ pub enum Error<'a> {
|
||||
BadIncrDecrReferenceType(Span),
|
||||
InvalidResolve(ResolveError),
|
||||
InvalidForInitializer(Span),
|
||||
InvalidGatherComponent(Span, i32),
|
||||
/// A break if appeared outside of a continuing block
|
||||
InvalidBreakIf(Span),
|
||||
InvalidGatherComponent(Span, u32),
|
||||
InvalidConstructorComponentType(Span, i32),
|
||||
InvalidIdentifierUnderscore(Span),
|
||||
ReservedIdentifierPrefix(Span),
|
||||
@ -160,7 +141,9 @@ pub enum Error<'a> {
|
||||
UnknownType(Span),
|
||||
UnknownStorageFormat(Span),
|
||||
UnknownConservativeDepth(Span),
|
||||
ZeroSizeOrAlign(Span),
|
||||
SizeAttributeTooLow(Span, u32),
|
||||
AlignAttributeTooLow(Span, Alignment),
|
||||
NonPowerOfTwoAlignAttribute(Span),
|
||||
InconsistentBinding(Span),
|
||||
UnknownLocalFunction(Span),
|
||||
TypeNotConstructible(Span),
|
||||
@ -191,9 +174,7 @@ impl<'a> Error<'a> {
|
||||
Token::Separator(c) => format!("'{}'", c),
|
||||
Token::Paren(c) => format!("'{}'", c),
|
||||
Token::Attribute => "@".to_string(),
|
||||
Token::Number { value, .. } => {
|
||||
format!("number ({})", value)
|
||||
}
|
||||
Token::Number(_) => "number".to_string(),
|
||||
Token::Word(s) => s.to_string(),
|
||||
Token::Operation(c) => format!("operation ('{}')", c),
|
||||
Token::LogicalOperation(c) => format!("logical operation ('{}')", c),
|
||||
@ -209,25 +190,12 @@ impl<'a> Error<'a> {
|
||||
}
|
||||
}
|
||||
ExpectedToken::Identifier => "identifier".to_string(),
|
||||
ExpectedToken::Number { ty, width } => {
|
||||
let literal_ty_str = match ty {
|
||||
Some(NumberType::Float) => "floating-point",
|
||||
Some(NumberType::Uint) => "unsigned integer",
|
||||
Some(NumberType::Sint) => "signed integer",
|
||||
None => "arbitrary number",
|
||||
};
|
||||
if let Some(width) = width {
|
||||
format!(
|
||||
"{} literal of {}-bit width",
|
||||
literal_ty_str,
|
||||
width as u32 * 8,
|
||||
)
|
||||
} else {
|
||||
format!(
|
||||
"{} literal of arbitrary width",
|
||||
literal_ty_str,
|
||||
)
|
||||
}
|
||||
ExpectedToken::Number(ty) => {
|
||||
match ty {
|
||||
NumberType::I32 => "32-bit signed integer literal",
|
||||
NumberType::U32 => "32-bit unsigned integer literal",
|
||||
NumberType::F32 => "32-bit floating-point literal",
|
||||
}.to_string()
|
||||
},
|
||||
ExpectedToken::Integer => "unsigned/signed integer literal".to_string(),
|
||||
ExpectedToken::Constant => "constant".to_string(),
|
||||
@ -257,21 +225,13 @@ impl<'a> Error<'a> {
|
||||
labels: vec![(bad_span.clone(), "unexpected components".into())],
|
||||
notes: vec![],
|
||||
},
|
||||
Error::BadU32(ref bad_span, ref err) => ParseError {
|
||||
Error::BadNumber(ref bad_span, ref err) => ParseError {
|
||||
message: format!(
|
||||
"expected unsigned integer literal, found `{}`",
|
||||
&source[bad_span.clone()],
|
||||
"{}: `{}`",
|
||||
err,&source[bad_span.clone()],
|
||||
),
|
||||
labels: vec![(bad_span.clone(), "expected unsigned integer".into())],
|
||||
notes: vec![err.to_string()],
|
||||
},
|
||||
Error::BadI32(ref bad_span, ref err) => ParseError {
|
||||
message: format!(
|
||||
"expected integer literal, found `{}`",
|
||||
&source[bad_span.clone()],
|
||||
),
|
||||
labels: vec![(bad_span.clone(), "expected signed integer".into())],
|
||||
notes: vec![err.to_string()],
|
||||
labels: vec![(bad_span.clone(), err.to_string().into())],
|
||||
notes: vec![],
|
||||
},
|
||||
Error::NegativeInt(ref bad_span) => ParseError {
|
||||
message: format!(
|
||||
@ -281,14 +241,6 @@ impl<'a> Error<'a> {
|
||||
labels: vec![(bad_span.clone(), "expected non-negative integer".into())],
|
||||
notes: vec![],
|
||||
},
|
||||
Error::BadFloat(ref bad_span, ref err) => ParseError {
|
||||
message: format!(
|
||||
"expected floating-point literal, found `{}`",
|
||||
&source[bad_span.clone()],
|
||||
),
|
||||
labels: vec![(bad_span.clone(), "expected floating-point literal".into())],
|
||||
notes: vec![err.to_string()],
|
||||
},
|
||||
Error::BadU32Constant(ref bad_span) => ParseError {
|
||||
message: format!(
|
||||
"expected unsigned integer constant expression, found `{}`",
|
||||
@ -297,11 +249,6 @@ impl<'a> Error<'a> {
|
||||
labels: vec![(bad_span.clone(), "expected unsigned integer".into())],
|
||||
notes: vec![],
|
||||
},
|
||||
Error::BadScalarWidth(ref bad_span, width) => ParseError {
|
||||
message: format!("invalid width of `{}` bits for literal", width as u32 * 8,),
|
||||
labels: vec![(bad_span.clone(), "invalid width".into())],
|
||||
notes: vec!["the only valid width is 32 for now".to_string()],
|
||||
},
|
||||
Error::BadMatrixScalarKind(
|
||||
ref span,
|
||||
kind,
|
||||
@ -362,6 +309,11 @@ impl<'a> Error<'a> {
|
||||
labels: vec![(bad_span.clone(), "not an assignment or function call".into())],
|
||||
notes: vec![],
|
||||
},
|
||||
Error::InvalidBreakIf(ref bad_span) => ParseError {
|
||||
message: "A break if is only allowed in a continuing block".to_string(),
|
||||
labels: vec![(bad_span.clone(), "not in a continuing block".into())],
|
||||
notes: vec![],
|
||||
},
|
||||
Error::InvalidGatherComponent(ref bad_span, component) => ParseError {
|
||||
message: format!("textureGather component {} doesn't exist, must be 0, 1, 2, or 3", component),
|
||||
labels: vec![(bad_span.clone(), "invalid component".into())],
|
||||
@ -422,9 +374,19 @@ impl<'a> Error<'a> {
|
||||
labels: vec![(bad_span.clone(), "unknown type".into())],
|
||||
notes: vec![],
|
||||
},
|
||||
Error::ZeroSizeOrAlign(ref bad_span) => ParseError {
|
||||
message: "struct member size or alignment must not be 0".to_string(),
|
||||
labels: vec![(bad_span.clone(), "struct member size or alignment must not be 0".into())],
|
||||
Error::SizeAttributeTooLow(ref bad_span, min_size) => ParseError {
|
||||
message: format!("struct member size must be at least {}", min_size),
|
||||
labels: vec![(bad_span.clone(), format!("must be at least {}", min_size).into())],
|
||||
notes: vec![],
|
||||
},
|
||||
Error::AlignAttributeTooLow(ref bad_span, min_align) => ParseError {
|
||||
message: format!("struct member alignment must be at least {}", min_align),
|
||||
labels: vec![(bad_span.clone(), format!("must be at least {}", min_align).into())],
|
||||
notes: vec![],
|
||||
},
|
||||
Error::NonPowerOfTwoAlignAttribute(ref bad_span) => ParseError {
|
||||
message: "struct member alignment must be a power of 2".to_string(),
|
||||
labels: vec![(bad_span.clone(), "must be a power of 2".into())],
|
||||
notes: vec![],
|
||||
},
|
||||
Error::InconsistentBinding(ref span) => ParseError {
|
||||
@ -1193,8 +1155,8 @@ impl Composition {
|
||||
#[derive(Default)]
|
||||
struct TypeAttributes {
|
||||
// Although WGSL nas no type attributes at the moment, it had them in the past
|
||||
// (`[[stride]]`) and may as well acquire some again in the future.
|
||||
// Therefore, we are leaving the plumbing in for now.
|
||||
// (`[[stride]]`) and may as well acquire some again in the future.
|
||||
// Therefore, we are leaving the plumbing in for now.
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
@ -1234,7 +1196,7 @@ impl BindingParser {
|
||||
match name {
|
||||
"location" => {
|
||||
lexer.expect(Token::Paren('('))?;
|
||||
self.location = Some(parse_non_negative_sint_literal(lexer, 4)?);
|
||||
self.location = Some(Parser::parse_non_negative_i32_literal(lexer)?);
|
||||
lexer.expect(Token::Paren(')'))?;
|
||||
}
|
||||
"builtin" => {
|
||||
@ -1365,19 +1327,11 @@ impl ParseError {
|
||||
writer.into_string()
|
||||
}
|
||||
|
||||
/// Returns the 1-based line number and column of the first label in the
|
||||
/// error message.
|
||||
pub fn location(&self, source: &str) -> (usize, usize) {
|
||||
let files = SimpleFile::new("wgsl", source);
|
||||
match self.labels.get(0) {
|
||||
Some(label) => {
|
||||
let location = files
|
||||
.location((), label.0.start)
|
||||
.expect("invalid span location");
|
||||
(location.line_number, location.column_number)
|
||||
}
|
||||
None => (1, 1),
|
||||
}
|
||||
/// Returns a [`SourceLocation`] for the first label in the error message.
|
||||
pub fn location(&self, source: &str) -> Option<SourceLocation> {
|
||||
self.labels
|
||||
.get(0)
|
||||
.map(|label| NagaSpan::new(label.0.start as u32, label.0.end as u32).location(source))
|
||||
}
|
||||
}
|
||||
|
||||
@ -1431,38 +1385,45 @@ impl Parser {
|
||||
lexer.span_from(initial)
|
||||
}
|
||||
|
||||
fn get_constant_inner<'a>(
|
||||
word: &'a str,
|
||||
ty: NumberType,
|
||||
token_span: TokenSpan<'a>,
|
||||
) -> Result<ConstantInner, Error<'a>> {
|
||||
let span = token_span.1;
|
||||
|
||||
let value = match ty {
|
||||
NumberType::Sint => {
|
||||
get_i32_literal(word, span).map(|val| crate::ScalarValue::Sint(val as i64))?
|
||||
}
|
||||
NumberType::Uint => {
|
||||
get_u32_literal(word, span).map(|val| crate::ScalarValue::Uint(val as u64))?
|
||||
}
|
||||
NumberType::Float => {
|
||||
get_f32_literal(word, span).map(|val| crate::ScalarValue::Float(val as f64))?
|
||||
}
|
||||
};
|
||||
|
||||
Ok(crate::ConstantInner::Scalar { value, width: 4 })
|
||||
}
|
||||
|
||||
fn parse_switch_value<'a>(lexer: &mut Lexer<'a>, uint: bool) -> Result<i32, Error<'a>> {
|
||||
let token_span = lexer.next();
|
||||
let word = match token_span.0 {
|
||||
Token::Number { value, .. } => value,
|
||||
_ => return Err(Error::Unexpected(token_span, ExpectedToken::Integer)),
|
||||
};
|
||||
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, ExpectedToken::Integer)),
|
||||
}
|
||||
}
|
||||
|
||||
match uint {
|
||||
true => get_u32_literal(word, token_span.1).map(|v| v as i32),
|
||||
false => get_i32_literal(word, token_span.1),
|
||||
/// Parse a non-negative signed integer literal.
|
||||
/// This is for attributes like `size`, `location` and others.
|
||||
fn parse_non_negative_i32_literal<'a>(lexer: &mut Lexer<'a>) -> Result<u32, Error<'a>> {
|
||||
match lexer.next() {
|
||||
(Token::Number(Ok(Number::I32(num))), span) => {
|
||||
u32::try_from(num).map_err(|_| Error::NegativeInt(span))
|
||||
}
|
||||
(Token::Number(Err(e)), span) => Err(Error::BadNumber(span, e)),
|
||||
other => Err(Error::Unexpected(
|
||||
other,
|
||||
ExpectedToken::Number(NumberType::I32),
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Parse a non-negative integer literal that may be either signed or unsigned.
|
||||
/// This is for the `workgroup_size` attribute and array lengths.
|
||||
/// Note: these values should be no larger than [`i32::MAX`], but this is not checked here.
|
||||
fn parse_generic_non_negative_int_literal<'a>(lexer: &mut Lexer<'a>) -> Result<u32, Error<'a>> {
|
||||
match lexer.next() {
|
||||
(Token::Number(Ok(Number::I32(num))), span) => {
|
||||
u32::try_from(num).map_err(|_| Error::NegativeInt(span))
|
||||
}
|
||||
(Token::Number(Ok(Number::U32(num))), _) => Ok(num),
|
||||
(Token::Number(Err(e)), span) => Err(Error::BadNumber(span, e)),
|
||||
other => Err(Error::Unexpected(
|
||||
other,
|
||||
ExpectedToken::Number(NumberType::I32),
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
@ -1615,7 +1576,7 @@ impl Parser {
|
||||
"bitcast" => {
|
||||
let _ = lexer.next();
|
||||
lexer.expect_generic_paren('<')?;
|
||||
let ((ty, _access), type_span) = lexer.capture_span(|lexer| {
|
||||
let (ty, type_span) = lexer.capture_span(|lexer| {
|
||||
self.parse_type_decl(lexer, None, ctx.types, ctx.constants)
|
||||
})?;
|
||||
lexer.expect_generic_paren('>')?;
|
||||
@ -2006,17 +1967,9 @@ impl Parser {
|
||||
"textureGather" => {
|
||||
let _ = lexer.next();
|
||||
lexer.open_arguments()?;
|
||||
let component = if let (
|
||||
Token::Number {
|
||||
value,
|
||||
ty: NumberType::Sint,
|
||||
},
|
||||
span,
|
||||
) = lexer.peek()
|
||||
{
|
||||
let _ = lexer.next();
|
||||
let component = if let (Token::Number(..), span) = lexer.peek() {
|
||||
let index = Self::parse_non_negative_i32_literal(lexer)?;
|
||||
lexer.expect(Token::Separator(','))?;
|
||||
let index = get_i32_literal(value, span.clone())?;
|
||||
*crate::SwizzleComponent::XYZW
|
||||
.get(index as usize)
|
||||
.ok_or(Error::InvalidGatherComponent(span, index))?
|
||||
@ -2219,9 +2172,22 @@ impl Parser {
|
||||
let inner = match first_token_span {
|
||||
(Token::Word("true"), _) => crate::ConstantInner::boolean(true),
|
||||
(Token::Word("false"), _) => crate::ConstantInner::boolean(false),
|
||||
(Token::Number { value, ty }, _) => {
|
||||
Self::get_constant_inner(value, ty, first_token_span)?
|
||||
}
|
||||
(Token::Number(num), _) => match num {
|
||||
Ok(Number::I32(num)) => crate::ConstantInner::Scalar {
|
||||
value: crate::ScalarValue::Sint(num as i64),
|
||||
width: 4,
|
||||
},
|
||||
Ok(Number::U32(num)) => crate::ConstantInner::Scalar {
|
||||
value: crate::ScalarValue::Uint(num as u64),
|
||||
width: 4,
|
||||
},
|
||||
Ok(Number::F32(num)) => crate::ConstantInner::Scalar {
|
||||
value: crate::ScalarValue::Float(num as f64),
|
||||
width: 4,
|
||||
},
|
||||
Ok(Number::AbstractInt(_) | Number::AbstractFloat(_)) => unreachable!(),
|
||||
Err(e) => return Err(Error::BadNumber(first_token_span.1, e)),
|
||||
},
|
||||
(Token::Word(name), name_span) => {
|
||||
// look for an existing constant first
|
||||
for (handle, var) in const_arena.iter() {
|
||||
@ -2312,10 +2278,8 @@ impl Parser {
|
||||
self.pop_scope(lexer);
|
||||
expr
|
||||
}
|
||||
token @ (Token::Word("true" | "false") | Token::Number { .. }, _) => {
|
||||
let _ = lexer.next();
|
||||
let const_handle =
|
||||
self.parse_const_expression_impl(token, lexer, None, ctx.types, ctx.constants)?;
|
||||
(Token::Word("true" | "false") | Token::Number(..), _) => {
|
||||
let const_handle = self.parse_const_expression(lexer, ctx.types, ctx.constants)?;
|
||||
let span = NagaSpan::from(self.pop_scope(lexer));
|
||||
TypedExpression::non_reference(
|
||||
ctx.interrupt_emitter(crate::Expression::Constant(const_handle), span),
|
||||
@ -2781,11 +2745,11 @@ impl Parser {
|
||||
lexer: &mut Lexer<'a>,
|
||||
type_arena: &mut UniqueArena<crate::Type>,
|
||||
const_arena: &mut Arena<crate::Constant>,
|
||||
) -> Result<(&'a str, Span, Handle<crate::Type>, crate::StorageAccess), Error<'a>> {
|
||||
) -> Result<(&'a str, Span, Handle<crate::Type>), Error<'a>> {
|
||||
let (name, name_span) = lexer.next_ident_with_span()?;
|
||||
lexer.expect(Token::Separator(':'))?;
|
||||
let (ty, access) = self.parse_type_decl(lexer, None, type_arena, const_arena)?;
|
||||
Ok((name, name_span, ty, access))
|
||||
let ty = self.parse_type_decl(lexer, None, type_arena, const_arena)?;
|
||||
Ok((name, name_span, ty))
|
||||
}
|
||||
|
||||
fn parse_variable_decl<'a>(
|
||||
@ -2815,7 +2779,7 @@ impl Parser {
|
||||
}
|
||||
let name = lexer.next_ident()?;
|
||||
lexer.expect(Token::Separator(':'))?;
|
||||
let (ty, _access) = self.parse_type_decl(lexer, None, type_arena, const_arena)?;
|
||||
let ty = self.parse_type_decl(lexer, None, type_arena, const_arena)?;
|
||||
|
||||
let init = if lexer.skip(Token::Operation('=')) {
|
||||
let handle = self.parse_const_expression(lexer, type_arena, const_arena)?;
|
||||
@ -2841,7 +2805,7 @@ impl Parser {
|
||||
const_arena: &mut Arena<crate::Constant>,
|
||||
) -> Result<(Vec<crate::StructMember>, u32), Error<'a>> {
|
||||
let mut offset = 0;
|
||||
let mut alignment = Alignment::new(1).unwrap();
|
||||
let mut struct_alignment = Alignment::ONE;
|
||||
let mut members = Vec::new();
|
||||
|
||||
lexer.expect(Token::Paren('{'))?;
|
||||
@ -2853,30 +2817,32 @@ impl Parser {
|
||||
ExpectedToken::Token(Token::Separator(',')),
|
||||
));
|
||||
}
|
||||
let (mut size, mut align) = (None, None);
|
||||
let (mut size_attr, mut align_attr) = (None, None);
|
||||
self.push_scope(Scope::Attribute, lexer);
|
||||
let mut bind_parser = BindingParser::default();
|
||||
while lexer.skip(Token::Attribute) {
|
||||
match lexer.next_ident_with_span()? {
|
||||
("size", _) => {
|
||||
lexer.expect(Token::Paren('('))?;
|
||||
let (value, span) = lexer
|
||||
.capture_span(|lexer| parse_non_negative_sint_literal(lexer, 4))?;
|
||||
let (value, span) =
|
||||
lexer.capture_span(Self::parse_non_negative_i32_literal)?;
|
||||
lexer.expect(Token::Paren(')'))?;
|
||||
size = Some(NonZeroU32::new(value).ok_or(Error::ZeroSizeOrAlign(span))?);
|
||||
size_attr = Some((value, span));
|
||||
}
|
||||
("align", _) => {
|
||||
lexer.expect(Token::Paren('('))?;
|
||||
let (value, span) = lexer
|
||||
.capture_span(|lexer| parse_non_negative_sint_literal(lexer, 4))?;
|
||||
let (value, span) =
|
||||
lexer.capture_span(Self::parse_non_negative_i32_literal)?;
|
||||
lexer.expect(Token::Paren(')'))?;
|
||||
align = Some(NonZeroU32::new(value).ok_or(Error::ZeroSizeOrAlign(span))?);
|
||||
align_attr = Some((value, span));
|
||||
}
|
||||
(word, word_span) => bind_parser.parse(lexer, word, word_span)?,
|
||||
}
|
||||
}
|
||||
|
||||
let bind_span = self.pop_scope(lexer);
|
||||
let mut binding = bind_parser.finish(bind_span)?;
|
||||
|
||||
let (name, span) = match lexer.next() {
|
||||
(Token::Word(word), span) => (word, span),
|
||||
other => return Err(Error::Unexpected(other, ExpectedToken::FieldName)),
|
||||
@ -2885,29 +2851,57 @@ impl Parser {
|
||||
return Err(Error::ReservedKeyword(span));
|
||||
}
|
||||
lexer.expect(Token::Separator(':'))?;
|
||||
let (ty, _access) = self.parse_type_decl(lexer, None, type_arena, const_arena)?;
|
||||
let ty = self.parse_type_decl(lexer, None, type_arena, const_arena)?;
|
||||
ready = lexer.skip(Token::Separator(','));
|
||||
|
||||
self.layouter.update(type_arena, const_arena).unwrap();
|
||||
|
||||
let (range, align) = self.layouter.member_placement(offset, ty, align, size);
|
||||
alignment = alignment.max(align);
|
||||
offset = range.end;
|
||||
let member_min_size = self.layouter[ty].size;
|
||||
let member_min_alignment = self.layouter[ty].alignment;
|
||||
|
||||
let member_size = if let Some((size, span)) = size_attr {
|
||||
if size < member_min_size {
|
||||
return Err(Error::SizeAttributeTooLow(span, member_min_size));
|
||||
} else {
|
||||
size
|
||||
}
|
||||
} else {
|
||||
member_min_size
|
||||
};
|
||||
|
||||
let member_alignment = if let Some((align, span)) = align_attr {
|
||||
if let Some(alignment) = Alignment::new(align) {
|
||||
if alignment < member_min_alignment {
|
||||
return Err(Error::AlignAttributeTooLow(span, member_min_alignment));
|
||||
} else {
|
||||
alignment
|
||||
}
|
||||
} else {
|
||||
return Err(Error::NonPowerOfTwoAlignAttribute(span));
|
||||
}
|
||||
} else {
|
||||
member_min_alignment
|
||||
};
|
||||
|
||||
offset = member_alignment.round_up(offset);
|
||||
struct_alignment = struct_alignment.max(member_alignment);
|
||||
|
||||
let mut binding = bind_parser.finish(bind_span)?;
|
||||
if let Some(ref mut binding) = binding {
|
||||
binding.apply_default_interpolation(&type_arena[ty].inner);
|
||||
}
|
||||
|
||||
members.push(crate::StructMember {
|
||||
name: Some(name.to_owned()),
|
||||
ty,
|
||||
binding,
|
||||
offset: range.start,
|
||||
offset,
|
||||
});
|
||||
|
||||
offset += member_size;
|
||||
}
|
||||
|
||||
let span = Layouter::round_up(alignment, offset);
|
||||
Ok((members, span))
|
||||
let struct_size = struct_alignment.round_up(offset);
|
||||
Ok((members, struct_size))
|
||||
}
|
||||
|
||||
fn parse_matrix_scalar_type<'a>(
|
||||
@ -3012,7 +3006,7 @@ impl Parser {
|
||||
let (ident, span) = lexer.next_ident_with_span()?;
|
||||
let mut space = conv::map_address_space(ident, span)?;
|
||||
lexer.expect(Token::Separator(','))?;
|
||||
let (base, _access) = self.parse_type_decl(lexer, None, type_arena, const_arena)?;
|
||||
let base = self.parse_type_decl(lexer, None, type_arena, const_arena)?;
|
||||
if let crate::AddressSpace::Storage { ref mut access } = space {
|
||||
*access = if lexer.skip(Token::Separator(',')) {
|
||||
lexer.next_storage_access()?
|
||||
@ -3025,7 +3019,7 @@ impl Parser {
|
||||
}
|
||||
"array" => {
|
||||
lexer.expect_generic_paren('<')?;
|
||||
let (base, _access) = self.parse_type_decl(lexer, None, type_arena, const_arena)?;
|
||||
let base = self.parse_type_decl(lexer, None, type_arena, const_arena)?;
|
||||
let size = if lexer.skip(Token::Separator(',')) {
|
||||
let const_handle =
|
||||
self.parse_const_expression(lexer, type_arena, const_arena)?;
|
||||
@ -3043,7 +3037,7 @@ impl Parser {
|
||||
}
|
||||
"binding_array" => {
|
||||
lexer.expect_generic_paren('<')?;
|
||||
let (base, _access) = self.parse_type_decl(lexer, None, type_arena, const_arena)?;
|
||||
let base = self.parse_type_decl(lexer, None, type_arena, const_arena)?;
|
||||
let size = if lexer.skip(Token::Separator(',')) {
|
||||
let const_handle =
|
||||
self.parse_const_expression(lexer, type_arena, const_arena)?;
|
||||
@ -3258,7 +3252,7 @@ impl Parser {
|
||||
debug_name: Option<&'a str>,
|
||||
type_arena: &mut UniqueArena<crate::Type>,
|
||||
const_arena: &mut Arena<crate::Constant>,
|
||||
) -> Result<(Handle<crate::Type>, crate::StorageAccess), Error<'a>> {
|
||||
) -> Result<Handle<crate::Type>, Error<'a>> {
|
||||
self.push_scope(Scope::TypeDecl, lexer);
|
||||
let attribute = TypeAttributes::default();
|
||||
|
||||
@ -3267,7 +3261,6 @@ impl Parser {
|
||||
return Err(Error::Unexpected(other, ExpectedToken::TypeAttribute));
|
||||
}
|
||||
|
||||
let storage_access = crate::StorageAccess::default();
|
||||
let (name, name_span) = lexer.next_ident_with_span()?;
|
||||
let handle = self.parse_type_decl_name(
|
||||
lexer,
|
||||
@ -3282,7 +3275,7 @@ impl Parser {
|
||||
// Only set span if it's the first occurrence of the type.
|
||||
// Type spans therefore should only be used for errors in type declarations;
|
||||
// use variable spans/expression spans/etc. otherwise
|
||||
Ok((handle, storage_access))
|
||||
Ok(handle)
|
||||
}
|
||||
|
||||
/// Parse an assignment statement (will also parse increment and decrement statements)
|
||||
@ -3510,7 +3503,7 @@ impl Parser {
|
||||
return Err(Error::ReservedKeyword(name_span));
|
||||
}
|
||||
let given_ty = if lexer.skip(Token::Separator(':')) {
|
||||
let (ty, _access) = self.parse_type_decl(
|
||||
let ty = self.parse_type_decl(
|
||||
lexer,
|
||||
None,
|
||||
context.types,
|
||||
@ -3571,7 +3564,7 @@ impl Parser {
|
||||
return Err(Error::ReservedKeyword(name_span));
|
||||
}
|
||||
let given_ty = if lexer.skip(Token::Separator(':')) {
|
||||
let (ty, _access) = self.parse_type_decl(
|
||||
let ty = self.parse_type_decl(
|
||||
lexer,
|
||||
None,
|
||||
context.types,
|
||||
@ -3825,26 +3818,7 @@ impl Parser {
|
||||
|
||||
Some(crate::Statement::Switch { selector, cases })
|
||||
}
|
||||
"loop" => {
|
||||
let _ = lexer.next();
|
||||
let mut body = crate::Block::new();
|
||||
let mut continuing = crate::Block::new();
|
||||
lexer.expect(Token::Paren('{'))?;
|
||||
|
||||
loop {
|
||||
if lexer.skip(Token::Word("continuing")) {
|
||||
continuing = self.parse_block(lexer, context.reborrow(), false)?;
|
||||
lexer.expect(Token::Paren('}'))?;
|
||||
break;
|
||||
}
|
||||
if lexer.skip(Token::Paren('}')) {
|
||||
break;
|
||||
}
|
||||
self.parse_statement(lexer, context.reborrow(), &mut body, false)?;
|
||||
}
|
||||
|
||||
Some(crate::Statement::Loop { body, continuing })
|
||||
}
|
||||
"loop" => Some(self.parse_loop(lexer, context.reborrow(), &mut emitter)?),
|
||||
"while" => {
|
||||
let _ = lexer.next();
|
||||
let mut body = crate::Block::new();
|
||||
@ -3877,6 +3851,7 @@ impl Parser {
|
||||
Some(crate::Statement::Loop {
|
||||
body,
|
||||
continuing: crate::Block::new(),
|
||||
break_if: None,
|
||||
})
|
||||
}
|
||||
"for" => {
|
||||
@ -3949,10 +3924,22 @@ impl Parser {
|
||||
self.parse_statement(lexer, context.reborrow(), &mut body, false)?;
|
||||
}
|
||||
|
||||
Some(crate::Statement::Loop { body, continuing })
|
||||
Some(crate::Statement::Loop {
|
||||
body,
|
||||
continuing,
|
||||
break_if: None,
|
||||
})
|
||||
}
|
||||
"break" => {
|
||||
let _ = lexer.next();
|
||||
let (_, mut span) = lexer.next();
|
||||
// Check if the next token is an `if`, this indicates
|
||||
// that the user tried to type out a `break if` which
|
||||
// is illegal in this position.
|
||||
let (peeked_token, peeked_span) = lexer.peek();
|
||||
if let Token::Word("if") = peeked_token {
|
||||
span.end = peeked_span.end;
|
||||
return Err(Error::InvalidBreakIf(span));
|
||||
}
|
||||
Some(crate::Statement::Break)
|
||||
}
|
||||
"continue" => {
|
||||
@ -4055,6 +4042,84 @@ impl Parser {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn parse_loop<'a>(
|
||||
&mut self,
|
||||
lexer: &mut Lexer<'a>,
|
||||
mut context: StatementContext<'a, '_, '_>,
|
||||
emitter: &mut super::Emitter,
|
||||
) -> Result<crate::Statement, Error<'a>> {
|
||||
let _ = lexer.next();
|
||||
let mut body = crate::Block::new();
|
||||
let mut continuing = crate::Block::new();
|
||||
let mut break_if = None;
|
||||
lexer.expect(Token::Paren('{'))?;
|
||||
|
||||
loop {
|
||||
if lexer.skip(Token::Word("continuing")) {
|
||||
// Branch for the `continuing` block, this must be
|
||||
// the last thing in the loop body
|
||||
|
||||
// Expect a opening brace to start the continuing block
|
||||
lexer.expect(Token::Paren('{'))?;
|
||||
loop {
|
||||
if lexer.skip(Token::Word("break")) {
|
||||
// Branch for the `break if` statement, this statement
|
||||
// has the form `break if <expr>;` and must be the last
|
||||
// statement in a continuing block
|
||||
|
||||
// The break must be followed by an `if` to form
|
||||
// the break if
|
||||
lexer.expect(Token::Word("if"))?;
|
||||
|
||||
// Start the emitter to begin parsing an expression
|
||||
emitter.start(context.expressions);
|
||||
let condition = self.parse_general_expression(
|
||||
lexer,
|
||||
context.as_expression(&mut body, emitter),
|
||||
)?;
|
||||
// Add all emits to the continuing body
|
||||
continuing.extend(emitter.finish(context.expressions));
|
||||
// Set the condition of the break if to the newly parsed
|
||||
// expression
|
||||
break_if = Some(condition);
|
||||
|
||||
// Expext a semicolon to close the statement
|
||||
lexer.expect(Token::Separator(';'))?;
|
||||
// Expect a closing brace to close the continuing block,
|
||||
// since the break if must be the last statement
|
||||
lexer.expect(Token::Paren('}'))?;
|
||||
// Stop parsing the continuing block
|
||||
break;
|
||||
} else if lexer.skip(Token::Paren('}')) {
|
||||
// If we encounter a closing brace it means we have reached
|
||||
// the end of the continuing block and should stop processing
|
||||
break;
|
||||
} else {
|
||||
// Otherwise try to parse a statement
|
||||
self.parse_statement(lexer, context.reborrow(), &mut continuing, false)?;
|
||||
}
|
||||
}
|
||||
// Since the continuing block must be the last part of the loop body,
|
||||
// we expect to see a closing brace to end the loop body
|
||||
lexer.expect(Token::Paren('}'))?;
|
||||
break;
|
||||
}
|
||||
if lexer.skip(Token::Paren('}')) {
|
||||
// If we encounter a closing brace it means we have reached
|
||||
// the end of the loop body and should stop processing
|
||||
break;
|
||||
}
|
||||
// Otherwise try to parse a statement
|
||||
self.parse_statement(lexer, context.reborrow(), &mut body, false)?;
|
||||
}
|
||||
|
||||
Ok(crate::Statement::Loop {
|
||||
body,
|
||||
continuing,
|
||||
break_if,
|
||||
})
|
||||
}
|
||||
|
||||
fn parse_block<'a>(
|
||||
&mut self,
|
||||
lexer: &mut Lexer<'a>,
|
||||
@ -4146,7 +4211,7 @@ impl Parser {
|
||||
));
|
||||
}
|
||||
let mut binding = self.parse_varying_binding(lexer)?;
|
||||
let (param_name, param_name_span, param_type, _access) =
|
||||
let (param_name, param_name_span, param_type) =
|
||||
self.parse_variable_ident_decl(lexer, &mut module.types, &mut module.constants)?;
|
||||
if crate::keywords::wgsl::RESERVED.contains(¶m_name) {
|
||||
return Err(Error::ReservedKeyword(param_name_span));
|
||||
@ -4176,8 +4241,7 @@ impl Parser {
|
||||
// read return type
|
||||
let result = if lexer.skip(Token::Arrow) && !lexer.skip(Token::Word("void")) {
|
||||
let mut binding = self.parse_varying_binding(lexer)?;
|
||||
let (ty, _access) =
|
||||
self.parse_type_decl(lexer, None, &mut module.types, &mut module.constants)?;
|
||||
let ty = self.parse_type_decl(lexer, None, &mut module.types, &mut module.constants)?;
|
||||
if let Some(ref mut binding) = binding {
|
||||
binding.apply_default_interpolation(&module.types[ty].inner);
|
||||
}
|
||||
@ -4244,12 +4308,12 @@ impl Parser {
|
||||
match lexer.next_ident_with_span()? {
|
||||
("binding", _) => {
|
||||
lexer.expect(Token::Paren('('))?;
|
||||
bind_index = Some(parse_non_negative_sint_literal(lexer, 4)?);
|
||||
bind_index = Some(Self::parse_non_negative_i32_literal(lexer)?);
|
||||
lexer.expect(Token::Paren(')'))?;
|
||||
}
|
||||
("group", _) => {
|
||||
lexer.expect(Token::Paren('('))?;
|
||||
bind_group = Some(parse_non_negative_sint_literal(lexer, 4)?);
|
||||
bind_group = Some(Self::parse_non_negative_i32_literal(lexer)?);
|
||||
lexer.expect(Token::Paren(')'))?;
|
||||
}
|
||||
("vertex", _) => {
|
||||
@ -4263,8 +4327,9 @@ impl Parser {
|
||||
}
|
||||
("workgroup_size", _) => {
|
||||
lexer.expect(Token::Paren('('))?;
|
||||
workgroup_size = [1u32; 3];
|
||||
for (i, size) in workgroup_size.iter_mut().enumerate() {
|
||||
*size = parse_generic_non_negative_int_literal(lexer, 4)?;
|
||||
*size = Self::parse_generic_non_negative_int_literal(lexer)?;
|
||||
match lexer.next() {
|
||||
(Token::Paren(')'), _) => break,
|
||||
(Token::Separator(','), _) if i != 2 => (),
|
||||
@ -4276,11 +4341,6 @@ impl Parser {
|
||||
}
|
||||
}
|
||||
}
|
||||
for size in workgroup_size.iter_mut() {
|
||||
if *size == 0 {
|
||||
*size = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
("early_depth_test", _) => {
|
||||
let conservative = if lexer.skip(Token::Paren('(')) {
|
||||
@ -4334,7 +4394,7 @@ impl Parser {
|
||||
(Token::Word("type"), _) => {
|
||||
let name = lexer.next_ident()?;
|
||||
lexer.expect(Token::Operation('='))?;
|
||||
let (ty, _access) = self.parse_type_decl(
|
||||
let ty = self.parse_type_decl(
|
||||
lexer,
|
||||
Some(name),
|
||||
&mut module.types,
|
||||
@ -4358,7 +4418,7 @@ impl Parser {
|
||||
});
|
||||
}
|
||||
let given_ty = if lexer.skip(Token::Separator(':')) {
|
||||
let (ty, _access) = self.parse_type_decl(
|
||||
let ty = self.parse_type_decl(
|
||||
lexer,
|
||||
None,
|
||||
&mut module.types,
|
||||
|
445
third_party/rust/naga/src/front/wgsl/number.rs
vendored
Normal file
445
third_party/rust/naga/src/front/wgsl/number.rs
vendored
Normal file
@ -0,0 +1,445 @@
|
||||
use std::borrow::Cow;
|
||||
|
||||
use super::{NumberError, Token};
|
||||
|
||||
/// When using this type assume no Abstract Int/Float for now
|
||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||
pub enum Number {
|
||||
/// Abstract Int (-2^63 ≤ i < 2^63)
|
||||
AbstractInt(i64),
|
||||
/// Abstract Float (IEEE-754 binary64)
|
||||
AbstractFloat(f64),
|
||||
/// Concrete i32
|
||||
I32(i32),
|
||||
/// Concrete u32
|
||||
U32(u32),
|
||||
/// Concrete f32
|
||||
F32(f32),
|
||||
}
|
||||
|
||||
impl Number {
|
||||
/// Convert abstract numbers to a plausible concrete counterpart.
|
||||
///
|
||||
/// Return concrete numbers unchanged. If the conversion would be
|
||||
/// lossy, return an error.
|
||||
fn abstract_to_concrete(self) -> Result<Number, NumberError> {
|
||||
match self {
|
||||
Number::AbstractInt(num) => {
|
||||
use std::convert::TryFrom;
|
||||
i32::try_from(num)
|
||||
.map(Number::I32)
|
||||
.map_err(|_| NumberError::NotRepresentable)
|
||||
}
|
||||
Number::AbstractFloat(num) => {
|
||||
let num = num as f32;
|
||||
if num.is_finite() {
|
||||
Ok(Number::F32(num))
|
||||
} else {
|
||||
Err(NumberError::NotRepresentable)
|
||||
}
|
||||
}
|
||||
num => Ok(num),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: when implementing Creation-Time Expressions, remove the ability to match the minus sign
|
||||
|
||||
pub(super) fn consume_number(input: &str) -> (Token<'_>, &str) {
|
||||
let (result, rest) = parse(input);
|
||||
(
|
||||
Token::Number(result.and_then(Number::abstract_to_concrete)),
|
||||
rest,
|
||||
)
|
||||
}
|
||||
|
||||
enum Kind {
|
||||
Int(IntKind),
|
||||
Float(FloatKind),
|
||||
}
|
||||
|
||||
enum IntKind {
|
||||
I32,
|
||||
U32,
|
||||
}
|
||||
|
||||
enum FloatKind {
|
||||
F32,
|
||||
F16,
|
||||
}
|
||||
|
||||
// The following regexes (from the WGSL spec) will be matched:
|
||||
|
||||
// int_literal:
|
||||
// | / 0 [iu]? /
|
||||
// | / [1-9][0-9]* [iu]? /
|
||||
// | / 0[xX][0-9a-fA-F]+ [iu]? /
|
||||
|
||||
// decimal_float_literal:
|
||||
// | / 0 [fh] /
|
||||
// | / [1-9][0-9]* [fh] /
|
||||
// | / [0-9]* \.[0-9]+ ([eE][+-]?[0-9]+)? [fh]? /
|
||||
// | / [0-9]+ \.[0-9]* ([eE][+-]?[0-9]+)? [fh]? /
|
||||
// | / [0-9]+ [eE][+-]?[0-9]+ [fh]? /
|
||||
|
||||
// hex_float_literal:
|
||||
// | / 0[xX][0-9a-fA-F]* \.[0-9a-fA-F]+ ([pP][+-]?[0-9]+ [fh]?)? /
|
||||
// | / 0[xX][0-9a-fA-F]+ \.[0-9a-fA-F]* ([pP][+-]?[0-9]+ [fh]?)? /
|
||||
// | / 0[xX][0-9a-fA-F]+ [pP][+-]?[0-9]+ [fh]? /
|
||||
|
||||
// You could visualize the regex below via https://debuggex.com to get a rough idea what `parse` is doing
|
||||
// -?(?:0[xX](?:([0-9a-fA-F]+\.[0-9a-fA-F]*|[0-9a-fA-F]*\.[0-9a-fA-F]+)(?:([pP][+-]?[0-9]+)([fh]?))?|([0-9a-fA-F]+)([pP][+-]?[0-9]+)([fh]?)|([0-9a-fA-F]+)([iu]?))|((?:[0-9]+[eE][+-]?[0-9]+|(?:[0-9]+\.[0-9]*|[0-9]*\.[0-9]+)(?:[eE][+-]?[0-9]+)?))([fh]?)|((?:[0-9]|[1-9][0-9]+))([iufh]?))
|
||||
|
||||
fn parse(input: &str) -> (Result<Number, NumberError>, &str) {
|
||||
/// returns `true` and consumes `X` bytes from the given byte buffer
|
||||
/// if the given `X` nr of patterns are found at the start of the buffer
|
||||
macro_rules! consume {
|
||||
($bytes:ident, $($($pattern:pat)|*),*) => {
|
||||
match $bytes {
|
||||
&[$($($pattern)|*),*, ref rest @ ..] => { $bytes = rest; true },
|
||||
_ => false,
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// consumes one byte from the given byte buffer
|
||||
/// if one of the given patterns are found at the start of the buffer
|
||||
/// returning the corresponding expr for the matched pattern
|
||||
macro_rules! consume_map {
|
||||
($bytes:ident, [$($($pattern:pat)|* => $to:expr),*]) => {
|
||||
match $bytes {
|
||||
$( &[$($pattern)|*, ref rest @ ..] => { $bytes = rest; Some($to) }, )*
|
||||
_ => None,
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/// consumes all consecutive bytes matched by the `0-9` pattern from the given byte buffer
|
||||
/// returning the number of consumed bytes
|
||||
macro_rules! consume_dec_digits {
|
||||
($bytes:ident) => {{
|
||||
let start_len = $bytes.len();
|
||||
while let &[b'0'..=b'9', ref rest @ ..] = $bytes {
|
||||
$bytes = rest;
|
||||
}
|
||||
start_len - $bytes.len()
|
||||
}};
|
||||
}
|
||||
|
||||
/// consumes all consecutive bytes matched by the `0-9 | a-f | A-F` pattern from the given byte buffer
|
||||
/// returning the number of consumed bytes
|
||||
macro_rules! consume_hex_digits {
|
||||
($bytes:ident) => {{
|
||||
let start_len = $bytes.len();
|
||||
while let &[b'0'..=b'9' | b'a'..=b'f' | b'A'..=b'F', ref rest @ ..] = $bytes {
|
||||
$bytes = rest;
|
||||
}
|
||||
start_len - $bytes.len()
|
||||
}};
|
||||
}
|
||||
|
||||
/// maps the given `&[u8]` (tail of the initial `input: &str`) to a `&str`
|
||||
macro_rules! rest_to_str {
|
||||
($bytes:ident) => {
|
||||
&input[input.len() - $bytes.len()..]
|
||||
};
|
||||
}
|
||||
|
||||
struct ExtractSubStr<'a>(&'a str);
|
||||
|
||||
impl<'a> ExtractSubStr<'a> {
|
||||
/// given an `input` and a `start` (tail of the `input`)
|
||||
/// creates a new [ExtractSubStr]
|
||||
fn start(input: &'a str, start: &'a [u8]) -> Self {
|
||||
let start = input.len() - start.len();
|
||||
Self(&input[start..])
|
||||
}
|
||||
/// given an `end` (tail of the initial `input`)
|
||||
/// returns a substring of `input`
|
||||
fn end(&self, end: &'a [u8]) -> &'a str {
|
||||
let end = self.0.len() - end.len();
|
||||
&self.0[..end]
|
||||
}
|
||||
}
|
||||
|
||||
let mut bytes = input.as_bytes();
|
||||
|
||||
let general_extract = ExtractSubStr::start(input, bytes);
|
||||
|
||||
let is_negative = consume!(bytes, b'-');
|
||||
|
||||
if consume!(bytes, b'0', b'x' | b'X') {
|
||||
let digits_extract = ExtractSubStr::start(input, bytes);
|
||||
|
||||
let consumed = consume_hex_digits!(bytes);
|
||||
|
||||
if consume!(bytes, b'.') {
|
||||
let consumed_after_period = consume_hex_digits!(bytes);
|
||||
|
||||
if consumed + consumed_after_period == 0 {
|
||||
return (Err(NumberError::Invalid), rest_to_str!(bytes));
|
||||
}
|
||||
|
||||
let significand = general_extract.end(bytes);
|
||||
|
||||
if consume!(bytes, b'p' | b'P') {
|
||||
consume!(bytes, b'+' | b'-');
|
||||
let consumed = consume_dec_digits!(bytes);
|
||||
|
||||
if consumed == 0 {
|
||||
return (Err(NumberError::Invalid), rest_to_str!(bytes));
|
||||
}
|
||||
|
||||
let number = general_extract.end(bytes);
|
||||
|
||||
let kind = consume_map!(bytes, [b'f' => FloatKind::F32, b'h' => FloatKind::F16]);
|
||||
|
||||
(parse_hex_float(number, kind), rest_to_str!(bytes))
|
||||
} else {
|
||||
(
|
||||
parse_hex_float_missing_exponent(significand, None),
|
||||
rest_to_str!(bytes),
|
||||
)
|
||||
}
|
||||
} else {
|
||||
if consumed == 0 {
|
||||
return (Err(NumberError::Invalid), rest_to_str!(bytes));
|
||||
}
|
||||
|
||||
let significand = general_extract.end(bytes);
|
||||
let digits = digits_extract.end(bytes);
|
||||
|
||||
let exp_extract = ExtractSubStr::start(input, bytes);
|
||||
|
||||
if consume!(bytes, b'p' | b'P') {
|
||||
consume!(bytes, b'+' | b'-');
|
||||
let consumed = consume_dec_digits!(bytes);
|
||||
|
||||
if consumed == 0 {
|
||||
return (Err(NumberError::Invalid), rest_to_str!(bytes));
|
||||
}
|
||||
|
||||
let exponent = exp_extract.end(bytes);
|
||||
|
||||
let kind = consume_map!(bytes, [b'f' => FloatKind::F32, b'h' => FloatKind::F16]);
|
||||
|
||||
(
|
||||
parse_hex_float_missing_period(significand, exponent, kind),
|
||||
rest_to_str!(bytes),
|
||||
)
|
||||
} else {
|
||||
let kind = consume_map!(bytes, [b'i' => IntKind::I32, b'u' => IntKind::U32]);
|
||||
|
||||
(
|
||||
parse_hex_int(is_negative, digits, kind),
|
||||
rest_to_str!(bytes),
|
||||
)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
let is_first_zero = bytes.first() == Some(&b'0');
|
||||
|
||||
let consumed = consume_dec_digits!(bytes);
|
||||
|
||||
if consume!(bytes, b'.') {
|
||||
let consumed_after_period = consume_dec_digits!(bytes);
|
||||
|
||||
if consumed + consumed_after_period == 0 {
|
||||
return (Err(NumberError::Invalid), rest_to_str!(bytes));
|
||||
}
|
||||
|
||||
if consume!(bytes, b'e' | b'E') {
|
||||
consume!(bytes, b'+' | b'-');
|
||||
let consumed = consume_dec_digits!(bytes);
|
||||
|
||||
if consumed == 0 {
|
||||
return (Err(NumberError::Invalid), rest_to_str!(bytes));
|
||||
}
|
||||
}
|
||||
|
||||
let number = general_extract.end(bytes);
|
||||
|
||||
let kind = consume_map!(bytes, [b'f' => FloatKind::F32, b'h' => FloatKind::F16]);
|
||||
|
||||
(parse_dec_float(number, kind), rest_to_str!(bytes))
|
||||
} else {
|
||||
if consumed == 0 {
|
||||
return (Err(NumberError::Invalid), rest_to_str!(bytes));
|
||||
}
|
||||
|
||||
if consume!(bytes, b'e' | b'E') {
|
||||
consume!(bytes, b'+' | b'-');
|
||||
let consumed = consume_dec_digits!(bytes);
|
||||
|
||||
if consumed == 0 {
|
||||
return (Err(NumberError::Invalid), rest_to_str!(bytes));
|
||||
}
|
||||
|
||||
let number = general_extract.end(bytes);
|
||||
|
||||
let kind = consume_map!(bytes, [b'f' => FloatKind::F32, b'h' => FloatKind::F16]);
|
||||
|
||||
(parse_dec_float(number, kind), rest_to_str!(bytes))
|
||||
} else {
|
||||
// make sure the multi-digit numbers don't start with zero
|
||||
if consumed > 1 && is_first_zero {
|
||||
return (Err(NumberError::Invalid), rest_to_str!(bytes));
|
||||
}
|
||||
|
||||
let digits_with_sign = general_extract.end(bytes);
|
||||
|
||||
let kind = consume_map!(bytes, [
|
||||
b'i' => Kind::Int(IntKind::I32),
|
||||
b'u' => Kind::Int(IntKind::U32),
|
||||
b'f' => Kind::Float(FloatKind::F32),
|
||||
b'h' => Kind::Float(FloatKind::F16)
|
||||
]);
|
||||
|
||||
(
|
||||
parse_dec(is_negative, digits_with_sign, kind),
|
||||
rest_to_str!(bytes),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_hex_float_missing_exponent(
|
||||
// format: -?0[xX] ( [0-9a-fA-F]+\.[0-9a-fA-F]* | [0-9a-fA-F]*\.[0-9a-fA-F]+ )
|
||||
significand: &str,
|
||||
kind: Option<FloatKind>,
|
||||
) -> Result<Number, NumberError> {
|
||||
let hexf_input = format!("{}{}", significand, "p0");
|
||||
parse_hex_float(&hexf_input, kind)
|
||||
}
|
||||
|
||||
fn parse_hex_float_missing_period(
|
||||
// format: -?0[xX] [0-9a-fA-F]+
|
||||
significand: &str,
|
||||
// format: [pP][+-]?[0-9]+
|
||||
exponent: &str,
|
||||
kind: Option<FloatKind>,
|
||||
) -> Result<Number, NumberError> {
|
||||
let hexf_input = format!("{}.{}", significand, exponent);
|
||||
parse_hex_float(&hexf_input, kind)
|
||||
}
|
||||
|
||||
fn parse_hex_int(
|
||||
is_negative: bool,
|
||||
// format: [0-9a-fA-F]+
|
||||
digits: &str,
|
||||
kind: Option<IntKind>,
|
||||
) -> Result<Number, NumberError> {
|
||||
let digits_with_sign = if is_negative {
|
||||
Cow::Owned(format!("-{}", digits))
|
||||
} else {
|
||||
Cow::Borrowed(digits)
|
||||
};
|
||||
parse_int(&digits_with_sign, kind, 16, is_negative)
|
||||
}
|
||||
|
||||
fn parse_dec(
|
||||
is_negative: bool,
|
||||
// format: -? ( [0-9] | [1-9][0-9]+ )
|
||||
digits_with_sign: &str,
|
||||
kind: Option<Kind>,
|
||||
) -> Result<Number, NumberError> {
|
||||
match kind {
|
||||
None => parse_int(digits_with_sign, None, 10, is_negative),
|
||||
Some(Kind::Int(kind)) => parse_int(digits_with_sign, Some(kind), 10, is_negative),
|
||||
Some(Kind::Float(kind)) => parse_dec_float(digits_with_sign, Some(kind)),
|
||||
}
|
||||
}
|
||||
|
||||
// Float parsing notes
|
||||
|
||||
// The following chapters of IEEE 754-2019 are relevant:
|
||||
//
|
||||
// 7.4 Overflow (largest finite number is exceeded by what would have been
|
||||
// the rounded floating-point result were the exponent range unbounded)
|
||||
//
|
||||
// 7.5 Underflow (tiny non-zero result is detected;
|
||||
// for decimal formats tininess is detected before rounding when a non-zero result
|
||||
// computed as though both the exponent range and the precision were unbounded
|
||||
// would lie strictly between 2^−126)
|
||||
//
|
||||
// 7.6 Inexact (rounded result differs from what would have been computed
|
||||
// were both exponent range and precision unbounded)
|
||||
|
||||
// The WGSL spec requires us to error:
|
||||
// on overflow for decimal floating point literals
|
||||
// on overflow and inexact for hexadecimal floating point literals
|
||||
// (underflow is not mentioned)
|
||||
|
||||
// hexf_parse errors on overflow, underflow, inexact
|
||||
// rust std lib float from str handles overflow, underflow, inexact transparently (rounds and will not error)
|
||||
|
||||
// Therefore we only check for overflow manually for decimal floating point literals
|
||||
|
||||
// input format: -?0[xX] ( [0-9a-fA-F]+\.[0-9a-fA-F]* | [0-9a-fA-F]*\.[0-9a-fA-F]+ ) [pP][+-]?[0-9]+
|
||||
fn parse_hex_float(input: &str, kind: Option<FloatKind>) -> Result<Number, NumberError> {
|
||||
match kind {
|
||||
None => match hexf_parse::parse_hexf64(input, false) {
|
||||
Ok(num) => Ok(Number::AbstractFloat(num)),
|
||||
// can only be ParseHexfErrorKind::Inexact but we can't check since it's private
|
||||
_ => Err(NumberError::NotRepresentable),
|
||||
},
|
||||
Some(FloatKind::F32) => match hexf_parse::parse_hexf32(input, false) {
|
||||
Ok(num) => Ok(Number::F32(num)),
|
||||
// can only be ParseHexfErrorKind::Inexact but we can't check since it's private
|
||||
_ => Err(NumberError::NotRepresentable),
|
||||
},
|
||||
Some(FloatKind::F16) => Err(NumberError::UnimplementedF16),
|
||||
}
|
||||
}
|
||||
|
||||
// input format: -? ( [0-9]+\.[0-9]* | [0-9]*\.[0-9]+ ) ([eE][+-]?[0-9]+)?
|
||||
// | -? [0-9]+ [eE][+-]?[0-9]+
|
||||
fn parse_dec_float(input: &str, kind: Option<FloatKind>) -> Result<Number, NumberError> {
|
||||
match kind {
|
||||
None => {
|
||||
let num = input.parse::<f64>().unwrap(); // will never fail
|
||||
num.is_finite()
|
||||
.then(|| 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))
|
||||
.ok_or(NumberError::NotRepresentable)
|
||||
}
|
||||
Some(FloatKind::F16) => Err(NumberError::UnimplementedF16),
|
||||
}
|
||||
}
|
||||
|
||||
fn parse_int(
|
||||
input: &str,
|
||||
kind: Option<IntKind>,
|
||||
radix: u32,
|
||||
is_negative: bool,
|
||||
) -> Result<Number, NumberError> {
|
||||
fn map_err(e: core::num::ParseIntError) -> NumberError {
|
||||
match *e.kind() {
|
||||
core::num::IntErrorKind::PosOverflow | core::num::IntErrorKind::NegOverflow => {
|
||||
NumberError::NotRepresentable
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
match kind {
|
||||
None => match i64::from_str_radix(input, radix) {
|
||||
Ok(num) => Ok(Number::AbstractInt(num)),
|
||||
Err(e) => Err(map_err(e)),
|
||||
},
|
||||
Some(IntKind::I32) => match i32::from_str_radix(input, radix) {
|
||||
Ok(num) => Ok(Number::I32(num)),
|
||||
Err(e) => Err(map_err(e)),
|
||||
},
|
||||
Some(IntKind::U32) if is_negative => Err(NumberError::NotRepresentable),
|
||||
Some(IntKind::U32) => match u32::from_str_radix(input, radix) {
|
||||
Ok(num) => Ok(Number::U32(num)),
|
||||
Err(e) => Err(map_err(e)),
|
||||
},
|
||||
}
|
||||
}
|
@ -1,204 +0,0 @@
|
||||
use std::convert::TryFrom;
|
||||
|
||||
use hexf_parse::parse_hexf32;
|
||||
|
||||
use crate::Bytes;
|
||||
|
||||
use super::{
|
||||
lexer::{try_skip_prefix, Lexer},
|
||||
BadFloatError, BadIntError, Error, ExpectedToken, NumberType, Span, Token,
|
||||
};
|
||||
|
||||
fn check_int_literal(word_without_minus: &str, minus: bool, hex: bool) -> Result<(), BadIntError> {
|
||||
let leading_zeros = word_without_minus
|
||||
.bytes()
|
||||
.take_while(|&b| b == b'0')
|
||||
.count();
|
||||
|
||||
if word_without_minus == "0" && minus {
|
||||
Err(BadIntError::NegativeZero)
|
||||
} else if word_without_minus != "0" && !hex && leading_zeros != 0 {
|
||||
Err(BadIntError::LeadingZeros)
|
||||
} else {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
pub fn get_i32_literal(word: &str, span: Span) -> Result<i32, Error<'_>> {
|
||||
let (minus, word_without_minus, _) = try_skip_prefix(word, "-");
|
||||
let (hex, word_without_minus_and_0x, _) = try_skip_prefix(word_without_minus, "0x");
|
||||
|
||||
check_int_literal(word_without_minus, minus, hex)
|
||||
.map_err(|e| Error::BadI32(span.clone(), e))?;
|
||||
|
||||
let parsed_val = match (hex, minus) {
|
||||
(true, true) => i32::from_str_radix(&format!("-{}", word_without_minus_and_0x), 16),
|
||||
(true, false) => i32::from_str_radix(word_without_minus_and_0x, 16),
|
||||
(false, _) => word.parse(),
|
||||
};
|
||||
|
||||
parsed_val.map_err(|e| Error::BadI32(span, e.into()))
|
||||
}
|
||||
|
||||
pub fn get_u32_literal(word: &str, span: Span) -> Result<u32, Error<'_>> {
|
||||
let (minus, word_without_minus, _) = try_skip_prefix(word, "-");
|
||||
let (hex, word_without_minus_and_0x, _) = try_skip_prefix(word_without_minus, "0x");
|
||||
|
||||
check_int_literal(word_without_minus, minus, hex)
|
||||
.map_err(|e| Error::BadU32(span.clone(), e))?;
|
||||
|
||||
// We need to add a minus here as well, since the lexer also accepts syntactically incorrect negative uints
|
||||
let parsed_val = match (hex, minus) {
|
||||
(true, true) => u32::from_str_radix(&format!("-{}", word_without_minus_and_0x), 16),
|
||||
(true, false) => u32::from_str_radix(word_without_minus_and_0x, 16),
|
||||
(false, _) => word.parse(),
|
||||
};
|
||||
|
||||
parsed_val.map_err(|e| Error::BadU32(span, e.into()))
|
||||
}
|
||||
|
||||
pub fn get_f32_literal(word: &str, span: Span) -> Result<f32, Error<'_>> {
|
||||
let hex = word.starts_with("0x") || word.starts_with("-0x");
|
||||
|
||||
let parsed_val = if hex {
|
||||
parse_hexf32(word, false).map_err(BadFloatError::ParseHexfError)
|
||||
} else {
|
||||
word.parse::<f32>().map_err(BadFloatError::ParseFloatError)
|
||||
};
|
||||
|
||||
parsed_val.map_err(|e| Error::BadFloat(span, e))
|
||||
}
|
||||
|
||||
pub(super) fn _parse_uint_literal<'a>(
|
||||
lexer: &mut Lexer<'a>,
|
||||
width: Bytes,
|
||||
) -> Result<u32, Error<'a>> {
|
||||
let token_span = lexer.next();
|
||||
|
||||
if width != 4 {
|
||||
// Only 32-bit literals supported by the spec and naga for now!
|
||||
return Err(Error::BadScalarWidth(token_span.1, width));
|
||||
}
|
||||
|
||||
match token_span {
|
||||
(
|
||||
Token::Number {
|
||||
value,
|
||||
ty: NumberType::Uint,
|
||||
},
|
||||
span,
|
||||
) => get_u32_literal(value, span),
|
||||
other => Err(Error::Unexpected(
|
||||
other,
|
||||
ExpectedToken::Number {
|
||||
ty: Some(NumberType::Uint),
|
||||
width: Some(width),
|
||||
},
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Parse a non-negative signed integer literal.
|
||||
/// This is for attributes like `size`, `location` and others.
|
||||
pub(super) fn parse_non_negative_sint_literal<'a>(
|
||||
lexer: &mut Lexer<'a>,
|
||||
width: Bytes,
|
||||
) -> Result<u32, Error<'a>> {
|
||||
let token_span = lexer.next();
|
||||
|
||||
if width != 4 {
|
||||
// Only 32-bit literals supported by the spec and naga for now!
|
||||
return Err(Error::BadScalarWidth(token_span.1, width));
|
||||
}
|
||||
|
||||
match token_span {
|
||||
(
|
||||
Token::Number {
|
||||
value,
|
||||
ty: NumberType::Sint,
|
||||
},
|
||||
span,
|
||||
) => {
|
||||
let i32_val = get_i32_literal(value, span.clone())?;
|
||||
u32::try_from(i32_val).map_err(|_| Error::NegativeInt(span))
|
||||
}
|
||||
other => Err(Error::Unexpected(
|
||||
other,
|
||||
ExpectedToken::Number {
|
||||
ty: Some(NumberType::Sint),
|
||||
width: Some(width),
|
||||
},
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
/// Parse a non-negative integer literal that may be either signed or unsigned.
|
||||
/// This is for the `workgroup_size` attribute and array lengths.
|
||||
/// Note: these values should be no larger than [`i32::MAX`], but this is not checked here.
|
||||
pub(super) fn parse_generic_non_negative_int_literal<'a>(
|
||||
lexer: &mut Lexer<'a>,
|
||||
width: Bytes,
|
||||
) -> Result<u32, Error<'a>> {
|
||||
let token_span = lexer.next();
|
||||
|
||||
if width != 4 {
|
||||
// Only 32-bit literals supported by the spec and naga for now!
|
||||
return Err(Error::BadScalarWidth(token_span.1, width));
|
||||
}
|
||||
|
||||
match token_span {
|
||||
(
|
||||
Token::Number {
|
||||
value,
|
||||
ty: NumberType::Sint,
|
||||
},
|
||||
span,
|
||||
) => {
|
||||
let i32_val = get_i32_literal(value, span.clone())?;
|
||||
u32::try_from(i32_val).map_err(|_| Error::NegativeInt(span))
|
||||
}
|
||||
(
|
||||
Token::Number {
|
||||
value,
|
||||
ty: NumberType::Uint,
|
||||
},
|
||||
span,
|
||||
) => get_u32_literal(value, span),
|
||||
other => Err(Error::Unexpected(
|
||||
other,
|
||||
ExpectedToken::Number {
|
||||
ty: Some(NumberType::Sint),
|
||||
width: Some(width),
|
||||
},
|
||||
)),
|
||||
}
|
||||
}
|
||||
|
||||
pub(super) fn _parse_float_literal<'a>(
|
||||
lexer: &mut Lexer<'a>,
|
||||
width: Bytes,
|
||||
) -> Result<f32, Error<'a>> {
|
||||
let token_span = lexer.next();
|
||||
|
||||
if width != 4 {
|
||||
// Only 32-bit literals supported by the spec and naga for now!
|
||||
return Err(Error::BadScalarWidth(token_span.1, width));
|
||||
}
|
||||
|
||||
match token_span {
|
||||
(
|
||||
Token::Number {
|
||||
value,
|
||||
ty: NumberType::Float,
|
||||
},
|
||||
span,
|
||||
) => get_f32_literal(value, span),
|
||||
other => Err(Error::Unexpected(
|
||||
other,
|
||||
ExpectedToken::Number {
|
||||
ty: Some(NumberType::Float),
|
||||
width: Some(width),
|
||||
},
|
||||
)),
|
||||
}
|
||||
}
|
72
third_party/rust/naga/src/front/wgsl/tests.rs
vendored
72
third_party/rust/naga/src/front/wgsl/tests.rs
vendored
@ -14,76 +14,6 @@ fn parse_comment() {
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
// Regexes for the literals are taken from the working draft at
|
||||
// https://www.w3.org/TR/2021/WD-WGSL-20210806/#literals
|
||||
|
||||
#[test]
|
||||
fn parse_decimal_floats() {
|
||||
// /^(-?[0-9]*\.[0-9]+|-?[0-9]+\.[0-9]*)((e|E)(\+|-)?[0-9]+)?$/
|
||||
parse_str("let a : f32 = -1.;").unwrap();
|
||||
parse_str("let a : f32 = -.1;").unwrap();
|
||||
parse_str("let a : f32 = 42.1234;").unwrap();
|
||||
parse_str("let a : f32 = -1.E3;").unwrap();
|
||||
parse_str("let a : f32 = -.1e-5;").unwrap();
|
||||
parse_str("let a : f32 = 2.3e+55;").unwrap();
|
||||
|
||||
assert!(parse_str("let a : f32 = 42.1234f;").is_err());
|
||||
assert!(parse_str("let a : f32 = 42.1234f32;").is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_hex_floats() {
|
||||
// /^-?0x([0-9a-fA-F]*\.?[0-9a-fA-F]+|[0-9a-fA-F]+\.[0-9a-fA-F]*)(p|P)(\+|-)?[0-9]+$/
|
||||
parse_str("let a : f32 = -0xa.p1;").unwrap();
|
||||
parse_str("let a : f32 = -0x.fp9;").unwrap();
|
||||
parse_str("let a : f32 = 0x2a.4D2P4;").unwrap();
|
||||
parse_str("let a : f32 = -0x.1p-5;").unwrap();
|
||||
parse_str("let a : f32 = 0xC.8p+55;").unwrap();
|
||||
parse_str("let a : f32 = 0x1p1;").unwrap();
|
||||
|
||||
assert!(parse_str("let a : f32 = 0x1p1f;").is_err());
|
||||
assert!(parse_str("let a : f32 = 0x1p1f32;").is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_decimal_ints() {
|
||||
// i32 /^-?0x[0-9a-fA-F]+|0|-?[1-9][0-9]*$/
|
||||
parse_str("let a : i32 = 0;").unwrap();
|
||||
parse_str("let a : i32 = 1092;").unwrap();
|
||||
parse_str("let a : i32 = -9923;").unwrap();
|
||||
|
||||
assert!(parse_str("let a : i32 = -0;").is_err());
|
||||
assert!(parse_str("let a : i32 = 01;").is_err());
|
||||
assert!(parse_str("let a : i32 = 1.0;").is_err());
|
||||
assert!(parse_str("let a : i32 = 1i;").is_err());
|
||||
assert!(parse_str("let a : i32 = 1i32;").is_err());
|
||||
|
||||
// u32 /^0x[0-9a-fA-F]+u|0u|[1-9][0-9]*u$/
|
||||
parse_str("let a : u32 = 0u;").unwrap();
|
||||
parse_str("let a : u32 = 1092u;").unwrap();
|
||||
|
||||
assert!(parse_str("let a : u32 = -0u;").is_err());
|
||||
assert!(parse_str("let a : u32 = 01u;").is_err());
|
||||
assert!(parse_str("let a : u32 = 1.0u;").is_err());
|
||||
assert!(parse_str("let a : u32 = 1u32;").is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_hex_ints() {
|
||||
// i32 /^-?0x[0-9a-fA-F]+|0|-?[1-9][0-9]*$/
|
||||
parse_str("let a : i32 = -0x0;").unwrap();
|
||||
parse_str("let a : i32 = 0x2a4D2;").unwrap();
|
||||
|
||||
assert!(parse_str("let a : i32 = 0x2a4D2i;").is_err());
|
||||
assert!(parse_str("let a : i32 = 0x2a4D2i32;").is_err());
|
||||
|
||||
// u32 /^0x[0-9a-fA-F]+u|0u|[1-9][0-9]*u$/
|
||||
parse_str("let a : u32 = 0x0u;").unwrap();
|
||||
parse_str("let a : u32 = 0x2a4D2u;").unwrap();
|
||||
|
||||
assert!(parse_str("let a : u32 = 0x2a4D2u32;").is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parse_types() {
|
||||
parse_str("let a : i32 = 2;").unwrap();
|
||||
@ -161,7 +91,7 @@ fn parse_struct() {
|
||||
struct Bar {
|
||||
@size(16) x: vec2<i32>,
|
||||
@align(16) y: f32,
|
||||
@size(32) @align(8) z: vec3<f32>,
|
||||
@size(32) @align(128) z: vec3<f32>,
|
||||
};
|
||||
struct Empty {}
|
||||
var<storage,read_write> s: Foo;
|
||||
|
16
third_party/rust/naga/src/lib.rs
vendored
16
third_party/rust/naga/src/lib.rs
vendored
@ -211,7 +211,7 @@ pub mod valid;
|
||||
|
||||
pub use crate::arena::{Arena, Handle, Range, UniqueArena};
|
||||
|
||||
pub use crate::span::{Span, SpanContext, WithSpan};
|
||||
pub use crate::span::{SourceLocation, Span, SpanContext, WithSpan};
|
||||
#[cfg(feature = "arbitrary")]
|
||||
use arbitrary::Arbitrary;
|
||||
#[cfg(feature = "deserialize")]
|
||||
@ -1439,11 +1439,23 @@ pub enum Statement {
|
||||
/// this loop. (It may have `Break` and `Continue` statements targeting
|
||||
/// loops or switches nested within the `continuing` block.)
|
||||
///
|
||||
/// If present, `break_if` is an expression which is evaluated after the
|
||||
/// continuing block. If its value is true, control continues after the
|
||||
/// `Loop` statement, rather than branching back to the top of body as
|
||||
/// usual. The `break_if` expression corresponds to a "break if" statement
|
||||
/// in WGSL, or a loop whose back edge is an `OpBranchConditional`
|
||||
/// instruction in SPIR-V.
|
||||
///
|
||||
/// [`Break`]: Statement::Break
|
||||
/// [`Continue`]: Statement::Continue
|
||||
/// [`Kill`]: Statement::Kill
|
||||
/// [`Return`]: Statement::Return
|
||||
Loop { body: Block, continuing: Block },
|
||||
/// [`break if`]: Self::Loop::break_if
|
||||
Loop {
|
||||
body: Block,
|
||||
continuing: Block,
|
||||
break_if: Option<Handle<Expression>>,
|
||||
},
|
||||
|
||||
/// Exits the innermost enclosing [`Loop`] or [`Switch`].
|
||||
///
|
||||
|
175
third_party/rust/naga/src/proc/layouter.rs
vendored
175
third_party/rust/naga/src/proc/layouter.rs
vendored
@ -1,7 +1,86 @@
|
||||
use crate::arena::{Arena, BadHandle, Handle, UniqueArena};
|
||||
use std::{num::NonZeroU32, ops};
|
||||
use std::{fmt::Display, num::NonZeroU32, ops};
|
||||
|
||||
pub type Alignment = NonZeroU32;
|
||||
/// A newtype struct where its only valid values are powers of 2
|
||||
#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq, PartialOrd, Ord)]
|
||||
#[cfg_attr(feature = "serialize", derive(serde::Serialize))]
|
||||
#[cfg_attr(feature = "deserialize", derive(serde::Deserialize))]
|
||||
pub struct Alignment(NonZeroU32);
|
||||
|
||||
impl Alignment {
|
||||
pub const ONE: Self = Self(unsafe { NonZeroU32::new_unchecked(1) });
|
||||
pub const TWO: Self = Self(unsafe { NonZeroU32::new_unchecked(2) });
|
||||
pub const FOUR: Self = Self(unsafe { NonZeroU32::new_unchecked(4) });
|
||||
pub const EIGHT: Self = Self(unsafe { NonZeroU32::new_unchecked(8) });
|
||||
pub const SIXTEEN: Self = Self(unsafe { NonZeroU32::new_unchecked(16) });
|
||||
|
||||
pub const MIN_UNIFORM: Self = Self::SIXTEEN;
|
||||
|
||||
pub const fn new(n: u32) -> Option<Self> {
|
||||
if n.is_power_of_two() {
|
||||
// SAFETY: value can't be 0 since we just checked if it's a power of 2
|
||||
Some(Self(unsafe { NonZeroU32::new_unchecked(n) }))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// # Panics
|
||||
/// If `width` is not a power of 2
|
||||
pub fn from_width(width: u8) -> Self {
|
||||
Self::new(width as u32).unwrap()
|
||||
}
|
||||
|
||||
/// Returns whether or not `n` is a multiple of this alignment.
|
||||
pub const fn is_aligned(&self, n: u32) -> bool {
|
||||
// equivalent to: `n % self.0.get() == 0` but much faster
|
||||
n & (self.0.get() - 1) == 0
|
||||
}
|
||||
|
||||
/// Round `n` up to the nearest alignment boundary.
|
||||
pub const fn round_up(&self, n: u32) -> u32 {
|
||||
// equivalent to:
|
||||
// match n % self.0.get() {
|
||||
// 0 => n,
|
||||
// rem => n + (self.0.get() - rem),
|
||||
// }
|
||||
let mask = self.0.get() - 1;
|
||||
(n + mask) & !mask
|
||||
}
|
||||
}
|
||||
|
||||
impl Display for Alignment {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
self.0.get().fmt(f)
|
||||
}
|
||||
}
|
||||
|
||||
impl ops::Mul<u32> for Alignment {
|
||||
type Output = u32;
|
||||
|
||||
fn mul(self, rhs: u32) -> Self::Output {
|
||||
self.0.get() * rhs
|
||||
}
|
||||
}
|
||||
|
||||
impl ops::Mul for Alignment {
|
||||
type Output = Alignment;
|
||||
|
||||
fn mul(self, rhs: Alignment) -> Self::Output {
|
||||
// SAFETY: both lhs and rhs are powers of 2, the result will be a power of 2
|
||||
Self(unsafe { NonZeroU32::new_unchecked(self.0.get() * rhs.0.get()) })
|
||||
}
|
||||
}
|
||||
|
||||
impl From<crate::VectorSize> for Alignment {
|
||||
fn from(size: crate::VectorSize) -> Self {
|
||||
match size {
|
||||
crate::VectorSize::Bi => Alignment::TWO,
|
||||
crate::VectorSize::Tri => Alignment::FOUR,
|
||||
crate::VectorSize::Quad => Alignment::FOUR,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Size and alignment information for a type.
|
||||
#[derive(Clone, Copy, Debug, Hash, PartialEq)]
|
||||
@ -15,7 +94,7 @@ pub struct TypeLayout {
|
||||
impl TypeLayout {
|
||||
/// Produce the stride as if this type is a base of an array.
|
||||
pub const fn to_stride(&self) -> u32 {
|
||||
Layouter::round_up(self.alignment, self.size)
|
||||
self.alignment.round_up(self.size)
|
||||
}
|
||||
}
|
||||
|
||||
@ -49,8 +128,8 @@ pub enum LayoutErrorInner {
|
||||
InvalidArrayElementType(Handle<crate::Type>),
|
||||
#[error("Struct member[{0}] type {1:?} doesn't exist")]
|
||||
InvalidStructMemberType(u32, Handle<crate::Type>),
|
||||
#[error("Zero width is not supported")]
|
||||
ZeroWidth,
|
||||
#[error("Type width must be a power of two")]
|
||||
NonPowerOfTwoWidth,
|
||||
#[error("Array size is a bad handle")]
|
||||
BadHandle(#[from] BadHandle),
|
||||
}
|
||||
@ -74,40 +153,6 @@ impl Layouter {
|
||||
self.layouts.clear();
|
||||
}
|
||||
|
||||
/// Round `offset` up to the nearest `alignment` boundary.
|
||||
pub const fn round_up(alignment: Alignment, offset: u32) -> u32 {
|
||||
match offset & (alignment.get() - 1) {
|
||||
0 => offset,
|
||||
other => offset + alignment.get() - other,
|
||||
}
|
||||
}
|
||||
|
||||
/// Return the offset and span of a struct member.
|
||||
///
|
||||
/// The member must fall at or after `offset`. The member's alignment and
|
||||
/// size are `align` and `size` if given, defaulting to the values this
|
||||
/// `Layouter` has previously determined for `ty`.
|
||||
///
|
||||
/// The return value is the range of offsets within the containing struct to
|
||||
/// reserve for this member, along with the alignment used. The containing
|
||||
/// struct must have sufficient space and alignment to accommodate these.
|
||||
pub fn member_placement(
|
||||
&self,
|
||||
offset: u32,
|
||||
ty: Handle<crate::Type>,
|
||||
align: Option<Alignment>,
|
||||
size: Option<NonZeroU32>,
|
||||
) -> (ops::Range<u32>, Alignment) {
|
||||
let layout = self.layouts[ty.index()];
|
||||
let alignment = align.unwrap_or(layout.alignment);
|
||||
let start = Self::round_up(alignment, offset);
|
||||
let span = match size {
|
||||
Some(size) => size.get(),
|
||||
None => layout.size,
|
||||
};
|
||||
(start..start + span, alignment)
|
||||
}
|
||||
|
||||
/// Extend this `Layouter` with layouts for any new entries in `types`.
|
||||
///
|
||||
/// Ensure that every type in `types` has a corresponding [TypeLayout] in
|
||||
@ -135,42 +180,38 @@ impl Layouter {
|
||||
.try_size(constants)
|
||||
.map_err(|error| LayoutErrorInner::BadHandle(error).with(ty_handle))?;
|
||||
let layout = match ty.inner {
|
||||
Ti::Scalar { width, .. } | Ti::Atomic { width, .. } => TypeLayout {
|
||||
size,
|
||||
alignment: Alignment::new(width as u32)
|
||||
.ok_or(LayoutErrorInner::ZeroWidth.with(ty_handle))?,
|
||||
},
|
||||
Ti::Scalar { width, .. } | Ti::Atomic { width, .. } => {
|
||||
let alignment = Alignment::new(width as u32)
|
||||
.ok_or(LayoutErrorInner::NonPowerOfTwoWidth.with(ty_handle))?;
|
||||
TypeLayout { size, alignment }
|
||||
}
|
||||
Ti::Vector {
|
||||
size: vec_size,
|
||||
width,
|
||||
..
|
||||
} => TypeLayout {
|
||||
size,
|
||||
alignment: {
|
||||
let count = if vec_size >= crate::VectorSize::Tri {
|
||||
4
|
||||
} else {
|
||||
2
|
||||
};
|
||||
Alignment::new(count * width as u32)
|
||||
.ok_or(LayoutErrorInner::ZeroWidth.with(ty_handle))?
|
||||
},
|
||||
},
|
||||
} => {
|
||||
let alignment = Alignment::new(width as u32)
|
||||
.ok_or(LayoutErrorInner::NonPowerOfTwoWidth.with(ty_handle))?;
|
||||
TypeLayout {
|
||||
size,
|
||||
alignment: Alignment::from(vec_size) * alignment,
|
||||
}
|
||||
}
|
||||
Ti::Matrix {
|
||||
columns: _,
|
||||
rows,
|
||||
width,
|
||||
} => TypeLayout {
|
||||
size,
|
||||
alignment: {
|
||||
let count = if rows >= crate::VectorSize::Tri { 4 } else { 2 };
|
||||
Alignment::new(count * width as u32)
|
||||
.ok_or(LayoutErrorInner::ZeroWidth.with(ty_handle))?
|
||||
},
|
||||
},
|
||||
} => {
|
||||
let alignment = Alignment::new(width as u32)
|
||||
.ok_or(LayoutErrorInner::NonPowerOfTwoWidth.with(ty_handle))?;
|
||||
TypeLayout {
|
||||
size,
|
||||
alignment: Alignment::from(rows) * alignment,
|
||||
}
|
||||
}
|
||||
Ti::Pointer { .. } | Ti::ValuePointer { .. } => TypeLayout {
|
||||
size,
|
||||
alignment: Alignment::new(1).unwrap(),
|
||||
alignment: Alignment::ONE,
|
||||
},
|
||||
Ti::Array {
|
||||
base,
|
||||
@ -185,7 +226,7 @@ impl Layouter {
|
||||
},
|
||||
},
|
||||
Ti::Struct { span, ref members } => {
|
||||
let mut alignment = Alignment::new(1).unwrap();
|
||||
let mut alignment = Alignment::ONE;
|
||||
for (index, member) in members.iter().enumerate() {
|
||||
alignment = if member.ty < ty_handle {
|
||||
alignment.max(self[member.ty].alignment)
|
||||
@ -204,7 +245,7 @@ impl Layouter {
|
||||
}
|
||||
Ti::Image { .. } | Ti::Sampler { .. } | Ti::BindingArray { .. } => TypeLayout {
|
||||
size,
|
||||
alignment: Alignment::new(1).unwrap(),
|
||||
alignment: Alignment::ONE,
|
||||
},
|
||||
};
|
||||
debug_assert!(size <= layout.size);
|
||||
|
5
third_party/rust/naga/src/proc/mod.rs
vendored
5
third_party/rust/naga/src/proc/mod.rs
vendored
@ -113,10 +113,7 @@ impl super::TypeInner {
|
||||
columns,
|
||||
rows,
|
||||
width,
|
||||
} => {
|
||||
let aligned_rows = if rows > crate::VectorSize::Bi { 4 } else { 2 };
|
||||
columns as u32 * aligned_rows * width as u32
|
||||
}
|
||||
} => Alignment::from(rows) * width as u32 * columns as u32,
|
||||
Self::Pointer { .. } | Self::ValuePointer { .. } => POINTER_SPAN,
|
||||
Self::Array {
|
||||
base: _,
|
||||
|
154
third_party/rust/naga/src/span.rs
vendored
154
third_party/rust/naga/src/span.rs
vendored
@ -59,6 +59,21 @@ impl Span {
|
||||
pub fn is_defined(&self) -> bool {
|
||||
*self != Self::default()
|
||||
}
|
||||
|
||||
/// Return a [`SourceLocation`] for this span in the provided source.
|
||||
pub fn location(&self, source: &str) -> SourceLocation {
|
||||
let prefix = &source[..self.start as usize];
|
||||
let line_number = prefix.matches('\n').count() as u32 + 1;
|
||||
let line_start = prefix.rfind('\n').map(|pos| pos + 1).unwrap_or(0);
|
||||
let line_position = source[line_start..self.start as usize].chars().count() as u32 + 1;
|
||||
|
||||
SourceLocation {
|
||||
line_number,
|
||||
line_position,
|
||||
offset: self.start,
|
||||
length: self.end - self.start,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Range<usize>> for Span {
|
||||
@ -70,6 +85,25 @@ impl From<Range<usize>> for Span {
|
||||
}
|
||||
}
|
||||
|
||||
/// A human-readable representation for a span, tailored for text source.
|
||||
///
|
||||
/// Corresponds to the positional members of [`GPUCompilationMessage`][gcm] from
|
||||
/// the WebGPU specification, except that `offset` and `length` are in bytes
|
||||
/// (UTF-8 code units), instead of UTF-16 code units.
|
||||
///
|
||||
/// [gcm]: https://www.w3.org/TR/webgpu/#gpucompilationmessage
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
pub struct SourceLocation {
|
||||
/// 1-based line number.
|
||||
pub line_number: u32,
|
||||
/// 1-based column of the start of this span
|
||||
pub line_position: u32,
|
||||
/// 0-based Offset in code units (in bytes) of the start of the span.
|
||||
pub offset: u32,
|
||||
/// Length in code units (in bytes) of the span.
|
||||
pub length: u32,
|
||||
}
|
||||
|
||||
/// A source code span together with "context", a user-readable description of what part of the error it refers to.
|
||||
pub type SpanContext = (Span, String);
|
||||
|
||||
@ -186,6 +220,22 @@ impl<E> WithSpan<E> {
|
||||
res.spans.extend(self.spans);
|
||||
res
|
||||
}
|
||||
|
||||
#[cfg(feature = "span")]
|
||||
/// Return a [`SourceLocation`] for our first span, if we have one.
|
||||
pub fn location(&self, source: &str) -> Option<SourceLocation> {
|
||||
if self.spans.is_empty() {
|
||||
return None;
|
||||
}
|
||||
|
||||
Some(self.spans[0].0.location(source))
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "span"))]
|
||||
/// Return a [`SourceLocation`] for our first span, if we have one.
|
||||
pub fn location(&self, _source: &str) -> Option<SourceLocation> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Convenience trait for [`Error`] to be able to apply spans to anything.
|
||||
@ -273,3 +323,107 @@ impl<T, E, E2> MapErrWithSpan<E, E2> for Result<T, WithSpan<E>> {
|
||||
self.map_err(|e| e.and_then(func).into_other::<E2>())
|
||||
}
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn span_location() {
|
||||
let source = "12\n45\n\n89\n";
|
||||
assert_eq!(
|
||||
Span { start: 0, end: 1 }.location(source),
|
||||
SourceLocation {
|
||||
line_number: 1,
|
||||
line_position: 1,
|
||||
offset: 0,
|
||||
length: 1
|
||||
}
|
||||
);
|
||||
assert_eq!(
|
||||
Span { start: 1, end: 2 }.location(source),
|
||||
SourceLocation {
|
||||
line_number: 1,
|
||||
line_position: 2,
|
||||
offset: 1,
|
||||
length: 1
|
||||
}
|
||||
);
|
||||
assert_eq!(
|
||||
Span { start: 2, end: 3 }.location(source),
|
||||
SourceLocation {
|
||||
line_number: 1,
|
||||
line_position: 3,
|
||||
offset: 2,
|
||||
length: 1
|
||||
}
|
||||
);
|
||||
assert_eq!(
|
||||
Span { start: 3, end: 5 }.location(source),
|
||||
SourceLocation {
|
||||
line_number: 2,
|
||||
line_position: 1,
|
||||
offset: 3,
|
||||
length: 2
|
||||
}
|
||||
);
|
||||
assert_eq!(
|
||||
Span { start: 4, end: 6 }.location(source),
|
||||
SourceLocation {
|
||||
line_number: 2,
|
||||
line_position: 2,
|
||||
offset: 4,
|
||||
length: 2
|
||||
}
|
||||
);
|
||||
assert_eq!(
|
||||
Span { start: 5, end: 6 }.location(source),
|
||||
SourceLocation {
|
||||
line_number: 2,
|
||||
line_position: 3,
|
||||
offset: 5,
|
||||
length: 1
|
||||
}
|
||||
);
|
||||
assert_eq!(
|
||||
Span { start: 6, end: 7 }.location(source),
|
||||
SourceLocation {
|
||||
line_number: 3,
|
||||
line_position: 1,
|
||||
offset: 6,
|
||||
length: 1
|
||||
}
|
||||
);
|
||||
assert_eq!(
|
||||
Span { start: 7, end: 8 }.location(source),
|
||||
SourceLocation {
|
||||
line_number: 4,
|
||||
line_position: 1,
|
||||
offset: 7,
|
||||
length: 1
|
||||
}
|
||||
);
|
||||
assert_eq!(
|
||||
Span { start: 8, end: 9 }.location(source),
|
||||
SourceLocation {
|
||||
line_number: 4,
|
||||
line_position: 2,
|
||||
offset: 8,
|
||||
length: 1
|
||||
}
|
||||
);
|
||||
assert_eq!(
|
||||
Span { start: 9, end: 10 }.location(source),
|
||||
SourceLocation {
|
||||
line_number: 4,
|
||||
line_position: 3,
|
||||
offset: 9,
|
||||
length: 1
|
||||
}
|
||||
);
|
||||
assert_eq!(
|
||||
Span { start: 10, end: 11 }.location(source),
|
||||
SourceLocation {
|
||||
line_number: 5,
|
||||
line_position: 1,
|
||||
offset: 10,
|
||||
length: 1
|
||||
}
|
||||
);
|
||||
}
|
||||
|
1
third_party/rust/naga/src/valid/analyzer.rs
vendored
1
third_party/rust/naga/src/valid/analyzer.rs
vendored
@ -841,6 +841,7 @@ impl FunctionInfo {
|
||||
S::Loop {
|
||||
ref body,
|
||||
ref continuing,
|
||||
break_if: _,
|
||||
} => {
|
||||
let body_uniformity =
|
||||
self.process_block(body, other_functions, disruptor, expression_arena)?;
|
||||
|
28
third_party/rust/naga/src/valid/function.rs
vendored
28
third_party/rust/naga/src/valid/function.rs
vendored
@ -86,6 +86,8 @@ pub enum FunctionError {
|
||||
},
|
||||
#[error("Argument '{name}' at index {index} has a type that can't be passed into functions.")]
|
||||
InvalidArgumentType { index: usize, name: String },
|
||||
#[error("The function's given return type cannot be returned from functions")]
|
||||
NonConstructibleReturnType,
|
||||
#[error("Argument '{name}' at index {index} is a pointer of space {space:?}, which can't be passed into functions.")]
|
||||
InvalidArgumentPointerSpace {
|
||||
index: usize,
|
||||
@ -497,6 +499,7 @@ impl super::Validator {
|
||||
S::Loop {
|
||||
ref body,
|
||||
ref continuing,
|
||||
break_if,
|
||||
} => {
|
||||
// special handling for block scoping is needed here,
|
||||
// because the continuing{} block inherits the scope
|
||||
@ -518,6 +521,20 @@ impl super::Validator {
|
||||
&context.with_abilities(ControlFlowAbility::empty()),
|
||||
)?
|
||||
.stages;
|
||||
|
||||
if let Some(condition) = break_if {
|
||||
match *context.resolve_type(condition, &self.valid_expression_set)? {
|
||||
Ti::Scalar {
|
||||
kind: crate::ScalarKind::Bool,
|
||||
width: _,
|
||||
} => {}
|
||||
_ => {
|
||||
return Err(FunctionError::InvalidIfType(condition)
|
||||
.with_span_handle(condition, context.expressions))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for handle in self.valid_expression_list.drain(base_expression_count..) {
|
||||
self.valid_expression_set.remove(handle.index());
|
||||
}
|
||||
@ -894,6 +911,17 @@ impl super::Validator {
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "validate")]
|
||||
if let Some(ref result) = fun.result {
|
||||
if !self.types[result.ty.index()]
|
||||
.flags
|
||||
.contains(super::TypeFlags::CONSTRUCTIBLE)
|
||||
{
|
||||
return Err(FunctionError::NonConstructibleReturnType
|
||||
.with_span_handle(result.ty, &module.types));
|
||||
}
|
||||
}
|
||||
|
||||
self.valid_expression_set.clear();
|
||||
self.valid_expression_list.clear();
|
||||
for (handle, expr) in fun.expressions.iter() {
|
||||
|
33
third_party/rust/naga/src/valid/interface.rs
vendored
33
third_party/rust/naga/src/valid/interface.rs
vendored
@ -135,6 +135,16 @@ impl VaryingContext<'_> {
|
||||
}
|
||||
self.built_ins.insert(canonical);
|
||||
|
||||
let required = match built_in {
|
||||
Bi::ClipDistance => Capabilities::CLIP_DISTANCE,
|
||||
Bi::CullDistance => Capabilities::CULL_DISTANCE,
|
||||
Bi::PrimitiveIndex => Capabilities::PRIMITIVE_INDEX,
|
||||
_ => Capabilities::empty(),
|
||||
};
|
||||
if !self.capabilities.contains(required) {
|
||||
return Err(VaryingError::UnsupportedCapability(required));
|
||||
}
|
||||
|
||||
let width = 4;
|
||||
let (visible, type_good) = match built_in {
|
||||
Bi::BaseInstance | Bi::BaseVertex | Bi::InstanceIndex | Bi::VertexIndex => (
|
||||
@ -206,21 +216,14 @@ impl VaryingContext<'_> {
|
||||
width: crate::BOOL_WIDTH,
|
||||
},
|
||||
),
|
||||
Bi::PrimitiveIndex => {
|
||||
if !self.capabilities.contains(Capabilities::PRIMITIVE_INDEX) {
|
||||
return Err(VaryingError::UnsupportedCapability(
|
||||
Capabilities::PRIMITIVE_INDEX,
|
||||
));
|
||||
}
|
||||
(
|
||||
self.stage == St::Fragment && !self.output,
|
||||
*ty_inner
|
||||
== Ti::Scalar {
|
||||
kind: Sk::Uint,
|
||||
width,
|
||||
},
|
||||
)
|
||||
}
|
||||
Bi::PrimitiveIndex => (
|
||||
self.stage == St::Fragment && !self.output,
|
||||
*ty_inner
|
||||
== Ti::Scalar {
|
||||
kind: Sk::Uint,
|
||||
width,
|
||||
},
|
||||
),
|
||||
Bi::SampleIndex => (
|
||||
self.stage == St::Fragment && !self.output,
|
||||
*ty_inner
|
||||
|
8
third_party/rust/naga/src/valid/mod.rs
vendored
8
third_party/rust/naga/src/valid/mod.rs
vendored
@ -82,11 +82,11 @@ bitflags::bitflags! {
|
||||
#[cfg_attr(feature = "serialize", derive(serde::Serialize))]
|
||||
#[cfg_attr(feature = "deserialize", derive(serde::Deserialize))]
|
||||
pub struct Capabilities: u8 {
|
||||
/// Support for `AddressSpace:PushConstant`.
|
||||
/// Support for [`AddressSpace:PushConstant`].
|
||||
const PUSH_CONSTANT = 0x1;
|
||||
/// Float values with width = 8.
|
||||
const FLOAT64 = 0x2;
|
||||
/// Support for `Builtin:PrimitiveIndex`.
|
||||
/// Support for [`Builtin:PrimitiveIndex`].
|
||||
const PRIMITIVE_INDEX = 0x4;
|
||||
/// Support for non-uniform indexing of sampled textures and storage buffer arrays.
|
||||
const SAMPLED_TEXTURE_AND_STORAGE_BUFFER_ARRAY_NON_UNIFORM_INDEXING = 0x8;
|
||||
@ -94,6 +94,10 @@ bitflags::bitflags! {
|
||||
const UNIFORM_BUFFER_AND_STORAGE_TEXTURE_ARRAY_NON_UNIFORM_INDEXING = 0x10;
|
||||
/// Support for non-uniform indexing of samplers.
|
||||
const SAMPLER_NON_UNIFORM_INDEXING = 0x20;
|
||||
/// Support for [`Builtin::ClipDistance`].
|
||||
const CLIP_DISTANCE = 0x40;
|
||||
/// Support for [`Builtin::CullDistance`].
|
||||
const CULL_DISTANCE = 0x80;
|
||||
}
|
||||
}
|
||||
|
||||
|
124
third_party/rust/naga/src/valid/type.rs
vendored
124
third_party/rust/naga/src/valid/type.rs
vendored
@ -4,8 +4,6 @@ use crate::{
|
||||
proc::Alignment,
|
||||
};
|
||||
|
||||
const UNIFORM_MIN_ALIGNMENT: Alignment = unsafe { Alignment::new_unchecked(16) };
|
||||
|
||||
bitflags::bitflags! {
|
||||
/// Flags associated with [`Type`]s by [`Validator`].
|
||||
///
|
||||
@ -52,20 +50,29 @@ bitflags::bitflags! {
|
||||
|
||||
/// This type can be passed as a function argument.
|
||||
const ARGUMENT = 0x40;
|
||||
|
||||
/// A WGSL [constructible] type.
|
||||
///
|
||||
/// The constructible types are scalars, vectors, matrices, fixed-size
|
||||
/// arrays of constructible types, and structs whose members are all
|
||||
/// constructible.
|
||||
///
|
||||
/// [constructible]: https://gpuweb.github.io/gpuweb/wgsl/#constructible
|
||||
const CONSTRUCTIBLE = 0x80;
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, thiserror::Error)]
|
||||
pub enum Disalignment {
|
||||
#[error("The array stride {stride} is not a multiple of the required alignment {alignment}")]
|
||||
ArrayStride { stride: u32, alignment: u32 },
|
||||
ArrayStride { stride: u32, alignment: Alignment },
|
||||
#[error("The struct span {span}, is not a multiple of the required alignment {alignment}")]
|
||||
StructSpan { span: u32, alignment: u32 },
|
||||
StructSpan { span: u32, alignment: Alignment },
|
||||
#[error("The struct member[{index}] offset {offset} is not a multiple of the required alignment {alignment}")]
|
||||
MemberOffset {
|
||||
index: u32,
|
||||
offset: u32,
|
||||
alignment: u32,
|
||||
alignment: Alignment,
|
||||
},
|
||||
#[error("The struct member[{index}] offset {offset} must be at least {expected}")]
|
||||
MemberOffsetAfterStruct {
|
||||
@ -125,8 +132,8 @@ pub enum TypeError {
|
||||
EmptyStruct,
|
||||
}
|
||||
|
||||
// Only makes sense if `flags.contains(HOST_SHARED)`
|
||||
type LayoutCompatibility = Result<Option<Alignment>, (Handle<crate::Type>, Disalignment)>;
|
||||
// Only makes sense if `flags.contains(HOST_SHAREABLE)`
|
||||
type LayoutCompatibility = Result<Alignment, (Handle<crate::Type>, Disalignment)>;
|
||||
|
||||
fn check_member_layout(
|
||||
accum: &mut LayoutCompatibility,
|
||||
@ -136,20 +143,18 @@ fn check_member_layout(
|
||||
parent_handle: Handle<crate::Type>,
|
||||
) {
|
||||
*accum = match (*accum, member_layout) {
|
||||
(Ok(cur_alignment), Ok(align)) => {
|
||||
let align = align.unwrap().get();
|
||||
if member.offset % align != 0 {
|
||||
(Ok(cur_alignment), Ok(alignment)) => {
|
||||
if alignment.is_aligned(member.offset) {
|
||||
Ok(cur_alignment.max(alignment))
|
||||
} else {
|
||||
Err((
|
||||
parent_handle,
|
||||
Disalignment::MemberOffset {
|
||||
index: member_index,
|
||||
offset: member.offset,
|
||||
alignment: align,
|
||||
alignment,
|
||||
},
|
||||
))
|
||||
} else {
|
||||
let combined_alignment = ((cur_alignment.unwrap().get() - 1) | (align - 1)) + 1;
|
||||
Ok(Alignment::new(combined_alignment))
|
||||
}
|
||||
}
|
||||
(Err(e), _) | (_, Err(e)) => Err(e),
|
||||
@ -183,13 +188,12 @@ impl TypeInfo {
|
||||
const fn dummy() -> Self {
|
||||
TypeInfo {
|
||||
flags: TypeFlags::empty(),
|
||||
uniform_layout: Ok(None),
|
||||
storage_layout: Ok(None),
|
||||
uniform_layout: Ok(Alignment::ONE),
|
||||
storage_layout: Ok(Alignment::ONE),
|
||||
}
|
||||
}
|
||||
|
||||
const fn new(flags: TypeFlags, align: u32) -> Self {
|
||||
let alignment = Alignment::new(align);
|
||||
const fn new(flags: TypeFlags, alignment: Alignment) -> Self {
|
||||
TypeInfo {
|
||||
flags,
|
||||
uniform_layout: Ok(alignment),
|
||||
@ -237,8 +241,9 @@ impl super::Validator {
|
||||
| TypeFlags::SIZED
|
||||
| TypeFlags::COPY
|
||||
| TypeFlags::ARGUMENT
|
||||
| TypeFlags::CONSTRUCTIBLE
|
||||
| shareable,
|
||||
width as u32,
|
||||
Alignment::from_width(width),
|
||||
)
|
||||
}
|
||||
Ti::Vector { size, kind, width } => {
|
||||
@ -250,15 +255,15 @@ impl super::Validator {
|
||||
} else {
|
||||
TypeFlags::empty()
|
||||
};
|
||||
let count = if size >= crate::VectorSize::Tri { 4 } else { 2 };
|
||||
TypeInfo::new(
|
||||
TypeFlags::DATA
|
||||
| TypeFlags::SIZED
|
||||
| TypeFlags::COPY
|
||||
| TypeFlags::HOST_SHAREABLE
|
||||
| TypeFlags::ARGUMENT
|
||||
| TypeFlags::CONSTRUCTIBLE
|
||||
| shareable,
|
||||
count * (width as u32),
|
||||
Alignment::from(size) * Alignment::from_width(width),
|
||||
)
|
||||
}
|
||||
Ti::Matrix {
|
||||
@ -269,14 +274,14 @@ impl super::Validator {
|
||||
if !self.check_width(crate::ScalarKind::Float, width) {
|
||||
return Err(TypeError::InvalidWidth(crate::ScalarKind::Float, width));
|
||||
}
|
||||
let count = if rows >= crate::VectorSize::Tri { 4 } else { 2 };
|
||||
TypeInfo::new(
|
||||
TypeFlags::DATA
|
||||
| TypeFlags::SIZED
|
||||
| TypeFlags::COPY
|
||||
| TypeFlags::HOST_SHAREABLE
|
||||
| TypeFlags::ARGUMENT,
|
||||
count * (width as u32),
|
||||
| TypeFlags::ARGUMENT
|
||||
| TypeFlags::CONSTRUCTIBLE,
|
||||
Alignment::from(rows) * Alignment::from_width(width),
|
||||
)
|
||||
}
|
||||
Ti::Atomic { kind, width } => {
|
||||
@ -289,7 +294,7 @@ impl super::Validator {
|
||||
}
|
||||
TypeInfo::new(
|
||||
TypeFlags::DATA | TypeFlags::SIZED | TypeFlags::HOST_SHAREABLE,
|
||||
width as u32,
|
||||
Alignment::from_width(width),
|
||||
)
|
||||
}
|
||||
Ti::Pointer { base, space } => {
|
||||
@ -332,7 +337,10 @@ impl super::Validator {
|
||||
|
||||
// Pointers cannot be stored in variables, structure members, or
|
||||
// array elements, so we do not mark them as `DATA`.
|
||||
TypeInfo::new(argument_flag | TypeFlags::SIZED | TypeFlags::COPY, 0)
|
||||
TypeInfo::new(
|
||||
argument_flag | TypeFlags::SIZED | TypeFlags::COPY,
|
||||
Alignment::ONE,
|
||||
)
|
||||
}
|
||||
Ti::ValuePointer {
|
||||
size: _,
|
||||
@ -359,7 +367,10 @@ impl super::Validator {
|
||||
|
||||
// Pointers cannot be stored in variables, structure members, or
|
||||
// array elements, so we do not mark them as `DATA`.
|
||||
TypeInfo::new(argument_flag | TypeFlags::SIZED | TypeFlags::COPY, 0)
|
||||
TypeInfo::new(
|
||||
argument_flag | TypeFlags::SIZED | TypeFlags::COPY,
|
||||
Alignment::ONE,
|
||||
)
|
||||
}
|
||||
Ti::Array { base, size, stride } => {
|
||||
if base >= handle {
|
||||
@ -379,42 +390,27 @@ impl super::Validator {
|
||||
});
|
||||
}
|
||||
|
||||
let general_alignment = base_layout.alignment.get();
|
||||
let general_alignment = base_layout.alignment;
|
||||
let uniform_layout = match base_info.uniform_layout {
|
||||
Ok(base_alignment) => {
|
||||
// combine the alignment requirements
|
||||
let align = base_alignment
|
||||
.unwrap()
|
||||
.get()
|
||||
let alignment = base_alignment
|
||||
.max(general_alignment)
|
||||
.max(UNIFORM_MIN_ALIGNMENT.get());
|
||||
if stride % align != 0 {
|
||||
Err((
|
||||
handle,
|
||||
Disalignment::ArrayStride {
|
||||
stride,
|
||||
alignment: align,
|
||||
},
|
||||
))
|
||||
.max(Alignment::MIN_UNIFORM);
|
||||
if alignment.is_aligned(stride) {
|
||||
Ok(alignment)
|
||||
} else {
|
||||
Ok(Alignment::new(align))
|
||||
Err((handle, Disalignment::ArrayStride { stride, alignment }))
|
||||
}
|
||||
}
|
||||
Err(e) => Err(e),
|
||||
};
|
||||
let storage_layout = match base_info.storage_layout {
|
||||
Ok(base_alignment) => {
|
||||
let align = base_alignment.unwrap().get().max(general_alignment);
|
||||
if stride % align != 0 {
|
||||
Err((
|
||||
handle,
|
||||
Disalignment::ArrayStride {
|
||||
stride,
|
||||
alignment: align,
|
||||
},
|
||||
))
|
||||
let alignment = base_alignment.max(general_alignment);
|
||||
if alignment.is_aligned(stride) {
|
||||
Ok(alignment)
|
||||
} else {
|
||||
Ok(Alignment::new(align))
|
||||
Err((handle, Disalignment::ArrayStride { stride, alignment }))
|
||||
}
|
||||
}
|
||||
Err(e) => Err(e),
|
||||
@ -467,7 +463,7 @@ impl super::Validator {
|
||||
return Err(TypeError::NonPositiveArrayLength(const_handle));
|
||||
}
|
||||
|
||||
TypeFlags::SIZED | TypeFlags::ARGUMENT
|
||||
TypeFlags::SIZED | TypeFlags::ARGUMENT | TypeFlags::CONSTRUCTIBLE
|
||||
}
|
||||
crate::ArraySize::Dynamic => {
|
||||
// Non-SIZED types may only appear as the last element of a structure.
|
||||
@ -495,10 +491,11 @@ impl super::Validator {
|
||||
| TypeFlags::COPY
|
||||
| TypeFlags::HOST_SHAREABLE
|
||||
| TypeFlags::IO_SHAREABLE
|
||||
| TypeFlags::ARGUMENT,
|
||||
1,
|
||||
| TypeFlags::ARGUMENT
|
||||
| TypeFlags::CONSTRUCTIBLE,
|
||||
Alignment::ONE,
|
||||
);
|
||||
ti.uniform_layout = Ok(Some(UNIFORM_MIN_ALIGNMENT));
|
||||
ti.uniform_layout = Ok(Alignment::MIN_UNIFORM);
|
||||
|
||||
let mut min_offset = 0;
|
||||
|
||||
@ -523,7 +520,7 @@ impl super::Validator {
|
||||
ti.flags &= base_info.flags;
|
||||
|
||||
if member.offset < min_offset {
|
||||
//HACK: this could be nicer. We want to allow some structures
|
||||
// HACK: this could be nicer. We want to allow some structures
|
||||
// to not bother with offsets/alignments if they are never
|
||||
// used for host sharing.
|
||||
if member.offset == 0 {
|
||||
@ -536,7 +533,6 @@ impl super::Validator {
|
||||
}
|
||||
}
|
||||
|
||||
//Note: `unwrap()` is fine because `Layouter` goes first and checks this
|
||||
let base_size = types[member.ty].inner.size(constants);
|
||||
min_offset = member.offset + base_size;
|
||||
if min_offset > span {
|
||||
@ -568,7 +564,7 @@ impl super::Validator {
|
||||
// the start of any following member must be at least roundUp(16, SizeOf(S)).
|
||||
if let Some((span, offset)) = prev_struct_data {
|
||||
let diff = member.offset - offset;
|
||||
let min = crate::valid::Layouter::round_up(UNIFORM_MIN_ALIGNMENT, span);
|
||||
let min = Alignment::MIN_UNIFORM.round_up(span);
|
||||
if diff < min {
|
||||
ti.uniform_layout = Err((
|
||||
handle,
|
||||
@ -603,16 +599,18 @@ impl super::Validator {
|
||||
}
|
||||
}
|
||||
|
||||
let alignment = self.layouter[handle].alignment.get();
|
||||
if span % alignment != 0 {
|
||||
let alignment = self.layouter[handle].alignment;
|
||||
if !alignment.is_aligned(span) {
|
||||
ti.uniform_layout = Err((handle, Disalignment::StructSpan { span, alignment }));
|
||||
ti.storage_layout = Err((handle, Disalignment::StructSpan { span, alignment }));
|
||||
}
|
||||
|
||||
ti
|
||||
}
|
||||
Ti::Image { .. } | Ti::Sampler { .. } => TypeInfo::new(TypeFlags::ARGUMENT, 0),
|
||||
Ti::BindingArray { .. } => TypeInfo::new(TypeFlags::empty(), 0),
|
||||
Ti::Image { .. } | Ti::Sampler { .. } => {
|
||||
TypeInfo::new(TypeFlags::ARGUMENT, Alignment::ONE)
|
||||
}
|
||||
Ti::BindingArray { .. } => TypeInfo::new(TypeFlags::empty(), Alignment::ONE),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -1 +1 @@
|
||||
{"files":{"Cargo.toml":"73d22ddbc04b486026d12675ef898363c6eea04ae23a9251acdd1b000c73b126","LICENSE.APACHE":"a6cba85bc92e0cff7a450b1d873c0eaa2e9fc96bf472df0247a26bec77bf3ff9","LICENSE.MIT":"c7fea58d1cfe49634cd92e54fc10a9d871f4b275321a4cd8c09e449122caaeb4","build.rs":"fedccfe06a4d75ba68233f0756de4161213c5d25851963f5b0521d8b7292b395","src/binding_model.rs":"79f024cdb136f44066d67ec7dc56bde7fdf3bf9e89874dc4db40e504099b2822","src/command/bind.rs":"309f3f1b1719d25115d385368cff0a2c85e94da825b2930141db78235901c673","src/command/bundle.rs":"6f940e6de1e84b858790e2801ba82c83f2bc0c6afbff962576b3dd64ac315de3","src/command/clear.rs":"568aaf9d0843bada18b68980b52dd8021830c28fff36551459fad5f6baea72e1","src/command/compute.rs":"b58ae86ffbd8280af27f063d514b17c5dafee3f3ddfd5637ca050135681eb764","src/command/draw.rs":"1b9b6531b7536bc0f864ab9fdeff376993de04e33554e84c7b2db7dc65e31327","src/command/memory_init.rs":"03c3267b311f389af859615ceea8a648b402a323062cc8f0fe2690a0fb390b97","src/command/mod.rs":"c0f00529bce224972d844d2fdc9f659ffa065512086315b7bcd767501961ee1a","src/command/query.rs":"34d22d33e4713ff7ca0b345b14cdbb6177236e782b5dfb38d907215c4deb6907","src/command/render.rs":"b21201c5b9574e98c066f988bcf91b1cde0d1847fc1db683291cb059a10f3dd8","src/command/transfer.rs":"7e5e13f04fef63e036291b2838c0f0097717ec497f98f420b71296b2cc691907","src/conv.rs":"87097903e86048c9110f526f7df1749186f84cb663d75d9d40a0c467befc89ea","src/device/life.rs":"857a71da94f5f6f043f304ada7dc9ab95c6a26ed0ff63f3d64a77942e28bcafe","src/device/mod.rs":"8b886c68cd2aaec9aabdbaea0f2f256fe546ae0242fe7c9b0b8a55686f215071","src/device/queue.rs":"5fe332a0d27dafff720b19e436d991a35affd2a8031f78c2a81439a49105edd6","src/device/trace.rs":"de575a8213c8ae9df711e4b6afe5736d71ac65bf141375fe044d3b6c4375e039","src/error.rs":"34a4adbb6ec669d8de22b932363506eeef1b1115c422bcc8daa3b26f62378518","src/hub.rs":"4cc404cc79578d7a6757f74ab1fbeeb357a13a4de5f0fe87affaea8895395c8d","src/id.rs":"3ec97d09f900f34f9ad38a555ddcadb77bd9977d3d39bfad030b9b34649cf502","src/init_tracker/buffer.rs":"ccdddaace101f921463bf6c62ed5aca01a6520717a850b5d4442c3551e9f1875","src/init_tracker/mod.rs":"273c6ee67a927784a617899c6fe5560e47108248ab67cabdc2eebcba53133364","src/init_tracker/texture.rs":"d02babc4f194f91853b5e9a71bd5b20d9434842cf242f29ed9d39661bfa44980","src/instance.rs":"4a19ac634a4dd22938586e3bc554ab69f079abb2d836ef932f06cee1655d9336","src/lib.rs":"f44250478f095aa7d61fb4773692037f465d1e8df9c5626000723d4e1961166e","src/pipeline.rs":"ffabdc74656717276241b1ca2ed043fabf18795662a523828193aea99d7a9ef5","src/present.rs":"5b760e252242be41d70f09cc46b95f2bfcb8258c3482755a7bec3b5a7e4bbcb6","src/resource.rs":"50021911ff214165a32129eabc2275945c2fd22bb736fad2977634ea8ef8362d","src/track/buffer.rs":"1a7400ec55f3c16bc074c46d11b9515762b558a333d36eb236d2e7d99701bbe5","src/track/mod.rs":"3a4b07c8f1ff168609ca521b441e1e2acc00c62d7e9e4dc39cb8ab83d9813d58","src/track/range.rs":"5bbfed6e103b3234d9de8e42057022da6d628c2cc1db6bb51b88f87f2d8adf8b","src/track/stateless.rs":"593ec39e01e18048100ab0e1869f430851b83b96bd0497b8e524efda38782a46","src/track/texture.rs":"de154923e4825fa120360aae61aec27370b44196464edea6468bf933976ea20c","src/validation.rs":"27c76c48eaf3ca6be111855d7b1ab8ef94c2f73ed5d5e4f758d82799099f014b"},"package":null}
|
||||
{"files":{"Cargo.toml":"d5e071d03da58ff1154b4667fd8863b802928abcf9e732fa00b6414481a8201e","LICENSE.APACHE":"a6cba85bc92e0cff7a450b1d873c0eaa2e9fc96bf472df0247a26bec77bf3ff9","LICENSE.MIT":"c7fea58d1cfe49634cd92e54fc10a9d871f4b275321a4cd8c09e449122caaeb4","build.rs":"fedccfe06a4d75ba68233f0756de4161213c5d25851963f5b0521d8b7292b395","src/binding_model.rs":"630414d5f50f18ede6b52387d49d0a3528d40b4804c2339d22e28a119b961666","src/command/bind.rs":"309f3f1b1719d25115d385368cff0a2c85e94da825b2930141db78235901c673","src/command/bundle.rs":"1d8e00a4b654e3d84bb18f311e686c9387c93d7307313b0f2797ff8e66e77439","src/command/clear.rs":"681837be0d0cda73b4e8ffb22b2e9dd8a663bd7cce191a14ff83ebb3489968cd","src/command/compute.rs":"fe6d43d09583a7c71da4de7e5a7ef0318b753d09c1081b5f3f52ef80add3a442","src/command/draw.rs":"bc9fa98a0897e82e88404433716b70ab66922e5128d524a4d5a2c63c627f78e8","src/command/memory_init.rs":"03c3267b311f389af859615ceea8a648b402a323062cc8f0fe2690a0fb390b97","src/command/mod.rs":"f096b26f9bc200a2e1c279225bf1197df82746b1b5f53174647f2f9ccb846d0b","src/command/query.rs":"34d22d33e4713ff7ca0b345b14cdbb6177236e782b5dfb38d907215c4deb6907","src/command/render.rs":"bb5670d09998f40f4e73315f8a5d59be603f8be3bba75ea5f8629ad45556a891","src/command/transfer.rs":"7e5e13f04fef63e036291b2838c0f0097717ec497f98f420b71296b2cc691907","src/conv.rs":"d2a18b98fd9361aab8d32044093e57c532140a1914b33b290eb2eb1306a801b7","src/device/life.rs":"6390be9658a59b0470288c9fd81f6a94f76a0afd34eafd80c14640064b779b6c","src/device/mod.rs":"3d7d289edaa50d06992b83c4d1494084e448fcb6895c55e394598ff93d5b3d1d","src/device/queue.rs":"d77574ceb5fa573b03026b6d2c37cc7c89e1e4e8934868bf2001a3fce737a0d5","src/device/trace.rs":"9c03f5ec06cae37f294179a2285e2af7a5497db8a5572bf1dbe50136943d69be","src/error.rs":"34a4adbb6ec669d8de22b932363506eeef1b1115c422bcc8daa3b26f62378518","src/hub.rs":"d8a904e188a9168c1552765b44944a15565f2f18dc6692c03e8eb533cdee02e6","src/id.rs":"910d5ef3403b0c476ef17bbaf27270653cf50382a0e1092206004ab775a91246","src/init_tracker/buffer.rs":"ccdddaace101f921463bf6c62ed5aca01a6520717a850b5d4442c3551e9f1875","src/init_tracker/mod.rs":"273c6ee67a927784a617899c6fe5560e47108248ab67cabdc2eebcba53133364","src/init_tracker/texture.rs":"d02babc4f194f91853b5e9a71bd5b20d9434842cf242f29ed9d39661bfa44980","src/instance.rs":"8494a66e40267370fd7a61d7c73c12549f3ef88c2dd5b2e268fdf177576024f0","src/lib.rs":"5f2803283022856ac9c2534d3096cb10e0e39f3cc7ddd7c973e8beffe9b3cc1f","src/pipeline.rs":"9b3d1ce4452a585c6a5b88201799f4bebb85ad236c62098e0ebb54c43522bdcf","src/present.rs":"89d0c226a6dec63c3e4255578634fb635a46c32412fd16065717fadd71435201","src/resource.rs":"51236b4893f1471d21e3e74cecf084f201b3fb0ac89fe1398619f6caf75a64bd","src/track/buffer.rs":"30b072df6d128d9beb3d885d01e6578d3559bfa4f15c36fd0236996975bbe596","src/track/mod.rs":"73a6bb425a28b4bf639a197a2eea9134548aaca869745bd8153e276561034984","src/track/range.rs":"5bbfed6e103b3234d9de8e42057022da6d628c2cc1db6bb51b88f87f2d8adf8b","src/track/stateless.rs":"593ec39e01e18048100ab0e1869f430851b83b96bd0497b8e524efda38782a46","src/track/texture.rs":"de154923e4825fa120360aae61aec27370b44196464edea6468bf933976ea20c","src/validation.rs":"27c76c48eaf3ca6be111855d7b1ab8ef94c2f73ed5d5e4f758d82799099f014b"},"package":null}
|
3
third_party/rust/wgpu-core/Cargo.toml
vendored
3
third_party/rust/wgpu-core/Cargo.toml
vendored
@ -42,7 +42,7 @@ thiserror = "1"
|
||||
|
||||
[dependencies.naga]
|
||||
git = "https://github.com/gfx-rs/naga"
|
||||
rev = "571302e"
|
||||
rev = "27d38aae"
|
||||
#version = "0.8"
|
||||
features = ["span", "validate", "wgsl-in"]
|
||||
|
||||
@ -58,6 +58,7 @@ version = "0.12"
|
||||
|
||||
[target.'cfg(target_arch = "wasm32")'.dependencies]
|
||||
hal = { path = "../wgpu-hal", package = "wgpu-hal", version = "0.12", features = ["gles"] }
|
||||
web-sys = { version = "0.3", features = ["HtmlCanvasElement"] }
|
||||
|
||||
[target.'cfg(all(not(target_arch = "wasm32"), any(target_os = "ios", target_os = "macos")))'.dependencies]
|
||||
hal = { path = "../wgpu-hal", package = "wgpu-hal", version = "0.12", features = ["metal"] }
|
||||
|
@ -24,6 +24,8 @@ use thiserror::Error;
|
||||
pub enum BindGroupLayoutEntryError {
|
||||
#[error("cube dimension is not expected for texture storage")]
|
||||
StorageTextureCube,
|
||||
#[error("Read-write and read-only storage textures are not allowed by webgpu, they require the native only feature TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES")]
|
||||
StorageTextureReadWrite,
|
||||
#[error("arrays of bindings unsupported for this type of binding")]
|
||||
ArrayUnsupported,
|
||||
#[error(transparent)]
|
||||
|
508
third_party/rust/wgpu-core/src/command/bundle.rs
vendored
508
third_party/rust/wgpu-core/src/command/bundle.rs
vendored
@ -6,6 +6,18 @@ times, on different encoders. Constructing a render bundle lets `wgpu` validate
|
||||
and analyze its commands up front, so that replaying a bundle can be more
|
||||
efficient than simply re-recording its commands each time.
|
||||
|
||||
Not all commands are available in bundles; for example, a render bundle may not
|
||||
contain a [`RenderCommand::SetViewport`] command.
|
||||
|
||||
Most of `wgpu`'s backend graphics APIs have something like bundles. For example,
|
||||
Vulkan calls them "secondary command buffers", and Metal calls them "indirect
|
||||
command buffers". Although we plan to take advantage of these platform features
|
||||
at some point in the future, for now `wgpu`'s implementation of render bundles
|
||||
does not use them: at the hal level, `wgpu` render bundles just replay the
|
||||
commands.
|
||||
|
||||
## Render Bundle Isolation
|
||||
|
||||
One important property of render bundles is that the draw calls in a render
|
||||
bundle depend solely on the pipeline and state established within the render
|
||||
bundle itself. A draw call in a bundle will never use a vertex buffer, say, that
|
||||
@ -17,14 +29,11 @@ Render passes are also isolated from the effects of bundles. After executing a
|
||||
render bundle, a render pass's pipeline, bind groups, and vertex and index
|
||||
buffers are are unset, so the bundle cannot affect later draw calls in the pass.
|
||||
|
||||
Not all commands are available in bundles; for example, a render bundle may not
|
||||
contain a [`RenderCommand::SetViewport`] command.
|
||||
|
||||
Most of `wgpu`'s backend graphics APIs have something like bundles. For example,
|
||||
Vulkan calls them "secondary command buffers", and Metal calls them "indirect
|
||||
command buffers". However, `wgpu`'s implementation of render bundles does not
|
||||
take advantage of those underlying platform features. At the hal level, `wgpu`
|
||||
render bundles just replay the commands.
|
||||
A render pass is not fully isolated from a bundle's effects on push constant
|
||||
values. Draw calls following a bundle's execution will see whatever values the
|
||||
bundle writes to push constant storage. Setting a pipeline initializes any push
|
||||
constant storage it could access to zero, and this initialization may also be
|
||||
visible after bundle execution.
|
||||
|
||||
## Render Bundle Lifecycle
|
||||
|
||||
@ -105,7 +114,7 @@ pub struct RenderBundleEncoderDescriptor<'a> {
|
||||
pub label: Label<'a>,
|
||||
/// The formats of the color attachments that this render bundle is capable to rendering to. This
|
||||
/// must match the formats of the color attachments in the renderpass this render bundle is executed in.
|
||||
pub color_formats: Cow<'a, [wgt::TextureFormat]>,
|
||||
pub color_formats: Cow<'a, [Option<wgt::TextureFormat>]>,
|
||||
/// Information about the depth attachment that this render bundle is capable to rendering to. The format
|
||||
/// must match the format of the depth attachments in the renderpass this render bundle is executed in.
|
||||
pub depth_stencil: Option<wgt::RenderBundleDepthStencil>,
|
||||
@ -122,7 +131,8 @@ pub struct RenderBundleEncoder {
|
||||
base: BasePass<RenderCommand>,
|
||||
parent_id: id::DeviceId,
|
||||
pub(crate) context: RenderPassContext,
|
||||
pub(crate) is_ds_read_only: bool,
|
||||
pub(crate) is_depth_read_only: bool,
|
||||
pub(crate) is_stencil_read_only: bool,
|
||||
|
||||
// Resource binding dedupe state.
|
||||
#[cfg_attr(feature = "serial-pass", serde(skip))]
|
||||
@ -137,6 +147,20 @@ impl RenderBundleEncoder {
|
||||
parent_id: id::DeviceId,
|
||||
base: Option<BasePass<RenderCommand>>,
|
||||
) -> Result<Self, CreateRenderBundleError> {
|
||||
let (is_depth_read_only, is_stencil_read_only) = match desc.depth_stencil {
|
||||
Some(ds) => {
|
||||
let aspects = hal::FormatAspects::from(ds.format);
|
||||
(
|
||||
!aspects.contains(hal::FormatAspects::DEPTH) || ds.depth_read_only,
|
||||
!aspects.contains(hal::FormatAspects::STENCIL) || ds.stencil_read_only,
|
||||
)
|
||||
}
|
||||
// There's no depth/stencil attachment, so these values just don't
|
||||
// matter. Choose the most accommodating value, to simplify
|
||||
// validation.
|
||||
None => (true, true),
|
||||
};
|
||||
|
||||
//TODO: validate that attachment formats are renderable,
|
||||
// have expected aspects, support multisampling.
|
||||
Ok(Self {
|
||||
@ -144,7 +168,7 @@ impl RenderBundleEncoder {
|
||||
parent_id,
|
||||
context: RenderPassContext {
|
||||
attachments: AttachmentData {
|
||||
colors: if desc.color_formats.len() > hal::MAX_COLOR_TARGETS {
|
||||
colors: if desc.color_formats.len() > hal::MAX_COLOR_ATTACHMENTS {
|
||||
return Err(CreateRenderBundleError::TooManyColorAttachments);
|
||||
} else {
|
||||
desc.color_formats.iter().cloned().collect()
|
||||
@ -161,15 +185,9 @@ impl RenderBundleEncoder {
|
||||
},
|
||||
multiview: desc.multiview,
|
||||
},
|
||||
is_ds_read_only: match desc.depth_stencil {
|
||||
Some(ds) => {
|
||||
let aspects = hal::FormatAspects::from(ds.format);
|
||||
(!aspects.contains(hal::FormatAspects::DEPTH) || ds.depth_read_only)
|
||||
&& (!aspects.contains(hal::FormatAspects::STENCIL) || ds.stencil_read_only)
|
||||
}
|
||||
None => false,
|
||||
},
|
||||
|
||||
is_depth_read_only,
|
||||
is_stencil_read_only,
|
||||
current_bind_groups: BindGroupStateChange::new(),
|
||||
current_pipeline: StateChange::new(),
|
||||
})
|
||||
@ -188,7 +206,8 @@ impl RenderBundleEncoder {
|
||||
sample_count: 0,
|
||||
multiview: None,
|
||||
},
|
||||
is_ds_read_only: false,
|
||||
is_depth_read_only: false,
|
||||
is_stencil_read_only: false,
|
||||
|
||||
current_bind_groups: BindGroupStateChange::new(),
|
||||
current_pipeline: StateChange::new(),
|
||||
@ -236,18 +255,13 @@ impl RenderBundleEncoder {
|
||||
&*pipeline_guard,
|
||||
&*query_set_guard,
|
||||
),
|
||||
index: IndexState::new(),
|
||||
vertex: (0..hal::MAX_VERTEX_BUFFERS)
|
||||
.map(|_| VertexState::new())
|
||||
.collect(),
|
||||
bind: (0..hal::MAX_BIND_GROUPS).map(|_| None).collect(),
|
||||
push_constant_ranges: PushConstantState::new(),
|
||||
flat_dynamic_offsets: Vec::new(),
|
||||
used_bind_groups: 0,
|
||||
pipeline: None,
|
||||
bind: (0..hal::MAX_BIND_GROUPS).map(|_| None).collect(),
|
||||
vertex: (0..hal::MAX_VERTEX_BUFFERS).map(|_| None).collect(),
|
||||
index: None,
|
||||
flat_dynamic_offsets: Vec::new(),
|
||||
};
|
||||
let mut commands = Vec::new();
|
||||
let mut pipeline_layout_id = None::<id::Valid<id::PipelineLayoutId>>;
|
||||
let mut buffer_memory_init_actions = Vec::new();
|
||||
let mut texture_memory_init_actions = Vec::new();
|
||||
|
||||
@ -328,8 +342,6 @@ impl RenderBundleEncoder {
|
||||
RenderCommand::SetPipeline(pipeline_id) => {
|
||||
let scope = PassErrorScope::SetPipelineRender(pipeline_id);
|
||||
|
||||
state.pipeline = Some(pipeline_id);
|
||||
|
||||
let pipeline: &pipeline::RenderPipeline<A> = state
|
||||
.trackers
|
||||
.render_pipelines
|
||||
@ -344,26 +356,27 @@ impl RenderBundleEncoder {
|
||||
.map_err(RenderCommandError::IncompatiblePipelineTargets)
|
||||
.map_pass_err(scope)?;
|
||||
|
||||
if pipeline.flags.contains(PipelineFlags::WRITES_DEPTH_STENCIL)
|
||||
&& self.is_ds_read_only
|
||||
if (pipeline.flags.contains(PipelineFlags::WRITES_DEPTH)
|
||||
&& self.is_depth_read_only)
|
||||
|| (pipeline.flags.contains(PipelineFlags::WRITES_STENCIL)
|
||||
&& self.is_stencil_read_only)
|
||||
{
|
||||
return Err(RenderCommandError::IncompatiblePipelineRods)
|
||||
.map_pass_err(scope);
|
||||
}
|
||||
|
||||
let layout = &pipeline_layout_guard[pipeline.layout_id.value];
|
||||
pipeline_layout_id = Some(pipeline.layout_id.value);
|
||||
let pipeline_state = PipelineState::new(pipeline_id, pipeline, layout);
|
||||
|
||||
state.set_pipeline(
|
||||
pipeline.strip_index_format,
|
||||
&pipeline.vertex_strides,
|
||||
&layout.bind_group_layout_ids,
|
||||
&layout.push_constant_ranges,
|
||||
);
|
||||
commands.push(command);
|
||||
if let Some(iter) = state.flush_push_constants() {
|
||||
|
||||
// If this pipeline uses push constants, zero out their values.
|
||||
if let Some(iter) = pipeline_state.zero_push_constants() {
|
||||
commands.extend(iter)
|
||||
}
|
||||
|
||||
state.invalidate_bind_groups(&pipeline_state, layout);
|
||||
state.pipeline = Some(pipeline_state);
|
||||
}
|
||||
RenderCommand::SetIndexBuffer {
|
||||
buffer_id,
|
||||
@ -391,8 +404,7 @@ impl RenderBundleEncoder {
|
||||
offset..end,
|
||||
MemoryInitKind::NeedsInitializedMemory,
|
||||
));
|
||||
state.index.set_format(index_format);
|
||||
state.index.set_buffer(buffer_id, offset..end);
|
||||
state.set_index_buffer(buffer_id, index_format, offset..end);
|
||||
}
|
||||
RenderCommand::SetVertexBuffer {
|
||||
slot,
|
||||
@ -420,7 +432,7 @@ impl RenderBundleEncoder {
|
||||
offset..end,
|
||||
MemoryInitKind::NeedsInitializedMemory,
|
||||
));
|
||||
state.vertex[slot as usize].set_buffer(buffer_id, offset..end);
|
||||
state.vertex[slot as usize] = Some(VertexState::new(buffer_id, offset..end));
|
||||
}
|
||||
RenderCommand::SetPushConstant {
|
||||
stages,
|
||||
@ -431,10 +443,8 @@ impl RenderBundleEncoder {
|
||||
let scope = PassErrorScope::SetPushConstant;
|
||||
let end_offset = offset + size_bytes;
|
||||
|
||||
let pipeline_layout_id = pipeline_layout_id
|
||||
.ok_or(DrawError::MissingPipeline)
|
||||
.map_pass_err(scope)?;
|
||||
let pipeline_layout = &pipeline_layout_guard[pipeline_layout_id];
|
||||
let pipeline = state.pipeline(scope)?;
|
||||
let pipeline_layout = &pipeline_layout_guard[pipeline.layout_id];
|
||||
|
||||
pipeline_layout
|
||||
.validate_push_constant_ranges(stages, offset, end_offset)
|
||||
@ -451,9 +461,11 @@ impl RenderBundleEncoder {
|
||||
let scope = PassErrorScope::Draw {
|
||||
indexed: false,
|
||||
indirect: false,
|
||||
pipeline: state.pipeline,
|
||||
pipeline: state.pipeline_id(),
|
||||
};
|
||||
let vertex_limits = state.vertex_limits();
|
||||
let pipeline = state.pipeline(scope)?;
|
||||
let used_bind_groups = pipeline.used_bind_groups;
|
||||
let vertex_limits = state.vertex_limits(pipeline);
|
||||
let last_vertex = first_vertex + vertex_count;
|
||||
if last_vertex > vertex_limits.vertex_limit {
|
||||
return Err(DrawError::VertexBeyondLimit {
|
||||
@ -473,7 +485,7 @@ impl RenderBundleEncoder {
|
||||
.map_pass_err(scope);
|
||||
}
|
||||
commands.extend(state.flush_vertices());
|
||||
commands.extend(state.flush_binds(base.dynamic_offsets));
|
||||
commands.extend(state.flush_binds(used_bind_groups, base.dynamic_offsets));
|
||||
commands.push(command);
|
||||
}
|
||||
RenderCommand::DrawIndexed {
|
||||
@ -486,11 +498,17 @@ impl RenderBundleEncoder {
|
||||
let scope = PassErrorScope::Draw {
|
||||
indexed: true,
|
||||
indirect: false,
|
||||
pipeline: state.pipeline,
|
||||
pipeline: state.pipeline_id(),
|
||||
};
|
||||
let pipeline = state.pipeline(scope)?;
|
||||
let used_bind_groups = pipeline.used_bind_groups;
|
||||
let index = match state.index {
|
||||
Some(ref index) => index,
|
||||
None => return Err(DrawError::MissingIndexBuffer).map_pass_err(scope),
|
||||
};
|
||||
//TODO: validate that base_vertex + max_index() is within the provided range
|
||||
let vertex_limits = state.vertex_limits();
|
||||
let index_limit = state.index.limit();
|
||||
let vertex_limits = state.vertex_limits(pipeline);
|
||||
let index_limit = index.limit();
|
||||
let last_index = first_index + index_count;
|
||||
if last_index > index_limit {
|
||||
return Err(DrawError::IndexBeyondLimit {
|
||||
@ -508,9 +526,9 @@ impl RenderBundleEncoder {
|
||||
})
|
||||
.map_pass_err(scope);
|
||||
}
|
||||
commands.extend(state.index.flush());
|
||||
commands.extend(state.flush_index());
|
||||
commands.extend(state.flush_vertices());
|
||||
commands.extend(state.flush_binds(base.dynamic_offsets));
|
||||
commands.extend(state.flush_binds(used_bind_groups, base.dynamic_offsets));
|
||||
commands.push(command);
|
||||
}
|
||||
RenderCommand::MultiDrawIndirect {
|
||||
@ -522,12 +540,15 @@ impl RenderBundleEncoder {
|
||||
let scope = PassErrorScope::Draw {
|
||||
indexed: false,
|
||||
indirect: true,
|
||||
pipeline: state.pipeline,
|
||||
pipeline: state.pipeline_id(),
|
||||
};
|
||||
device
|
||||
.require_downlevel_flags(wgt::DownlevelFlags::INDIRECT_EXECUTION)
|
||||
.map_pass_err(scope)?;
|
||||
|
||||
let pipeline = state.pipeline(scope)?;
|
||||
let used_bind_groups = pipeline.used_bind_groups;
|
||||
|
||||
let buffer: &resource::Buffer<A> = state
|
||||
.trackers
|
||||
.buffers
|
||||
@ -545,7 +566,7 @@ impl RenderBundleEncoder {
|
||||
));
|
||||
|
||||
commands.extend(state.flush_vertices());
|
||||
commands.extend(state.flush_binds(base.dynamic_offsets));
|
||||
commands.extend(state.flush_binds(used_bind_groups, base.dynamic_offsets));
|
||||
commands.push(command);
|
||||
}
|
||||
RenderCommand::MultiDrawIndirect {
|
||||
@ -557,12 +578,15 @@ impl RenderBundleEncoder {
|
||||
let scope = PassErrorScope::Draw {
|
||||
indexed: true,
|
||||
indirect: true,
|
||||
pipeline: state.pipeline,
|
||||
pipeline: state.pipeline_id(),
|
||||
};
|
||||
device
|
||||
.require_downlevel_flags(wgt::DownlevelFlags::INDIRECT_EXECUTION)
|
||||
.map_pass_err(scope)?;
|
||||
|
||||
let pipeline = state.pipeline(scope)?;
|
||||
let used_bind_groups = pipeline.used_bind_groups;
|
||||
|
||||
let buffer: &resource::Buffer<A> = state
|
||||
.trackers
|
||||
.buffers
|
||||
@ -579,9 +603,14 @@ impl RenderBundleEncoder {
|
||||
MemoryInitKind::NeedsInitializedMemory,
|
||||
));
|
||||
|
||||
commands.extend(state.index.flush());
|
||||
let index = match state.index {
|
||||
Some(ref mut index) => index,
|
||||
None => return Err(DrawError::MissingIndexBuffer).map_pass_err(scope),
|
||||
};
|
||||
|
||||
commands.extend(index.flush());
|
||||
commands.extend(state.flush_vertices());
|
||||
commands.extend(state.flush_binds(base.dynamic_offsets));
|
||||
commands.extend(state.flush_binds(used_bind_groups, base.dynamic_offsets));
|
||||
commands.push(command);
|
||||
}
|
||||
RenderCommand::MultiDrawIndirect { .. }
|
||||
@ -589,7 +618,7 @@ impl RenderBundleEncoder {
|
||||
RenderCommand::PushDebugGroup { color: _, len: _ } => unimplemented!(),
|
||||
RenderCommand::InsertDebugMarker { color: _, len: _ } => unimplemented!(),
|
||||
RenderCommand::PopDebugGroup => unimplemented!(),
|
||||
RenderCommand::WriteTimestamp { .. }
|
||||
RenderCommand::WriteTimestamp { .. } // Must check the WRITE_TIMESTAMP_INSIDE_PASSES feature
|
||||
| RenderCommand::BeginPipelineStatisticsQuery { .. }
|
||||
| RenderCommand::EndPipelineStatisticsQuery => unimplemented!(),
|
||||
RenderCommand::ExecuteBundle(_)
|
||||
@ -608,7 +637,8 @@ impl RenderBundleEncoder {
|
||||
string_data: Vec::new(),
|
||||
push_constant_data: Vec::new(),
|
||||
},
|
||||
is_ds_read_only: self.is_ds_read_only,
|
||||
is_depth_read_only: self.is_depth_read_only,
|
||||
is_stencil_read_only: self.is_stencil_read_only,
|
||||
device_id: Stored {
|
||||
value: id::Valid(self.parent_id),
|
||||
ref_count: device.life_guard.add_ref(),
|
||||
@ -686,7 +716,8 @@ pub struct RenderBundle<A: HalApi> {
|
||||
// Normalized command stream. It can be executed verbatim,
|
||||
// without re-binding anything on the pipeline change.
|
||||
base: BasePass<RenderCommand>,
|
||||
pub(super) is_ds_read_only: bool,
|
||||
pub(super) is_depth_read_only: bool,
|
||||
pub(super) is_stencil_read_only: bool,
|
||||
pub(crate) device_id: Stored<id::DeviceId>,
|
||||
pub(crate) used: RenderBundleScope<A>,
|
||||
pub(super) buffer_memory_init_actions: Vec<BufferInitTrackerAction>,
|
||||
@ -907,39 +938,22 @@ impl<A: HalApi> Resource for RenderBundle<A> {
|
||||
|
||||
/// A render bundle's current index buffer state.
|
||||
///
|
||||
/// [`RenderBundleEncoder::finish`] uses this to drop redundant
|
||||
/// `SetIndexBuffer` commands from the final [`RenderBundle`]. It
|
||||
/// records index buffer state changes here, and then calls this
|
||||
/// type's [`flush`] method before any indexed draw command to produce
|
||||
/// [`RenderBundleEncoder::finish`] records the currently set index buffer here,
|
||||
/// and calls [`State::flush_index`] before any indexed draw command to produce
|
||||
/// a `SetIndexBuffer` command if one is necessary.
|
||||
///
|
||||
/// [`flush`]: IndexState::flush
|
||||
#[derive(Debug)]
|
||||
struct IndexState {
|
||||
buffer: Option<id::BufferId>,
|
||||
buffer: id::BufferId,
|
||||
format: wgt::IndexFormat,
|
||||
pipeline_format: Option<wgt::IndexFormat>,
|
||||
range: Range<wgt::BufferAddress>,
|
||||
is_dirty: bool,
|
||||
}
|
||||
|
||||
impl IndexState {
|
||||
/// Return a fresh state: no index buffer has been set yet.
|
||||
fn new() -> Self {
|
||||
Self {
|
||||
buffer: None,
|
||||
format: wgt::IndexFormat::default(),
|
||||
pipeline_format: None,
|
||||
range: 0..0,
|
||||
is_dirty: false,
|
||||
}
|
||||
}
|
||||
|
||||
/// Return the number of entries in the current index buffer.
|
||||
///
|
||||
/// Panic if no index buffer has been set.
|
||||
fn limit(&self) -> u32 {
|
||||
assert!(self.buffer.is_some());
|
||||
let bytes_per_index = match self.format {
|
||||
wgt::IndexFormat::Uint16 => 2,
|
||||
wgt::IndexFormat::Uint32 => 4,
|
||||
@ -947,13 +961,13 @@ impl IndexState {
|
||||
((self.range.end - self.range.start) / bytes_per_index) as u32
|
||||
}
|
||||
|
||||
/// Prepare for an indexed draw, producing a `SetIndexBuffer`
|
||||
/// command if necessary.
|
||||
/// Generate a `SetIndexBuffer` command to prepare for an indexed draw
|
||||
/// command, if needed.
|
||||
fn flush(&mut self) -> Option<RenderCommand> {
|
||||
if self.is_dirty {
|
||||
self.is_dirty = false;
|
||||
Some(RenderCommand::SetIndexBuffer {
|
||||
buffer_id: self.buffer.unwrap(),
|
||||
buffer_id: self.buffer,
|
||||
index_format: self.format,
|
||||
offset: self.range.start,
|
||||
size: wgt::BufferSize::new(self.range.end - self.range.start),
|
||||
@ -962,21 +976,6 @@ impl IndexState {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Set the current index buffer's format.
|
||||
fn set_format(&mut self, format: wgt::IndexFormat) {
|
||||
if self.format != format {
|
||||
self.format = format;
|
||||
self.is_dirty = true;
|
||||
}
|
||||
}
|
||||
|
||||
/// Set the current index buffer.
|
||||
fn set_buffer(&mut self, id: id::BufferId, range: Range<wgt::BufferAddress>) {
|
||||
self.buffer = Some(id);
|
||||
self.range = range;
|
||||
self.is_dirty = true;
|
||||
}
|
||||
}
|
||||
|
||||
/// The state of a single vertex buffer slot during render bundle encoding.
|
||||
@ -990,33 +989,20 @@ impl IndexState {
|
||||
/// [`flush`]: IndexState::flush
|
||||
#[derive(Debug)]
|
||||
struct VertexState {
|
||||
buffer: Option<id::BufferId>,
|
||||
buffer: id::BufferId,
|
||||
range: Range<wgt::BufferAddress>,
|
||||
stride: wgt::BufferAddress,
|
||||
rate: wgt::VertexStepMode,
|
||||
is_dirty: bool,
|
||||
}
|
||||
|
||||
impl VertexState {
|
||||
/// Construct a fresh `VertexState`: no buffer has been set for
|
||||
/// this slot.
|
||||
fn new() -> Self {
|
||||
fn new(buffer: id::BufferId, range: Range<wgt::BufferAddress>) -> Self {
|
||||
Self {
|
||||
buffer: None,
|
||||
range: 0..0,
|
||||
stride: 0,
|
||||
rate: wgt::VertexStepMode::Vertex,
|
||||
is_dirty: false,
|
||||
buffer,
|
||||
range,
|
||||
is_dirty: true,
|
||||
}
|
||||
}
|
||||
|
||||
/// Set this slot's vertex buffer.
|
||||
fn set_buffer(&mut self, buffer_id: id::BufferId, range: Range<wgt::BufferAddress>) {
|
||||
self.buffer = Some(buffer_id);
|
||||
self.range = range;
|
||||
self.is_dirty = true;
|
||||
}
|
||||
|
||||
/// Generate a `SetVertexBuffer` command for this slot, if necessary.
|
||||
///
|
||||
/// `slot` is the index of the vertex buffer slot that `self` tracks.
|
||||
@ -1025,7 +1011,7 @@ impl VertexState {
|
||||
self.is_dirty = false;
|
||||
Some(RenderCommand::SetVertexBuffer {
|
||||
slot,
|
||||
buffer_id: self.buffer.unwrap(),
|
||||
buffer_id: self.buffer,
|
||||
offset: self.range.start,
|
||||
size: wgt::BufferSize::new(self.range.end - self.range.start),
|
||||
})
|
||||
@ -1053,30 +1039,6 @@ struct BindState {
|
||||
is_dirty: bool,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct PushConstantState {
|
||||
ranges: ArrayVec<wgt::PushConstantRange, { SHADER_STAGE_COUNT }>,
|
||||
is_dirty: bool,
|
||||
}
|
||||
impl PushConstantState {
|
||||
fn new() -> Self {
|
||||
Self {
|
||||
ranges: ArrayVec::new(),
|
||||
is_dirty: false,
|
||||
}
|
||||
}
|
||||
|
||||
fn set_push_constants(&mut self, new_ranges: &[wgt::PushConstantRange]) -> bool {
|
||||
if &*self.ranges != new_ranges {
|
||||
self.ranges = new_ranges.iter().cloned().collect();
|
||||
self.is_dirty = true;
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct VertexLimitState {
|
||||
/// Length of the shortest vertex rate vertex buffer
|
||||
@ -1089,6 +1051,64 @@ struct VertexLimitState {
|
||||
instance_limit_slot: u32,
|
||||
}
|
||||
|
||||
/// The bundle's current pipeline, and some cached information needed for validation.
|
||||
struct PipelineState {
|
||||
/// The pipeline's id.
|
||||
id: id::RenderPipelineId,
|
||||
|
||||
/// The id of the pipeline's layout.
|
||||
layout_id: id::Valid<id::PipelineLayoutId>,
|
||||
|
||||
/// How this pipeline's vertex shader traverses each vertex buffer, indexed
|
||||
/// by vertex buffer slot number.
|
||||
steps: Vec<pipeline::VertexStep>,
|
||||
|
||||
/// Ranges of push constants this pipeline uses, copied from the pipeline
|
||||
/// layout.
|
||||
push_constant_ranges: ArrayVec<wgt::PushConstantRange, { SHADER_STAGE_COUNT }>,
|
||||
|
||||
/// The number of bind groups this pipeline uses.
|
||||
used_bind_groups: usize,
|
||||
}
|
||||
|
||||
impl PipelineState {
|
||||
fn new<A: HalApi>(
|
||||
pipeline_id: id::RenderPipelineId,
|
||||
pipeline: &pipeline::RenderPipeline<A>,
|
||||
layout: &binding_model::PipelineLayout<A>,
|
||||
) -> Self {
|
||||
Self {
|
||||
id: pipeline_id,
|
||||
layout_id: pipeline.layout_id.value,
|
||||
steps: pipeline.vertex_steps.to_vec(),
|
||||
push_constant_ranges: layout.push_constant_ranges.iter().cloned().collect(),
|
||||
used_bind_groups: layout.bind_group_layout_ids.len(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Return a sequence of commands to zero the push constant ranges this
|
||||
/// pipeline uses. If no initialization is necessary, return `None`.
|
||||
fn zero_push_constants(&self) -> Option<impl Iterator<Item = RenderCommand>> {
|
||||
if !self.push_constant_ranges.is_empty() {
|
||||
let nonoverlapping_ranges =
|
||||
super::bind::compute_nonoverlapping_ranges(&self.push_constant_ranges);
|
||||
|
||||
Some(
|
||||
nonoverlapping_ranges
|
||||
.into_iter()
|
||||
.map(|range| RenderCommand::SetPushConstant {
|
||||
stages: range.stages,
|
||||
offset: range.range.start,
|
||||
size_bytes: range.range.end - range.range.start,
|
||||
values_offset: None, // write zeros
|
||||
}),
|
||||
)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// State for analyzing and cleaning up bundle command streams.
|
||||
///
|
||||
/// To minimize state updates, [`RenderBundleEncoder::finish`]
|
||||
@ -1096,21 +1116,25 @@ struct VertexLimitState {
|
||||
/// [`SetIndexBuffer`] to the simulated state stored here, and then
|
||||
/// calls the `flush_foo` methods before draw calls to produce the
|
||||
/// update commands we actually need.
|
||||
///
|
||||
/// [`SetBindGroup`]: RenderCommand::SetBindGroup
|
||||
/// [`SetIndexBuffer`]: RenderCommand::SetIndexBuffer
|
||||
struct State<A: HalApi> {
|
||||
/// Resources used by this bundle. This will become [`RenderBundle::used`].
|
||||
trackers: RenderBundleScope<A>,
|
||||
|
||||
/// The current index buffer. We flush this state before indexed
|
||||
/// draw commands.
|
||||
index: IndexState,
|
||||
|
||||
/// The state of each vertex buffer slot.
|
||||
vertex: ArrayVec<VertexState, { hal::MAX_VERTEX_BUFFERS }>,
|
||||
/// The currently set pipeline, if any.
|
||||
pipeline: Option<PipelineState>,
|
||||
|
||||
/// The bind group set at each index, if any.
|
||||
bind: ArrayVec<Option<BindState>, { hal::MAX_BIND_GROUPS }>,
|
||||
|
||||
push_constant_ranges: PushConstantState,
|
||||
/// The state of each vertex buffer slot.
|
||||
vertex: ArrayVec<Option<VertexState>, { hal::MAX_VERTEX_BUFFERS }>,
|
||||
|
||||
/// The current index buffer, if one has been set. We flush this state
|
||||
/// before indexed draw commands.
|
||||
index: Option<IndexState>,
|
||||
|
||||
/// Dynamic offset values used by the cleaned-up command sequence.
|
||||
///
|
||||
@ -1119,35 +1143,31 @@ struct State<A: HalApi> {
|
||||
///
|
||||
/// [`dynamic_offsets`]: BasePass::dynamic_offsets
|
||||
flat_dynamic_offsets: Vec<wgt::DynamicOffset>,
|
||||
|
||||
used_bind_groups: usize,
|
||||
pipeline: Option<id::RenderPipelineId>,
|
||||
}
|
||||
|
||||
impl<A: HalApi> State<A> {
|
||||
fn vertex_limits(&self) -> VertexLimitState {
|
||||
fn vertex_limits(&self, pipeline: &PipelineState) -> VertexLimitState {
|
||||
let mut vert_state = VertexLimitState {
|
||||
vertex_limit: u32::MAX,
|
||||
vertex_limit_slot: 0,
|
||||
instance_limit: u32::MAX,
|
||||
instance_limit_slot: 0,
|
||||
};
|
||||
for (idx, vbs) in self.vertex.iter().enumerate() {
|
||||
if vbs.stride == 0 {
|
||||
continue;
|
||||
}
|
||||
let limit = ((vbs.range.end - vbs.range.start) / vbs.stride) as u32;
|
||||
match vbs.rate {
|
||||
wgt::VertexStepMode::Vertex => {
|
||||
if limit < vert_state.vertex_limit {
|
||||
vert_state.vertex_limit = limit;
|
||||
vert_state.vertex_limit_slot = idx as _;
|
||||
for (idx, (vbs, step)) in self.vertex.iter().zip(&pipeline.steps).enumerate() {
|
||||
if let Some(ref vbs) = *vbs {
|
||||
let limit = ((vbs.range.end - vbs.range.start) / step.stride) as u32;
|
||||
match step.mode {
|
||||
wgt::VertexStepMode::Vertex => {
|
||||
if limit < vert_state.vertex_limit {
|
||||
vert_state.vertex_limit = limit;
|
||||
vert_state.vertex_limit_slot = idx as _;
|
||||
}
|
||||
}
|
||||
}
|
||||
wgt::VertexStepMode::Instance => {
|
||||
if limit < vert_state.instance_limit {
|
||||
vert_state.instance_limit = limit;
|
||||
vert_state.instance_limit_slot = idx as _;
|
||||
wgt::VertexStepMode::Instance => {
|
||||
if limit < vert_state.instance_limit {
|
||||
vert_state.instance_limit = limit;
|
||||
vert_state.instance_limit_slot = idx as _;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1155,8 +1175,21 @@ impl<A: HalApi> State<A> {
|
||||
vert_state
|
||||
}
|
||||
|
||||
/// Return the id of the current pipeline, if any.
|
||||
fn pipeline_id(&self) -> Option<id::RenderPipelineId> {
|
||||
self.pipeline.as_ref().map(|p| p.id)
|
||||
}
|
||||
|
||||
/// Return the current pipeline state. Return an error if none is set.
|
||||
fn pipeline(&self, scope: PassErrorScope) -> Result<&PipelineState, RenderBundleError> {
|
||||
self.pipeline
|
||||
.as_ref()
|
||||
.ok_or(DrawError::MissingPipeline)
|
||||
.map_pass_err(scope)
|
||||
}
|
||||
|
||||
/// Mark all non-empty bind group table entries from `index` onwards as dirty.
|
||||
fn invalidate_group_from(&mut self, index: usize) {
|
||||
fn invalidate_bind_group_from(&mut self, index: usize) {
|
||||
for contents in self.bind[index..].iter_mut().flatten() {
|
||||
contents.is_dirty = true;
|
||||
}
|
||||
@ -1190,83 +1223,106 @@ impl<A: HalApi> State<A> {
|
||||
|
||||
// Once we've changed the bind group at a particular index, all
|
||||
// subsequent indices need to be rewritten.
|
||||
self.invalidate_group_from(slot as usize + 1);
|
||||
self.invalidate_bind_group_from(slot as usize + 1);
|
||||
}
|
||||
|
||||
fn set_pipeline(
|
||||
/// Determine which bind group slots need to be re-set after a pipeline change.
|
||||
///
|
||||
/// Given that we are switching from the current pipeline state to `new`,
|
||||
/// whose layout is `layout`, mark all the bind group slots that we need to
|
||||
/// emit new `SetBindGroup` commands for as dirty.
|
||||
///
|
||||
/// According to `wgpu_hal`'s rules:
|
||||
///
|
||||
/// - If the layout of any bind group slot changes, then that slot and
|
||||
/// all following slots must have their bind groups re-established.
|
||||
///
|
||||
/// - Changing the push constant ranges at all requires re-establishing
|
||||
/// all bind groups.
|
||||
fn invalidate_bind_groups(
|
||||
&mut self,
|
||||
index_format: Option<wgt::IndexFormat>,
|
||||
vertex_strides: &[(wgt::BufferAddress, wgt::VertexStepMode)],
|
||||
layout_ids: &[id::Valid<id::BindGroupLayoutId>],
|
||||
push_constant_layouts: &[wgt::PushConstantRange],
|
||||
new: &PipelineState,
|
||||
layout: &binding_model::PipelineLayout<A>,
|
||||
) {
|
||||
self.index.pipeline_format = index_format;
|
||||
match self.pipeline {
|
||||
None => {
|
||||
// Establishing entirely new pipeline state.
|
||||
self.invalidate_bind_group_from(0);
|
||||
}
|
||||
Some(ref old) => {
|
||||
if old.id == new.id {
|
||||
// Everything is derived from the pipeline, so if the id has
|
||||
// not changed, there's no need to consider anything else.
|
||||
return;
|
||||
}
|
||||
|
||||
for (vs, &(stride, step_mode)) in self.vertex.iter_mut().zip(vertex_strides) {
|
||||
if vs.stride != stride || vs.rate != step_mode {
|
||||
vs.stride = stride;
|
||||
vs.rate = step_mode;
|
||||
vs.is_dirty = true;
|
||||
// Any push constant change invalidates all groups.
|
||||
if old.push_constant_ranges != new.push_constant_ranges {
|
||||
self.invalidate_bind_group_from(0);
|
||||
} else {
|
||||
let first_changed = self
|
||||
.bind
|
||||
.iter()
|
||||
.zip(&layout.bind_group_layout_ids)
|
||||
.position(|(entry, &layout_id)| match *entry {
|
||||
Some(ref contents) => contents.layout_id != layout_id,
|
||||
None => false,
|
||||
});
|
||||
if let Some(slot) = first_changed {
|
||||
self.invalidate_bind_group_from(slot);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let push_constants_changed = self
|
||||
.push_constant_ranges
|
||||
.set_push_constants(push_constant_layouts);
|
||||
|
||||
self.used_bind_groups = layout_ids.len();
|
||||
let invalid_from = if push_constants_changed {
|
||||
Some(0)
|
||||
} else {
|
||||
self.bind
|
||||
.iter()
|
||||
.zip(layout_ids)
|
||||
.position(|(entry, &layout_id)| match *entry {
|
||||
Some(ref contents) => contents.layout_id != layout_id,
|
||||
None => false,
|
||||
})
|
||||
};
|
||||
if let Some(slot) = invalid_from {
|
||||
self.invalidate_group_from(slot);
|
||||
}
|
||||
}
|
||||
|
||||
fn flush_push_constants(&mut self) -> Option<impl Iterator<Item = RenderCommand>> {
|
||||
let is_dirty = self.push_constant_ranges.is_dirty;
|
||||
|
||||
if is_dirty {
|
||||
let nonoverlapping_ranges =
|
||||
super::bind::compute_nonoverlapping_ranges(&self.push_constant_ranges.ranges);
|
||||
|
||||
Some(
|
||||
nonoverlapping_ranges
|
||||
.into_iter()
|
||||
.map(|range| RenderCommand::SetPushConstant {
|
||||
stages: range.stages,
|
||||
offset: range.range.start,
|
||||
size_bytes: range.range.end - range.range.start,
|
||||
values_offset: None,
|
||||
}),
|
||||
)
|
||||
} else {
|
||||
None
|
||||
/// Set the bundle's current index buffer and its associated parameters.
|
||||
fn set_index_buffer(
|
||||
&mut self,
|
||||
buffer: id::BufferId,
|
||||
format: wgt::IndexFormat,
|
||||
range: Range<wgt::BufferAddress>,
|
||||
) {
|
||||
match self.index {
|
||||
Some(ref current)
|
||||
if current.buffer == buffer
|
||||
&& current.format == format
|
||||
&& current.range == range =>
|
||||
{
|
||||
return
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
|
||||
self.index = Some(IndexState {
|
||||
buffer,
|
||||
format,
|
||||
range,
|
||||
is_dirty: true,
|
||||
});
|
||||
}
|
||||
|
||||
/// Generate a `SetIndexBuffer` command to prepare for an indexed draw
|
||||
/// command, if needed.
|
||||
fn flush_index(&mut self) -> Option<RenderCommand> {
|
||||
self.index.as_mut().and_then(|index| index.flush())
|
||||
}
|
||||
|
||||
fn flush_vertices(&mut self) -> impl Iterator<Item = RenderCommand> + '_ {
|
||||
self.vertex
|
||||
.iter_mut()
|
||||
.enumerate()
|
||||
.flat_map(|(i, vs)| vs.flush(i as u32))
|
||||
.flat_map(|(i, vs)| vs.as_mut().and_then(|vs| vs.flush(i as u32)))
|
||||
}
|
||||
|
||||
/// Generate `SetBindGroup` commands for any bind groups that need to be updated.
|
||||
fn flush_binds(
|
||||
&mut self,
|
||||
used_bind_groups: usize,
|
||||
dynamic_offsets: &[wgt::DynamicOffset],
|
||||
) -> impl Iterator<Item = RenderCommand> + '_ {
|
||||
// Append each dirty bind group's dynamic offsets to `flat_dynamic_offsets`.
|
||||
for contents in self.bind[..self.used_bind_groups].iter().flatten() {
|
||||
for contents in self.bind[..used_bind_groups].iter().flatten() {
|
||||
if contents.is_dirty {
|
||||
self.flat_dynamic_offsets
|
||||
.extend_from_slice(&dynamic_offsets[contents.dynamic_offsets.clone()]);
|
||||
@ -1275,7 +1331,7 @@ impl<A: HalApi> State<A> {
|
||||
|
||||
// Then, generate `SetBindGroup` commands to update the dirty bind
|
||||
// groups. After this, all bind groups are clean.
|
||||
self.bind[..self.used_bind_groups]
|
||||
self.bind[..used_bind_groups]
|
||||
.iter_mut()
|
||||
.enumerate()
|
||||
.flat_map(|(i, entry)| {
|
||||
|
@ -408,7 +408,7 @@ fn clear_texture_via_render_passes<A: hal::Api>(
|
||||
for depth_or_layer in layer_or_depth_range {
|
||||
let color_attachments_tmp;
|
||||
let (color_attachments, depth_stencil_attachment) = if is_color {
|
||||
color_attachments_tmp = [hal::ColorAttachment {
|
||||
color_attachments_tmp = [Some(hal::ColorAttachment {
|
||||
target: hal::Attachment {
|
||||
view: dst_texture.get_clear_view(mip_level, depth_or_layer),
|
||||
usage: hal::TextureUses::COLOR_TARGET,
|
||||
@ -416,7 +416,7 @@ fn clear_texture_via_render_passes<A: hal::Api>(
|
||||
resolve_target: None,
|
||||
ops: hal::AttachmentOps::STORE,
|
||||
clear_value: wgt::Color::TRANSPARENT,
|
||||
}];
|
||||
})];
|
||||
(&color_attachments_tmp[..], None)
|
||||
} else {
|
||||
(
|
||||
|
@ -9,7 +9,7 @@ use crate::{
|
||||
BasePass, BasePassRef, BindGroupStateChange, CommandBuffer, CommandEncoderError,
|
||||
CommandEncoderStatus, MapPassErr, PassErrorScope, QueryUseError, StateChange,
|
||||
},
|
||||
device::MissingDownlevelFlags,
|
||||
device::{MissingDownlevelFlags, MissingFeatures},
|
||||
error::{ErrorFormatter, PrettyError},
|
||||
hub::{Global, GlobalIdentityHandlerFactory, HalApi, Storage, Token},
|
||||
id,
|
||||
@ -43,11 +43,24 @@ pub enum ComputeCommand {
|
||||
bind_group_id: id::BindGroupId,
|
||||
},
|
||||
SetPipeline(id::ComputePipelineId),
|
||||
|
||||
/// Set a range of push constants to values stored in [`BasePass::push_constant_data`].
|
||||
SetPushConstant {
|
||||
/// The byte offset within the push constant storage to write to. This
|
||||
/// must be a multiple of four.
|
||||
offset: u32,
|
||||
|
||||
/// The number of bytes to write. This must be a multiple of four.
|
||||
size_bytes: u32,
|
||||
|
||||
/// Index in [`BasePass::push_constant_data`] of the start of the data
|
||||
/// to be written.
|
||||
///
|
||||
/// Note: this is not a byte offset like `offset`. Rather, it is the
|
||||
/// index of the first `u32` element in `push_constant_data` to read.
|
||||
values_offset: u32,
|
||||
},
|
||||
|
||||
Dispatch([u32; 3]),
|
||||
DispatchIndirect {
|
||||
buffer_id: id::BufferId,
|
||||
@ -179,6 +192,8 @@ pub enum ComputePassErrorInner {
|
||||
#[error(transparent)]
|
||||
QueryUse(#[from] QueryUseError),
|
||||
#[error(transparent)]
|
||||
MissingFeatures(#[from] MissingFeatures),
|
||||
#[error(transparent)]
|
||||
MissingDownlevelFlags(#[from] MissingDownlevelFlags),
|
||||
}
|
||||
|
||||
@ -253,6 +268,7 @@ impl<A: HalApi> State<A> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
// `extra_buffer` is there to represent the indirect buffer that is also part of the usage scope.
|
||||
fn flush_states(
|
||||
&mut self,
|
||||
raw_encoder: &mut A::CommandEncoder,
|
||||
@ -260,6 +276,7 @@ impl<A: HalApi> State<A> {
|
||||
bind_group_guard: &Storage<BindGroup<A>, id::BindGroupId>,
|
||||
buffer_guard: &Storage<Buffer<A>, id::BufferId>,
|
||||
texture_guard: &Storage<Texture<A>, id::TextureId>,
|
||||
indirect_buffer: Option<id::Valid<id::BufferId>>,
|
||||
) -> Result<(), UsageConflict> {
|
||||
for id in self.binder.list_active() {
|
||||
unsafe {
|
||||
@ -280,6 +297,13 @@ impl<A: HalApi> State<A> {
|
||||
}
|
||||
}
|
||||
|
||||
// Add the state of the indirect buffer if it hasn't been hit before.
|
||||
unsafe {
|
||||
base_trackers
|
||||
.buffers
|
||||
.set_and_remove_from_usage_scope_sparse(&mut self.scope.buffers, indirect_buffer);
|
||||
}
|
||||
|
||||
log::trace!("Encoding dispatch barriers");
|
||||
|
||||
CommandBuffer::drain_barriers(raw_encoder, base_trackers, buffer_guard, texture_guard);
|
||||
@ -569,6 +593,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
||||
&*bind_group_guard,
|
||||
&*buffer_guard,
|
||||
&*texture_guard,
|
||||
None,
|
||||
)
|
||||
.map_pass_err(scope)?;
|
||||
|
||||
@ -644,6 +669,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
||||
&*bind_group_guard,
|
||||
&*buffer_guard,
|
||||
&*texture_guard,
|
||||
Some(id::Valid(buffer_id)),
|
||||
)
|
||||
.map_pass_err(scope)?;
|
||||
unsafe {
|
||||
@ -685,6 +711,10 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
||||
} => {
|
||||
let scope = PassErrorScope::WriteTimestamp;
|
||||
|
||||
device
|
||||
.require_features(wgt::Features::WRITE_TIMESTAMP_INSIDE_PASSES)
|
||||
.map_pass_err(scope)?;
|
||||
|
||||
let query_set: &resource::QuerySet<A> = cmd_buf
|
||||
.trackers
|
||||
.query_sets
|
||||
|
33
third_party/rust/wgpu-core/src/command/draw.rs
vendored
33
third_party/rust/wgpu-core/src/command/draw.rs
vendored
@ -87,10 +87,12 @@ pub enum RenderCommandError {
|
||||
MissingTextureUsage(#[from] MissingTextureUsageError),
|
||||
#[error(transparent)]
|
||||
PushConstants(#[from] PushConstantUploadError),
|
||||
#[error("Invalid Viewport parameters")]
|
||||
InvalidViewport,
|
||||
#[error("Invalid ScissorRect parameters")]
|
||||
InvalidScissorRect,
|
||||
#[error("Viewport width {0} and/or height {1} are less than or equal to 0")]
|
||||
InvalidViewportDimension(f32, f32),
|
||||
#[error("Viewport minDepth {0} and/or maxDepth {1} are not in [0, 1]")]
|
||||
InvalidViewportDepth(f32, f32),
|
||||
#[error("Scissor {0:?} is not contained in the render target {1:?}")]
|
||||
InvalidScissorRect(Rect<u32>, wgt::Extent3d),
|
||||
#[error("Support for {0} is not implemented yet")]
|
||||
Unimplemented(&'static str),
|
||||
}
|
||||
@ -170,13 +172,32 @@ pub enum RenderCommand {
|
||||
depth_max: f32,
|
||||
},
|
||||
SetScissor(Rect<u32>),
|
||||
|
||||
/// Set a range of push constants to values stored in [`BasePass::push_constant_data`].
|
||||
///
|
||||
/// See [`wgpu::RenderPass::set_push_constants`] for a detailed explanation
|
||||
/// of the restrictions these commands must satisfy.
|
||||
SetPushConstant {
|
||||
/// Which stages we are setting push constant values for.
|
||||
stages: wgt::ShaderStages,
|
||||
|
||||
/// The byte offset within the push constant storage to write to. This
|
||||
/// must be a multiple of four.
|
||||
offset: u32,
|
||||
|
||||
/// The number of bytes to write. This must be a multiple of four.
|
||||
size_bytes: u32,
|
||||
/// None means there is no data and the data should be an array of zeros.
|
||||
|
||||
/// Index in [`BasePass::push_constant_data`] of the start of the data
|
||||
/// to be written.
|
||||
///
|
||||
/// Facilitates clears in renderbundles which explicitly do their clears.
|
||||
/// Note: this is not a byte offset like `offset`. Rather, it is the
|
||||
/// index of the first `u32` element in `push_constant_data` to read.
|
||||
///
|
||||
/// `None` means zeros should be written to the destination range, and
|
||||
/// there is no corresponding data in `push_constant_data`. This is used
|
||||
/// by render bundles, which explicitly clear out any state that
|
||||
/// post-bundle code might see.
|
||||
values_offset: Option<u32>,
|
||||
},
|
||||
Draw {
|
||||
|
@ -287,6 +287,10 @@ pub struct BasePass<C> {
|
||||
/// instruction consumes the next `len` bytes from this vector.
|
||||
pub string_data: Vec<u8>,
|
||||
|
||||
/// Data used by `SetPushConstant` instructions.
|
||||
///
|
||||
/// See the documentation for [`RenderCommand::SetPushConstant`]
|
||||
/// and [`ComputeCommand::SetPushConstant`] for details.
|
||||
pub push_constant_data: Vec<u32>,
|
||||
}
|
||||
|
||||
|
214
third_party/rust/wgpu-core/src/command/render.rs
vendored
214
third_party/rust/wgpu-core/src/command/render.rs
vendored
@ -17,7 +17,7 @@ use crate::{
|
||||
hub::{Global, GlobalIdentityHandlerFactory, HalApi, Storage, Token},
|
||||
id,
|
||||
init_tracker::{MemoryInitKind, TextureInitRange, TextureInitTrackerAction},
|
||||
pipeline::PipelineFlags,
|
||||
pipeline::{self, PipelineFlags},
|
||||
resource::{self, Buffer, Texture, TextureView},
|
||||
track::{TextureSelector, UsageConflict, UsageScope},
|
||||
validation::{
|
||||
@ -131,20 +131,40 @@ pub struct RenderPassDepthStencilAttachment {
|
||||
}
|
||||
|
||||
impl RenderPassDepthStencilAttachment {
|
||||
fn is_read_only(&self, aspects: hal::FormatAspects) -> Result<bool, RenderPassErrorInner> {
|
||||
if aspects.contains(hal::FormatAspects::DEPTH) && !self.depth.read_only {
|
||||
return Ok(false);
|
||||
/// Validate the given aspects' read-only flags against their load
|
||||
/// and store ops.
|
||||
///
|
||||
/// When an aspect is read-only, its load and store ops must be
|
||||
/// `LoadOp::Load` and `StoreOp::Store`.
|
||||
///
|
||||
/// On success, return a pair `(depth, stencil)` indicating
|
||||
/// whether the depth and stencil passes are read-only.
|
||||
fn depth_stencil_read_only(
|
||||
&self,
|
||||
aspects: hal::FormatAspects,
|
||||
) -> Result<(bool, bool), RenderPassErrorInner> {
|
||||
let mut depth_read_only = true;
|
||||
let mut stencil_read_only = true;
|
||||
|
||||
if aspects.contains(hal::FormatAspects::DEPTH) {
|
||||
if self.depth.read_only
|
||||
&& (self.depth.load_op, self.depth.store_op) != (LoadOp::Load, StoreOp::Store)
|
||||
{
|
||||
return Err(RenderPassErrorInner::InvalidDepthOps);
|
||||
}
|
||||
depth_read_only = self.depth.read_only;
|
||||
}
|
||||
if (self.depth.load_op, self.depth.store_op) != (LoadOp::Load, StoreOp::Store) {
|
||||
return Err(RenderPassErrorInner::InvalidDepthOps);
|
||||
|
||||
if aspects.contains(hal::FormatAspects::STENCIL) {
|
||||
if self.stencil.read_only
|
||||
&& (self.stencil.load_op, self.stencil.store_op) != (LoadOp::Load, StoreOp::Store)
|
||||
{
|
||||
return Err(RenderPassErrorInner::InvalidStencilOps);
|
||||
}
|
||||
stencil_read_only = self.stencil.read_only;
|
||||
}
|
||||
if aspects.contains(hal::FormatAspects::STENCIL) && !self.stencil.read_only {
|
||||
return Ok(false);
|
||||
}
|
||||
if (self.stencil.load_op, self.stencil.store_op) != (LoadOp::Load, StoreOp::Store) {
|
||||
return Err(RenderPassErrorInner::InvalidStencilOps);
|
||||
}
|
||||
Ok(true)
|
||||
|
||||
Ok((depth_read_only, stencil_read_only))
|
||||
}
|
||||
}
|
||||
|
||||
@ -153,7 +173,7 @@ impl RenderPassDepthStencilAttachment {
|
||||
pub struct RenderPassDescriptor<'a> {
|
||||
pub label: Label<'a>,
|
||||
/// The color attachments of the render pass.
|
||||
pub color_attachments: Cow<'a, [RenderPassColorAttachment]>,
|
||||
pub color_attachments: Cow<'a, [Option<RenderPassColorAttachment>]>,
|
||||
/// The depth and stencil attachment of the render pass, if any.
|
||||
pub depth_stencil_attachment: Option<&'a RenderPassDepthStencilAttachment>,
|
||||
}
|
||||
@ -162,7 +182,7 @@ pub struct RenderPassDescriptor<'a> {
|
||||
pub struct RenderPass {
|
||||
base: BasePass<RenderCommand>,
|
||||
parent_id: id::CommandEncoderId,
|
||||
color_targets: ArrayVec<RenderPassColorAttachment, { hal::MAX_COLOR_TARGETS }>,
|
||||
color_targets: ArrayVec<Option<RenderPassColorAttachment>, { hal::MAX_COLOR_ATTACHMENTS }>,
|
||||
depth_stencil_target: Option<RenderPassDepthStencilAttachment>,
|
||||
|
||||
// Resource binding dedupe state.
|
||||
@ -278,16 +298,17 @@ impl IndexState {
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
struct VertexBufferState {
|
||||
total_size: BufferAddress,
|
||||
stride: BufferAddress,
|
||||
rate: VertexStepMode,
|
||||
step: pipeline::VertexStep,
|
||||
bound: bool,
|
||||
}
|
||||
|
||||
impl VertexBufferState {
|
||||
const EMPTY: Self = Self {
|
||||
total_size: 0,
|
||||
stride: 0,
|
||||
rate: VertexStepMode::Vertex,
|
||||
step: pipeline::VertexStep {
|
||||
stride: 0,
|
||||
mode: VertexStepMode::Vertex,
|
||||
},
|
||||
bound: false,
|
||||
};
|
||||
}
|
||||
@ -312,11 +333,11 @@ impl VertexState {
|
||||
self.vertex_limit = u32::MAX;
|
||||
self.instance_limit = u32::MAX;
|
||||
for (idx, vbs) in self.inputs.iter().enumerate() {
|
||||
if vbs.stride == 0 || !vbs.bound {
|
||||
if vbs.step.stride == 0 || !vbs.bound {
|
||||
continue;
|
||||
}
|
||||
let limit = (vbs.total_size / vbs.stride) as u32;
|
||||
match vbs.rate {
|
||||
let limit = (vbs.total_size / vbs.step.stride) as u32;
|
||||
match vbs.step.mode {
|
||||
VertexStepMode::Vertex => {
|
||||
if limit < self.vertex_limit {
|
||||
self.vertex_limit = limit;
|
||||
@ -420,7 +441,7 @@ pub enum RenderPassErrorInner {
|
||||
InvalidDepthStencilAttachmentFormat(wgt::TextureFormat),
|
||||
#[error("attachment format {0:?} can not be resolved")]
|
||||
UnsupportedResolveTargetFormat(wgt::TextureFormat),
|
||||
#[error("necessary attachments are missing")]
|
||||
#[error("missing color or depth_stencil attachments, at least one is required.")]
|
||||
MissingAttachments,
|
||||
#[error("attachments have differing sizes: {previous:?} is followed by {mismatch:?}")]
|
||||
AttachmentsDimensionMismatch {
|
||||
@ -474,8 +495,18 @@ pub enum RenderPassErrorInner {
|
||||
ResourceUsageConflict(#[from] UsageConflict),
|
||||
#[error("render bundle has incompatible targets, {0}")]
|
||||
IncompatibleBundleTargets(#[from] RenderPassCompatibilityError),
|
||||
#[error("render bundle has an incompatible read-only depth/stencil flag: bundle is {bundle}, while the pass is {pass}")]
|
||||
IncompatibleBundleRods { pass: bool, bundle: bool },
|
||||
#[error(
|
||||
"render bundle has incompatible read-only flags: \
|
||||
bundle has flags depth = {bundle_depth} and stencil = {bundle_stencil}, \
|
||||
while the pass has flags depth = {pass_depth} and stencil = {pass_stencil}. \
|
||||
Read-only renderpasses are only compatible with read-only bundles for that aspect."
|
||||
)]
|
||||
IncompatibleBundleRods {
|
||||
pass_depth: bool,
|
||||
pass_stencil: bool,
|
||||
bundle_depth: bool,
|
||||
bundle_stencil: bool,
|
||||
},
|
||||
#[error(transparent)]
|
||||
RenderCommand(#[from] RenderCommandError),
|
||||
#[error(transparent)]
|
||||
@ -558,14 +589,15 @@ impl<A: hal::Api> TextureView<A> {
|
||||
}
|
||||
}
|
||||
|
||||
const MAX_TOTAL_ATTACHMENTS: usize = hal::MAX_COLOR_TARGETS + hal::MAX_COLOR_TARGETS + 1;
|
||||
const MAX_TOTAL_ATTACHMENTS: usize = hal::MAX_COLOR_ATTACHMENTS + hal::MAX_COLOR_ATTACHMENTS + 1;
|
||||
type AttachmentDataVec<T> = ArrayVec<T, MAX_TOTAL_ATTACHMENTS>;
|
||||
|
||||
struct RenderPassInfo<'a, A: HalApi> {
|
||||
context: RenderPassContext,
|
||||
usage_scope: UsageScope<A>,
|
||||
render_attachments: AttachmentDataVec<RenderAttachment<'a>>, // All render attachments, including depth/stencil
|
||||
is_ds_read_only: bool,
|
||||
is_depth_read_only: bool,
|
||||
is_stencil_read_only: bool,
|
||||
extent: wgt::Extent3d,
|
||||
_phantom: PhantomData<A>,
|
||||
|
||||
@ -614,7 +646,7 @@ impl<'a, A: HalApi> RenderPassInfo<'a, A> {
|
||||
fn start(
|
||||
device: &Device<A>,
|
||||
label: Option<&str>,
|
||||
color_attachments: &[RenderPassColorAttachment],
|
||||
color_attachments: &[Option<RenderPassColorAttachment>],
|
||||
depth_stencil_attachment: Option<&RenderPassDepthStencilAttachment>,
|
||||
cmd_buf: &mut CommandBuffer<A>,
|
||||
view_guard: &'a Storage<TextureView<A>, id::TextureViewId>,
|
||||
@ -626,7 +658,8 @@ impl<'a, A: HalApi> RenderPassInfo<'a, A> {
|
||||
// We default to false intentionally, even if depth-stencil isn't used at all.
|
||||
// This allows us to use the primary raw pipeline in `RenderPipeline`,
|
||||
// instead of the special read-only one, which would be `None`.
|
||||
let mut is_ds_read_only = false;
|
||||
let mut is_depth_read_only = false;
|
||||
let mut is_stencil_read_only = false;
|
||||
|
||||
let mut render_attachments = AttachmentDataVec::<RenderAttachment>::new();
|
||||
let mut discarded_surfaces = AttachmentDataVec::new();
|
||||
@ -689,15 +722,19 @@ impl<'a, A: HalApi> RenderPassInfo<'a, A> {
|
||||
expected: sample_count,
|
||||
});
|
||||
}
|
||||
if sample_count != 1 && sample_count != 4 {
|
||||
return Err(RenderPassErrorInner::InvalidSampleCount(sample_count));
|
||||
}
|
||||
attachment_type_name = type_name;
|
||||
Ok(())
|
||||
};
|
||||
|
||||
let mut colors = ArrayVec::<hal::ColorAttachment<A>, { hal::MAX_COLOR_TARGETS }>::new();
|
||||
let mut colors =
|
||||
ArrayVec::<Option<hal::ColorAttachment<A>>, { hal::MAX_COLOR_ATTACHMENTS }>::new();
|
||||
let mut depth_stencil = None;
|
||||
|
||||
if let Some(at) = depth_stencil_attachment {
|
||||
let view = cmd_buf
|
||||
let view: &TextureView<A> = cmd_buf
|
||||
.trackers
|
||||
.views
|
||||
.add_single(&*view_guard, at.view)
|
||||
@ -786,8 +823,9 @@ impl<'a, A: HalApi> RenderPassInfo<'a, A> {
|
||||
}
|
||||
}
|
||||
|
||||
let usage = if at.is_read_only(ds_aspects)? {
|
||||
is_ds_read_only = true;
|
||||
(is_depth_read_only, is_stencil_read_only) = at.depth_stencil_read_only(ds_aspects)?;
|
||||
|
||||
let usage = if is_depth_read_only && is_stencil_read_only {
|
||||
hal::TextureUses::DEPTH_STENCIL_READ | hal::TextureUses::RESOURCE
|
||||
} else {
|
||||
hal::TextureUses::DEPTH_STENCIL_WRITE
|
||||
@ -806,7 +844,13 @@ impl<'a, A: HalApi> RenderPassInfo<'a, A> {
|
||||
}
|
||||
|
||||
for at in color_attachments {
|
||||
let color_view = cmd_buf
|
||||
let at = if let Some(attachment) = at.as_ref() {
|
||||
attachment
|
||||
} else {
|
||||
colors.push(None);
|
||||
continue;
|
||||
};
|
||||
let color_view: &TextureView<A> = cmd_buf
|
||||
.trackers
|
||||
.views
|
||||
.add_single(&*view_guard, at.view)
|
||||
@ -836,7 +880,7 @@ impl<'a, A: HalApi> RenderPassInfo<'a, A> {
|
||||
|
||||
let mut hal_resolve_target = None;
|
||||
if let Some(resolve_target) = at.resolve_target {
|
||||
let resolve_view = cmd_buf
|
||||
let resolve_view: &TextureView<A> = cmd_buf
|
||||
.trackers
|
||||
.views
|
||||
.add_single(&*view_guard, resolve_target)
|
||||
@ -885,7 +929,7 @@ impl<'a, A: HalApi> RenderPassInfo<'a, A> {
|
||||
});
|
||||
}
|
||||
|
||||
colors.push(hal::ColorAttachment {
|
||||
colors.push(Some(hal::ColorAttachment {
|
||||
target: hal::Attachment {
|
||||
view: &color_view.raw,
|
||||
usage: hal::TextureUses::COLOR_TARGET,
|
||||
@ -893,28 +937,30 @@ impl<'a, A: HalApi> RenderPassInfo<'a, A> {
|
||||
resolve_target: hal_resolve_target,
|
||||
ops: at.channel.hal_ops(),
|
||||
clear_value: at.channel.clear_value,
|
||||
});
|
||||
}));
|
||||
}
|
||||
|
||||
if sample_count != 1 && sample_count != 4 {
|
||||
return Err(RenderPassErrorInner::InvalidSampleCount(sample_count));
|
||||
}
|
||||
let extent = extent.ok_or(RenderPassErrorInner::MissingAttachments)?;
|
||||
let multiview = detected_multiview.expect("Multiview was not detected, no attachments");
|
||||
|
||||
let view_data = AttachmentData {
|
||||
colors: color_attachments
|
||||
.iter()
|
||||
.map(|at| view_guard.get(at.view).unwrap())
|
||||
.map(|at| at.as_ref().map(|at| view_guard.get(at.view).unwrap()))
|
||||
.collect(),
|
||||
resolves: color_attachments
|
||||
.iter()
|
||||
.filter_map(|at| at.resolve_target)
|
||||
.map(|attachment| view_guard.get(attachment).unwrap())
|
||||
.filter_map(|at| match *at {
|
||||
Some(RenderPassColorAttachment {
|
||||
resolve_target: Some(resolve),
|
||||
..
|
||||
}) => Some(view_guard.get(resolve).unwrap()),
|
||||
_ => None,
|
||||
})
|
||||
.collect(),
|
||||
depth_stencil: depth_stencil_attachment.map(|at| view_guard.get(at.view).unwrap()),
|
||||
};
|
||||
let extent = extent.ok_or(RenderPassErrorInner::MissingAttachments)?;
|
||||
|
||||
let multiview = detected_multiview.expect("Multiview was not detected, no attachments");
|
||||
let context = RenderPassContext {
|
||||
attachments: view_data.map(|view| view.desc.format),
|
||||
sample_count,
|
||||
@ -937,7 +983,8 @@ impl<'a, A: HalApi> RenderPassInfo<'a, A> {
|
||||
context,
|
||||
usage_scope: UsageScope::new(buffer_guard, texture_guard),
|
||||
render_attachments,
|
||||
is_ds_read_only,
|
||||
is_depth_read_only,
|
||||
is_stencil_read_only,
|
||||
extent,
|
||||
_phantom: PhantomData,
|
||||
pending_discard_init_fixups,
|
||||
@ -1041,7 +1088,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
||||
&self,
|
||||
encoder_id: id::CommandEncoderId,
|
||||
base: BasePassRef<RenderCommand>,
|
||||
color_attachments: &[RenderPassColorAttachment],
|
||||
color_attachments: &[Option<RenderPassColorAttachment>],
|
||||
depth_stencil_attachment: Option<&RenderPassDepthStencilAttachment>,
|
||||
) -> Result<(), RenderPassError> {
|
||||
profiling::scope!("run_render_pass", "CommandEncoder");
|
||||
@ -1156,7 +1203,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
||||
);
|
||||
dynamic_offset_count += num_dynamic_offsets as usize;
|
||||
|
||||
let bind_group = cmd_buf
|
||||
let bind_group: &crate::binding_model::BindGroup<A> = cmd_buf
|
||||
.trackers
|
||||
.bind_groups
|
||||
.add_single(&*bind_group_guard, bind_group_id)
|
||||
@ -1220,7 +1267,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
||||
let scope = PassErrorScope::SetPipelineRender(pipeline_id);
|
||||
state.pipeline = Some(pipeline_id);
|
||||
|
||||
let pipeline = cmd_buf
|
||||
let pipeline: &pipeline::RenderPipeline<A> = cmd_buf
|
||||
.trackers
|
||||
.render_pipelines
|
||||
.add_single(&*render_pipeline_guard, pipeline_id)
|
||||
@ -1234,8 +1281,10 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
||||
|
||||
state.pipeline_flags = pipeline.flags;
|
||||
|
||||
if pipeline.flags.contains(PipelineFlags::WRITES_DEPTH_STENCIL)
|
||||
&& info.is_ds_read_only
|
||||
if (pipeline.flags.contains(PipelineFlags::WRITES_DEPTH)
|
||||
&& info.is_depth_read_only)
|
||||
|| (pipeline.flags.contains(PipelineFlags::WRITES_STENCIL)
|
||||
&& info.is_stencil_read_only)
|
||||
{
|
||||
return Err(RenderCommandError::IncompatiblePipelineRods)
|
||||
.map_pass_err(scope);
|
||||
@ -1304,24 +1353,25 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
||||
|
||||
state.index.pipeline_format = pipeline.strip_index_format;
|
||||
|
||||
let vertex_strides_len = pipeline.vertex_strides.len();
|
||||
state.vertex.buffers_required = vertex_strides_len as u32;
|
||||
let vertex_steps_len = pipeline.vertex_steps.len();
|
||||
state.vertex.buffers_required = vertex_steps_len as u32;
|
||||
|
||||
while state.vertex.inputs.len() < vertex_strides_len {
|
||||
// Initialize each `vertex.inputs[i].step` from
|
||||
// `pipeline.vertex_steps[i]`. Enlarge `vertex.inputs`
|
||||
// as necessary to accomodate all slots in the
|
||||
// pipeline. If `vertex.inputs` is longer, fill the
|
||||
// extra entries with default `VertexStep`s.
|
||||
while state.vertex.inputs.len() < vertex_steps_len {
|
||||
state.vertex.inputs.push(VertexBufferState::EMPTY);
|
||||
}
|
||||
|
||||
// Update vertex buffer limits
|
||||
for (vbs, &(stride, rate)) in
|
||||
state.vertex.inputs.iter_mut().zip(&pipeline.vertex_strides)
|
||||
{
|
||||
vbs.stride = stride;
|
||||
vbs.rate = rate;
|
||||
}
|
||||
for vbs in state.vertex.inputs.iter_mut().skip(vertex_strides_len) {
|
||||
vbs.stride = 0;
|
||||
vbs.rate = VertexStepMode::Vertex;
|
||||
// This is worse as a `zip`, but it's close.
|
||||
let mut steps = pipeline.vertex_steps.iter();
|
||||
for input in state.vertex.inputs.iter_mut() {
|
||||
input.step = steps.next().cloned().unwrap_or_default();
|
||||
}
|
||||
|
||||
// Update vertex buffer limits.
|
||||
state.vertex.update_limits();
|
||||
}
|
||||
RenderCommand::SetIndexBuffer {
|
||||
@ -1451,14 +1501,17 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
||||
depth_max,
|
||||
} => {
|
||||
let scope = PassErrorScope::SetViewport;
|
||||
if rect.w <= 0.0
|
||||
|| rect.h <= 0.0
|
||||
|| depth_min < 0.0
|
||||
|| depth_min > 1.0
|
||||
|| depth_max < 0.0
|
||||
|| depth_max > 1.0
|
||||
{
|
||||
return Err(RenderCommandError::InvalidViewport).map_pass_err(scope);
|
||||
if rect.w <= 0.0 || rect.h <= 0.0 {
|
||||
return Err(RenderCommandError::InvalidViewportDimension(
|
||||
rect.w, rect.h,
|
||||
))
|
||||
.map_pass_err(scope);
|
||||
}
|
||||
if !(0.0..=1.0).contains(&depth_min) || !(0.0..=1.0).contains(&depth_max) {
|
||||
return Err(RenderCommandError::InvalidViewportDepth(
|
||||
depth_min, depth_max,
|
||||
))
|
||||
.map_pass_err(scope);
|
||||
}
|
||||
let r = hal::Rect {
|
||||
x: rect.x,
|
||||
@ -1510,7 +1563,8 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
||||
|| rect.x + rect.w > info.extent.width
|
||||
|| rect.y + rect.h > info.extent.height
|
||||
{
|
||||
return Err(RenderCommandError::InvalidScissorRect).map_pass_err(scope);
|
||||
return Err(RenderCommandError::InvalidScissorRect(*rect, info.extent))
|
||||
.map_pass_err(scope);
|
||||
}
|
||||
let r = hal::Rect {
|
||||
x: rect.x,
|
||||
@ -1827,6 +1881,10 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
||||
} => {
|
||||
let scope = PassErrorScope::WriteTimestamp;
|
||||
|
||||
device
|
||||
.require_features(wgt::Features::WRITE_TIMESTAMP_INSIDE_PASSES)
|
||||
.map_pass_err(scope)?;
|
||||
|
||||
let query_set: &resource::QuerySet<A> = cmd_buf
|
||||
.trackers
|
||||
.query_sets
|
||||
@ -1886,10 +1944,14 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
||||
.map_err(RenderPassErrorInner::IncompatibleBundleTargets)
|
||||
.map_pass_err(scope)?;
|
||||
|
||||
if info.is_ds_read_only != bundle.is_ds_read_only {
|
||||
if (info.is_depth_read_only && !bundle.is_depth_read_only)
|
||||
|| (info.is_stencil_read_only && !bundle.is_stencil_read_only)
|
||||
{
|
||||
return Err(RenderPassErrorInner::IncompatibleBundleRods {
|
||||
pass: info.is_ds_read_only,
|
||||
bundle: bundle.is_ds_read_only,
|
||||
pass_depth: info.is_depth_read_only,
|
||||
pass_stencil: info.is_stencil_read_only,
|
||||
bundle_depth: bundle.is_depth_read_only,
|
||||
bundle_stencil: bundle.is_stencil_read_only,
|
||||
})
|
||||
.map_pass_err(scope);
|
||||
}
|
||||
|
9
third_party/rust/wgpu-core/src/conv.rs
vendored
9
third_party/rust/wgpu-core/src/conv.rs
vendored
@ -116,14 +116,7 @@ pub fn check_texture_dimension_size(
|
||||
use wgt::TextureDimension::*;
|
||||
|
||||
let (extent_limits, sample_limit) = match dimension {
|
||||
D1 => (
|
||||
[
|
||||
limits.max_texture_dimension_1d,
|
||||
1,
|
||||
limits.max_texture_array_layers,
|
||||
],
|
||||
1,
|
||||
),
|
||||
D1 => ([limits.max_texture_dimension_1d, 1, 1], 1),
|
||||
D2 => (
|
||||
[
|
||||
limits.max_texture_dimension_2d,
|
||||
|
@ -224,6 +224,8 @@ struct ActiveSubmission<A: hal::Api> {
|
||||
pub enum WaitIdleError {
|
||||
#[error(transparent)]
|
||||
Device(#[from] DeviceError),
|
||||
#[error("Tried to wait using a submission index from the wrong device. Submission index is from device {0:?}. Called poll on device {1:?}.")]
|
||||
WrongSubmissionIndex(id::QueueId, id::DeviceId),
|
||||
#[error("GPU got stuck :(")]
|
||||
StuckGpu,
|
||||
}
|
||||
@ -459,7 +461,7 @@ impl<A: hal::Api> LifetimeTracker<A> {
|
||||
impl<A: HalApi> LifetimeTracker<A> {
|
||||
/// Identify resources to free, according to `trackers` and `self.suspected_resources`.
|
||||
///
|
||||
/// Given `trackers`, the [`TrackerSet`] belonging to same [`Device`] as
|
||||
/// Given `trackers`, the [`Tracker`] belonging to same [`Device`] as
|
||||
/// `self`, and `hub`, the [`Hub`] to which that `Device` belongs:
|
||||
///
|
||||
/// Remove from `trackers` each resource mentioned in
|
||||
|
356
third_party/rust/wgpu-core/src/device/mod.rs
vendored
356
third_party/rust/wgpu-core/src/device/mod.rs
vendored
@ -52,15 +52,15 @@ pub enum HostMap {
|
||||
#[derive(Clone, Debug, Hash, PartialEq)]
|
||||
#[cfg_attr(feature = "serial-pass", derive(serde::Deserialize, serde::Serialize))]
|
||||
pub(crate) struct AttachmentData<T> {
|
||||
pub colors: ArrayVec<T, { hal::MAX_COLOR_TARGETS }>,
|
||||
pub resolves: ArrayVec<T, { hal::MAX_COLOR_TARGETS }>,
|
||||
pub colors: ArrayVec<Option<T>, { hal::MAX_COLOR_ATTACHMENTS }>,
|
||||
pub resolves: ArrayVec<T, { hal::MAX_COLOR_ATTACHMENTS }>,
|
||||
pub depth_stencil: Option<T>,
|
||||
}
|
||||
impl<T: PartialEq> Eq for AttachmentData<T> {}
|
||||
impl<T> AttachmentData<T> {
|
||||
pub(crate) fn map<U, F: Fn(&T) -> U>(&self, fun: F) -> AttachmentData<U> {
|
||||
AttachmentData {
|
||||
colors: self.colors.iter().map(&fun).collect(),
|
||||
colors: self.colors.iter().map(|c| c.as_ref().map(&fun)).collect(),
|
||||
resolves: self.resolves.iter().map(&fun).collect(),
|
||||
depth_stencil: self.depth_stencil.as_ref().map(&fun),
|
||||
}
|
||||
@ -78,8 +78,8 @@ pub(crate) struct RenderPassContext {
|
||||
pub enum RenderPassCompatibilityError {
|
||||
#[error("Incompatible color attachment: the renderpass expected {0:?} but was given {1:?}")]
|
||||
IncompatibleColorAttachment(
|
||||
ArrayVec<TextureFormat, { hal::MAX_COLOR_TARGETS }>,
|
||||
ArrayVec<TextureFormat, { hal::MAX_COLOR_TARGETS }>,
|
||||
ArrayVec<Option<TextureFormat>, { hal::MAX_COLOR_ATTACHMENTS }>,
|
||||
ArrayVec<Option<TextureFormat>, { hal::MAX_COLOR_ATTACHMENTS }>,
|
||||
),
|
||||
#[error(
|
||||
"Incompatible depth-stencil attachment: the renderpass expected {0:?} but was given {1:?}"
|
||||
@ -428,6 +428,9 @@ impl<A: HalApi> Device<A> {
|
||||
|
||||
/// Check this device for completed commands.
|
||||
///
|
||||
/// The `maintain` argument tells how the maintence function should behave, either
|
||||
/// blocking or just polling the current state of the gpu.
|
||||
///
|
||||
/// Return a pair `(closures, queue_empty)`, where:
|
||||
///
|
||||
/// - `closures` is a list of actions to take: mapping buffers, notifying the user
|
||||
@ -439,7 +442,7 @@ impl<A: HalApi> Device<A> {
|
||||
fn maintain<'this, 'token: 'this, G: GlobalIdentityHandlerFactory>(
|
||||
&'this self,
|
||||
hub: &Hub<A, G>,
|
||||
force_wait: bool,
|
||||
maintain: wgt::Maintain<queue::WrappedSubmissionIndex>,
|
||||
token: &mut Token<'token, Self>,
|
||||
) -> Result<(UserClosures, bool), WaitIdleError> {
|
||||
profiling::scope!("maintain", "Device");
|
||||
@ -463,14 +466,21 @@ impl<A: HalApi> Device<A> {
|
||||
);
|
||||
life_tracker.triage_mapped(hub, token);
|
||||
|
||||
let last_done_index = if force_wait {
|
||||
let current_index = self.active_submission_index;
|
||||
let last_done_index = if maintain.is_wait() {
|
||||
let index_to_wait_for = match maintain {
|
||||
wgt::Maintain::WaitForSubmissionIndex(submission_index) => {
|
||||
// We don't need to check to see if the queue id matches
|
||||
// as we already checked this from inside the poll call.
|
||||
submission_index.index
|
||||
}
|
||||
_ => self.active_submission_index,
|
||||
};
|
||||
unsafe {
|
||||
self.raw
|
||||
.wait(&self.fence, current_index, CLEANUP_WAIT_MS)
|
||||
.wait(&self.fence, index_to_wait_for, CLEANUP_WAIT_MS)
|
||||
.map_err(DeviceError::from)?
|
||||
};
|
||||
current_index
|
||||
index_to_wait_for
|
||||
} else {
|
||||
unsafe {
|
||||
self.raw
|
||||
@ -566,6 +576,14 @@ impl<A: HalApi> Device<A> {
|
||||
transient: bool,
|
||||
) -> Result<resource::Buffer<A>, resource::CreateBufferError> {
|
||||
debug_assert_eq!(self_id.backend(), A::VARIANT);
|
||||
|
||||
if desc.size > self.limits.max_buffer_size {
|
||||
return Err(resource::CreateBufferError::MaxBufferSize {
|
||||
requested: desc.size,
|
||||
maximum: self.limits.max_buffer_size,
|
||||
});
|
||||
}
|
||||
|
||||
let mut usage = conv::map_buffer_usage(desc.usage);
|
||||
|
||||
if desc.usage.is_empty() {
|
||||
@ -668,47 +686,10 @@ impl<A: HalApi> Device<A> {
|
||||
adapter: &crate::instance::Adapter<A>,
|
||||
desc: &resource::TextureDescriptor,
|
||||
) -> Result<resource::Texture<A>, resource::CreateTextureError> {
|
||||
let format_desc = desc.format.describe();
|
||||
|
||||
if desc.dimension != wgt::TextureDimension::D2 {
|
||||
// Depth textures can only be 2D
|
||||
if format_desc.sample_type == wgt::TextureSampleType::Depth {
|
||||
return Err(resource::CreateTextureError::InvalidDepthDimension(
|
||||
desc.dimension,
|
||||
desc.format,
|
||||
));
|
||||
}
|
||||
// Renderable textures can only be 2D
|
||||
if desc.usage.contains(wgt::TextureUsages::RENDER_ATTACHMENT) {
|
||||
return Err(resource::CreateTextureError::InvalidDimensionUsages(
|
||||
wgt::TextureUsages::RENDER_ATTACHMENT,
|
||||
desc.dimension,
|
||||
));
|
||||
}
|
||||
|
||||
// Compressed textures can only be 2D
|
||||
if format_desc.is_compressed() {
|
||||
return Err(resource::CreateTextureError::InvalidCompressedDimension(
|
||||
desc.dimension,
|
||||
desc.format,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
let format_features = self
|
||||
.describe_format_features(adapter, desc.format)
|
||||
.map_err(|error| resource::CreateTextureError::MissingFeatures(desc.format, error))?;
|
||||
use resource::{CreateTextureError, TextureDimensionError};
|
||||
|
||||
if desc.usage.is_empty() {
|
||||
return Err(resource::CreateTextureError::EmptyUsage);
|
||||
}
|
||||
|
||||
let missing_allowed_usages = desc.usage - format_features.allowed_usages;
|
||||
if !missing_allowed_usages.is_empty() {
|
||||
return Err(resource::CreateTextureError::InvalidFormatUsages(
|
||||
missing_allowed_usages,
|
||||
desc.format,
|
||||
));
|
||||
return Err(CreateTextureError::EmptyUsage);
|
||||
}
|
||||
|
||||
conv::check_texture_dimension_size(
|
||||
@ -718,15 +699,114 @@ impl<A: HalApi> Device<A> {
|
||||
&self.limits,
|
||||
)?;
|
||||
|
||||
let format_desc = desc.format.describe();
|
||||
|
||||
if desc.dimension != wgt::TextureDimension::D2 {
|
||||
// Depth textures can only be 2D
|
||||
if format_desc.sample_type == wgt::TextureSampleType::Depth {
|
||||
return Err(CreateTextureError::InvalidDepthDimension(
|
||||
desc.dimension,
|
||||
desc.format,
|
||||
));
|
||||
}
|
||||
// Renderable textures can only be 2D
|
||||
if desc.usage.contains(wgt::TextureUsages::RENDER_ATTACHMENT) {
|
||||
return Err(CreateTextureError::InvalidDimensionUsages(
|
||||
wgt::TextureUsages::RENDER_ATTACHMENT,
|
||||
desc.dimension,
|
||||
));
|
||||
}
|
||||
|
||||
// Compressed textures can only be 2D
|
||||
if format_desc.is_compressed() {
|
||||
return Err(CreateTextureError::InvalidCompressedDimension(
|
||||
desc.dimension,
|
||||
desc.format,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
if format_desc.is_compressed() {
|
||||
let block_width = format_desc.block_dimensions.0 as u32;
|
||||
let block_height = format_desc.block_dimensions.1 as u32;
|
||||
|
||||
if desc.size.width % block_width != 0 {
|
||||
return Err(CreateTextureError::InvalidDimension(
|
||||
TextureDimensionError::NotMultipleOfBlockWidth {
|
||||
width: desc.size.width,
|
||||
block_width,
|
||||
format: desc.format,
|
||||
},
|
||||
));
|
||||
}
|
||||
|
||||
if desc.size.height % block_height != 0 {
|
||||
return Err(CreateTextureError::InvalidDimension(
|
||||
TextureDimensionError::NotMultipleOfBlockHeight {
|
||||
height: desc.size.height,
|
||||
block_height,
|
||||
format: desc.format,
|
||||
},
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
if desc.sample_count > 1 {
|
||||
if desc.mip_level_count != 1 {
|
||||
return Err(CreateTextureError::InvalidMipLevelCount {
|
||||
requested: desc.mip_level_count,
|
||||
maximum: 1,
|
||||
});
|
||||
}
|
||||
|
||||
if desc.size.depth_or_array_layers != 1 {
|
||||
return Err(CreateTextureError::InvalidDimension(
|
||||
TextureDimensionError::MultisampledDepthOrArrayLayer(
|
||||
desc.size.depth_or_array_layers,
|
||||
),
|
||||
));
|
||||
}
|
||||
|
||||
if desc.usage.contains(wgt::TextureUsages::STORAGE_BINDING) {
|
||||
return Err(CreateTextureError::InvalidMultisampledStorageBinding);
|
||||
}
|
||||
|
||||
if !desc.usage.contains(wgt::TextureUsages::RENDER_ATTACHMENT) {
|
||||
return Err(CreateTextureError::MultisampledNotRenderAttachment);
|
||||
}
|
||||
|
||||
if !format_desc
|
||||
.guaranteed_format_features
|
||||
.flags
|
||||
.contains(wgt::TextureFormatFeatureFlags::MULTISAMPLE)
|
||||
{
|
||||
return Err(CreateTextureError::InvalidMultisampledFormat(desc.format));
|
||||
}
|
||||
}
|
||||
|
||||
let mips = desc.mip_level_count;
|
||||
let max_levels_allowed = desc.size.max_mips(desc.dimension).min(hal::MAX_MIP_LEVELS);
|
||||
if mips == 0 || mips > max_levels_allowed {
|
||||
return Err(resource::CreateTextureError::InvalidMipLevelCount {
|
||||
return Err(CreateTextureError::InvalidMipLevelCount {
|
||||
requested: mips,
|
||||
maximum: max_levels_allowed,
|
||||
});
|
||||
}
|
||||
|
||||
let format_features = self
|
||||
.describe_format_features(adapter, desc.format)
|
||||
.map_err(|error| CreateTextureError::MissingFeatures(desc.format, error))?;
|
||||
|
||||
let missing_allowed_usages = desc.usage - format_features.allowed_usages;
|
||||
if !missing_allowed_usages.is_empty() {
|
||||
return Err(CreateTextureError::InvalidFormatUsages(
|
||||
missing_allowed_usages,
|
||||
desc.format,
|
||||
));
|
||||
}
|
||||
|
||||
// TODO: validate missing TextureDescriptor::view_formats.
|
||||
|
||||
// Enforce having COPY_DST/DEPTH_STENCIL_WRIT/COLOR_TARGET otherwise we wouldn't be able to initialize the texture.
|
||||
let hal_usage = conv::map_texture_usage(desc.usage, desc.format.into())
|
||||
| if format_desc.sample_type == wgt::TextureSampleType::Depth {
|
||||
@ -1117,6 +1197,18 @@ impl<A: HalApi> Device<A> {
|
||||
}
|
||||
pipeline::ShaderModuleSource::Naga(module) => (module, String::new()),
|
||||
};
|
||||
for (_, var) in module.global_variables.iter() {
|
||||
match var.binding {
|
||||
Some(ref br) if br.group >= self.limits.max_bind_groups => {
|
||||
return Err(pipeline::CreateShaderModuleError::InvalidGroupIndex {
|
||||
bind: br.clone(),
|
||||
group: br.group,
|
||||
limit: self.limits.max_bind_groups,
|
||||
});
|
||||
}
|
||||
_ => continue,
|
||||
};
|
||||
}
|
||||
|
||||
use naga::valid::Capabilities as Caps;
|
||||
profiling::scope!("naga::validate");
|
||||
@ -1370,6 +1462,20 @@ impl<A: HalApi> Device<A> {
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
match access {
|
||||
wgt::StorageTextureAccess::ReadOnly
|
||||
| wgt::StorageTextureAccess::ReadWrite
|
||||
if !self.features.contains(
|
||||
wgt::Features::TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES,
|
||||
) =>
|
||||
{
|
||||
return Err(binding_model::CreateBindGroupLayoutError::Entry {
|
||||
binding: entry.binding,
|
||||
error: binding_model::BindGroupLayoutEntryError::StorageTextureReadWrite,
|
||||
});
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
(
|
||||
Some(
|
||||
wgt::Features::TEXTURE_BINDING_ARRAY
|
||||
@ -2359,9 +2465,11 @@ impl<A: HalApi> Device<A> {
|
||||
.map_or(&[][..], |fragment| &fragment.targets);
|
||||
let depth_stencil_state = desc.depth_stencil.as_ref();
|
||||
|
||||
if !color_targets.is_empty() && {
|
||||
let first = &color_targets[0];
|
||||
color_targets[1..]
|
||||
let cts: ArrayVec<_, { hal::MAX_COLOR_ATTACHMENTS }> =
|
||||
color_targets.iter().filter_map(|x| x.as_ref()).collect();
|
||||
if !cts.is_empty() && {
|
||||
let first = &cts[0];
|
||||
cts[1..]
|
||||
.iter()
|
||||
.any(|ct| ct.write_mask != first.write_mask || ct.blend != first.blend)
|
||||
} {
|
||||
@ -2372,13 +2480,14 @@ impl<A: HalApi> Device<A> {
|
||||
let mut io = validation::StageIo::default();
|
||||
let mut validated_stages = wgt::ShaderStages::empty();
|
||||
|
||||
let mut vertex_strides = Vec::with_capacity(desc.vertex.buffers.len());
|
||||
let mut vertex_steps = Vec::with_capacity(desc.vertex.buffers.len());
|
||||
let mut vertex_buffers = Vec::with_capacity(desc.vertex.buffers.len());
|
||||
let mut total_attributes = 0;
|
||||
for (i, vb_state) in desc.vertex.buffers.iter().enumerate() {
|
||||
vertex_strides
|
||||
.alloc()
|
||||
.init((vb_state.array_stride, vb_state.step_mode));
|
||||
vertex_steps.alloc().init(pipeline::VertexStep {
|
||||
stride: vb_state.array_stride,
|
||||
mode: vb_state.step_mode,
|
||||
});
|
||||
if vb_state.attributes.is_empty() {
|
||||
continue;
|
||||
}
|
||||
@ -2473,29 +2582,32 @@ impl<A: HalApi> Device<A> {
|
||||
}
|
||||
|
||||
for (i, cs) in color_targets.iter().enumerate() {
|
||||
let error = loop {
|
||||
let format_features = self.describe_format_features(adapter, cs.format)?;
|
||||
if !format_features
|
||||
.allowed_usages
|
||||
.contains(wgt::TextureUsages::RENDER_ATTACHMENT)
|
||||
{
|
||||
break Some(pipeline::ColorStateError::FormatNotRenderable(cs.format));
|
||||
}
|
||||
if cs.blend.is_some() && !format_features.flags.contains(Tfff::FILTERABLE) {
|
||||
break Some(pipeline::ColorStateError::FormatNotBlendable(cs.format));
|
||||
}
|
||||
if !hal::FormatAspects::from(cs.format).contains(hal::FormatAspects::COLOR) {
|
||||
break Some(pipeline::ColorStateError::FormatNotColor(cs.format));
|
||||
}
|
||||
if desc.multisample.count > 1 && !format_features.flags.contains(Tfff::MULTISAMPLE)
|
||||
{
|
||||
break Some(pipeline::ColorStateError::FormatNotMultisampled(cs.format));
|
||||
}
|
||||
if let Some(cs) = cs.as_ref() {
|
||||
let error = loop {
|
||||
let format_features = self.describe_format_features(adapter, cs.format)?;
|
||||
if !format_features
|
||||
.allowed_usages
|
||||
.contains(wgt::TextureUsages::RENDER_ATTACHMENT)
|
||||
{
|
||||
break Some(pipeline::ColorStateError::FormatNotRenderable(cs.format));
|
||||
}
|
||||
if cs.blend.is_some() && !format_features.flags.contains(Tfff::FILTERABLE) {
|
||||
break Some(pipeline::ColorStateError::FormatNotBlendable(cs.format));
|
||||
}
|
||||
if !hal::FormatAspects::from(cs.format).contains(hal::FormatAspects::COLOR) {
|
||||
break Some(pipeline::ColorStateError::FormatNotColor(cs.format));
|
||||
}
|
||||
if desc.multisample.count > 1
|
||||
&& !format_features.flags.contains(Tfff::MULTISAMPLE)
|
||||
{
|
||||
break Some(pipeline::ColorStateError::FormatNotMultisampled(cs.format));
|
||||
}
|
||||
|
||||
break None;
|
||||
};
|
||||
if let Some(e) = error {
|
||||
return Err(pipeline::CreateRenderPipelineError::ColorState(i as u8, e));
|
||||
break None;
|
||||
};
|
||||
if let Some(e) = error {
|
||||
return Err(pipeline::CreateRenderPipelineError::ColorState(i as u8, e));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -2647,13 +2759,13 @@ impl<A: HalApi> Device<A> {
|
||||
};
|
||||
|
||||
if validated_stages.contains(wgt::ShaderStages::FRAGMENT) {
|
||||
for (i, state) in color_targets.iter().enumerate() {
|
||||
match io.get(&(i as wgt::ShaderLocation)) {
|
||||
Some(output) => {
|
||||
for (i, output) in io.iter() {
|
||||
match color_targets.get(*i as usize) {
|
||||
Some(&Some(ref state)) => {
|
||||
validation::check_texture_format(state.format, &output.ty).map_err(
|
||||
|pipeline| {
|
||||
pipeline::CreateRenderPipelineError::ColorState(
|
||||
i as u8,
|
||||
*i as u8,
|
||||
pipeline::ColorStateError::IncompatibleFormat {
|
||||
pipeline,
|
||||
shader: output.ty,
|
||||
@ -2662,11 +2774,14 @@ impl<A: HalApi> Device<A> {
|
||||
},
|
||||
)?;
|
||||
}
|
||||
None if state.write_mask.is_empty() => {}
|
||||
None => {
|
||||
log::warn!("Missing fragment output[{}], expected {:?}", i, state,);
|
||||
Some(&None) => {
|
||||
return Err(
|
||||
pipeline::CreateRenderPipelineError::InvalidFragmentOutputLocation(*i),
|
||||
);
|
||||
}
|
||||
_ => {
|
||||
return Err(pipeline::CreateRenderPipelineError::ColorState(
|
||||
i as u8,
|
||||
*i as u8,
|
||||
pipeline::ColorStateError::Missing,
|
||||
));
|
||||
}
|
||||
@ -2700,6 +2815,14 @@ impl<A: HalApi> Device<A> {
|
||||
self.require_features(wgt::Features::MULTIVIEW)?;
|
||||
}
|
||||
|
||||
for size in shader_binding_sizes.values() {
|
||||
if size.get() % 16 != 0 {
|
||||
self.require_downlevel_flags(
|
||||
wgt::DownlevelFlags::BUFFER_BINDINGS_NOT_16_BYTE_ALIGNED,
|
||||
)?;
|
||||
}
|
||||
}
|
||||
|
||||
let late_sized_buffer_groups =
|
||||
Device::make_late_sized_buffer_groups(&shader_binding_sizes, layout, &*bgl_guard);
|
||||
|
||||
@ -2735,7 +2858,10 @@ impl<A: HalApi> Device<A> {
|
||||
|
||||
let pass_context = RenderPassContext {
|
||||
attachments: AttachmentData {
|
||||
colors: color_targets.iter().map(|state| state.format).collect(),
|
||||
colors: color_targets
|
||||
.iter()
|
||||
.map(|state| state.as_ref().map(|s| s.format))
|
||||
.collect(),
|
||||
resolves: ArrayVec::new(),
|
||||
depth_stencil: depth_stencil_state.as_ref().map(|state| state.format),
|
||||
},
|
||||
@ -2744,7 +2870,7 @@ impl<A: HalApi> Device<A> {
|
||||
};
|
||||
|
||||
let mut flags = pipeline::PipelineFlags::empty();
|
||||
for state in color_targets.iter() {
|
||||
for state in color_targets.iter().filter_map(|s| s.as_ref()) {
|
||||
if let Some(ref bs) = state.blend {
|
||||
if bs.color.uses_constant() | bs.alpha.uses_constant() {
|
||||
flags |= pipeline::PipelineFlags::BLEND_CONSTANT;
|
||||
@ -2755,8 +2881,11 @@ impl<A: HalApi> Device<A> {
|
||||
if ds.stencil.is_enabled() && ds.stencil.needs_ref_value() {
|
||||
flags |= pipeline::PipelineFlags::STENCIL_REFERENCE;
|
||||
}
|
||||
if !ds.is_read_only() {
|
||||
flags |= pipeline::PipelineFlags::WRITES_DEPTH_STENCIL;
|
||||
if !ds.is_depth_read_only() {
|
||||
flags |= pipeline::PipelineFlags::WRITES_DEPTH;
|
||||
}
|
||||
if !ds.is_stencil_read_only() {
|
||||
flags |= pipeline::PipelineFlags::WRITES_STENCIL;
|
||||
}
|
||||
}
|
||||
|
||||
@ -2773,7 +2902,7 @@ impl<A: HalApi> Device<A> {
|
||||
pass_context,
|
||||
flags,
|
||||
strip_index_format: desc.primitive.strip_index_format,
|
||||
vertex_strides,
|
||||
vertex_steps,
|
||||
late_sized_buffer_groups,
|
||||
life_guard: LifeGuard::new(desc.label.borrow_or_default()),
|
||||
};
|
||||
@ -3003,12 +3132,12 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
||||
.map_err(|_| instance::IsSurfaceSupportedError::InvalidSurface)?;
|
||||
Ok(adapter.is_surface_supported(surface))
|
||||
}
|
||||
pub fn surface_get_preferred_format<A: HalApi>(
|
||||
pub fn surface_get_supported_formats<A: HalApi>(
|
||||
&self,
|
||||
surface_id: id::SurfaceId,
|
||||
adapter_id: id::AdapterId,
|
||||
) -> Result<TextureFormat, instance::GetSurfacePreferredFormatError> {
|
||||
profiling::scope!("surface_get_preferred_format");
|
||||
) -> Result<Vec<TextureFormat>, instance::GetSurfacePreferredFormatError> {
|
||||
profiling::scope!("Surface::get_supported_formats");
|
||||
let hub = A::hub(self);
|
||||
let mut token = Token::root();
|
||||
|
||||
@ -3021,7 +3150,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
||||
.get(surface_id)
|
||||
.map_err(|_| instance::GetSurfacePreferredFormatError::InvalidSurface)?;
|
||||
|
||||
surface.get_preferred_format(adapter)
|
||||
surface.get_supported_formats(adapter)
|
||||
}
|
||||
|
||||
pub fn device_features<A: HalApi>(
|
||||
@ -3221,6 +3350,18 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
||||
fid.assign_error(label.borrow_or_default(), &mut token);
|
||||
}
|
||||
|
||||
/// Assign `id_in` an error with the given `label`.
|
||||
///
|
||||
/// See `create_buffer_error` for more context and explaination.
|
||||
pub fn create_texture_error<A: HalApi>(&self, id_in: Input<G, id::TextureId>, label: Label) {
|
||||
let hub = A::hub(self);
|
||||
let mut token = Token::root();
|
||||
let fid = hub.textures.prepare(id_in);
|
||||
|
||||
let (_, mut token) = hub.devices.read(&mut token);
|
||||
fid.assign_error(label.borrow_or_default(), &mut token);
|
||||
}
|
||||
|
||||
#[cfg(feature = "replay")]
|
||||
pub fn device_wait_for_buffer<A: HalApi>(
|
||||
&self,
|
||||
@ -4368,7 +4509,8 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
||||
desc: trace::new_render_bundle_encoder_descriptor(
|
||||
desc.label.clone(),
|
||||
&bundle_encoder.context,
|
||||
bundle_encoder.is_ds_read_only,
|
||||
bundle_encoder.is_depth_read_only,
|
||||
bundle_encoder.is_stencil_read_only,
|
||||
),
|
||||
base: bundle_encoder.to_base_pass(),
|
||||
});
|
||||
@ -4957,16 +5099,25 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
||||
pub fn device_poll<A: HalApi>(
|
||||
&self,
|
||||
device_id: id::DeviceId,
|
||||
force_wait: bool,
|
||||
maintain: wgt::Maintain<queue::WrappedSubmissionIndex>,
|
||||
) -> Result<bool, WaitIdleError> {
|
||||
let (closures, queue_empty) = {
|
||||
if let wgt::Maintain::WaitForSubmissionIndex(submission_index) = maintain {
|
||||
if submission_index.queue_id != device_id {
|
||||
return Err(WaitIdleError::WrongSubmissionIndex(
|
||||
submission_index.queue_id,
|
||||
device_id,
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
let hub = A::hub(self);
|
||||
let mut token = Token::root();
|
||||
let (device_guard, mut token) = hub.devices.read(&mut token);
|
||||
device_guard
|
||||
.get(device_id)
|
||||
.map_err(|_| DeviceError::Invalid)?
|
||||
.maintain(hub, force_wait, &mut token)?
|
||||
.maintain(hub, maintain, &mut token)?
|
||||
};
|
||||
|
||||
closures.fire();
|
||||
@ -4994,7 +5145,12 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
||||
let (device_guard, mut token) = hub.devices.read(&mut token);
|
||||
|
||||
for (id, device) in device_guard.iter(A::VARIANT) {
|
||||
let (cbs, queue_empty) = device.maintain(hub, force_wait, &mut token)?;
|
||||
let maintain = if force_wait {
|
||||
wgt::Maintain::Wait
|
||||
} else {
|
||||
wgt::Maintain::Poll
|
||||
};
|
||||
let (cbs, queue_empty) = device.maintain(hub, maintain, &mut token)?;
|
||||
all_queue_empty = all_queue_empty && queue_empty;
|
||||
|
||||
// If the device's own `RefCount` clone is the only one left, and
|
||||
|
27
third_party/rust/wgpu-core/src/device/queue.rs
vendored
27
third_party/rust/wgpu-core/src/device/queue.rs
vendored
@ -12,7 +12,7 @@ use crate::{
|
||||
id,
|
||||
init_tracker::{has_copy_partial_init_tracker_coverage, TextureInitRange},
|
||||
resource::{BufferAccessError, BufferMapState, TextureInner},
|
||||
track, FastHashSet,
|
||||
track, FastHashSet, SubmissionIndex,
|
||||
};
|
||||
|
||||
use hal::{CommandEncoder as _, Device as _, Queue as _};
|
||||
@ -30,8 +30,8 @@ const WRITE_COMMAND_BUFFERS_PER_POOL: usize = 64;
|
||||
|
||||
#[repr(C)]
|
||||
pub struct SubmittedWorkDoneClosureC {
|
||||
callback: unsafe extern "C" fn(user_data: *mut u8),
|
||||
user_data: *mut u8,
|
||||
pub callback: unsafe extern "C" fn(user_data: *mut u8),
|
||||
pub user_data: *mut u8,
|
||||
}
|
||||
|
||||
unsafe impl Send for SubmittedWorkDoneClosureC {}
|
||||
@ -79,6 +79,13 @@ impl SubmittedWorkDoneClosure {
|
||||
}
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub struct WrappedSubmissionIndex {
|
||||
pub queue_id: id::QueueId,
|
||||
pub index: SubmissionIndex,
|
||||
}
|
||||
|
||||
struct StagingData<A: hal::Api> {
|
||||
buffer: A::Buffer,
|
||||
}
|
||||
@ -620,10 +627,10 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
||||
&self,
|
||||
queue_id: id::QueueId,
|
||||
command_buffer_ids: &[id::CommandBufferId],
|
||||
) -> Result<(), QueueSubmitError> {
|
||||
) -> Result<WrappedSubmissionIndex, QueueSubmitError> {
|
||||
profiling::scope!("submit", "Queue");
|
||||
|
||||
let callbacks = {
|
||||
let (submit_index, callbacks) = {
|
||||
let hub = A::hub(self);
|
||||
let mut token = Token::root();
|
||||
|
||||
@ -958,23 +965,27 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
||||
|
||||
// This will schedule destruction of all resources that are no longer needed
|
||||
// by the user but used in the command stream, among other things.
|
||||
let (closures, _) = match device.maintain(hub, false, &mut token) {
|
||||
let (closures, _) = match device.maintain(hub, wgt::Maintain::Wait, &mut token) {
|
||||
Ok(closures) => closures,
|
||||
Err(WaitIdleError::Device(err)) => return Err(QueueSubmitError::Queue(err)),
|
||||
Err(WaitIdleError::StuckGpu) => return Err(QueueSubmitError::StuckGpu),
|
||||
Err(WaitIdleError::WrongSubmissionIndex(..)) => unreachable!(),
|
||||
};
|
||||
|
||||
device.pending_writes.temp_resources = pending_write_resources;
|
||||
device.temp_suspected.clear();
|
||||
device.lock_life(&mut token).post_submit();
|
||||
|
||||
closures
|
||||
(submit_index, closures)
|
||||
};
|
||||
|
||||
// the closures should execute with nothing locked!
|
||||
callbacks.fire();
|
||||
|
||||
Ok(())
|
||||
Ok(WrappedSubmissionIndex {
|
||||
queue_id,
|
||||
index: submit_index,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn queue_get_timestamp_period<A: HalApi>(
|
||||
|
10
third_party/rust/wgpu-core/src/device/trace.rs
vendored
10
third_party/rust/wgpu-core/src/device/trace.rs
vendored
@ -13,17 +13,17 @@ pub const FILE_NAME: &str = "trace.ron";
|
||||
pub(crate) fn new_render_bundle_encoder_descriptor<'a>(
|
||||
label: crate::Label<'a>,
|
||||
context: &'a super::RenderPassContext,
|
||||
is_ds_read_only: bool,
|
||||
depth_read_only: bool,
|
||||
stencil_read_only: bool,
|
||||
) -> crate::command::RenderBundleEncoderDescriptor<'a> {
|
||||
crate::command::RenderBundleEncoderDescriptor {
|
||||
label,
|
||||
color_formats: Cow::Borrowed(&context.attachments.colors),
|
||||
depth_stencil: context.attachments.depth_stencil.map(|format| {
|
||||
let aspects = hal::FormatAspects::from(format);
|
||||
wgt::RenderBundleDepthStencil {
|
||||
format,
|
||||
depth_read_only: is_ds_read_only && aspects.contains(hal::FormatAspects::DEPTH),
|
||||
stencil_read_only: is_ds_read_only && aspects.contains(hal::FormatAspects::STENCIL),
|
||||
depth_read_only,
|
||||
stencil_read_only,
|
||||
}
|
||||
}),
|
||||
sample_count: context.sample_count,
|
||||
@ -176,7 +176,7 @@ pub enum Command {
|
||||
},
|
||||
RunRenderPass {
|
||||
base: crate::command::BasePass<crate::command::RenderCommand>,
|
||||
target_colors: Vec<crate::command::RenderPassColorAttachment>,
|
||||
target_colors: Vec<Option<crate::command::RenderPassColorAttachment>>,
|
||||
target_depth_stencil: Option<crate::command::RenderPassDepthStencilAttachment>,
|
||||
},
|
||||
}
|
||||
|
12
third_party/rust/wgpu-core/src/hub.rs
vendored
12
third_party/rust/wgpu-core/src/hub.rs
vendored
@ -930,6 +930,18 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
||||
hal_instance_callback(hal_instance)
|
||||
}
|
||||
|
||||
/// # Safety
|
||||
///
|
||||
/// - The raw handles obtained from the Instance must not be manually destroyed
|
||||
pub unsafe fn from_instance(factory: G, instance: Instance) -> Self {
|
||||
profiling::scope!("new", "Global");
|
||||
Self {
|
||||
instance,
|
||||
surfaces: Registry::without_backend(&factory, "Surface"),
|
||||
hubs: Hubs::new(&factory),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn clear_backend<A: HalApi>(&self, _dummy: ()) {
|
||||
let mut surface_guard = self.surfaces.data.write();
|
||||
let hub = A::hub(self);
|
||||
|
29
third_party/rust/wgpu-core/src/id.rs
vendored
29
third_party/rust/wgpu-core/src/id.rs
vendored
@ -22,6 +22,35 @@ const BACKEND_SHIFT: usize = INDEX_BITS * 2 - BACKEND_BITS;
|
||||
pub const EPOCH_MASK: u32 = (1 << (EPOCH_BITS)) - 1;
|
||||
type Dummy = hal::api::Empty;
|
||||
|
||||
/// An identifier for a wgpu object.
|
||||
///
|
||||
/// An `Id<T>` value identifies a value stored in a [`Global`]'s [`Hub`]'s [`Storage`].
|
||||
/// `Storage` implements [`Index`] and [`IndexMut`], accepting `Id` values as indices.
|
||||
///
|
||||
/// ## Note on `Id` typing
|
||||
///
|
||||
/// You might assume that an `Id<T>` can only be used to retrieve a resource of
|
||||
/// type `T`, but that is not quite the case. The id types in `wgpu-core`'s
|
||||
/// public API ([`TextureId`], for example) can refer to resources belonging to
|
||||
/// any backend, but the corresponding resource types ([`Texture<A>`], for
|
||||
/// example) are always parameterized by a specific backend `A`.
|
||||
///
|
||||
/// So the `T` in `Id<T>` is usually a resource type like `Texture<Empty>`,
|
||||
/// where [`Empty`] is the `wgpu_hal` dummy back end. These empty types are
|
||||
/// never actually used, beyond just making sure you access each `Storage` with
|
||||
/// the right kind of identifier. The members of [`Hub<A>`] pair up each
|
||||
/// `X<Empty>` type with the resource type `X<A>`, for some specific backend
|
||||
/// `A`.
|
||||
///
|
||||
/// [`Global`]: crate::hub::Global
|
||||
/// [`Hub`]: crate::hub::Hub
|
||||
/// [`Hub<A>`]: crate::hub::Hub
|
||||
/// [`Storage`]: crate::hub::Storage
|
||||
/// [`Texture<A>`]: crate::resource::Texture
|
||||
/// [`Index`]: std::ops::Index
|
||||
/// [`IndexMut`]: std::ops::IndexMut
|
||||
/// [`Registry`]: crate::hub::Registry
|
||||
/// [`Empty`]: hal::api::Empty
|
||||
#[repr(transparent)]
|
||||
#[cfg_attr(feature = "trace", derive(serde::Serialize), serde(into = "SerialId"))]
|
||||
#[cfg_attr(
|
||||
|
78
third_party/rust/wgpu-core/src/instance.rs
vendored
78
third_party/rust/wgpu-core/src/instance.rs
vendored
@ -23,8 +23,8 @@ pub struct HalSurface<A: hal::Api> {
|
||||
#[error("Limit '{name}' value {requested} is better than allowed {allowed}")]
|
||||
pub struct FailedLimit {
|
||||
name: &'static str,
|
||||
requested: u32,
|
||||
allowed: u32,
|
||||
requested: u64,
|
||||
allowed: u64,
|
||||
}
|
||||
|
||||
fn check_limits(requested: &wgt::Limits, allowed: &wgt::Limits) -> Vec<FailedLimit> {
|
||||
@ -152,23 +152,12 @@ impl crate::hub::Resource for Surface {
|
||||
}
|
||||
|
||||
impl Surface {
|
||||
pub fn get_preferred_format<A: HalApi>(
|
||||
pub fn get_supported_formats<A: HalApi>(
|
||||
&self,
|
||||
adapter: &Adapter<A>,
|
||||
) -> Result<wgt::TextureFormat, GetSurfacePreferredFormatError> {
|
||||
// Check the four formats mentioned in the WebGPU spec.
|
||||
// Also, prefer sRGB over linear as it is better in
|
||||
// representing perceived colors.
|
||||
let preferred_formats = [
|
||||
wgt::TextureFormat::Bgra8UnormSrgb,
|
||||
wgt::TextureFormat::Rgba8UnormSrgb,
|
||||
wgt::TextureFormat::Bgra8Unorm,
|
||||
wgt::TextureFormat::Rgba8Unorm,
|
||||
wgt::TextureFormat::Rgba16Float,
|
||||
];
|
||||
|
||||
) -> Result<Vec<wgt::TextureFormat>, GetSurfacePreferredFormatError> {
|
||||
let suf = A::get_surface(self);
|
||||
let caps = unsafe {
|
||||
let mut caps = unsafe {
|
||||
profiling::scope!("surface_capabilities");
|
||||
adapter
|
||||
.raw
|
||||
@ -177,11 +166,10 @@ impl Surface {
|
||||
.ok_or(GetSurfacePreferredFormatError::UnsupportedQueueFamily)?
|
||||
};
|
||||
|
||||
preferred_formats
|
||||
.iter()
|
||||
.cloned()
|
||||
.find(|preferred| caps.formats.contains(preferred))
|
||||
.ok_or(GetSurfacePreferredFormatError::NotFound)
|
||||
// TODO: maybe remove once we support texture view changing srgb-ness
|
||||
caps.formats.sort_by_key(|f| !f.describe().srgb);
|
||||
|
||||
Ok(caps.formats)
|
||||
}
|
||||
}
|
||||
|
||||
@ -354,8 +342,6 @@ pub enum IsSurfaceSupportedError {
|
||||
|
||||
#[derive(Clone, Debug, Error)]
|
||||
pub enum GetSurfacePreferredFormatError {
|
||||
#[error("no suitable format found")]
|
||||
NotFound,
|
||||
#[error("invalid adapter")]
|
||||
InvalidAdapter,
|
||||
#[error("invalid surface")]
|
||||
@ -493,6 +479,52 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
||||
id.0
|
||||
}
|
||||
|
||||
#[cfg(all(target_arch = "wasm32", not(target_os = "emscripten")))]
|
||||
pub fn create_surface_webgl_canvas(
|
||||
&self,
|
||||
canvas: &web_sys::HtmlCanvasElement,
|
||||
id_in: Input<G, SurfaceId>,
|
||||
) -> SurfaceId {
|
||||
profiling::scope!("create_surface_webgl_canvas", "Instance");
|
||||
|
||||
let surface = Surface {
|
||||
presentation: None,
|
||||
gl: self.instance.gl.as_ref().map(|inst| HalSurface {
|
||||
raw: {
|
||||
inst.create_surface_from_canvas(canvas)
|
||||
.expect("Create surface from canvas")
|
||||
},
|
||||
}),
|
||||
};
|
||||
|
||||
let mut token = Token::root();
|
||||
let id = self.surfaces.prepare(id_in).assign(surface, &mut token);
|
||||
id.0
|
||||
}
|
||||
|
||||
#[cfg(all(target_arch = "wasm32", not(target_os = "emscripten")))]
|
||||
pub fn create_surface_webgl_offscreen_canvas(
|
||||
&self,
|
||||
canvas: &web_sys::OffscreenCanvas,
|
||||
id_in: Input<G, SurfaceId>,
|
||||
) -> SurfaceId {
|
||||
profiling::scope!("create_surface_webgl_offscreen_canvas", "Instance");
|
||||
|
||||
let surface = Surface {
|
||||
presentation: None,
|
||||
gl: self.instance.gl.as_ref().map(|inst| HalSurface {
|
||||
raw: {
|
||||
inst.create_surface_from_offscreen_canvas(canvas)
|
||||
.expect("Create surface from offscreen canvas")
|
||||
},
|
||||
}),
|
||||
};
|
||||
|
||||
let mut token = Token::root();
|
||||
let id = self.surfaces.prepare(id_in).assign(surface, &mut token);
|
||||
id.0
|
||||
}
|
||||
|
||||
#[cfg(dx12)]
|
||||
/// # Safety
|
||||
///
|
||||
|
54
third_party/rust/wgpu-core/src/lib.rs
vendored
54
third_party/rust/wgpu-core/src/lib.rs
vendored
@ -48,7 +48,7 @@ pub mod resource;
|
||||
mod track;
|
||||
mod validation;
|
||||
|
||||
pub use hal::{api, MAX_BIND_GROUPS, MAX_COLOR_TARGETS, MAX_VERTEX_BUFFERS};
|
||||
pub use hal::{api, MAX_BIND_GROUPS, MAX_COLOR_ATTACHMENTS, MAX_VERTEX_BUFFERS};
|
||||
|
||||
use atomic::{AtomicUsize, Ordering};
|
||||
|
||||
@ -238,36 +238,50 @@ If you are running this program on native and not in a browser and wish to work
|
||||
Adapter::downlevel_properties or Device::downlevel_properties to get a listing of the features the current \
|
||||
platform supports.";
|
||||
|
||||
/// Call a `Global` method, dispatching dynamically to the appropriate back end.
|
||||
/// Dispatch on an [`Id`]'s backend to a backend-generic method.
|
||||
///
|
||||
/// Uses of this macro have the form:
|
||||
///
|
||||
/// ```ignore
|
||||
///
|
||||
/// gfx_select!(id => global.method(args...))
|
||||
/// gfx_select!(id => value.method(args...))
|
||||
///
|
||||
/// ```
|
||||
///
|
||||
/// where `id` is some [`id::Id`] resource id, `global` is a [`hub::Global`],
|
||||
/// and `method` is any method on [`Global`] that takes a single generic
|
||||
/// parameter that implements [`hal::Api`] (for example,
|
||||
/// [`Global::device_create_buffer`]).
|
||||
/// This expands to an expression that calls `value.method::<A>(args...)` for
|
||||
/// the backend `A` selected by `id`. The expansion matches on `id.backend()`,
|
||||
/// with an arm for each backend type in [`wgpu_types::Backend`] which calls the
|
||||
/// specialization of `method` for the given backend. This allows resource
|
||||
/// identifiers to select backends dynamically, even though many `wgpu_core`
|
||||
/// methods are compiled and optimized for a specific back end.
|
||||
///
|
||||
/// The `wgpu-core` crate can support multiple back ends simultaneously (Vulkan,
|
||||
/// Metal, etc.), depending on features and availability. Each [`Id`]'s value
|
||||
/// indicates which back end its resource belongs to. This macro does a switch
|
||||
/// on `id`'s back end, and calls the `Global` method specialized for that back
|
||||
/// end.
|
||||
/// This macro is typically used to call methods on [`wgpu_core::hub::Global`],
|
||||
/// many of which take a single `hal::Api` type parameter. For example, to
|
||||
/// create a new buffer on the device indicated by `device_id`, one would say:
|
||||
///
|
||||
/// Internally to `wgpu-core`, most types take the back end (some type that
|
||||
/// implements `hal::Api`) as a generic parameter, so their methods are compiled
|
||||
/// with full knowledge of which back end they're working with. This macro
|
||||
/// serves as the boundary between dynamic `Id` values provided by `wgpu-core`'s
|
||||
/// users and the crate's mostly-monomorphized implementation, selecting the
|
||||
/// `hal::Api` implementation appropriate to the `Id` value's back end.
|
||||
/// ```ignore
|
||||
/// gfx_select!(device_id => global.device_create_buffer(device_id, ...))
|
||||
/// ```
|
||||
///
|
||||
/// [`Global`]: hub::Global
|
||||
/// [`Global::device_create_buffer`]: hub::Global::device_create_buffer
|
||||
/// where the `device_create_buffer` method is defined like this:
|
||||
///
|
||||
/// ```ignore
|
||||
/// impl<...> Global<...> {
|
||||
/// pub fn device_create_buffer<A: hal::Api>(&self, ...) -> ...
|
||||
/// { ... }
|
||||
/// }
|
||||
/// ```
|
||||
///
|
||||
/// That `gfx_select!` call uses `device_id`'s backend to select the right
|
||||
/// backend type `A` for a call to `Global::device_create_buffer<A>`.
|
||||
///
|
||||
/// However, there's nothing about this macro that is specific to `hub::Global`.
|
||||
/// For example, Firefox's embedding of `wgpu_core` defines its own types with
|
||||
/// methods that take `hal::Api` type parameters. Firefox uses `gfx_select!` to
|
||||
/// dynamically dispatch to the right specialization based on the resource's id.
|
||||
///
|
||||
/// [`wgpu_types::Backend`]: wgt::Backend
|
||||
/// [`wgpu_core::hub::Global`]: crate::hub::Global
|
||||
/// [`Id`]: id::Id
|
||||
#[macro_export]
|
||||
macro_rules! gfx_select {
|
||||
|
46
third_party/rust/wgpu-core/src/pipeline.rs
vendored
46
third_party/rust/wgpu-core/src/pipeline.rs
vendored
@ -124,6 +124,24 @@ pub enum CreateShaderModuleError {
|
||||
Validation(#[from] ShaderError<naga::WithSpan<naga::valid::ValidationError>>),
|
||||
#[error(transparent)]
|
||||
MissingFeatures(#[from] MissingFeatures),
|
||||
#[error(
|
||||
"shader global {bind:?} uses a group index {group} that exceeds the max_bind_groups limit of {limit}."
|
||||
)]
|
||||
InvalidGroupIndex {
|
||||
bind: naga::ResourceBinding,
|
||||
group: u32,
|
||||
limit: u32,
|
||||
},
|
||||
}
|
||||
|
||||
impl CreateShaderModuleError {
|
||||
pub fn location(&self, source: &str) -> Option<naga::SourceLocation> {
|
||||
match *self {
|
||||
CreateShaderModuleError::Parsing(ref err) => err.inner.location(source),
|
||||
CreateShaderModuleError::Validation(ref err) => err.inner.location(source),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Describes a programmable pipeline stage.
|
||||
@ -231,7 +249,7 @@ pub struct FragmentState<'a> {
|
||||
/// The compiled fragment stage and its entry point.
|
||||
pub stage: ProgrammableStageDescriptor<'a>,
|
||||
/// The effect of draw calls on the color aspect of the output target.
|
||||
pub targets: Cow<'a, [wgt::ColorTargetState]>,
|
||||
pub targets: Cow<'a, [Option<wgt::ColorTargetState>]>,
|
||||
}
|
||||
|
||||
/// Describes a render (graphics) pipeline.
|
||||
@ -299,6 +317,8 @@ pub enum CreateRenderPipelineError {
|
||||
Device(#[from] DeviceError),
|
||||
#[error("pipeline layout is invalid")]
|
||||
InvalidLayout,
|
||||
#[error("fragment output @location({0}) is invalid")]
|
||||
InvalidFragmentOutputLocation(u32),
|
||||
#[error("unable to derive an implicit layout")]
|
||||
Implicit(#[from] ImplicitLayoutError),
|
||||
#[error("color state [{0}] is invalid")]
|
||||
@ -352,7 +372,27 @@ bitflags::bitflags! {
|
||||
pub struct PipelineFlags: u32 {
|
||||
const BLEND_CONSTANT = 1 << 0;
|
||||
const STENCIL_REFERENCE = 1 << 1;
|
||||
const WRITES_DEPTH_STENCIL = 1 << 2;
|
||||
const WRITES_DEPTH = 1 << 2;
|
||||
const WRITES_STENCIL = 1 << 3;
|
||||
}
|
||||
}
|
||||
|
||||
/// How a render pipeline will retrieve attributes from a particular vertex buffer.
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct VertexStep {
|
||||
/// The byte stride in the buffer between one attribute value and the next.
|
||||
pub stride: wgt::BufferAddress,
|
||||
|
||||
/// Whether the buffer is indexed by vertex number or instance number.
|
||||
pub mode: wgt::VertexStepMode,
|
||||
}
|
||||
|
||||
impl Default for VertexStep {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
stride: 0,
|
||||
mode: wgt::VertexStepMode::Vertex,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -364,7 +404,7 @@ pub struct RenderPipeline<A: hal::Api> {
|
||||
pub(crate) pass_context: RenderPassContext,
|
||||
pub(crate) flags: PipelineFlags,
|
||||
pub(crate) strip_index_format: Option<wgt::IndexFormat>,
|
||||
pub(crate) vertex_strides: Vec<(wgt::BufferAddress, wgt::VertexStepMode)>,
|
||||
pub(crate) vertex_steps: Vec<VertexStep>,
|
||||
pub(crate) late_sized_buffer_groups: ArrayVec<LateSizedBufferGroup, { hal::MAX_BIND_GROUPS }>,
|
||||
pub(crate) life_guard: LifeGuard,
|
||||
}
|
||||
|
7
third_party/rust/wgpu-core/src/present.rs
vendored
7
third_party/rust/wgpu-core/src/present.rs
vendored
@ -123,7 +123,12 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
|
||||
let _ = device;
|
||||
|
||||
let suf = A::get_surface_mut(surface);
|
||||
let (texture_id, status) = match unsafe { suf.raw.acquire_texture(FRAME_TIMEOUT_MS) } {
|
||||
let (texture_id, status) = match unsafe {
|
||||
suf.raw
|
||||
.acquire_texture(Some(std::time::Duration::from_millis(
|
||||
FRAME_TIMEOUT_MS as u64,
|
||||
)))
|
||||
} {
|
||||
Ok(Some(ast)) => {
|
||||
let clear_view_desc = hal::TextureViewDescriptor {
|
||||
label: Some("(wgpu internal) clear surface texture view"),
|
||||
|
30
third_party/rust/wgpu-core/src/resource.rs
vendored
30
third_party/rust/wgpu-core/src/resource.rs
vendored
@ -47,8 +47,8 @@ unsafe impl<A: hal::Api> Sync for BufferMapState<A> {}
|
||||
|
||||
#[repr(C)]
|
||||
pub struct BufferMapCallbackC {
|
||||
callback: unsafe extern "C" fn(status: BufferMapAsyncStatus, user_data: *mut u8),
|
||||
user_data: *mut u8,
|
||||
pub callback: unsafe extern "C" fn(status: BufferMapAsyncStatus, user_data: *mut u8),
|
||||
pub user_data: *mut u8,
|
||||
}
|
||||
|
||||
unsafe impl Send for BufferMapCallbackC {}
|
||||
@ -174,6 +174,8 @@ pub enum CreateBufferError {
|
||||
EmptyUsage,
|
||||
#[error("`MAP` usage can only be combined with the opposite `COPY`, requested {0:?}")]
|
||||
UsageMismatch(wgt::BufferUsages),
|
||||
#[error("Buffer size {requested} is greater than the maximum buffer size ({maximum})")]
|
||||
MaxBufferSize { requested: u64, maximum: u64 },
|
||||
}
|
||||
|
||||
impl<A: hal::Api> Resource for Buffer<A> {
|
||||
@ -330,14 +332,28 @@ pub enum TextureErrorDimension {
|
||||
pub enum TextureDimensionError {
|
||||
#[error("Dimension {0:?} is zero")]
|
||||
Zero(TextureErrorDimension),
|
||||
#[error("Dimension {0:?} value {given} exceeds the limit of {limit}")]
|
||||
#[error("Dimension {dim:?} value {given} exceeds the limit of {limit}")]
|
||||
LimitExceeded {
|
||||
dim: TextureErrorDimension,
|
||||
given: u32,
|
||||
limit: u32,
|
||||
},
|
||||
#[error("sample count {0} is invalid")]
|
||||
#[error("Sample count {0} is invalid")]
|
||||
InvalidSampleCount(u32),
|
||||
#[error("Width {width} is not a multiple of {format:?}'s block width ({block_width})")]
|
||||
NotMultipleOfBlockWidth {
|
||||
width: u32,
|
||||
block_width: u32,
|
||||
format: wgt::TextureFormat,
|
||||
},
|
||||
#[error("Height {height} is not a multiple of {format:?}'s block height ({block_height})")]
|
||||
NotMultipleOfBlockHeight {
|
||||
height: u32,
|
||||
block_height: u32,
|
||||
format: wgt::TextureFormat,
|
||||
},
|
||||
#[error("Multisampled texture depth or array layers must be 1, got {0}")]
|
||||
MultisampledDepthOrArrayLayer(u32),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Error)]
|
||||
@ -360,6 +376,12 @@ pub enum CreateTextureError {
|
||||
InvalidFormatUsages(wgt::TextureUsages, wgt::TextureFormat),
|
||||
#[error("Texture usages {0:?} are not allowed on a texture of dimensions {1:?}")]
|
||||
InvalidDimensionUsages(wgt::TextureUsages, wgt::TextureDimension),
|
||||
#[error("Texture usage STORAGE_BINDING is not allowed for multisampled textures")]
|
||||
InvalidMultisampledStorageBinding,
|
||||
#[error("Format {0:?} does not support multisampling")]
|
||||
InvalidMultisampledFormat(wgt::TextureFormat),
|
||||
#[error("Multisampled textures must have RENDER_ATTACHMENT usage")]
|
||||
MultisampledNotRenderAttachment,
|
||||
#[error("Texture format {0:?} can't be used due to missing features.")]
|
||||
MissingFeatures(wgt::TextureFormat, #[source] MissingFeatures),
|
||||
}
|
||||
|
15
third_party/rust/wgpu-core/src/track/buffer.rs
vendored
15
third_party/rust/wgpu-core/src/track/buffer.rs
vendored
@ -467,8 +467,8 @@ impl<A: hub::HalApi> BufferTracker<A> {
|
||||
/// This is a really funky method used by Compute Passes to generate
|
||||
/// barriers after a call to dispatch without needing to iterate
|
||||
/// over all elements in the usage scope. We use each the
|
||||
/// bind group as a source of which IDs to look at. The bind groups
|
||||
/// must have first been added to the usage scope.
|
||||
/// a given iterator of ids as a source of which IDs to look at.
|
||||
/// All the IDs must have first been added to the usage scope.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
@ -477,15 +477,15 @@ impl<A: hub::HalApi> BufferTracker<A> {
|
||||
pub unsafe fn set_and_remove_from_usage_scope_sparse(
|
||||
&mut self,
|
||||
scope: &mut BufferUsageScope<A>,
|
||||
bind_group_state: &BufferBindGroupState<A>,
|
||||
id_source: impl IntoIterator<Item = Valid<BufferId>>,
|
||||
) {
|
||||
let incoming_size = scope.state.len();
|
||||
if incoming_size > self.start.len() {
|
||||
self.set_size(incoming_size);
|
||||
}
|
||||
|
||||
for &(id, ref ref_count, _) in bind_group_state.buffers.iter() {
|
||||
let (index32, epoch, _) = id.0.unzip();
|
||||
for id in id_source {
|
||||
let (index32, _, _) = id.0.unzip();
|
||||
let index = index32 as usize;
|
||||
|
||||
scope.debug_assert_in_bounds(index);
|
||||
@ -504,9 +504,8 @@ impl<A: hub::HalApi> BufferTracker<A> {
|
||||
state: &scope.state,
|
||||
},
|
||||
None,
|
||||
ResourceMetadataProvider::Direct {
|
||||
epoch,
|
||||
ref_count: Cow::Borrowed(ref_count),
|
||||
ResourceMetadataProvider::Indirect {
|
||||
metadata: &scope.metadata,
|
||||
},
|
||||
&mut self.temp,
|
||||
);
|
||||
|
9
third_party/rust/wgpu-core/src/track/mod.rs
vendored
9
third_party/rust/wgpu-core/src/track/mod.rs
vendored
@ -690,9 +690,10 @@ impl<A: hub::HalApi> Tracker<A> {
|
||||
/// the state given for those resources in the UsageScope. It also
|
||||
/// removes all touched resources from the usage scope.
|
||||
///
|
||||
/// If a transition is needed to get the resources into the needed state,
|
||||
/// those transitions are stored within the tracker. A subsequent
|
||||
/// call to [`Self::drain`] is needed to get those transitions.
|
||||
/// If a transition is needed to get the resources into the needed
|
||||
/// state, those transitions are stored within the tracker. A
|
||||
/// subsequent call to [`BufferTracker::drain`] or
|
||||
/// [`TextureTracker::drain`] is needed to get those transitions.
|
||||
///
|
||||
/// This is a really funky method used by Compute Passes to generate
|
||||
/// barriers after a call to dispatch without needing to iterate
|
||||
@ -714,7 +715,7 @@ impl<A: hub::HalApi> Tracker<A> {
|
||||
bind_group: &BindGroupStates<A>,
|
||||
) {
|
||||
self.buffers
|
||||
.set_and_remove_from_usage_scope_sparse(&mut scope.buffers, &bind_group.buffers);
|
||||
.set_and_remove_from_usage_scope_sparse(&mut scope.buffers, bind_group.buffers.used());
|
||||
self.textures.set_and_remove_from_usage_scope_sparse(
|
||||
textures,
|
||||
&mut scope.textures,
|
||||
|
@ -1 +1 @@
|
||||
{"files":{"Cargo.toml":"61752b031b63d9ce967085f9a43eb3dbbad3b472bd264612a88faf3e7d6fcd57","LICENSE.APACHE":"a6cba85bc92e0cff7a450b1d873c0eaa2e9fc96bf472df0247a26bec77bf3ff9","LICENSE.MIT":"c7fea58d1cfe49634cd92e54fc10a9d871f4b275321a4cd8c09e449122caaeb4","README.md":"78377f5876fafd77963eff7e3c2ba3a7e3ad5cf9201b09ed5612e49c2288eb18","examples/halmark/main.rs":"fefa4f8d16f1a40156e0c0ce7aee06569b222a7a6284b69a000adeebb34a915d","examples/halmark/shader.wgsl":"59e3628abe34c66708bf0106658e791ef24357df3cae72194d34ff07b40e8007","examples/raw-gles.em.html":"70fbe68394a1a4522192de1dcfaf7d399f60d7bdf5de70b708f9bb0417427546","examples/raw-gles.rs":"d6db84b269b934409ef85fa31914b3f4bc7e7012e40269aad3eff5454eae2a09","src/auxil/dxgi/conv.rs":"310a23866e652257e3dca55c85c78420118e6dea4e69ff907db4a52bda9ac1c5","src/auxil/dxgi/exception.rs":"f8d69d3d475e03c4d96d22778e5a6f322afd98fcfafb1414cd4a76239fa97a37","src/auxil/dxgi/factory.rs":"82451fcfcc1f73a570ae9e708c94efa9c125d269dfb7396de97da5b32f8a4090","src/auxil/dxgi/mod.rs":"63db737b48378d4843e2f7904f104790688029ff614bc80387cd9efe444f1e52","src/auxil/dxgi/result.rs":"20c8eb03d738062dff198feca6327addb9882ed0462be842c789eadf7dca0573","src/auxil/mod.rs":"f899555124ad6d44f5326ef935f4911702539fd933ec2ab07c6009badb3ea22c","src/auxil/renderdoc.rs":"3a4da908ebd6230177ca55c541c8278639e83d78badb4595a941aea30dd7f80f","src/dx11/adapter.rs":"bf123464ef748d021f2e0c40d27b3f6bdd50222c6f91cce6d25686a912eef093","src/dx11/command.rs":"cdad8dcdb800acba56c931f1726ddada652af18db0f066465af643f82a034492","src/dx11/device.rs":"76ac52095c639482adc2058509cd3acafd49cebc0694fcd64f8d9f53abc823de","src/dx11/instance.rs":"3bbf2730956472cb8023bd8fbd2d53e49f93c5e4ce3d14664112a293a165d191","src/dx11/library.rs":"0da08a780eefa7ff50f2e0998117202f26e5dd3d3a433c58b585801cff9863d2","src/dx11/mod.rs":"e4f7c6100e1bec479b41f3e3af96e01d53e6597c1c3a8fcde6f14cc9eb8537f8","src/dx12/adapter.rs":"3d830a70684c568a0b3f226beecc8e0dd311c3efd2b1be2caa629f688e98511e","src/dx12/command.rs":"e48636f686f4ff9efc1758f4e54522aeda284d27439c87c6a669a55352294d58","src/dx12/conv.rs":"e1bc82d9f0c019bb67aa7ee8d59e4677c047e56fee4ce3154ebc50e5388850cd","src/dx12/descriptor.rs":"7145d3dc6be13fae4cf6bb8bf34a1ea1749ad87e5f429b84f3cbbea7bf63c148","src/dx12/device.rs":"1dd830070de6e0a755164f96408d50e5c8a1bbfee539a1183a57c8f93c79e669","src/dx12/instance.rs":"ccc36443cb1df8ab8ed8366cf8599ec3d75fb5fefa5f9bb0f0f0b5e6fc1c5102","src/dx12/mod.rs":"e88f7396dca4aba859a6e28d3f9de64a57a0df85acd53cecd6ada3d96386062c","src/dx12/view.rs":"b7a5cb8933f30517a97b4bd767e10b4c09f686dbf493d53b9b265d2d0b16f1a6","src/empty.rs":"6bf65e405f63eff49b25d079af644b352b95a9c7edcf3a57be2e96a50307b66b","src/gles/adapter.rs":"47403c6cf736659b6c035873346e0aa1760b8b4b5763e64b9783e1358e599ba0","src/gles/command.rs":"31c85f3841131dc34553f7a66339396650ceb19763fa6c194c10fb4a5a3fc07e","src/gles/conv.rs":"1462ce906a4fe83139cc8375e385f8ce5a15d70588b81083ae8d5d9104f4457e","src/gles/device.rs":"66c30c4010f410bf3b8a03ee9d8e14753832fa2b6e17b518481281f06e3d7cd9","src/gles/egl.rs":"16516ef1ad62a976996a1b2123fd89ce6835a8468a2915841efd558516bb8b4f","src/gles/mod.rs":"75612e8ddd91735ba7b1bb7ecb58210b7b8469bde9671e437206c010600d16a2","src/gles/queue.rs":"b6dd8404ff53f1f9a8c9de87d4b78bd42468c146560d26fb585801d813919dab","src/gles/shaders/clear.frag":"aac702eed9ece5482db5ba6783a678b119a5e7802b1ecf93f4975dee8acab0b3","src/gles/shaders/clear.vert":"8f636168e1da2cac48091c466a543c3b09fb4a0dd8c60c1c9bf34cc890766740","src/gles/shaders/present.frag":"dd9a43c339a2fa4ccf7f6a1854c6f400cabf271a7d5e9230768e9f39d47f3ff5","src/gles/shaders/present.vert":"6e85d489403d80b81cc94790730bb53b309dfc5eeede8f1ea3412a660f31d357","src/gles/web.rs":"083500c0b36d079a82754895d06b993ea8ed4393690b226c85f07cbec373a730","src/lib.rs":"dbd24a5fa263412c16cf821e6ff51ebda07608776accbd8b0bfb792435740619","src/metal/adapter.rs":"78f4a9eff186ab919e7c8900c08cd0b3325e15bf1656d5dcdae30a96a9d76f87","src/metal/command.rs":"b06983d7e11cdde526b7c9f5f0b86f1ea8faef02b7666367cb231211a8301570","src/metal/conv.rs":"2349ec6331a7a471c06615be249dc22b808742aca222e6d8861662d848b0c094","src/metal/device.rs":"dd823c8e12ba3ed69ef7cdcb543e8d995d0056d1f838516b0901068c83d8ffe2","src/metal/mod.rs":"c4f3959732f5f506fa881aa5812205a6452d6a946d661d7f81d1c7785359a10c","src/metal/surface.rs":"82836cadc751d94fb016bd590cdfec5649cbfae2f44d14599ed074dfb0a004dc","src/vulkan/adapter.rs":"90c4f57483589a09d9840c3f93efb8da66bc9eb5be975899877aa0192f86e4bd","src/vulkan/command.rs":"60d1867acd0e46c34dabecea708cd776a1f435721b6673a506b5bb8aee87ff80","src/vulkan/conv.rs":"b480f9d1cde0df92d6f9a07e8a42b86aaeb251f9b0692038286f4994caf45fec","src/vulkan/device.rs":"9b264c74f581345be889f1ed61ad6f7ab22e12e04183eb954dbfed1681c32d0c","src/vulkan/instance.rs":"c078d529f6955a662a3adc7739ffdb8a01b83dbef8dd1e2c3810d232b82cbb18","src/vulkan/mod.rs":"1ba41f2ea7650dc0757c1444ef62c95a8aa0f6671d98c73b4ea80eb4ea60f289"},"package":null}
|
||||
{"files":{"Cargo.toml":"187fffaaf3f370c2fac8a1060093b5c11c680711ed067b5d5a607178f93e4b30","LICENSE.APACHE":"a6cba85bc92e0cff7a450b1d873c0eaa2e9fc96bf472df0247a26bec77bf3ff9","LICENSE.MIT":"c7fea58d1cfe49634cd92e54fc10a9d871f4b275321a4cd8c09e449122caaeb4","README.md":"78377f5876fafd77963eff7e3c2ba3a7e3ad5cf9201b09ed5612e49c2288eb18","examples/halmark/main.rs":"dc6e304e13882ba49f7cf81ed62982cb2dc1d239410e703d074a7a40db0ba362","examples/halmark/shader.wgsl":"59e3628abe34c66708bf0106658e791ef24357df3cae72194d34ff07b40e8007","examples/raw-gles.em.html":"70fbe68394a1a4522192de1dcfaf7d399f60d7bdf5de70b708f9bb0417427546","examples/raw-gles.rs":"4fcf856aac125c7de14665fb3032ff457782ba03446e96f77845e5d5623ec23e","src/auxil/dxgi/conv.rs":"310a23866e652257e3dca55c85c78420118e6dea4e69ff907db4a52bda9ac1c5","src/auxil/dxgi/exception.rs":"f8d69d3d475e03c4d96d22778e5a6f322afd98fcfafb1414cd4a76239fa97a37","src/auxil/dxgi/factory.rs":"82451fcfcc1f73a570ae9e708c94efa9c125d269dfb7396de97da5b32f8a4090","src/auxil/dxgi/mod.rs":"63db737b48378d4843e2f7904f104790688029ff614bc80387cd9efe444f1e52","src/auxil/dxgi/result.rs":"20c8eb03d738062dff198feca6327addb9882ed0462be842c789eadf7dca0573","src/auxil/mod.rs":"f899555124ad6d44f5326ef935f4911702539fd933ec2ab07c6009badb3ea22c","src/auxil/renderdoc.rs":"3a4da908ebd6230177ca55c541c8278639e83d78badb4595a941aea30dd7f80f","src/dx11/adapter.rs":"fe0c5f92e70520a9097e109ada0eeb135eb01eaa20918069bbfec56fa241bb8b","src/dx11/command.rs":"cdad8dcdb800acba56c931f1726ddada652af18db0f066465af643f82a034492","src/dx11/device.rs":"76ac52095c639482adc2058509cd3acafd49cebc0694fcd64f8d9f53abc823de","src/dx11/instance.rs":"3bbf2730956472cb8023bd8fbd2d53e49f93c5e4ce3d14664112a293a165d191","src/dx11/library.rs":"0da08a780eefa7ff50f2e0998117202f26e5dd3d3a433c58b585801cff9863d2","src/dx11/mod.rs":"41aead56ca3e00fd5e11d8f67ea3284f305247ac298089a0216dc7396b49bf57","src/dx12/adapter.rs":"cfbb6eb8b98d861457f43795e20e8c6badd12148b31722ff126ae0c065b1855e","src/dx12/command.rs":"7612cd93a97edce571c2215b3bfbbfe545daa13eaf8d83a0cedd7ca785f2e069","src/dx12/conv.rs":"996ebf03b9826ba568a4e00c088eba972c1fce706671b1d8007a8945154c7ca7","src/dx12/descriptor.rs":"7145d3dc6be13fae4cf6bb8bf34a1ea1749ad87e5f429b84f3cbbea7bf63c148","src/dx12/device.rs":"ea715d3b31c697678e610d1696ceb36fc7a46df6647cce2dd1b7dd00e4639298","src/dx12/instance.rs":"ccc36443cb1df8ab8ed8366cf8599ec3d75fb5fefa5f9bb0f0f0b5e6fc1c5102","src/dx12/mod.rs":"3c2c1d6c2c0774a841b81b56748f7b9ecf97e24a80ec6554703903350bb027d7","src/dx12/view.rs":"b7a5cb8933f30517a97b4bd767e10b4c09f686dbf493d53b9b265d2d0b16f1a6","src/empty.rs":"389ea75882d0974c26649bb028aacdade28f4e0ea418a55970118e2de949e6fc","src/gles/adapter.rs":"0dab3dfa4c9277ba40ed8224f31aac8f3255c56f2dad39680dec9bf2d901f796","src/gles/command.rs":"306301fcf2ca75fb6a638b2c69dc0597aa50b2ebb16eaca6ec7005b4cfd84a30","src/gles/conv.rs":"1462ce906a4fe83139cc8375e385f8ce5a15d70588b81083ae8d5d9104f4457e","src/gles/device.rs":"54555c751a99c5327b21561a5ef1d2c3b4aee34db24b926d4393c2ec5cb8d8ad","src/gles/egl.rs":"e4263e63112a53001cc8cb8527ac2cc2e8004f8502272aaf5212a6e2c12de31b","src/gles/mod.rs":"6a90b7e84c699fed4b0ef2ab1a97e35337095eba36275116191678c3232601f2","src/gles/queue.rs":"3aee4a426a9ef6b62b1085780b42ea920c9a2a889894d5bccb968c98ed990879","src/gles/shaders/clear.frag":"aac702eed9ece5482db5ba6783a678b119a5e7802b1ecf93f4975dee8acab0b3","src/gles/shaders/clear.vert":"8f636168e1da2cac48091c466a543c3b09fb4a0dd8c60c1c9bf34cc890766740","src/gles/shaders/present.frag":"dd9a43c339a2fa4ccf7f6a1854c6f400cabf271a7d5e9230768e9f39d47f3ff5","src/gles/shaders/present.vert":"6e85d489403d80b81cc94790730bb53b309dfc5eeede8f1ea3412a660f31d357","src/gles/web.rs":"24722575d4bb818dcc22b75f58af27f163a8b4d8284242e9f8ae5a27280086c7","src/lib.rs":"491c9aa88310a6dd7bb3fe6524edd7fbd88ad7e7733d42ae728ef3d74c23e62d","src/metal/adapter.rs":"bac7ba96fbea9aca823777442e72dc3fd407db7d890e2982b68926d179478770","src/metal/command.rs":"bae3efabc3bff841ebe1e584e25a8e9172ddaffdf3f3d363ba08f2aad43a68a8","src/metal/conv.rs":"2349ec6331a7a471c06615be249dc22b808742aca222e6d8861662d848b0c094","src/metal/device.rs":"a3bfbd9fd1db6eb465bc5d4732e441383c19deb19d617c74a451c8cca93ba509","src/metal/mod.rs":"c4f3959732f5f506fa881aa5812205a6452d6a946d661d7f81d1c7785359a10c","src/metal/surface.rs":"9ec61f4162b85b8a653e426bae037a36f66d62a300c62eea75129dc34f370a59","src/vulkan/adapter.rs":"617a9a5cba8139e1e4784ec2fe7833f3d04c593828aee3c960b57317a6722b90","src/vulkan/command.rs":"ddc38c2732ba34eb065d0785f923fb226cf7f95c39f5a9c8d7fcfa1c90cab38b","src/vulkan/conv.rs":"b480f9d1cde0df92d6f9a07e8a42b86aaeb251f9b0692038286f4994caf45fec","src/vulkan/device.rs":"2323b201cc594a3dd0ee1f552a655389bfd8bd3fcb6b763cfba179d255669bfe","src/vulkan/instance.rs":"27a7bed9d0a7304e4f1da57ff008457ef7904a03a952a33a70d32d8edf61de8b","src/vulkan/mod.rs":"93eb2f416669f810cd748e003a1d90cd9942f2aee365c89d01efd0a003d4e5c6"},"package":null}
|
7
third_party/rust/wgpu-hal/Cargo.toml
vendored
7
third_party/rust/wgpu-hal/Cargo.toml
vendored
@ -90,16 +90,19 @@ wasm-bindgen = { version = "0.2" }
|
||||
web-sys = { version = "0.3", features = ["Window", "HtmlCanvasElement", "WebGl2RenderingContext"] }
|
||||
js-sys = { version = "0.3" }
|
||||
|
||||
[target.'cfg(target_os = "android")'.dependencies]
|
||||
android_system_properties = "0.1.1"
|
||||
|
||||
[dependencies.naga]
|
||||
git = "https://github.com/gfx-rs/naga"
|
||||
rev = "571302e"
|
||||
rev = "27d38aae"
|
||||
#version = "0.8"
|
||||
|
||||
# DEV dependencies
|
||||
|
||||
[dev-dependencies.naga]
|
||||
git = "https://github.com/gfx-rs/naga"
|
||||
rev = "571302e"
|
||||
rev = "27d38aae"
|
||||
#version = "0.8"
|
||||
features = ["wgsl-in"]
|
||||
|
||||
|
@ -238,11 +238,11 @@ impl<A: hal::Api> Example<A> {
|
||||
},
|
||||
depth_stencil: None,
|
||||
multisample: wgt::MultisampleState::default(),
|
||||
color_targets: &[wgt::ColorTargetState {
|
||||
color_targets: &[Some(wgt::ColorTargetState {
|
||||
format: surface_config.format,
|
||||
blend: Some(wgt::BlendState::ALPHA_BLENDING),
|
||||
write_mask: wgt::ColorWrites::default(),
|
||||
}],
|
||||
})],
|
||||
multiview: None,
|
||||
};
|
||||
let pipeline = unsafe { device.create_render_pipeline(&pipeline_desc).unwrap() };
|
||||
@ -614,7 +614,7 @@ impl<A: hal::Api> Example<A> {
|
||||
|
||||
let ctx = &mut self.contexts[self.context_index];
|
||||
|
||||
let surface_tex = unsafe { self.surface.acquire_texture(!0).unwrap().unwrap().texture };
|
||||
let surface_tex = unsafe { self.surface.acquire_texture(None).unwrap().unwrap().texture };
|
||||
|
||||
let target_barrier0 = hal::TextureBarrier {
|
||||
texture: surface_tex.borrow(),
|
||||
@ -646,7 +646,7 @@ impl<A: hal::Api> Example<A> {
|
||||
depth_or_array_layers: 1,
|
||||
},
|
||||
sample_count: 1,
|
||||
color_attachments: &[hal::ColorAttachment {
|
||||
color_attachments: &[Some(hal::ColorAttachment {
|
||||
target: hal::Attachment {
|
||||
view: &surface_tex_view,
|
||||
usage: hal::TextureUses::COLOR_TARGET,
|
||||
@ -659,7 +659,7 @@ impl<A: hal::Api> Example<A> {
|
||||
b: 0.3,
|
||||
a: 1.0,
|
||||
},
|
||||
}],
|
||||
})],
|
||||
depth_stencil_attachment: None,
|
||||
multiview: None,
|
||||
};
|
||||
|
@ -163,7 +163,7 @@ fn fill_screen(exposed: &hal::ExposedAdapter<hal::api::Gles>, width: u32, height
|
||||
depth_or_array_layers: 1,
|
||||
},
|
||||
sample_count: 1,
|
||||
color_attachments: &[hal::ColorAttachment {
|
||||
color_attachments: &[Some(hal::ColorAttachment {
|
||||
target: hal::Attachment {
|
||||
view: &view,
|
||||
usage: hal::TextureUses::COLOR_TARGET,
|
||||
@ -171,7 +171,7 @@ fn fill_screen(exposed: &hal::ExposedAdapter<hal::api::Gles>, width: u32, height
|
||||
resolve_target: None,
|
||||
ops: hal::AttachmentOps::STORE,
|
||||
clear_value: wgt::Color::BLUE,
|
||||
}],
|
||||
})],
|
||||
depth_stencil_attachment: None,
|
||||
multiview: None,
|
||||
};
|
||||
|
@ -130,6 +130,7 @@ impl super::Adapter {
|
||||
|
||||
if feature_level >= FL11_0 {
|
||||
downlevel |= wgt::DownlevelFlags::INDIRECT_EXECUTION;
|
||||
downlevel |= wgt::DownlevelFlags::WEBGPU_TEXTURE_FORMAT_SUPPORT;
|
||||
features |= wgt::Features::TEXTURE_COMPRESSION_BC;
|
||||
}
|
||||
|
||||
@ -219,6 +220,8 @@ impl super::Adapter {
|
||||
max_compute_workgroup_size_y: max_workgroup_size_xy,
|
||||
max_compute_workgroup_size_z: max_workgroup_size_z,
|
||||
max_compute_workgroups_per_dimension,
|
||||
// D3D11_BUFFER_DESC represents the buffer size as a 32 bit int.
|
||||
max_buffer_size: u32::MAX as u64,
|
||||
};
|
||||
|
||||
//
|
||||
|
2
third_party/rust/wgpu-hal/src/dx11/mod.rs
vendored
2
third_party/rust/wgpu-hal/src/dx11/mod.rs
vendored
@ -124,7 +124,7 @@ impl crate::Surface<Api> for Surface {
|
||||
|
||||
unsafe fn acquire_texture(
|
||||
&mut self,
|
||||
timeout_ms: u32,
|
||||
_timeout: Option<std::time::Duration>,
|
||||
) -> Result<Option<crate::AcquiredSurfaceTexture<Api>>, crate::SurfaceError> {
|
||||
todo!()
|
||||
}
|
||||
|
@ -41,6 +41,10 @@ impl super::Adapter {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn raw_adapter(&self) -> &native::DxgiAdapter {
|
||||
&self.raw
|
||||
}
|
||||
|
||||
#[allow(trivial_casts)]
|
||||
pub(super) fn expose(
|
||||
adapter: native::DxgiAdapter,
|
||||
@ -198,6 +202,7 @@ impl super::Adapter {
|
||||
| wgt::Features::VERTEX_WRITABLE_STORAGE
|
||||
| wgt::Features::TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES
|
||||
| wgt::Features::TIMESTAMP_QUERY
|
||||
| wgt::Features::WRITE_TIMESTAMP_INSIDE_PASSES
|
||||
| wgt::Features::TEXTURE_COMPRESSION_BC
|
||||
| wgt::Features::CLEAR_TEXTURE
|
||||
| wgt::Features::TEXTURE_FORMAT_16BIT_NORM;
|
||||
@ -278,6 +283,7 @@ impl super::Adapter {
|
||||
max_compute_workgroup_size_z: d3d12::D3D12_CS_THREAD_GROUP_MAX_Z,
|
||||
max_compute_workgroups_per_dimension:
|
||||
d3d12::D3D12_CS_DISPATCH_MAX_THREAD_GROUPS_PER_DIMENSION,
|
||||
max_buffer_size: u64::MAX,
|
||||
},
|
||||
alignments: crate::Alignments {
|
||||
buffer_copy_offset: wgt::BufferSize::new(
|
||||
|
43
third_party/rust/wgpu-hal/src/dx12/command.rs
vendored
43
third_party/rust/wgpu-hal/src/dx12/command.rs
vendored
@ -579,11 +579,15 @@ impl crate::CommandEncoder<super::Api> for super::CommandEncoder {
|
||||
|
||||
unsafe fn begin_render_pass(&mut self, desc: &crate::RenderPassDescriptor<super::Api>) {
|
||||
self.begin_pass(super::PassKind::Render, desc.label);
|
||||
|
||||
let mut color_views = [native::CpuDescriptor { ptr: 0 }; crate::MAX_COLOR_TARGETS];
|
||||
let mut color_views = [native::CpuDescriptor { ptr: 0 }; crate::MAX_COLOR_ATTACHMENTS];
|
||||
for (rtv, cat) in color_views.iter_mut().zip(desc.color_attachments.iter()) {
|
||||
*rtv = cat.target.view.handle_rtv.unwrap().raw;
|
||||
if let Some(cat) = cat.as_ref() {
|
||||
*rtv = cat.target.view.handle_rtv.unwrap().raw;
|
||||
} else {
|
||||
*rtv = self.null_rtv_handle.raw;
|
||||
}
|
||||
}
|
||||
|
||||
let ds_view = match desc.depth_stencil_attachment {
|
||||
None => ptr::null(),
|
||||
Some(ref ds) => {
|
||||
@ -605,23 +609,26 @@ impl crate::CommandEncoder<super::Api> for super::CommandEncoder {
|
||||
|
||||
self.pass.resolves.clear();
|
||||
for (rtv, cat) in color_views.iter().zip(desc.color_attachments.iter()) {
|
||||
if !cat.ops.contains(crate::AttachmentOps::LOAD) {
|
||||
let value = [
|
||||
cat.clear_value.r as f32,
|
||||
cat.clear_value.g as f32,
|
||||
cat.clear_value.b as f32,
|
||||
cat.clear_value.a as f32,
|
||||
];
|
||||
list.clear_render_target_view(*rtv, value, &[]);
|
||||
}
|
||||
if let Some(ref target) = cat.resolve_target {
|
||||
self.pass.resolves.push(super::PassResolve {
|
||||
src: cat.target.view.target_base,
|
||||
dst: target.view.target_base,
|
||||
format: target.view.raw_format,
|
||||
});
|
||||
if let Some(cat) = cat.as_ref() {
|
||||
if !cat.ops.contains(crate::AttachmentOps::LOAD) {
|
||||
let value = [
|
||||
cat.clear_value.r as f32,
|
||||
cat.clear_value.g as f32,
|
||||
cat.clear_value.b as f32,
|
||||
cat.clear_value.a as f32,
|
||||
];
|
||||
list.clear_render_target_view(*rtv, value, &[]);
|
||||
}
|
||||
if let Some(ref target) = cat.resolve_target {
|
||||
self.pass.resolves.push(super::PassResolve {
|
||||
src: cat.target.view.target_base,
|
||||
dst: target.view.target_base,
|
||||
format: target.view.raw_format,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(ref ds) = desc.depth_stencil_attachment {
|
||||
let mut flags = native::ClearFlags::empty();
|
||||
let aspects = ds.target.view.format_aspects;
|
||||
|
26
third_party/rust/wgpu-hal/src/dx12/conv.rs
vendored
26
third_party/rust/wgpu-hal/src/dx12/conv.rs
vendored
@ -267,7 +267,7 @@ fn map_blend_component(
|
||||
}
|
||||
|
||||
pub fn map_render_targets(
|
||||
color_targets: &[wgt::ColorTargetState],
|
||||
color_targets: &[Option<wgt::ColorTargetState>],
|
||||
) -> [d3d12::D3D12_RENDER_TARGET_BLEND_DESC; d3d12::D3D12_SIMULTANEOUS_RENDER_TARGET_COUNT as usize]
|
||||
{
|
||||
let dummy_target = d3d12::D3D12_RENDER_TARGET_BLEND_DESC {
|
||||
@ -285,17 +285,19 @@ pub fn map_render_targets(
|
||||
let mut raw_targets = [dummy_target; d3d12::D3D12_SIMULTANEOUS_RENDER_TARGET_COUNT as usize];
|
||||
|
||||
for (raw, ct) in raw_targets.iter_mut().zip(color_targets.iter()) {
|
||||
raw.RenderTargetWriteMask = ct.write_mask.bits() as u8;
|
||||
if let Some(ref blend) = ct.blend {
|
||||
let (color_op, color_src, color_dst) = map_blend_component(&blend.color, false);
|
||||
let (alpha_op, alpha_src, alpha_dst) = map_blend_component(&blend.alpha, true);
|
||||
raw.BlendEnable = 1;
|
||||
raw.BlendOp = color_op;
|
||||
raw.SrcBlend = color_src;
|
||||
raw.DestBlend = color_dst;
|
||||
raw.BlendOpAlpha = alpha_op;
|
||||
raw.SrcBlendAlpha = alpha_src;
|
||||
raw.DestBlendAlpha = alpha_dst;
|
||||
if let Some(ct) = ct.as_ref() {
|
||||
raw.RenderTargetWriteMask = ct.write_mask.bits() as u8;
|
||||
if let Some(ref blend) = ct.blend {
|
||||
let (color_op, color_src, color_dst) = map_blend_component(&blend.color, false);
|
||||
let (alpha_op, alpha_src, alpha_dst) = map_blend_component(&blend.alpha, true);
|
||||
raw.BlendEnable = 1;
|
||||
raw.BlendOp = color_op;
|
||||
raw.SrcBlend = color_src;
|
||||
raw.DestBlend = color_dst;
|
||||
raw.BlendOpAlpha = alpha_op;
|
||||
raw.SrcBlendAlpha = alpha_src;
|
||||
raw.DestBlendAlpha = alpha_dst;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
26
third_party/rust/wgpu-hal/src/dx12/device.rs
vendored
26
third_party/rust/wgpu-hal/src/dx12/device.rs
vendored
@ -125,6 +125,20 @@ impl super::Device {
|
||||
)?,
|
||||
};
|
||||
|
||||
let mut rtv_pool = descriptor::CpuPool::new(raw, native::DescriptorHeapType::Rtv);
|
||||
let null_rtv_handle = rtv_pool.alloc_handle();
|
||||
// A null pResource is used to initialize a null descriptor,
|
||||
// which guarantees D3D11-like null binding behavior (reading 0s, writes are discarded)
|
||||
raw.create_render_target_view(
|
||||
native::WeakPtr::null(),
|
||||
&native::RenderTargetViewDesc::texture_2d(
|
||||
winapi::shared::dxgiformat::DXGI_FORMAT_R8G8B8A8_UNORM,
|
||||
0,
|
||||
0,
|
||||
),
|
||||
null_rtv_handle.raw,
|
||||
);
|
||||
|
||||
Ok(super::Device {
|
||||
raw,
|
||||
present_queue,
|
||||
@ -134,10 +148,7 @@ impl super::Device {
|
||||
},
|
||||
private_caps,
|
||||
shared: Arc::new(shared),
|
||||
rtv_pool: Mutex::new(descriptor::CpuPool::new(
|
||||
raw,
|
||||
native::DescriptorHeapType::Rtv,
|
||||
)),
|
||||
rtv_pool: Mutex::new(rtv_pool),
|
||||
dsv_pool: Mutex::new(descriptor::CpuPool::new(
|
||||
raw,
|
||||
native::DescriptorHeapType::Dsv,
|
||||
@ -153,6 +164,7 @@ impl super::Device {
|
||||
library: Arc::clone(library),
|
||||
#[cfg(feature = "renderdoc")]
|
||||
render_doc: Default::default(),
|
||||
null_rtv_handle,
|
||||
})
|
||||
}
|
||||
|
||||
@ -306,6 +318,7 @@ impl super::Device {
|
||||
|
||||
impl crate::Device<super::Api> for super::Device {
|
||||
unsafe fn exit(self, queue: super::Queue) {
|
||||
self.rtv_pool.lock().free_handle(self.null_rtv_handle);
|
||||
self.rtv_pool.into_inner().destroy();
|
||||
self.dsv_pool.into_inner().destroy();
|
||||
self.srv_uav_pool.into_inner().destroy();
|
||||
@ -658,6 +671,7 @@ impl crate::Device<super::Api> for super::Device {
|
||||
allocator,
|
||||
device: self.raw,
|
||||
shared: Arc::clone(&self.shared),
|
||||
null_rtv_handle: self.null_rtv_handle.clone(),
|
||||
list: None,
|
||||
free_lists: Vec::new(),
|
||||
pass: super::PassState::new(),
|
||||
@ -1283,7 +1297,9 @@ impl crate::Device<super::Api> for super::Device {
|
||||
let mut rtv_formats = [dxgiformat::DXGI_FORMAT_UNKNOWN;
|
||||
d3d12::D3D12_SIMULTANEOUS_RENDER_TARGET_COUNT as usize];
|
||||
for (rtv_format, ct) in rtv_formats.iter_mut().zip(desc.color_targets) {
|
||||
*rtv_format = auxil::dxgi::conv::map_texture_format(ct.format);
|
||||
if let Some(ct) = ct.as_ref() {
|
||||
*rtv_format = auxil::dxgi::conv::map_texture_format(ct.format);
|
||||
}
|
||||
}
|
||||
|
||||
let bias = desc
|
||||
|
19
third_party/rust/wgpu-hal/src/dx12/mod.rs
vendored
19
third_party/rust/wgpu-hal/src/dx12/mod.rs
vendored
@ -229,6 +229,7 @@ pub struct Device {
|
||||
library: Arc<native::D3D12Lib>,
|
||||
#[cfg(feature = "renderdoc")]
|
||||
render_doc: crate::auxil::renderdoc::RenderDoc,
|
||||
null_rtv_handle: descriptor::Handle,
|
||||
}
|
||||
|
||||
unsafe impl Send for Device {}
|
||||
@ -287,7 +288,7 @@ enum PassKind {
|
||||
|
||||
struct PassState {
|
||||
has_label: bool,
|
||||
resolves: ArrayVec<PassResolve, { crate::MAX_COLOR_TARGETS }>,
|
||||
resolves: ArrayVec<PassResolve, { crate::MAX_COLOR_ATTACHMENTS }>,
|
||||
layout: PipelineLayoutShared,
|
||||
root_elements: [RootElement; MAX_ROOT_ELEMENTS],
|
||||
dirty_root_elements: u64,
|
||||
@ -329,6 +330,7 @@ pub struct CommandEncoder {
|
||||
allocator: native::CommandAllocator,
|
||||
device: native::Device,
|
||||
shared: Arc<DeviceShared>,
|
||||
null_rtv_handle: descriptor::Handle,
|
||||
list: Option<native::GraphicsCommandList>,
|
||||
free_lists: Vec<native::GraphicsCommandList>,
|
||||
pass: PassState,
|
||||
@ -527,7 +529,14 @@ impl SwapChain {
|
||||
self.raw
|
||||
}
|
||||
|
||||
unsafe fn wait(&mut self, timeout_ms: u32) -> Result<bool, crate::SurfaceError> {
|
||||
unsafe fn wait(
|
||||
&mut self,
|
||||
timeout: Option<std::time::Duration>,
|
||||
) -> Result<bool, crate::SurfaceError> {
|
||||
let timeout_ms = match timeout {
|
||||
Some(duration) => duration.as_millis() as u32,
|
||||
None => winbase::INFINITE,
|
||||
};
|
||||
match synchapi::WaitForSingleObject(self.waitable, timeout_ms) {
|
||||
winbase::WAIT_ABANDONED | winbase::WAIT_FAILED => Err(crate::SurfaceError::Lost),
|
||||
winbase::WAIT_OBJECT_0 => Ok(true),
|
||||
@ -690,7 +699,7 @@ impl crate::Surface<Api> for Surface {
|
||||
|
||||
unsafe fn unconfigure(&mut self, device: &Device) {
|
||||
if let Some(mut sc) = self.swap_chain.take() {
|
||||
let _ = sc.wait(winbase::INFINITE);
|
||||
let _ = sc.wait(None);
|
||||
//TODO: this shouldn't be needed,
|
||||
// but it complains that the queue is still used otherwise
|
||||
let _ = device.wait_idle();
|
||||
@ -701,11 +710,11 @@ impl crate::Surface<Api> for Surface {
|
||||
|
||||
unsafe fn acquire_texture(
|
||||
&mut self,
|
||||
timeout_ms: u32,
|
||||
timeout: Option<std::time::Duration>,
|
||||
) -> Result<Option<crate::AcquiredSurfaceTexture<Api>>, crate::SurfaceError> {
|
||||
let sc = self.swap_chain.as_mut().unwrap();
|
||||
|
||||
sc.wait(timeout_ms)?;
|
||||
sc.wait(timeout)?;
|
||||
|
||||
let base_index = sc.raw.GetCurrentBackBufferIndex() as usize;
|
||||
let index = (base_index + sc.acquired_count) % sc.resources.len();
|
||||
|
2
third_party/rust/wgpu-hal/src/empty.rs
vendored
2
third_party/rust/wgpu-hal/src/empty.rs
vendored
@ -66,7 +66,7 @@ impl crate::Surface<Api> for Context {
|
||||
|
||||
unsafe fn acquire_texture(
|
||||
&mut self,
|
||||
timeout_ms: u32,
|
||||
timeout: Option<std::time::Duration>,
|
||||
) -> Result<Option<crate::AcquiredSurfaceTexture<Api>>, crate::SurfaceError> {
|
||||
Ok(None)
|
||||
}
|
||||
|
32
third_party/rust/wgpu-hal/src/gles/adapter.rs
vendored
32
third_party/rust/wgpu-hal/src/gles/adapter.rs
vendored
@ -212,6 +212,9 @@ impl super::Adapter {
|
||||
naga::back::glsl::Version::Embedded(value)
|
||||
};
|
||||
|
||||
// ANGLE provides renderer strings like: "ANGLE (Apple, Apple M1 Pro, OpenGL 4.1)"
|
||||
let is_angle = renderer.contains("ANGLE");
|
||||
|
||||
let vertex_shader_storage_blocks = if supports_storage {
|
||||
gl.get_parameter_i32(glow::MAX_VERTEX_SHADER_STORAGE_BLOCKS) as u32
|
||||
} else {
|
||||
@ -289,6 +292,12 @@ impl super::Adapter {
|
||||
wgt::DownlevelFlags::ANISOTROPIC_FILTERING,
|
||||
extensions.contains("EXT_texture_filter_anisotropic"),
|
||||
);
|
||||
downlevel_flags.set(
|
||||
wgt::DownlevelFlags::BUFFER_BINDINGS_NOT_16_BYTE_ALIGNED,
|
||||
!(cfg!(target_arch = "wasm32") || is_angle),
|
||||
);
|
||||
|
||||
let is_ext_color_buffer_float_supported = extensions.contains("EXT_color_buffer_float");
|
||||
|
||||
let mut features = wgt::Features::empty()
|
||||
| wgt::Features::TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES
|
||||
@ -459,6 +468,7 @@ impl super::Adapter {
|
||||
0
|
||||
},
|
||||
max_compute_workgroups_per_dimension,
|
||||
max_buffer_size: i32::MAX as u64,
|
||||
};
|
||||
|
||||
let mut workarounds = super::Workarounds::empty();
|
||||
@ -500,6 +510,7 @@ impl super::Adapter {
|
||||
workarounds,
|
||||
shading_language_version,
|
||||
max_texture_size,
|
||||
is_ext_color_buffer_float_supported,
|
||||
}),
|
||||
},
|
||||
info: Self::make_info(vendor, renderer),
|
||||
@ -619,6 +630,13 @@ impl crate::Adapter<super::Api> for super::Adapter {
|
||||
unfilterable | Tfc::COLOR_ATTACHMENT | Tfc::MULTISAMPLE | Tfc::MULTISAMPLE_RESOLVE;
|
||||
let filterable_renderable = filterable | renderable | Tfc::COLOR_ATTACHMENT_BLEND;
|
||||
let storage = Tfc::STORAGE | Tfc::STORAGE_READ_WRITE;
|
||||
|
||||
let float_renderable = if self.shared.is_ext_color_buffer_float_supported {
|
||||
Tfc::COLOR_ATTACHMENT | Tfc::COLOR_ATTACHMENT_BLEND
|
||||
} else {
|
||||
Tfc::empty()
|
||||
};
|
||||
|
||||
match format {
|
||||
Tf::R8Unorm => filterable_renderable,
|
||||
Tf::R8Snorm => filterable,
|
||||
@ -628,37 +646,37 @@ impl crate::Adapter<super::Api> for super::Adapter {
|
||||
Tf::R16Sint => renderable,
|
||||
Tf::R16Unorm => empty,
|
||||
Tf::R16Snorm => empty,
|
||||
Tf::R16Float => filterable,
|
||||
Tf::R16Float => filterable | float_renderable,
|
||||
Tf::Rg8Unorm => filterable_renderable,
|
||||
Tf::Rg8Snorm => filterable,
|
||||
Tf::Rg8Uint => renderable,
|
||||
Tf::Rg8Sint => renderable,
|
||||
Tf::R32Uint => renderable | storage,
|
||||
Tf::R32Sint => renderable | storage,
|
||||
Tf::R32Float => unfilterable | storage,
|
||||
Tf::R32Float => unfilterable | storage | float_renderable,
|
||||
Tf::Rg16Uint => renderable,
|
||||
Tf::Rg16Sint => renderable,
|
||||
Tf::Rg16Unorm => empty,
|
||||
Tf::Rg16Snorm => empty,
|
||||
Tf::Rg16Float => filterable,
|
||||
Tf::Rg16Float => filterable | float_renderable,
|
||||
Tf::Rgba8Unorm | Tf::Rgba8UnormSrgb => filterable_renderable | storage,
|
||||
Tf::Bgra8Unorm | Tf::Bgra8UnormSrgb => filterable_renderable,
|
||||
Tf::Rgba8Snorm => filterable,
|
||||
Tf::Rgba8Uint => renderable | storage,
|
||||
Tf::Rgba8Sint => renderable | storage,
|
||||
Tf::Rgb10a2Unorm => filterable_renderable,
|
||||
Tf::Rg11b10Float => filterable,
|
||||
Tf::Rg11b10Float => filterable | float_renderable,
|
||||
Tf::Rg32Uint => renderable,
|
||||
Tf::Rg32Sint => renderable,
|
||||
Tf::Rg32Float => unfilterable,
|
||||
Tf::Rg32Float => unfilterable | float_renderable,
|
||||
Tf::Rgba16Uint => renderable | storage,
|
||||
Tf::Rgba16Sint => renderable | storage,
|
||||
Tf::Rgba16Unorm => empty,
|
||||
Tf::Rgba16Snorm => empty,
|
||||
Tf::Rgba16Float => filterable | storage,
|
||||
Tf::Rgba16Float => filterable | storage | float_renderable,
|
||||
Tf::Rgba32Uint => renderable | storage,
|
||||
Tf::Rgba32Sint => renderable | storage,
|
||||
Tf::Rgba32Float => unfilterable | storage,
|
||||
Tf::Rgba32Float => unfilterable | storage | float_renderable,
|
||||
Tf::Depth32Float
|
||||
| Tf::Depth32FloatStencil8
|
||||
| Tf::Depth24Plus
|
||||
|
43
third_party/rust/wgpu-hal/src/gles/command.rs
vendored
43
third_party/rust/wgpu-hal/src/gles/command.rs
vendored
@ -17,14 +17,14 @@ pub(super) struct State {
|
||||
vertex_buffers:
|
||||
[(super::VertexBufferDesc, Option<super::BufferBinding>); crate::MAX_VERTEX_BUFFERS],
|
||||
vertex_attributes: ArrayVec<super::AttributeDesc, { super::MAX_VERTEX_ATTRIBUTES }>,
|
||||
color_targets: ArrayVec<super::ColorTargetDesc, { crate::MAX_COLOR_TARGETS }>,
|
||||
color_targets: ArrayVec<super::ColorTargetDesc, { crate::MAX_COLOR_ATTACHMENTS }>,
|
||||
stencil: super::StencilState,
|
||||
depth_bias: wgt::DepthBiasState,
|
||||
samplers: [Option<glow::Sampler>; super::MAX_SAMPLERS],
|
||||
texture_slots: [TextureSlotDesc; super::MAX_TEXTURE_SLOTS],
|
||||
render_size: wgt::Extent3d,
|
||||
resolve_attachments: ArrayVec<(u32, super::TextureView), { crate::MAX_COLOR_TARGETS }>,
|
||||
invalidate_attachments: ArrayVec<u32, { crate::MAX_COLOR_TARGETS + 2 }>,
|
||||
resolve_attachments: ArrayVec<(u32, super::TextureView), { crate::MAX_COLOR_ATTACHMENTS }>,
|
||||
invalidate_attachments: ArrayVec<u32, { crate::MAX_COLOR_ATTACHMENTS + 2 }>,
|
||||
has_pass_label: bool,
|
||||
instance_vbuf_mask: usize,
|
||||
dirty_vbuf_mask: usize,
|
||||
@ -327,6 +327,7 @@ impl crate::CommandEncoder<super::Api> for super::CommandEncoder {
|
||||
dst: dst_raw,
|
||||
dst_target,
|
||||
copy,
|
||||
dst_is_cubemap: dst.is_cubemap,
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -428,7 +429,8 @@ impl crate::CommandEncoder<super::Api> for super::CommandEncoder {
|
||||
match desc
|
||||
.color_attachments
|
||||
.first()
|
||||
.map(|at| &at.target.view.inner)
|
||||
.filter(|at| at.is_some())
|
||||
.and_then(|at| at.as_ref().map(|at| &at.target.view.inner))
|
||||
{
|
||||
// default framebuffer (provided externally)
|
||||
Some(&super::TextureInner::DefaultRenderbuffer) => {
|
||||
@ -443,18 +445,20 @@ impl crate::CommandEncoder<super::Api> for super::CommandEncoder {
|
||||
.push(C::ResetFramebuffer { is_default: false });
|
||||
|
||||
for (i, cat) in desc.color_attachments.iter().enumerate() {
|
||||
let attachment = glow::COLOR_ATTACHMENT0 + i as u32;
|
||||
self.cmd_buffer.commands.push(C::BindAttachment {
|
||||
attachment,
|
||||
view: cat.target.view.clone(),
|
||||
});
|
||||
if let Some(ref rat) = cat.resolve_target {
|
||||
self.state
|
||||
.resolve_attachments
|
||||
.push((attachment, rat.view.clone()));
|
||||
}
|
||||
if !cat.ops.contains(crate::AttachmentOps::STORE) {
|
||||
self.state.invalidate_attachments.push(attachment);
|
||||
if let Some(cat) = cat.as_ref() {
|
||||
let attachment = glow::COLOR_ATTACHMENT0 + i as u32;
|
||||
self.cmd_buffer.commands.push(C::BindAttachment {
|
||||
attachment,
|
||||
view: cat.target.view.clone(),
|
||||
});
|
||||
if let Some(ref rat) = cat.resolve_target {
|
||||
self.state
|
||||
.resolve_attachments
|
||||
.push((attachment, rat.view.clone()));
|
||||
}
|
||||
if !cat.ops.contains(crate::AttachmentOps::STORE) {
|
||||
self.state.invalidate_attachments.push(attachment);
|
||||
}
|
||||
}
|
||||
}
|
||||
if let Some(ref dsat) = desc.depth_stencil_attachment {
|
||||
@ -504,7 +508,12 @@ impl crate::CommandEncoder<super::Api> for super::CommandEncoder {
|
||||
});
|
||||
|
||||
// issue the clears
|
||||
for (i, cat) in desc.color_attachments.iter().enumerate() {
|
||||
for (i, cat) in desc
|
||||
.color_attachments
|
||||
.iter()
|
||||
.filter_map(|at| at.as_ref())
|
||||
.enumerate()
|
||||
{
|
||||
if !cat.ops.contains(crate::AttachmentOps::LOAD) {
|
||||
let c = &cat.clear_value;
|
||||
self.cmd_buffer
|
||||
|
39
third_party/rust/wgpu-hal/src/gles/device.rs
vendored
39
third_party/rust/wgpu-hal/src/gles/device.rs
vendored
@ -144,6 +144,23 @@ impl super::Device {
|
||||
.position(|ep| ep.name.as_str() == stage.entry_point)
|
||||
.ok_or(crate::PipelineError::EntryPoint(naga_stage))?;
|
||||
|
||||
use naga::proc::BoundsCheckPolicy;
|
||||
// The image bounds checks require the TEXTURE_LEVELS feature available in GL core 1.3+.
|
||||
let version = gl.version();
|
||||
let image_check = if !version.is_embedded && (version.major, version.minor) >= (1, 3) {
|
||||
BoundsCheckPolicy::ReadZeroSkipWrite
|
||||
} else {
|
||||
BoundsCheckPolicy::Unchecked
|
||||
};
|
||||
|
||||
// Other bounds check are either provided by glsl or not implemented yet.
|
||||
let policies = naga::proc::BoundsCheckPolicies {
|
||||
index: BoundsCheckPolicy::Unchecked,
|
||||
buffer: BoundsCheckPolicy::Unchecked,
|
||||
image: image_check,
|
||||
binding_array: BoundsCheckPolicy::Unchecked,
|
||||
};
|
||||
|
||||
let mut output = String::new();
|
||||
let mut writer = glsl::Writer::new(
|
||||
&mut output,
|
||||
@ -151,6 +168,7 @@ impl super::Device {
|
||||
&shader.info,
|
||||
&context.layout.naga_options,
|
||||
&pipeline_options,
|
||||
policies,
|
||||
)
|
||||
.map_err(|e| {
|
||||
let msg = format!("{}", e);
|
||||
@ -528,7 +546,7 @@ impl crate::Device<super::Api> for super::Device {
|
||||
depth: 1,
|
||||
};
|
||||
|
||||
let inner = if render_usage.contains(desc.usage)
|
||||
let (inner, is_cubemap) = if render_usage.contains(desc.usage)
|
||||
&& desc.dimension == wgt::TextureDimension::D2
|
||||
&& desc.size.depth_or_array_layers == 1
|
||||
{
|
||||
@ -559,10 +577,10 @@ impl crate::Device<super::Api> for super::Device {
|
||||
}
|
||||
|
||||
gl.bind_renderbuffer(glow::RENDERBUFFER, None);
|
||||
super::TextureInner::Renderbuffer { raw }
|
||||
(super::TextureInner::Renderbuffer { raw }, false)
|
||||
} else {
|
||||
let raw = gl.create_texture().unwrap();
|
||||
let (target, is_3d) = match desc.dimension {
|
||||
let (target, is_3d, is_cubemap) = match desc.dimension {
|
||||
wgt::TextureDimension::D1 | wgt::TextureDimension::D2 => {
|
||||
if desc.size.depth_or_array_layers > 1 {
|
||||
//HACK: detect a cube map
|
||||
@ -575,17 +593,17 @@ impl crate::Device<super::Api> for super::Device {
|
||||
None
|
||||
};
|
||||
match cube_count {
|
||||
None => (glow::TEXTURE_2D_ARRAY, true),
|
||||
Some(1) => (glow::TEXTURE_CUBE_MAP, false),
|
||||
Some(_) => (glow::TEXTURE_CUBE_MAP_ARRAY, true),
|
||||
None => (glow::TEXTURE_2D_ARRAY, true, false),
|
||||
Some(1) => (glow::TEXTURE_CUBE_MAP, false, true),
|
||||
Some(_) => (glow::TEXTURE_CUBE_MAP_ARRAY, true, true),
|
||||
}
|
||||
} else {
|
||||
(glow::TEXTURE_2D, false)
|
||||
(glow::TEXTURE_2D, false, false)
|
||||
}
|
||||
}
|
||||
wgt::TextureDimension::D3 => {
|
||||
copy_size.depth = desc.size.depth_or_array_layers;
|
||||
(glow::TEXTURE_3D, true)
|
||||
(glow::TEXTURE_3D, true, false)
|
||||
}
|
||||
};
|
||||
|
||||
@ -639,7 +657,7 @@ impl crate::Device<super::Api> for super::Device {
|
||||
}
|
||||
|
||||
gl.bind_texture(target, None);
|
||||
super::TextureInner::Texture { raw, target }
|
||||
(super::TextureInner::Texture { raw, target }, is_cubemap)
|
||||
};
|
||||
|
||||
Ok(super::Texture {
|
||||
@ -653,6 +671,7 @@ impl crate::Device<super::Api> for super::Device {
|
||||
format: desc.format,
|
||||
format_desc,
|
||||
copy_size,
|
||||
is_cubemap,
|
||||
})
|
||||
}
|
||||
unsafe fn destroy_texture(&self, texture: super::Texture) {
|
||||
@ -983,7 +1002,7 @@ impl crate::Device<super::Api> for super::Device {
|
||||
|
||||
let color_targets = {
|
||||
let mut targets = Vec::new();
|
||||
for ct in desc.color_targets.iter() {
|
||||
for ct in desc.color_targets.iter().filter_map(|at| at.as_ref()) {
|
||||
targets.push(super::ColorTargetDesc {
|
||||
mask: ct.write_mask,
|
||||
blend: ct.blend.as_ref().map(conv::map_blend),
|
||||
|
3
third_party/rust/wgpu-hal/src/gles/egl.rs
vendored
3
third_party/rust/wgpu-hal/src/gles/egl.rs
vendored
@ -1194,7 +1194,7 @@ impl crate::Surface<super::Api> for Surface {
|
||||
|
||||
unsafe fn acquire_texture(
|
||||
&mut self,
|
||||
_timeout_ms: u32, //TODO
|
||||
_timeout_ms: Option<Duration>, //TODO
|
||||
) -> Result<Option<crate::AcquiredSurfaceTexture<super::Api>>, crate::SurfaceError> {
|
||||
let sc = self.swapchain.as_ref().unwrap();
|
||||
let texture = super::Texture {
|
||||
@ -1210,6 +1210,7 @@ impl crate::Surface<super::Api> for Surface {
|
||||
height: sc.extent.height,
|
||||
depth: 1,
|
||||
},
|
||||
is_cubemap: false,
|
||||
};
|
||||
Ok(Some(crate::AcquiredSurfaceTexture {
|
||||
texture,
|
||||
|
6
third_party/rust/wgpu-hal/src/gles/mod.rs
vendored
6
third_party/rust/wgpu-hal/src/gles/mod.rs
vendored
@ -184,6 +184,7 @@ struct AdapterShared {
|
||||
workarounds: Workarounds,
|
||||
shading_language_version: naga::back::glsl::Version,
|
||||
max_texture_size: u32,
|
||||
is_ext_color_buffer_float_supported: bool,
|
||||
}
|
||||
|
||||
pub struct Adapter {
|
||||
@ -262,6 +263,7 @@ pub struct Texture {
|
||||
#[allow(unused)]
|
||||
format_desc: TextureFormatDesc,
|
||||
copy_size: crate::CopyExtent,
|
||||
is_cubemap: bool,
|
||||
}
|
||||
|
||||
impl Texture {
|
||||
@ -281,6 +283,7 @@ impl Texture {
|
||||
height: 0,
|
||||
depth: 0,
|
||||
},
|
||||
is_cubemap: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -564,7 +567,7 @@ struct PrimitiveState {
|
||||
unclipped_depth: bool,
|
||||
}
|
||||
|
||||
type InvalidatedAttachments = ArrayVec<u32, { crate::MAX_COLOR_TARGETS + 2 }>;
|
||||
type InvalidatedAttachments = ArrayVec<u32, { crate::MAX_COLOR_ATTACHMENTS + 2 }>;
|
||||
|
||||
#[derive(Debug)]
|
||||
enum Command {
|
||||
@ -616,6 +619,7 @@ enum Command {
|
||||
dst: glow::Texture,
|
||||
dst_target: BindTarget,
|
||||
copy: crate::TextureCopy,
|
||||
dst_is_cubemap: bool,
|
||||
},
|
||||
CopyBufferToTexture {
|
||||
src: Buffer,
|
||||
|
84
third_party/rust/wgpu-hal/src/gles/queue.rs
vendored
84
third_party/rust/wgpu-hal/src/gles/queue.rs
vendored
@ -50,7 +50,7 @@ impl super::Queue {
|
||||
// Reset the draw buffers to what they were before the clear
|
||||
let indices = (0..self.draw_buffer_count as u32)
|
||||
.map(|i| glow::COLOR_ATTACHMENT0 + i)
|
||||
.collect::<ArrayVec<_, { crate::MAX_COLOR_TARGETS }>>();
|
||||
.collect::<ArrayVec<_, { crate::MAX_COLOR_ATTACHMENTS }>>();
|
||||
gl.draw_buffers(&indices);
|
||||
}
|
||||
#[cfg(not(target_arch = "wasm32"))]
|
||||
@ -213,19 +213,39 @@ impl super::Queue {
|
||||
ref range,
|
||||
} => match dst.raw {
|
||||
Some(buffer) => {
|
||||
gl.bind_buffer(glow::COPY_READ_BUFFER, Some(self.zero_buffer));
|
||||
gl.bind_buffer(dst_target, Some(buffer));
|
||||
let mut dst_offset = range.start;
|
||||
while dst_offset < range.end {
|
||||
let size = (range.end - dst_offset).min(super::ZERO_BUFFER_SIZE as u64);
|
||||
gl.copy_buffer_sub_data(
|
||||
glow::COPY_READ_BUFFER,
|
||||
dst_target,
|
||||
0,
|
||||
dst_offset as i32,
|
||||
size as i32,
|
||||
);
|
||||
dst_offset += size;
|
||||
// When `INDEX_BUFFER_ROLE_CHANGE` isn't available, we can't copy into the
|
||||
// index buffer from the zero buffer. This would fail in Chrome with the
|
||||
// following message:
|
||||
//
|
||||
// > Cannot copy into an element buffer destination from a non-element buffer
|
||||
// > source
|
||||
//
|
||||
// Instead, we'll upload zeroes into the buffer.
|
||||
let can_use_zero_buffer = self
|
||||
.shared
|
||||
.private_caps
|
||||
.contains(super::PrivateCapabilities::INDEX_BUFFER_ROLE_CHANGE)
|
||||
|| dst_target != glow::ELEMENT_ARRAY_BUFFER;
|
||||
|
||||
if can_use_zero_buffer {
|
||||
gl.bind_buffer(glow::COPY_READ_BUFFER, Some(self.zero_buffer));
|
||||
gl.bind_buffer(dst_target, Some(buffer));
|
||||
let mut dst_offset = range.start;
|
||||
while dst_offset < range.end {
|
||||
let size = (range.end - dst_offset).min(super::ZERO_BUFFER_SIZE as u64);
|
||||
gl.copy_buffer_sub_data(
|
||||
glow::COPY_READ_BUFFER,
|
||||
dst_target,
|
||||
0,
|
||||
dst_offset as i32,
|
||||
size as i32,
|
||||
);
|
||||
dst_offset += size;
|
||||
}
|
||||
} else {
|
||||
gl.bind_buffer(dst_target, Some(buffer));
|
||||
let zeroes = vec![0u8; (range.end - range.start) as usize];
|
||||
gl.buffer_sub_data_u8_slice(dst_target, range.start as i32, &zeroes);
|
||||
}
|
||||
}
|
||||
None => {
|
||||
@ -308,10 +328,10 @@ impl super::Queue {
|
||||
src_target,
|
||||
dst,
|
||||
dst_target,
|
||||
dst_is_cubemap,
|
||||
ref copy,
|
||||
} => {
|
||||
//TODO: handle 3D copies
|
||||
//TODO: handle cubemap copies
|
||||
gl.bind_framebuffer(glow::READ_FRAMEBUFFER, Some(self.copy_fbo));
|
||||
if is_layered_target(src_target) {
|
||||
//TODO: handle GLES without framebuffer_texture_3d
|
||||
@ -333,7 +353,18 @@ impl super::Queue {
|
||||
}
|
||||
|
||||
gl.bind_texture(dst_target, Some(dst));
|
||||
if is_layered_target(dst_target) {
|
||||
if dst_is_cubemap {
|
||||
gl.copy_tex_sub_image_2d(
|
||||
CUBEMAP_FACES[copy.dst_base.array_layer as usize],
|
||||
copy.dst_base.mip_level as i32,
|
||||
copy.dst_base.origin.x as i32,
|
||||
copy.dst_base.origin.y as i32,
|
||||
copy.src_base.origin.x as i32,
|
||||
copy.src_base.origin.y as i32,
|
||||
copy.size.width as i32,
|
||||
copy.size.height as i32,
|
||||
);
|
||||
} else if is_layered_target(dst_target) {
|
||||
gl.copy_tex_sub_image_3d(
|
||||
dst_target,
|
||||
copy.dst_base.mip_level as i32,
|
||||
@ -397,7 +428,7 @@ impl super::Queue {
|
||||
}
|
||||
};
|
||||
match dst_target {
|
||||
glow::TEXTURE_3D | glow::TEXTURE_2D_ARRAY => {
|
||||
glow::TEXTURE_3D => {
|
||||
gl.tex_sub_image_3d(
|
||||
dst_target,
|
||||
copy.texture_base.mip_level as i32,
|
||||
@ -412,6 +443,21 @@ impl super::Queue {
|
||||
unpack_data,
|
||||
);
|
||||
}
|
||||
glow::TEXTURE_2D_ARRAY => {
|
||||
gl.tex_sub_image_3d(
|
||||
dst_target,
|
||||
copy.texture_base.mip_level as i32,
|
||||
copy.texture_base.origin.x as i32,
|
||||
copy.texture_base.origin.y as i32,
|
||||
copy.texture_base.array_layer as i32,
|
||||
copy.size.width as i32,
|
||||
copy.size.height as i32,
|
||||
copy.size.depth as i32,
|
||||
format_desc.external,
|
||||
format_desc.data_type,
|
||||
unpack_data,
|
||||
);
|
||||
}
|
||||
glow::TEXTURE_2D => {
|
||||
gl.tex_sub_image_2d(
|
||||
dst_target,
|
||||
@ -662,7 +708,7 @@ impl super::Queue {
|
||||
None,
|
||||
0,
|
||||
);
|
||||
for i in 0..crate::MAX_COLOR_TARGETS {
|
||||
for i in 0..crate::MAX_COLOR_ATTACHMENTS {
|
||||
let target = glow::COLOR_ATTACHMENT0 + i as u32;
|
||||
gl.framebuffer_texture_2d(
|
||||
glow::DRAW_FRAMEBUFFER,
|
||||
@ -717,7 +763,7 @@ impl super::Queue {
|
||||
self.draw_buffer_count = count;
|
||||
let indices = (0..count as u32)
|
||||
.map(|i| glow::COLOR_ATTACHMENT0 + i)
|
||||
.collect::<ArrayVec<_, { crate::MAX_COLOR_TARGETS }>>();
|
||||
.collect::<ArrayVec<_, { crate::MAX_COLOR_ATTACHMENTS }>>();
|
||||
gl.draw_buffers(&indices);
|
||||
|
||||
if self
|
||||
|
104
third_party/rust/wgpu-hal/src/gles/web.rs
vendored
104
third_party/rust/wgpu-hal/src/gles/web.rs
vendored
@ -5,7 +5,7 @@ use wasm_bindgen::JsCast;
|
||||
use super::TextureFormatDesc;
|
||||
|
||||
/// A wrapper around a [`glow::Context`] to provide a fake `lock()` api that makes it compatible
|
||||
/// with the `AdapterContext` API fromt the EGL implementation.
|
||||
/// with the `AdapterContext` API from the EGL implementation.
|
||||
pub struct AdapterContext {
|
||||
pub glow_context: glow::Context,
|
||||
}
|
||||
@ -25,7 +25,62 @@ impl AdapterContext {
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Instance {
|
||||
canvas: Mutex<Option<web_sys::HtmlCanvasElement>>,
|
||||
webgl2_context: Mutex<Option<web_sys::WebGl2RenderingContext>>,
|
||||
}
|
||||
|
||||
impl Instance {
|
||||
pub fn create_surface_from_canvas(
|
||||
&self,
|
||||
canvas: &web_sys::HtmlCanvasElement,
|
||||
) -> Result<Surface, crate::InstanceError> {
|
||||
let webgl2_context = canvas
|
||||
.get_context_with_context_options("webgl2", &Self::create_context_options())
|
||||
.expect("Cannot create WebGL2 context")
|
||||
.and_then(|context| context.dyn_into::<web_sys::WebGl2RenderingContext>().ok())
|
||||
.expect("Cannot convert into WebGL2 context");
|
||||
|
||||
*self.webgl2_context.lock() = Some(webgl2_context.clone());
|
||||
|
||||
Ok(Surface {
|
||||
webgl2_context,
|
||||
present_program: None,
|
||||
swapchain: None,
|
||||
texture: None,
|
||||
presentable: true,
|
||||
})
|
||||
}
|
||||
|
||||
pub fn create_surface_from_offscreen_canvas(
|
||||
&self,
|
||||
canvas: &web_sys::OffscreenCanvas,
|
||||
) -> Result<Surface, crate::InstanceError> {
|
||||
let webgl2_context = canvas
|
||||
.get_context_with_context_options("webgl2", &Self::create_context_options())
|
||||
.expect("Cannot create WebGL2 context")
|
||||
.and_then(|context| context.dyn_into::<web_sys::WebGl2RenderingContext>().ok())
|
||||
.expect("Cannot convert into WebGL2 context");
|
||||
|
||||
*self.webgl2_context.lock() = Some(webgl2_context.clone());
|
||||
|
||||
Ok(Surface {
|
||||
webgl2_context,
|
||||
present_program: None,
|
||||
swapchain: None,
|
||||
texture: None,
|
||||
presentable: true,
|
||||
})
|
||||
}
|
||||
|
||||
fn create_context_options() -> js_sys::Object {
|
||||
let context_options = js_sys::Object::new();
|
||||
js_sys::Reflect::set(
|
||||
&context_options,
|
||||
&"antialias".into(),
|
||||
&wasm_bindgen::JsValue::FALSE,
|
||||
)
|
||||
.expect("Cannot create context options");
|
||||
context_options
|
||||
}
|
||||
}
|
||||
|
||||
// SAFE: WASM doesn't have threads
|
||||
@ -35,28 +90,14 @@ unsafe impl Send for Instance {}
|
||||
impl crate::Instance<super::Api> for Instance {
|
||||
unsafe fn init(_desc: &crate::InstanceDescriptor) -> Result<Self, crate::InstanceError> {
|
||||
Ok(Instance {
|
||||
canvas: Mutex::new(None),
|
||||
webgl2_context: Mutex::new(None),
|
||||
})
|
||||
}
|
||||
|
||||
unsafe fn enumerate_adapters(&self) -> Vec<crate::ExposedAdapter<super::Api>> {
|
||||
let canvas_guard = self.canvas.lock();
|
||||
let gl = match *canvas_guard {
|
||||
Some(ref canvas) => {
|
||||
let context_options = js_sys::Object::new();
|
||||
js_sys::Reflect::set(
|
||||
&context_options,
|
||||
&"antialias".into(),
|
||||
&wasm_bindgen::JsValue::FALSE,
|
||||
)
|
||||
.expect("Cannot create context options");
|
||||
let webgl2_context = canvas
|
||||
.get_context_with_context_options("webgl2", &context_options)
|
||||
.expect("Cannot create WebGL2 context")
|
||||
.and_then(|context| context.dyn_into::<web_sys::WebGl2RenderingContext>().ok())
|
||||
.expect("Cannot convert into WebGL2 context");
|
||||
glow::Context::from_webgl2_context(webgl2_context)
|
||||
}
|
||||
let context_guard = self.webgl2_context.lock();
|
||||
let gl = match *context_guard {
|
||||
Some(ref webgl2_context) => glow::Context::from_webgl2_context(webgl2_context.clone()),
|
||||
None => return Vec::new(),
|
||||
};
|
||||
|
||||
@ -79,26 +120,18 @@ impl crate::Instance<super::Api> for Instance {
|
||||
.dyn_into()
|
||||
.expect("Failed to downcast to canvas type");
|
||||
|
||||
*self.canvas.lock() = Some(canvas.clone());
|
||||
|
||||
Ok(Surface {
|
||||
canvas,
|
||||
present_program: None,
|
||||
swapchain: None,
|
||||
texture: None,
|
||||
presentable: true,
|
||||
})
|
||||
self.create_surface_from_canvas(&canvas)
|
||||
} else {
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
|
||||
unsafe fn destroy_surface(&self, surface: Surface) {
|
||||
let mut canvas_option_ref = self.canvas.lock();
|
||||
let mut context_option_ref = self.webgl2_context.lock();
|
||||
|
||||
if let Some(canvas) = canvas_option_ref.as_ref() {
|
||||
if canvas == &surface.canvas {
|
||||
*canvas_option_ref = None;
|
||||
if let Some(context) = context_option_ref.as_ref() {
|
||||
if context == &surface.webgl2_context {
|
||||
*context_option_ref = None;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -106,7 +139,7 @@ impl crate::Instance<super::Api> for Instance {
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct Surface {
|
||||
canvas: web_sys::HtmlCanvasElement,
|
||||
webgl2_context: web_sys::WebGl2RenderingContext,
|
||||
pub(super) swapchain: Option<Swapchain>,
|
||||
texture: Option<glow::Texture>,
|
||||
pub(super) presentable: bool,
|
||||
@ -253,7 +286,7 @@ impl crate::Surface<super::Api> for Surface {
|
||||
|
||||
unsafe fn acquire_texture(
|
||||
&mut self,
|
||||
_timeout_ms: u32,
|
||||
_timeout_ms: Option<std::time::Duration>, //TODO
|
||||
) -> Result<Option<crate::AcquiredSurfaceTexture<super::Api>>, crate::SurfaceError> {
|
||||
let sc = self.swapchain.as_ref().unwrap();
|
||||
let texture = super::Texture {
|
||||
@ -270,6 +303,7 @@ impl crate::Surface<super::Api> for Surface {
|
||||
height: sc.extent.height,
|
||||
depth: 1,
|
||||
},
|
||||
is_cubemap: false,
|
||||
};
|
||||
Ok(Some(crate::AcquiredSurfaceTexture {
|
||||
texture,
|
||||
|
18
third_party/rust/wgpu-hal/src/lib.rs
vendored
18
third_party/rust/wgpu-hal/src/lib.rs
vendored
@ -97,7 +97,7 @@ use thiserror::Error;
|
||||
pub const MAX_ANISOTROPY: u8 = 16;
|
||||
pub const MAX_BIND_GROUPS: usize = 8;
|
||||
pub const MAX_VERTEX_BUFFERS: usize = 16;
|
||||
pub const MAX_COLOR_TARGETS: usize = 8;
|
||||
pub const MAX_COLOR_ATTACHMENTS: usize = 8;
|
||||
pub const MAX_MIP_LEVELS: u32 = 16;
|
||||
/// Size of a single occlusion/timestamp query, when copied into a buffer, in bytes.
|
||||
pub const QUERY_SIZE: wgt::BufferAddress = 8;
|
||||
@ -193,10 +193,19 @@ pub trait Surface<A: Api>: Send + Sync {
|
||||
|
||||
unsafe fn unconfigure(&mut self, device: &A::Device);
|
||||
|
||||
/// Returns the next texture to be presented by the swapchain for drawing
|
||||
///
|
||||
/// A `timeout` of `None` means to wait indefinitely, with no timeout.
|
||||
///
|
||||
/// # Portability
|
||||
///
|
||||
/// Some backends can't support a timeout when acquiring a texture and
|
||||
/// the timeout will be ignored.
|
||||
///
|
||||
/// Returns `None` on timing out.
|
||||
unsafe fn acquire_texture(
|
||||
&mut self,
|
||||
timeout_ms: u32,
|
||||
timeout: Option<std::time::Duration>,
|
||||
) -> Result<Option<AcquiredSurfaceTexture<A>>, SurfaceError>;
|
||||
unsafe fn discard_texture(&mut self, texture: A::SurfaceTexture);
|
||||
}
|
||||
@ -304,6 +313,7 @@ pub trait Device<A: Api>: Send + Sync {
|
||||
unsafe fn create_fence(&self) -> Result<A::Fence, DeviceError>;
|
||||
unsafe fn destroy_fence(&self, fence: A::Fence);
|
||||
unsafe fn get_fence_value(&self, fence: &A::Fence) -> Result<FenceValue, DeviceError>;
|
||||
/// Calling wait with a lower value than the current fence value will immediately return.
|
||||
unsafe fn wait(
|
||||
&self,
|
||||
fence: &A::Fence,
|
||||
@ -1014,7 +1024,7 @@ pub struct RenderPipelineDescriptor<'a, A: Api> {
|
||||
/// The fragment stage for this pipeline.
|
||||
pub fragment_stage: Option<ProgrammableStage<'a, A>>,
|
||||
/// The effect of draw calls on the color aspect of the output target.
|
||||
pub color_targets: &'a [wgt::ColorTargetState],
|
||||
pub color_targets: &'a [Option<wgt::ColorTargetState>],
|
||||
/// If the pipeline will be used with a multiview render pass, this indicates how many array
|
||||
/// layers the attachments will have.
|
||||
pub multiview: Option<NonZeroU32>,
|
||||
@ -1169,7 +1179,7 @@ pub struct RenderPassDescriptor<'a, A: Api> {
|
||||
pub label: Label<'a>,
|
||||
pub extent: wgt::Extent3d,
|
||||
pub sample_count: u32,
|
||||
pub color_attachments: &'a [ColorAttachment<'a, A>],
|
||||
pub color_attachments: &'a [Option<ColorAttachment<'a, A>>],
|
||||
pub depth_stencil_attachment: Option<DepthStencilAttachment<'a, A>>,
|
||||
pub multiview: Option<NonZeroU32>,
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user