Add memory_barrier and control_barrier (#519)

This commit is contained in:
XAMPPRocky 2021-04-01 16:00:30 +02:00 committed by GitHub
parent 40359bdb56
commit dd60da0be6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 212 additions and 0 deletions

View File

@ -6,10 +6,14 @@
use crate::{scalar::Scalar, vector::Vector};
mod arithmetic;
#[cfg(feature = "const-generics")]
mod barrier;
mod derivative;
mod primitive;
pub use arithmetic::*;
#[cfg(feature = "const-generics")]
pub use barrier::*;
pub use derivative::*;
pub use primitive::*;

View File

@ -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,
}
}

View File

@ -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>
@ -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;
mod textures;

View File

@ -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,
}

View File

@ -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 },
>();
}
}

View File

@ -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 },
>();
}
}