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 memory_barrier and control_barrier #519

Merged
merged 1 commit into from
Apr 1, 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
4 changes: 4 additions & 0 deletions crates/spirv-std/src/arch.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,14 @@ use crate::{scalar::Scalar, vector::Vector};

#[cfg(feature = "const-generics")]
mod arithmetic;
#[cfg(feature = "const-generics")]
mod barrier;
mod derivative;

#[cfg(feature = "const-generics")]
pub use arithmetic::*;
#[cfg(feature = "const-generics")]
pub use barrier::*;
pub use derivative::*;

/// Result is true if any component of `vector` is true, otherwise result is
Expand Down
77 changes: 77 additions & 0 deletions crates/spirv-std/src/arch/barrier.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
use crate::memory::{Scope, Semantics};

/// Wait for other invocations of this module to reach the current point
/// of execution.
///
/// All invocations of this module within Execution scope reach this point of
/// execution before any invocation proceeds beyond it.
///
/// When Execution is [`Scope::Workgroup`] or larger, behavior is undefined
/// unless all invocations within Execution execute the same dynamic instance of
/// this instruction. When Execution is Subgroup or Invocation, the behavior of
/// this instruction in non-uniform control flow is defined by the client API.
///
/// If [`Semantics`] is not [`Semantics::None`], this instruction also serves as
/// an [`memory_barrier`] function call, and also performs and adheres to the
/// description and semantics of an [`memory_barrier`] function with the same
/// `MEMORY` and `SEMANTICS` operands. This allows atomically specifying both a
/// control barrier and a memory barrier (that is, without needing two
/// instructions). If [`Semantics`] is [`Semantics::None`], `MEMORY` is ignored.
///
/// Before SPIRV-V version 1.3, it is only valid to use this instruction with
/// `TessellationControl`, `GLCompute`, or `Kernel` execution models. There is
/// no such restriction starting with version 1.3.
///
/// If used with the `TessellationControl` execution model, it also implicitly
/// synchronizes the [`crate::storage_class::Output`] Storage Class: Writes to
/// `Output` variables performed by any invocation executed prior to a
/// [`control_barrier`] are visible to any other invocation proceeding beyond
/// that [`control_barrier`].
#[spirv_std_macros::gpu_only]
#[doc(alias = "OpControlBarrier")]
#[inline]
pub unsafe fn control_barrier<
const EXECUTION: Scope,
const MEMORY: Scope,
const SEMANTICS: Semantics,
>() {
asm! {
"%u32 = OpTypeInt 32 0",
"%execution = OpConstant %u32 {execution}",
"%memory = OpConstant %u32 {memory}",
"%semantics = OpConstant %u32 {semantics}",
"OpControlBarrier %execution %memory %semantics",
execution = const EXECUTION as u8,
memory = const MEMORY as u8,
semantics = const SEMANTICS as u8,
}
}

/// Control the order that memory accesses are observed.
///
/// Ensures that memory accesses issued before this instruction are observed
/// before memory accesses issued after this instruction. This control is
/// ensured only for memory accesses issued by this invocation and observed by
/// another invocation executing within `MEMORY` scope. If the `vulkan` memory
/// model is declared, this ordering only applies to memory accesses that
/// use the `NonPrivatePointer` memory operand or `NonPrivateTexel`
/// image operand.
///
/// `SEMANTICS` declares what kind of memory is being controlled and what kind
/// of control to apply.
///
/// To execute both a memory barrier and a control barrier,
/// see [`control_barrier`].
#[spirv_std_macros::gpu_only]
#[doc(alias = "OpMemoryBarrier")]
#[inline]
pub unsafe fn memory_barrier<const MEMORY: Scope, const SEMANTICS: Semantics>() {
asm! {
"%u32 = OpTypeInt 32 0",
"%memory = OpConstant %u32 {memory}",
"%semantics = OpConstant %u32 {semantics}",
"OpMemoryBarrier %memory %semantics",
memory = const MEMORY as u8,
semantics = const SEMANTICS as u8,
}
}
6 changes: 6 additions & 0 deletions crates/spirv-std/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,11 @@
feature(asm, register_attr, repr_simd, core_intrinsics, lang_items),
register_attr(spirv)
)]
#![cfg_attr(
feature = "const-generics",
feature(const_generics),
allow(incomplete_features)
)]
// BEGIN - Embark standard lints v0.3
// do not change or add/remove here, but one can add exceptions after this section
// for more info see: <https://github.com/EmbarkStudios/rust-ecosystem/issues/59>
Expand Down Expand Up @@ -72,6 +77,7 @@ pub extern crate spirv_std_macros as macros;
pub mod arch;
pub mod float;
pub mod integer;
pub mod memory;
pub mod scalar;
pub(crate) mod sealed;
pub mod storage_class;
Expand Down
92 changes: 92 additions & 0 deletions crates/spirv-std/src/memory.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
//! Types for handling memory ordering constraints for concurrent memory access.

#[derive(Debug, PartialEq, Eq)]
pub enum Scope {
/// Crosses multiple devices.
CrossDevice = 0,

/// The current device.
Device = 1,

/// The current workgroup.
Workgroup = 2,

/// The current subgroup.
Subgroup = 3,

/// The current invocation.
Invocation = 4,

/// The current queue family.
QueueFamily = 5,
}

#[derive(Debug, PartialEq, Eq)]
pub enum Semantics {
/// No memory semantics.
None = 0,

/// On an atomic instruction, orders memory operations provided in program
/// order after this atomic instruction against this atomic instruction. On
/// a barrier, orders memory operations provided in program order after this
/// barrier against atomic instructions before this barrier.
Acquire = 0x2,

/// On an atomic instruction, orders memory operations provided in program
/// order before this atomic instruction against this atomic instruction. On
/// a barrier, orders memory operations provided in program order before
/// this barrier against atomic instructions after this barrier.
Release = 0x4,

/// Has the properties of both [`Self::Acquire`] and [`Self::Release`] semantics. It
/// is used for read-modify-write operations.
AcquireRelease = 0x8,

/// All observers see this memory access in the same order with respect to
/// other sequentially-consistent memory accesses from this invocation.
/// If the declared memory model is `vulkan`, `SequentiallyConsistent` must
/// not be used.
SequentiallyConsistent = 0x10,

/// Apply the memory-ordering constraints to
/// [`crate::storage_class::StorageBuffer`],
/// [`crate::storage_class::PhysicalStorageBuffer`], or
/// [`crate::storage_class::Uniform`] Storage Class memory.
UniformMemory = 0x40,

/// Apply the memory-ordering constraints to subgroup memory.
SubgroupMemory = 0x80,

/// Apply the memory-ordering constraints to
/// [`crate::storage_class::Workgroup`] Storage Class memory.
WorkgroupMemory = 0x100,

/// Apply the memory-ordering constraints to
/// [`crate::storage_class::CrossWorkgroup`] Storage Class memory.
CrossWorkgroupMemory = 0x200,

/// Apply the memory-ordering constraints to
/// [`crate::storage_class::AtomicCounter`] Storage Class memory.
AtomicCounterMemory = 0x400,

/// Apply the memory-ordering constraints to image contents (types declared
/// by `OpTypeImage`), or to accesses done through pointers to the
/// [`crate::storage_class::Image`] Storage Class.
ImageMemory = 0x800,

/// Apply the memory-ordering constraints to the
/// [`crate::storage_class::Output`] Storage Class memory.
OutputMemory = 0x1000,

/// Perform an availability operation on all references in the selected
/// storage classes.
MakeAvailable = 0x2000,

/// Perform a visibility operation on all references in the selected
/// storage classes.
MakeVisible = 0x4000,

/// This access cannot be eliminated, duplicated, or combined with
/// other accesses.
Volatile = 0x8000,
}
17 changes: 17 additions & 0 deletions tests/ui/arch/control_barrier.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
// build-pass

#![feature(const_generics)]
#![allow(incomplete_features)]

use spirv_std::memory::{Scope, Semantics};

#[spirv(fragment)]
pub fn main() {
unsafe {
spirv_std::arch::control_barrier::<
{ Scope::Workgroup },
{ Scope::Workgroup },
{ Semantics::None },
>();
}
}
16 changes: 16 additions & 0 deletions tests/ui/arch/memory_barrier.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// build-pass

#![feature(const_generics)]
#![allow(incomplete_features)]

use spirv_std::memory::{Scope, Semantics};

#[spirv(fragment)]
pub fn main() {
unsafe {
spirv_std::arch::memory_barrier::<
{ Scope::Workgroup },
{ Semantics::None },
>();
}
}