Add is_helper_invocation, and cleanup arch & asm functions (#612)

This commit is contained in:
XAMPPRocky 2021-05-04 12:51:20 +02:00 committed by GitHub
parent de23683aa3
commit 9c19414858
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
27 changed files with 129 additions and 77 deletions

View File

@ -8,6 +8,7 @@ use crate::{scalar::Scalar, vector::Vector};
mod arithmetic;
#[cfg(feature = "const-generics")]
mod barrier;
mod demote_to_helper_invocation_ext;
mod derivative;
mod primitive;
mod ray_tracing;
@ -15,6 +16,7 @@ mod ray_tracing;
pub use arithmetic::*;
#[cfg(feature = "const-generics")]
pub use barrier::*;
pub use demote_to_helper_invocation_ext::*;
pub use derivative::*;
pub use primitive::*;
pub use ray_tracing::*;
@ -140,3 +142,18 @@ pub unsafe fn vector_insert_dynamic<T: Scalar, V: Vector<T, N>, const N: usize>(
result
}
/// Fragment-shader discard. Equivalvent to `discard()` from GLSL
///
/// Ceases all further processing in any invocation that executes it: Only
/// instructions these invocations executed before [kill] have observable side
/// effects.
#[spirv_std_macros::gpu_only]
#[doc(alias = "OpKill", alias = "discard")]
#[allow(clippy::empty_loop)]
pub fn kill() -> ! {
unsafe {
asm!("OpKill", "%unused = OpLabel");
}
loop {}
}

View File

@ -0,0 +1,48 @@
/// Demote fragment shader invocation to a helper invocation. Equivalvent to
/// `discard()` in HLSL. Any stores to memory after this instruction are
/// suppressed and the fragment does not write outputs to the framebuffer.
///
/// Unlike [super::kill], this does not necessarily terminate the invocation. It
/// is not considered a flow control instruction (flow control does not become
/// non-uniform) and does not terminate the block.
///
/// - **Required Capabilities** `DemoteToHelperInvocationEXT`
/// - **Required Extensions** `SPV_EXT_demote_to_helper_invocation`
///
/// # Safety
/// After this instruction executes, the value of a `helper_invocation` builtin
/// variable is undefined. Use `is_helper_invocation` to determine whether
/// invocations are helper invocations in the presence
/// of [demote_to_helper_invocation].
#[spirv_std_macros::gpu_only]
#[doc(alias = "OpDemoteToHelperInvocationEXT", alias = "discard")]
pub unsafe fn demote_to_helper_invocation() {
asm!("OpDemoteToHelperInvocationEXT");
}
/// Returns `true` if the invocation is currently a helper invocation, otherwise
/// result is `false`. An invocation is currently a helper invocation if it was
/// originally invoked as a helper invocation or if it has been demoted to a
/// helper invocation by [demote_to_helper_invocation].
///
/// - **Required Capabilities** `DemoteToHelperInvocationEXT`
/// - **Required Extensions** `SPV_EXT_demote_to_helper_invocation`
#[spirv_std_macros::gpu_only]
#[doc(alias = "OpIsHelperInvocationEXT")]
pub fn is_helper_invocation() -> bool {
let result: u32;
unsafe {
asm! {
"%bool = OpTypeBool",
"%u32 = OpTypeInt 32 0",
"%zero = OpConstant %u32 0",
"%one = OpConstant %u32 1",
"%result = OpIsHelperInvocationEXT %bool",
"{} = OpSelect %u32 %result %one %zero",
out(reg) result
};
}
result != 0
}

View File

@ -1,19 +1,10 @@
use crate::float::Float;
#[cfg(target_arch = "spirv")]
macro_rules! deriv_caps {
(true) => {
asm!("OpCapability DerivativeControl")
};
(false) => {};
}
#[cfg(target_arch = "spirv")]
macro_rules! deriv_fn {
($p:ident, $inst:ident, $needs_caps:tt) => {
($p:ident, $inst:ident) => {
unsafe {
let mut o = Default::default();
deriv_caps!($needs_caps);
asm!(
"%input = OpLoad _ {0}",
concat!("%result = ", stringify!($inst), " _ %input"),
@ -32,7 +23,7 @@ macro_rules! deriv_fn {
#[crate::macros::vectorized]
#[crate::macros::gpu_only]
pub fn ddx<F: Float>(component: F) -> F {
deriv_fn!(component, OpDPdx, false)
deriv_fn!(component, OpDPdx)
}
/// Returns the partial derivative of `component` with respect to the window's X
@ -41,7 +32,7 @@ pub fn ddx<F: Float>(component: F) -> F {
#[crate::macros::vectorized]
#[crate::macros::gpu_only]
pub fn ddx_fine<F: Float>(component: F) -> F {
deriv_fn!(component, OpDPdxFine, true)
deriv_fn!(component, OpDPdxFine)
}
/// Returns the partial derivative of `component` with respect to the window's X
@ -53,7 +44,7 @@ pub fn ddx_fine<F: Float>(component: F) -> F {
#[crate::macros::vectorized]
#[crate::macros::gpu_only]
pub fn ddx_coarse<F: Float>(component: F) -> F {
deriv_fn!(component, OpDPdxCoarse, true)
deriv_fn!(component, OpDPdxCoarse)
}
/// Returns the partial derivative of `component` with respect to the window's Y
@ -62,7 +53,7 @@ pub fn ddx_coarse<F: Float>(component: F) -> F {
#[crate::macros::vectorized]
#[crate::macros::gpu_only]
pub fn ddy<F: Float>(component: F) -> F {
deriv_fn!(component, OpDPdy, false)
deriv_fn!(component, OpDPdy)
}
/// Returns the partial derivative of `component` with respect to the window's Y
@ -71,7 +62,7 @@ pub fn ddy<F: Float>(component: F) -> F {
#[crate::macros::vectorized]
#[crate::macros::gpu_only]
pub fn ddy_fine<F: Float>(component: F) -> F {
deriv_fn!(component, OpDPdyFine, true)
deriv_fn!(component, OpDPdyFine)
}
/// Returns the partial derivative of `component` with respect to the window's Y
@ -83,7 +74,7 @@ pub fn ddy_fine<F: Float>(component: F) -> F {
#[crate::macros::vectorized]
#[crate::macros::gpu_only]
pub fn ddy_coarse<F: Float>(component: F) -> F {
deriv_fn!(component, OpDPdyCoarse, true)
deriv_fn!(component, OpDPdyCoarse)
}
/// Returns the sum of the absolute values of [`ddx`] and [`ddy`] as a single
@ -91,7 +82,7 @@ pub fn ddy_coarse<F: Float>(component: F) -> F {
#[crate::macros::vectorized]
#[crate::macros::gpu_only]
pub fn fwidth<F: Float>(component: F) -> F {
deriv_fn!(component, OpFwidth, false)
deriv_fn!(component, OpFwidth)
}
/// Returns the sum of the absolute values of [`ddx_fine`] and [`ddy_fine`] as a
@ -99,7 +90,7 @@ pub fn fwidth<F: Float>(component: F) -> F {
#[crate::macros::vectorized]
#[crate::macros::gpu_only]
pub fn fwidth_fine<F: Float>(component: F) -> F {
deriv_fn!(component, OpFwidthFine, true)
deriv_fn!(component, OpFwidthFine)
}
/// Returns the sum of the absolute values of [`ddx_coarse`] and [`ddy_coarse`]
@ -107,5 +98,5 @@ pub fn fwidth_fine<F: Float>(component: F) -> F {
#[crate::macros::vectorized]
#[crate::macros::gpu_only]
pub fn fwidth_coarse<F: Float>(component: F) -> F {
deriv_fn!(component, OpFwidthCoarse, true)
deriv_fn!(component, OpFwidthCoarse)
}

View File

@ -38,24 +38,27 @@ pub unsafe fn report_intersection(hit: f32, hit_kind: u32) -> bool {
/// Ignores the current potential intersection, terminating the invocation that
/// executes it, and continues the ray traversal. This instruction is allowed
/// only in `any_hit` execution model. This instruction must be the last
/// instruction in a block.
/// only in `any_hit` execution model.
#[spirv_std_macros::gpu_only]
#[doc(alias = "OpIgnoreIntersectionKHR")]
#[inline]
pub unsafe fn ignore_intersection() {
asm!("OpIgnoreIntersectionKHR", "%unused = OpLabel")
#[allow(clippy::empty_loop)]
pub unsafe fn ignore_intersection() -> ! {
asm!("OpIgnoreIntersectionKHR", "%unused = OpLabel");
loop {}
}
/// Terminates the invocation that executes it, stops the ray traversal, accepts
/// the current hit, and invokes the `closest_hit` execution model
/// (if active). This instruction is allowed only in the `any_hit`
/// execution model. This instruction must be the last instruction in a block.
/// (if active). This instruction is allowed only in the `any_hit`
/// execution model.
#[spirv_std_macros::gpu_only]
#[doc(alias = "OpTerminateRayKHR")]
#[inline]
pub unsafe fn terminate_ray() {
asm!("OpTerminateRayKHR", "%unused = OpLabel")
#[allow(clippy::empty_loop)]
pub unsafe fn terminate_ray() -> ! {
asm!("OpTerminateRayKHR", "%unused = OpLabel");
loop {}
}
/// Invoke a callable shader.

View File

@ -93,26 +93,6 @@ pub use crate::macros::Image;
pub use num_traits;
pub use textures::*;
/// Calls the `OpDemoteToHelperInvocationEXT` instruction, which corresponds to discard() in HLSL
#[spirv_std_macros::gpu_only]
pub fn demote_to_helper_invocation() {
unsafe {
asm!(
"OpExtension \"SPV_EXT_demote_to_helper_invocation\"",
"OpCapability DemoteToHelperInvocationEXT",
"OpDemoteToHelperInvocationEXT"
);
}
}
/// Calls the `OpKill` instruction, which corresponds to discard() in GLSL
#[spirv_std_macros::gpu_only]
pub fn discard() {
unsafe {
asm!("OpKill", "%unused = OpLabel");
}
}
#[cfg(all(not(test), target_arch = "spirv"))]
#[panic_handler]
fn panic(_: &core::panic::PanicInfo<'_>) -> ! {

View File

@ -491,17 +491,15 @@ impl RayQuery {
#[doc(alias = "OpRayQueryGetIntersectionFrontFaceKHR")]
#[inline]
pub unsafe fn get_intersection_front_face<const INTERSECTION: u32>(&self) -> bool {
let mut result = 0u8;
let mut result: u32;
asm! {
"%u8 = OpTypeInt 8 0",
"%u32 = OpTypeInt 32 0",
"%intersection = OpConstant %u32 {intersection}",
"%result = OpRayQueryGetIntersectionFrontFaceKHR %u8 {ray_query} %intersection",
"OpStore {result} %result",
"{result} = OpRayQueryGetIntersectionFrontFaceKHR %u32 {ray_query} %intersection",
ray_query = in(reg) self,
intersection = const INTERSECTION,
result = in(reg) &mut result,
result = out(reg) result,
}
result != 0
@ -513,14 +511,13 @@ impl RayQuery {
#[doc(alias = "OpRayQueryGetIntersectionCandidateAABBOpaqueKHR")]
#[inline]
pub unsafe fn get_intersection_candidate_aabb_opaque(&self) -> bool {
let mut result = 0u8;
let result: u32;
asm! {
"%u8 = OpTypeInt 8 0",
"%result = OpRayQueryGetIntersectionCandidateAABBOpaqueKHR %u8 {ray_query}",
"OpStore {result} %result",
"%u32 = OpTypeInt 8 0",
"{result} = OpRayQueryGetIntersectionCandidateAABBOpaqueKHR %u32 {ray_query}",
ray_query = in(reg) self,
result = in(reg) &mut result,
result = out(reg) result,
}
result != 0

View File

@ -0,0 +1,9 @@
// build-pass
//
// compile-flags: -C target-feature=+DemoteToHelperInvocationEXT,+ext:SPV_EXT_demote_to_helper_invocation
#[spirv(fragment)]
pub fn main() {
unsafe { spirv_std::arch::demote_to_helper_invocation() };
assert!(spirv_std::arch::is_helper_invocation());
}

View File

@ -1,9 +1,9 @@
// build-pass
// compile-flags: -C target-feature=+GeometryStreams
#[spirv(geometry(input_lines = 2, output_points = 2))]
pub fn main() {
unsafe {
asm!("OpCapability GeometryStreams");
spirv_std::arch::emit_stream_vertex::<2>();
};
}

View File

@ -1,9 +1,9 @@
// build-pass
// compile-flags: -Ctarget-feature=+Geometry
#[spirv(geometry(input_lines = 2, output_points = 2))]
pub fn main() {
unsafe {
asm!("OpCapability Geometry");
spirv_std::arch::emit_vertex();
};
}

View File

@ -1,9 +1,9 @@
// build-pass
// compile-flags: -Ctarget-feature=+Geometry
#[spirv(geometry(input_lines = 2, output_points = 2))]
pub fn main() {
unsafe {
asm!("OpCapability Geometry");
spirv_std::arch::end_primitive();
};
}

View File

@ -1,9 +1,9 @@
// build-pass
// compile-flags: -C target-feature=+GeometryStreams
#[spirv(geometry(input_lines = 2, output_points = 2))]
pub fn main() {
unsafe {
asm!("OpCapability GeometryStreams");
spirv_std::arch::end_stream_primitive::<2>();
};
}

View File

@ -1,4 +1,5 @@
// build-pass
// compile-flags: -Ctarget-feature=+RayTracingKHR,+ext:SPV_KHR_ray_tracing
#[spirv(ray_generation)]
// Rustfmt will eat long attributes (https://github.com/rust-lang/rustfmt/issues/4579)
@ -9,8 +10,6 @@ pub fn main(
#[spirv(incoming_callable_data)] payload: &glam::Vec3,
) {
unsafe {
asm!(r#"OpExtension "SPV_KHR_ray_tracing""#);
asm!("OpCapability RayTracingKHR");
spirv_std::arch::execute_callable::<_, 5>(payload);
}
}

6
tests/ui/arch/kill.rs Normal file
View File

@ -0,0 +1,6 @@
// build-pass
#[spirv(fragment)]
pub fn main() {
spirv_std::arch::kill();
}

View File

@ -1,3 +1,7 @@
// build-pass
//
// compile-flags: -C target-feature=+StorageImageWriteWithoutFormat
use glam::*;
#[spirv(compute(threads(1)))]
@ -6,7 +10,6 @@ pub fn main_cs(
#[spirv(storage_buffer, descriptor_set = 0, binding = 0)] points_buffer: &mut [UVec2; 100],
#[spirv(descriptor_set = 1, binding = 1)] image: &spirv_std::Image!(2D, type=f32, sampled=false),
) {
unsafe { asm!("OpCapability StorageImageWriteWithoutFormat") };
let position = id.xy();
for i in 0..100usize {
let p0 = &points_buffer[i];

View File

@ -1,4 +1,5 @@
// build-pass
// compile-flags: -C target-feature=+ImageQuery
use spirv_std::{arch, Image};
@ -7,6 +8,5 @@ pub fn main(
#[spirv(descriptor_set = 0, binding = 0)] image: &Image!(2D, type=f32, sampled),
output: &mut u32,
) {
unsafe { asm!("OpCapability ImageQuery") };
*output = image.query_levels();
}

View File

@ -1,5 +1,6 @@
// build-fail
// normalize-stderr-test "\S*/crates/spirv-std/src/" -> "$$SPIRV_STD_SRC/"
// compile-flags: -C target-feature=+ImageQuery
use spirv_std::{arch, Image};
@ -8,6 +9,5 @@ pub fn main(
#[spirv(descriptor_set = 0, binding = 0)] image: &Image!(rect, type=f32, sampled),
output: &mut u32,
) {
unsafe { asm!("OpCapability ImageQuery") };
*output = image.query_levels();
}

View File

@ -1,4 +1,5 @@
// build-pass
// compile-flags: -C target-feature=+ImageQuery
use spirv_std::{arch, Image, Sampler};
@ -8,6 +9,5 @@ pub fn main(
#[spirv(descriptor_set = 0, binding = 1)] sampler: &Sampler,
output: &mut glam::Vec2,
) {
unsafe { asm!("OpCapability ImageQuery") };
*output = image.query_lod(*sampler, glam::Vec2::new(0.0, 1.0));
}

View File

@ -1,5 +1,6 @@
// build-fail
// normalize-stderr-test "\S*/crates/spirv-std/src/" -> "$$SPIRV_STD_SRC/"
// compile-flags: -C target-feature=+ImageQuery
use spirv_std::{arch, Image, Sampler};
@ -9,6 +10,5 @@ pub fn main(
#[spirv(descriptor_set = 0, binding = 1)] sampler: &Sampler,
output: &mut glam::Vec2,
) {
unsafe { asm!("OpCapability ImageQuery") };
*output = image.query_lod(*sampler, glam::Vec2::new(0.0, 1.0));
}

View File

@ -1,4 +1,5 @@
// build-pass
// compile-flags: -C target-feature=+ImageQuery
use spirv_std::{arch, Image};
@ -7,6 +8,5 @@ pub fn main(
#[spirv(descriptor_set = 0, binding = 0)] image: &Image!(2D, type=f32, sampled, multisampled),
output: &mut u32,
) {
unsafe { asm!("OpCapability ImageQuery") };
*output = image.query_samples();
}

View File

@ -1,4 +1,5 @@
// build-pass
// compile-flags: -C target-feature=+ImageQuery
use spirv_std::{arch, Image};
@ -7,6 +8,5 @@ pub fn main(
#[spirv(descriptor_set = 0, binding = 0)] image: &Image!(2D, type=f32, sampled=false),
output: &mut glam::UVec2,
) {
unsafe { asm!("OpCapability ImageQuery") };
*output = image.query_size();
}

View File

@ -1,5 +1,6 @@
// build-fail
// normalize-stderr-test "\S*/crates/spirv-std/src/" -> "$$SPIRV_STD_SRC/"
// compile-flags: -C target-feature=+ImageQuery
use spirv_std::{arch, Image};
@ -8,6 +9,5 @@ pub fn main(
#[spirv(descriptor_set = 0, binding = 0)] image: &Image!(2D, type=f32, sampled),
output: &mut glam::UVec2,
) {
unsafe { asm!("OpCapability ImageQuery") };
*output = image.query_size();
}

View File

@ -1,4 +1,5 @@
// build-pass
// compile-flags: -C target-feature=+ImageQuery
use spirv_std::{arch, Image};
@ -7,6 +8,5 @@ pub fn main(
#[spirv(descriptor_set = 0, binding = 0)] image: &Image!(2D, type=f32, sampled),
output: &mut glam::UVec2,
) {
unsafe { asm!("OpCapability ImageQuery") };
*output = image.query_size_lod(0);
}

View File

@ -1,5 +1,6 @@
// build-fail
// normalize-stderr-test "\S*/crates/spirv-std/src/" -> "$$SPIRV_STD_SRC/"
// compile-flags: -C target-feature=+ImageQuery
use spirv_std::{arch, Image};
@ -8,6 +9,5 @@ pub fn main(
#[spirv(descriptor_set = 0, binding = 0)] image: &Image!(rect, type=f32, sampled),
output: &mut glam::UVec2,
) {
unsafe { asm!("OpCapability ImageQuery") };
*output = image.query_size_lod(0);
}

View File

@ -1,5 +1,6 @@
// Test `OpImageRead`
// build-pass
// compile-flags: -C target-feature=+StorageImageReadWithoutFormat
use spirv_std::{arch, Image};
@ -8,7 +9,6 @@ pub fn main(
#[spirv(descriptor_set = 0, binding = 0)] image: &Image!(2D, type=f32, sampled=false),
output: &mut glam::Vec4,
) {
unsafe { asm!("OpCapability StorageImageReadWithoutFormat") };
let coords = image.read(glam::IVec2::new(0, 1));
*output = coords;
}

View File

@ -1,5 +1,6 @@
// Test `OpImageWrite`
// build-pass
// compile-flags: -C target-feature=+StorageImageWriteWithoutFormat
use spirv_std::{arch, Image};
@ -9,7 +10,6 @@ pub fn main(
#[spirv(descriptor_set = 0, binding = 0)] image: &Image!(2D, type=f32, sampled=false),
) {
unsafe {
asm!("OpCapability StorageImageWriteWithoutFormat");
image.write(glam::UVec2::new(0, 1), texels);
}
}

View File

@ -2,6 +2,7 @@
// contain references, and where the `T` values aren't immediatelly loaded from.
// build-pass
// compile-flags: -C target-feature=+VariablePointers
use spirv_std as _;
@ -26,7 +27,6 @@ pub fn main(
bool_out: &mut bool,
vec_out: &mut Vec2,
) {
unsafe { asm!("OpCapability VariablePointers") };
*scalar_out = deep_load(&&123);
*bool_out = vec_in == &Vec2::ZERO;
*vec_out = deep_transpose(&ROT90) * *vec_in;

View File

@ -7,4 +7,3 @@ use spirv_std as _;
pub fn main() {
unsafe { spirv_std::arch::terminate_ray() }
}