naga::front::spv

Struct Frontend

source
pub struct Frontend<I> {
Show 29 fields data: I, data_offset: usize, state: ModuleState, layouter: Layouter, temp_bytes: Vec<u8>, ext_glsl_id: Option<Word>, future_decor: FastHashMap<Word, Decoration>, future_member_decor: FastHashMap<(Word, u32), Decoration>, lookup_member: FastHashMap<(Handle<Type>, u32), LookupMember>, handle_sampling: FastHashMap<Handle<GlobalVariable>, SamplingFlags>, upgrade_atomics: Upgrades, lookup_type: FastHashMap<Word, LookupType>, lookup_void_type: Option<Word>, lookup_storage_buffer_types: FastHashMap<Handle<Type>, StorageAccess>, lookup_constant: FastHashMap<Word, LookupConstant>, lookup_variable: FastHashMap<Word, LookupVariable>, lookup_expression: FastHashMap<Word, LookupExpression>, lookup_load_override: FastHashMap<Word, LookupLoadOverride>, lookup_sampled_image: FastHashMap<Word, LookupSampledImage>, lookup_function_type: FastHashMap<Word, LookupFunctionType>, lookup_function: FastHashMap<Word, LookupFunction>, lookup_entry_point: FastHashMap<Word, EntryPoint>, deferred_entry_points: Vec<(EntryPoint, Word)>, deferred_function_calls: Vec<Word>, dummy_functions: Arena<Function>, function_call_graph: GraphMap<Word, (), Directed>, options: Options, switch_cases: FastIndexMap<Word, (usize, Vec<i32>)>, gl_per_vertex_builtin_access: FastHashSet<BuiltIn>,
}

Fields§

§data: I§data_offset: usize§state: ModuleState§layouter: Layouter§temp_bytes: Vec<u8>§ext_glsl_id: Option<Word>§future_decor: FastHashMap<Word, Decoration>§future_member_decor: FastHashMap<(Word, u32), Decoration>§lookup_member: FastHashMap<(Handle<Type>, u32), LookupMember>§handle_sampling: FastHashMap<Handle<GlobalVariable>, SamplingFlags>§upgrade_atomics: Upgrades

A record of what is accessed by Atomic statements we’ve generated, so we can upgrade the types of their operands.

§lookup_type: FastHashMap<Word, LookupType>§lookup_void_type: Option<Word>§lookup_storage_buffer_types: FastHashMap<Handle<Type>, StorageAccess>§lookup_constant: FastHashMap<Word, LookupConstant>§lookup_variable: FastHashMap<Word, LookupVariable>§lookup_expression: FastHashMap<Word, LookupExpression>§lookup_load_override: FastHashMap<Word, LookupLoadOverride>§lookup_sampled_image: FastHashMap<Word, LookupSampledImage>§lookup_function_type: FastHashMap<Word, LookupFunctionType>§lookup_function: FastHashMap<Word, LookupFunction>§lookup_entry_point: FastHashMap<Word, EntryPoint>§deferred_entry_points: Vec<(EntryPoint, Word)>§deferred_function_calls: Vec<Word>§dummy_functions: Arena<Function>§function_call_graph: GraphMap<Word, (), Directed>§options: Options§switch_cases: FastIndexMap<Word, (usize, Vec<i32>)>

Maps for a switch from a case target to the respective body and associated literals that use that target block id.

Used to preserve allocations between instruction parsing.

§gl_per_vertex_builtin_access: FastHashSet<BuiltIn>

Tracks access to gl_PerVertex’s builtins, it is used to cull unused builtins since initializing those can affect performance and the mere presence of some of these builtins might cause backends to error since they might be unsupported.

The problematic builtins are: PointSize, ClipDistance and CullDistance.

glslang declares those by default even though they are never written to (see https://github.com/KhronosGroup/glslang/issues/1868)

Implementations§

source§

impl<I: Iterator<Item = u32>> Frontend<I>

source

pub(super) fn add_call(&mut self, from: Word, to: Word) -> Handle<Function>

source

pub(super) fn parse_function( &mut self, module: &mut Module, ) -> Result<(), Error>

source

pub(super) fn process_entry_point( &mut self, module: &mut Module, ep: EntryPoint, fun_id: u32, ) -> Result<(), Error>

source§

impl<I: Iterator<Item = u32>> Frontend<I>

source

pub(super) fn parse_image_couple(&mut self) -> Result<(), Error>

source

pub(super) fn parse_image_uncouple( &mut self, block_id: Word, ) -> Result<(), Error>

source

pub(super) fn parse_image_write( &mut self, words_left: u16, ctx: &mut BlockContext<'_>, emitter: &mut Emitter, block: &mut Block, body_idx: usize, ) -> Result<Statement, Error>

source

pub(super) fn parse_image_load( &mut self, words_left: u16, ctx: &mut BlockContext<'_>, emitter: &mut Emitter, block: &mut Block, block_id: Word, body_idx: usize, ) -> Result<(), Error>

source

pub(super) fn parse_image_sample( &mut self, words_left: u16, options: SamplingOptions, ctx: &mut BlockContext<'_>, emitter: &mut Emitter, block: &mut Block, block_id: Word, body_idx: usize, ) -> Result<(), Error>

source

pub(super) fn parse_image_query_size( &mut self, at_level: bool, ctx: &mut BlockContext<'_>, emitter: &mut Emitter, block: &mut Block, block_id: Word, body_idx: usize, ) -> Result<(), Error>

source

pub(super) fn parse_image_query_other( &mut self, query: ImageQuery, ctx: &mut BlockContext<'_>, block_id: Word, ) -> Result<(), Error>

source§

impl<I: Iterator<Item = u32>> Frontend<I>

source

pub fn new(data: I, options: &Options) -> Self

source

fn span_from(&self, from: usize) -> Span

source

fn span_from_with_op(&self, from: usize) -> Span

source

fn next(&mut self) -> Result<u32, Error>

source

fn next_inst(&mut self) -> Result<Instruction, Error>

source

fn next_string(&mut self, count: u16) -> Result<(String, u16), Error>

source

fn next_decoration( &mut self, inst: Instruction, base_words: u16, dec: &mut Decoration, ) -> Result<(), Error>

source

fn get_expr_handle( &self, id: Word, lookup: &LookupExpression, ctx: &mut BlockContext<'_>, emitter: &mut Emitter, block: &mut Block, body_idx: usize, ) -> Handle<Expression>

Return the Naga Expression to use in body_idx to refer to the SPIR-V result id.

Ideally, we would just have a map from each SPIR-V instruction id to the Handle for the Naga Expression we generated for it. Unfortunately, SPIR-V and Naga IR are different enough that such a straightforward relationship isn’t possible.

In SPIR-V, an instruction’s result id can be used by any instruction dominated by that instruction. In Naga, an Expression is only in scope for the remainder of its Block. In pseudocode:

    loop {
        a = f();
        g(a);
        break;
    }
    h(a);

Suppose the calls to f, g, and h are SPIR-V instructions. In SPIR-V, both the g and h instructions are allowed to refer to a, because the loop body, including f, dominates both of them.

But if a is a Naga Expression, its scope ends at the end of the block it’s evaluated in: the loop body. Thus, while the Expression we generate for g can refer to a, the one we generate for h cannot.

Instead, the SPIR-V front end must generate Naga IR like this:

    var temp; // INTRODUCED
    loop {
        a = f();
        g(a);
        temp = a; // INTRODUCED
    }
    h(temp); // ADJUSTED

In other words, where a is in scope, Expressions can refer to it directly; but once it is out of scope, we need to spill it to a temporary and refer to that instead.

Given a SPIR-V expression id and the index body_idx of the body that wants to refer to it:

  • If the Naga Expression we generated for id is in scope in body_idx, then we simply return its Handle<Expression>.

  • Otherwise, introduce a new LocalVariable, and add an entry to BlockContext::phis to arrange for id’s value to be spilled to it. Then emit a fresh Load of that temporary variable for use in body_idx’s block, and return its Handle.

The SPIR-V domination rule ensures that the introduced LocalVariable will always have been initialized before it is used.

lookup must be the LookupExpression for id.

body_idx argument must be the index of the Body that hopes to use id’s Expression.

source

fn parse_expr_unary_op( &mut self, ctx: &mut BlockContext<'_>, emitter: &mut Emitter, block: &mut Block, block_id: Word, body_idx: usize, op: UnaryOperator, ) -> Result<(), Error>

source

fn parse_expr_binary_op( &mut self, ctx: &mut BlockContext<'_>, emitter: &mut Emitter, block: &mut Block, block_id: Word, body_idx: usize, op: BinaryOperator, ) -> Result<(), Error>

source

fn parse_expr_unary_op_sign_adjusted( &mut self, ctx: &mut BlockContext<'_>, emitter: &mut Emitter, block: &mut Block, block_id: Word, body_idx: usize, op: UnaryOperator, ) -> Result<(), Error>

A more complicated version of the unary op, where we force the operand to have the same type as the result.

source

fn parse_expr_binary_op_sign_adjusted( &mut self, ctx: &mut BlockContext<'_>, emitter: &mut Emitter, block: &mut Block, block_id: Word, body_idx: usize, op: BinaryOperator, anchor: SignAnchor, ) -> Result<(), Error>

A more complicated version of the binary op, where we force the operand to have the same type as the result. This is mostly needed for “i++” and “i–” coming from GLSL.

source

fn parse_expr_int_comparison( &mut self, ctx: &mut BlockContext<'_>, emitter: &mut Emitter, block: &mut Block, block_id: Word, body_idx: usize, op: BinaryOperator, kind: ScalarKind, ) -> Result<(), Error>

A version of the binary op where one or both of the arguments might need to be casted to a specific integer kind (unsigned or signed), used for operations like OpINotEqual or OpUGreaterThan.

source

fn parse_expr_shift_op( &mut self, ctx: &mut BlockContext<'_>, emitter: &mut Emitter, block: &mut Block, block_id: Word, body_idx: usize, op: BinaryOperator, ) -> Result<(), Error>

source

fn parse_expr_derivative( &mut self, ctx: &mut BlockContext<'_>, emitter: &mut Emitter, block: &mut Block, block_id: Word, body_idx: usize, (axis, ctrl): (DerivativeAxis, DerivativeControl), ) -> Result<(), Error>

source

fn insert_composite( &self, root_expr: Handle<Expression>, root_type_id: Word, object_expr: Handle<Expression>, selections: &[Word], type_arena: &UniqueArena<Type>, expressions: &mut Arena<Expression>, span: Span, ) -> Result<Handle<Expression>, Error>

source

fn get_exp_and_base_ty_handles( &self, pointer_id: Word, ctx: &mut BlockContext<'_>, emitter: &mut Emitter, block: &mut Block, body_idx: usize, ) -> Result<(Handle<Expression>, Handle<Type>), Error>

Return the Naga Expression for pointer_id, and its referent Type.

Return a Handle for a Naga Expression that holds the value of the SPIR-V instruction pointer_id, along with the Type to which it is a pointer.

This may entail spilling pointer_id’s value to a temporary: see get_expr_handle’s documentation.

source

fn parse_atomic_expr_with_value( &mut self, inst: Instruction, emitter: &mut Emitter, ctx: &mut BlockContext<'_>, block: &mut Block, block_id: Word, body_idx: usize, atomic_function: AtomicFunction, ) -> Result<(), Error>

source

fn next_block( &mut self, block_id: Word, ctx: &mut BlockContext<'_>, ) -> Result<(), Error>

Add the next SPIR-V block’s contents to block_ctx.

Except for the function’s entry block, block_id should be the label of a block we’ve seen mentioned before, with an entry in block_ctx.body_for_label to tell us which Body it contributes to.

source

fn make_expression_storage( &mut self, globals: &Arena<GlobalVariable>, constants: &Arena<Constant>, overrides: &Arena<Override>, ) -> Arena<Expression>

source

fn switch(&mut self, state: ModuleState, op: Op) -> Result<(), Error>

source

fn patch_statements( &mut self, statements: &mut Block, expressions: &mut Arena<Expression>, fun_parameter_sampling: &mut [SamplingFlags], ) -> Result<(), Error>

Walk the statement tree and patch it in the following cases:

  1. Function call targets are replaced by deferred_function_calls map
source

fn patch_function( &mut self, handle: Option<Handle<Function>>, fun: &mut Function, ) -> Result<(), Error>

source

pub fn parse(self) -> Result<Module, Error>

source

fn parse_capability(&mut self, inst: Instruction) -> Result<(), Error>

source

fn parse_extension(&mut self, inst: Instruction) -> Result<(), Error>

source

fn parse_ext_inst_import(&mut self, inst: Instruction) -> Result<(), Error>

source

fn parse_memory_model(&mut self, inst: Instruction) -> Result<(), Error>

source

fn parse_entry_point(&mut self, inst: Instruction) -> Result<(), Error>

source

fn parse_execution_mode(&mut self, inst: Instruction) -> Result<(), Error>

source

fn parse_string(&mut self, inst: Instruction) -> Result<(), Error>

source

fn parse_source(&mut self, inst: Instruction) -> Result<(), Error>

source

fn parse_source_extension(&mut self, inst: Instruction) -> Result<(), Error>

source

fn parse_name(&mut self, inst: Instruction) -> Result<(), Error>

source

fn parse_member_name(&mut self, inst: Instruction) -> Result<(), Error>

source

fn parse_module_processed(&mut self, inst: Instruction) -> Result<(), Error>

source

fn parse_decorate(&mut self, inst: Instruction) -> Result<(), Error>

source

fn parse_member_decorate(&mut self, inst: Instruction) -> Result<(), Error>

source

fn parse_type_void(&mut self, inst: Instruction) -> Result<(), Error>

source

fn parse_type_bool( &mut self, inst: Instruction, module: &mut Module, ) -> Result<(), Error>

source

fn parse_type_int( &mut self, inst: Instruction, module: &mut Module, ) -> Result<(), Error>

source

fn parse_type_float( &mut self, inst: Instruction, module: &mut Module, ) -> Result<(), Error>

source

fn parse_type_vector( &mut self, inst: Instruction, module: &mut Module, ) -> Result<(), Error>

source

fn parse_type_matrix( &mut self, inst: Instruction, module: &mut Module, ) -> Result<(), Error>

source

fn parse_type_function(&mut self, inst: Instruction) -> Result<(), Error>

source

fn parse_type_pointer( &mut self, inst: Instruction, module: &mut Module, ) -> Result<(), Error>

source

fn parse_type_array( &mut self, inst: Instruction, module: &mut Module, ) -> Result<(), Error>

source

fn parse_type_runtime_array( &mut self, inst: Instruction, module: &mut Module, ) -> Result<(), Error>

source

fn parse_type_struct( &mut self, inst: Instruction, module: &mut Module, ) -> Result<(), Error>

source

fn parse_type_image( &mut self, inst: Instruction, module: &mut Module, ) -> Result<(), Error>

source

fn parse_type_sampled_image(&mut self, inst: Instruction) -> Result<(), Error>

source

fn parse_type_sampler( &mut self, inst: Instruction, module: &mut Module, ) -> Result<(), Error>

source

fn parse_constant( &mut self, inst: Instruction, module: &mut Module, ) -> Result<(), Error>

source

fn parse_composite_constant( &mut self, inst: Instruction, module: &mut Module, ) -> Result<(), Error>

source

fn parse_null_constant( &mut self, inst: Instruction, module: &mut Module, ) -> Result<(), Error>

source

fn parse_bool_constant( &mut self, inst: Instruction, value: bool, module: &mut Module, ) -> Result<(), Error>

source

fn insert_parsed_constant( &mut self, module: &mut Module, id: u32, type_id: u32, ty: Handle<Type>, init: Handle<Expression>, span: Span, ) -> Result<(), Error>

source

fn parse_global_variable( &mut self, inst: Instruction, module: &mut Module, ) -> Result<(), Error>

source

fn record_atomic_access( &mut self, ctx: &BlockContext<'_>, handle: Handle<Expression>, ) -> Result<Handle<Type>, Error>

Record an atomic access to some component of a global variable.

Given handle, an expression referring to a scalar that has had an atomic operation applied to it, descend into the expression, noting which global variable it ultimately refers to, and which struct fields of that global’s value it accesses.

Return the handle of the type of the expression.

If the expression doesn’t actually refer to something in a global variable, we can’t upgrade its type in a way that Naga validation would pass, so reject the input instead.

Auto Trait Implementations§

§

impl<I> Freeze for Frontend<I>
where I: Freeze,

§

impl<I> RefUnwindSafe for Frontend<I>
where I: RefUnwindSafe,

§

impl<I> Send for Frontend<I>
where I: Send,

§

impl<I> Sync for Frontend<I>
where I: Sync,

§

impl<I> Unpin for Frontend<I>
where I: Unpin,

§

impl<I> UnwindSafe for Frontend<I>
where I: UnwindSafe,

Blanket Implementations§

source§

impl<T> Any for T
where T: 'static + ?Sized,

source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
source§

impl<T> Borrow<T> for T
where T: ?Sized,

source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
source§

impl<T> From<T> for T

source§

fn from(t: T) -> T

Returns the argument unchanged.

source§

impl<T, U> Into<U> for T
where U: From<T>,

source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

source§

type Error = Infallible

The type returned in the event of a conversion error.
source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.