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>
impl<I: Iterator<Item = u32>> Frontend<I>
pub(super) fn parse_image_couple(&mut self) -> Result<(), Error>
pub(super) fn parse_image_uncouple( &mut self, block_id: Word, ) -> Result<(), Error>
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>
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>
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>
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>
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>
impl<I: Iterator<Item = u32>> Frontend<I>
pub fn new(data: I, options: &Options) -> Self
fn span_from(&self, from: usize) -> Span
fn span_from_with_op(&self, from: usize) -> Span
fn next(&mut self) -> Result<u32, Error>
fn next_inst(&mut self) -> Result<Instruction, Error>
fn next_string(&mut self, count: u16) -> Result<(String, u16), Error>
fn next_decoration( &mut self, inst: Instruction, base_words: u16, dec: &mut Decoration, ) -> Result<(), Error>
sourcefn get_expr_handle(
&self,
id: Word,
lookup: &LookupExpression,
ctx: &mut BlockContext<'_>,
emitter: &mut Emitter,
block: &mut Block,
body_idx: usize,
) -> Handle<Expression>
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, Expression
s 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 forid
is in scope inbody_idx
, then we simply return itsHandle<Expression>
. -
Otherwise, introduce a new
LocalVariable
, and add an entry toBlockContext::phis
to arrange forid
’s value to be spilled to it. Then emit a freshLoad
of that temporary variable for use inbody_idx
’s block, and return itsHandle
.
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
.
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>
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>
sourcefn 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>
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.
sourcefn 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>
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.
sourcefn 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>
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.
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>
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>
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>
sourcefn 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>
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.
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>
sourcefn next_block(
&mut self,
block_id: Word,
ctx: &mut BlockContext<'_>,
) -> Result<(), Error>
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.
fn make_expression_storage( &mut self, globals: &Arena<GlobalVariable>, constants: &Arena<Constant>, overrides: &Arena<Override>, ) -> Arena<Expression>
fn switch(&mut self, state: ModuleState, op: Op) -> Result<(), Error>
sourcefn patch_statements(
&mut self,
statements: &mut Block,
expressions: &mut Arena<Expression>,
fun_parameter_sampling: &mut [SamplingFlags],
) -> Result<(), Error>
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:
- Function call targets are replaced by
deferred_function_calls
map
fn patch_function( &mut self, handle: Option<Handle<Function>>, fun: &mut Function, ) -> Result<(), Error>
pub fn parse(self) -> Result<Module, Error>
fn parse_capability(&mut self, inst: Instruction) -> Result<(), Error>
fn parse_extension(&mut self, inst: Instruction) -> Result<(), Error>
fn parse_ext_inst_import(&mut self, inst: Instruction) -> Result<(), Error>
fn parse_memory_model(&mut self, inst: Instruction) -> Result<(), Error>
fn parse_entry_point(&mut self, inst: Instruction) -> Result<(), Error>
fn parse_execution_mode(&mut self, inst: Instruction) -> Result<(), Error>
fn parse_string(&mut self, inst: Instruction) -> Result<(), Error>
fn parse_source(&mut self, inst: Instruction) -> Result<(), Error>
fn parse_source_extension(&mut self, inst: Instruction) -> Result<(), Error>
fn parse_name(&mut self, inst: Instruction) -> Result<(), Error>
fn parse_member_name(&mut self, inst: Instruction) -> Result<(), Error>
fn parse_module_processed(&mut self, inst: Instruction) -> Result<(), Error>
fn parse_decorate(&mut self, inst: Instruction) -> Result<(), Error>
fn parse_member_decorate(&mut self, inst: Instruction) -> Result<(), Error>
fn parse_type_void(&mut self, inst: Instruction) -> Result<(), Error>
fn parse_type_bool( &mut self, inst: Instruction, module: &mut Module, ) -> Result<(), Error>
fn parse_type_int( &mut self, inst: Instruction, module: &mut Module, ) -> Result<(), Error>
fn parse_type_float( &mut self, inst: Instruction, module: &mut Module, ) -> Result<(), Error>
fn parse_type_vector( &mut self, inst: Instruction, module: &mut Module, ) -> Result<(), Error>
fn parse_type_matrix( &mut self, inst: Instruction, module: &mut Module, ) -> Result<(), Error>
fn parse_type_function(&mut self, inst: Instruction) -> Result<(), Error>
fn parse_type_pointer( &mut self, inst: Instruction, module: &mut Module, ) -> Result<(), Error>
fn parse_type_array( &mut self, inst: Instruction, module: &mut Module, ) -> Result<(), Error>
fn parse_type_runtime_array( &mut self, inst: Instruction, module: &mut Module, ) -> Result<(), Error>
fn parse_type_struct( &mut self, inst: Instruction, module: &mut Module, ) -> Result<(), Error>
fn parse_type_image( &mut self, inst: Instruction, module: &mut Module, ) -> Result<(), Error>
fn parse_type_sampled_image(&mut self, inst: Instruction) -> Result<(), Error>
fn parse_type_sampler( &mut self, inst: Instruction, module: &mut Module, ) -> Result<(), Error>
fn parse_constant( &mut self, inst: Instruction, module: &mut Module, ) -> Result<(), Error>
fn parse_composite_constant( &mut self, inst: Instruction, module: &mut Module, ) -> Result<(), Error>
fn parse_null_constant( &mut self, inst: Instruction, module: &mut Module, ) -> Result<(), Error>
fn parse_bool_constant( &mut self, inst: Instruction, value: bool, module: &mut Module, ) -> Result<(), Error>
fn insert_parsed_constant( &mut self, module: &mut Module, id: u32, type_id: u32, ty: Handle<Type>, init: Handle<Expression>, span: Span, ) -> Result<(), Error>
fn parse_global_variable( &mut self, inst: Instruction, module: &mut Module, ) -> Result<(), Error>
sourcefn record_atomic_access(
&mut self,
ctx: &BlockContext<'_>,
handle: Handle<Expression>,
) -> Result<Handle<Type>, Error>
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.