Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add is_helper_invocation, and cleanup arch & asm functions #612

Merged
merged 1 commit into from
May 4, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions crates/spirv-std/src/arch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,15 @@ 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;

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::*;
Expand Down Expand Up @@ -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.
XAMPPRocky marked this conversation as resolved.
Show resolved Hide resolved
#[spirv_std_macros::gpu_only]
#[doc(alias = "OpKill", alias = "discard")]
#[allow(clippy::empty_loop)]
pub fn kill() -> ! {
unsafe {
asm!("OpKill", "%unused = OpLabel");
}
loop {}
}
48 changes: 48 additions & 0 deletions crates/spirv-std/src/arch/demote_to_helper_invocation_ext.rs
Original file line number Diff line number Diff line change
@@ -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`
Copy link
Contributor

@khyperia khyperia May 4, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Turning these into #[cfg] on the method would be nice and is something we should do eventually, but not required right now. (Same thing on derivatives, etc.)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, and I think we'd want to do more than #[cfg], as when it's cfg'ed out, the error will just be "demote_to_helper_invocation not found", when ideally the error message would tell you, you need those capabilities and extensions.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

... as in, doing a custom cfg attribute? we already decided against that in our discussions though, I'm confused

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not sure how it would be exactly, but I meant more like a macro which just expands to two versions of the function containing the cfg and a better message similar to gpu_only.

///
/// # 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
}
29 changes: 10 additions & 19 deletions crates/spirv-std/src/arch/derivative.rs
Original file line number Diff line number Diff line change
@@ -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"),
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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
Expand All @@ -83,29 +74,29 @@ 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
/// operation.
#[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
/// single operation.
#[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`]
/// as a single operation.
#[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)
}
19 changes: 11 additions & 8 deletions crates/spirv-std/src/arch/ray_tracing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
20 changes: 0 additions & 20 deletions crates/spirv-std/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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<'_>) -> ! {
Expand Down
17 changes: 7 additions & 10 deletions crates/spirv-std/src/ray_tracing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down
9 changes: 9 additions & 0 deletions tests/ui/arch/demote_to_helper_invocation.rs
Original file line number Diff line number Diff line change
@@ -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());
}
2 changes: 1 addition & 1 deletion tests/ui/arch/emit_stream_vertex.rs
Original file line number Diff line number Diff line change
@@ -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>();
};
}
2 changes: 1 addition & 1 deletion tests/ui/arch/emit_vertex.rs
Original file line number Diff line number Diff line change
@@ -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();
};
}
2 changes: 1 addition & 1 deletion tests/ui/arch/end_primitive.rs
Original file line number Diff line number Diff line change
@@ -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();
};
}
2 changes: 1 addition & 1 deletion tests/ui/arch/end_stream_primitive.rs
Original file line number Diff line number Diff line change
@@ -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>();
};
}
3 changes: 1 addition & 2 deletions tests/ui/arch/execute_callable.rs
Original file line number Diff line number Diff line change
@@ -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)
Expand All @@ -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 changes: 6 additions & 0 deletions tests/ui/arch/kill.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// build-pass

#[spirv(fragment)]
pub fn main() {
spirv_std::arch::kill();
}
5 changes: 4 additions & 1 deletion tests/ui/image/issue_527.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
// build-pass
//
// compile-flags: -C target-feature=+StorageImageWriteWithoutFormat

use glam::*;

#[spirv(compute(threads(1)))]
Expand All @@ -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];
Expand Down
2 changes: 1 addition & 1 deletion tests/ui/image/query/query_levels.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
// build-pass
// compile-flags: -C target-feature=+ImageQuery

use spirv_std::{arch, Image};

Expand All @@ -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();
}
Loading