cbuffers/tbuffers are handled differently than other resource types
becuase their members do not need to be qualified when using.
That is, their members are distinct entities. However, in SPIR-V,
we need to generate one single entity to hold them all for all
members in a cbuffer/tbuffer.
Previously we didn't record the marjorness annotated on each member,
which results in returning the wrong types for members.
For a struct T with only one member of type S, (T)<an-instance-of-S>
is allowed and will be interpreted as constructing an instance of
T using the instance of S as its member.
This commit add support for generating OpSpecConstant* instructions
with SpecId decorations. Spec constants are only allowed to be of
scalar boolean/integer/float types. Using spec constant as the array
size does not work at the moment.
Code had a (reasonable) assumption that sources have names, but the
entry point wasn't verifying that. For compat with prior compilers and
the annotation, we choose to support it. Unlikely other implementations,
the name is a simple hard-coded one, instead of varying, to allow for
easier deduping when the output is checked (name is in debug section).
Types and constants are interdependent. Previously we use different
members for storing them, which requires hacks to output types
and constants in the correct order to satisfy their dependencies.
However, with specialization constants, things will be more
complicated. Using the same member to store all of them solves
the problem naturally.
Specialization constants are more like variables. Unlike normal
constants, we cannot unify spec constants, even if two of them
have exactly the same arguments. Neither can we replace a spec
constant with its default value (initializer) or folding exprs
containing spec constants.
We can use the SpecId decoration to distinguish spec constants
with the same arguments, but the problem is that not all spec
constants have SpecId decorations. Only those explicitly marked
in the source code have, derived ones do not.
Also, we cannot decorate normal constants. So we can remove the
decoration member in the Constant class.
Two new Vulkan specific intrinsic resource types, SubpassInput and
SubpassInputMS, are introduced to provide a way to use subpass inputs
in Vulkan. Their signatures:
template<typename T>
class SubpassInput {
T SubpassLoad();
};
template<typename T>
class SubpassInputMS {
T SubpassLoad(int sample);
};
T can be a scalar or vector type.
If compiled without ENABLE_SPIRV_CODEGEN, the compiler will report
unknown type name for SubpassInput(MS). If compiled with SPIR-V
CodeGen and trying to use SubpassInput(MS) in DXIL CodeGen, the
compiler will report an error saying the type is Vulkan-specific.
A new Vulkan-specific attribute, vk::input_attachment_index is
introduced to provide a way to set the input attachment index for
SubpassInput(MS) objects.
This only supports .GetSamplePosition() for standard sample
positions, i.e., sample count is 1, 2, 4, 8, or 16. For other
cases, the method will just return float2(0, 0).
This requires us to regenerate the InstBuilder class and also
manually pin the SPIR-V version as 1.0 instead of relying on
spv::Version.
Also refreshed SPIRV-Tools
An implicit object is translated into the first argument to the
method call. If the implicit object is of struct type and its
fields have associated counters, we need to assign its associated
counters accordingly like other normal arguments.
For each such method, we generate associated counters for its
implicit object. At a call site, we assign the associated counters
from the real object to the ones associated with the method implict
object.
Also refreshed SPIRV-Tools
* [spirv] Better handling of ternary and binary ops.
This change does type handling more properly and removes a hack
regarding binary ops. Also fixes the way ternary ops provide hints about
usage of literal types.
This commit supports assigning nested structs as a whole,
which means to go over all fields' associated counters and
assign to the corresponding fields. The front end parsing
and semantic analysis should guarantee the type matching.
For structs containing RW/Append/Consume structured buffers, to
properly track aliasing and use the correct associated counter
for each buffer after legalization, we need to create temporary
counter variables for all struct fields that are RW/Append/Consume
structured buffers and assign them accordingly if the corresponding
buffer field is updated.
Because of structs, now we have four forms an alias RW/Append/
Consume structured buffer can be in:
* 1 (AssocCounter#1). A stand-alone variable,
* 2 (AssocCounter#2). A struct field,
* 3 (AssocCounter#3). A struct containing alias fields,
* 4 (AssocCounter#4). A nested struct containing alias fields.
For AssocCounter#3 and AssocCounter#4, it means we need to update
all fields' associated counters.
This commit only handles the first three forms.
* Loading the pointer from an alias variable turns the evaluation
info into lavalue again.
* loadIfGLValue() should suppress the immediate LValueToRValue
implicit cast.
* Counter variable emission is merged into getTypeForPotentialAliasVar
* Changed to use DeclaratorDecl instead of ValueDecl for some methods
* collectArrayStructIndices is extended to suppor raw index
Previously we will return defaultRowMajor as long as the type
is an array. That will hit an assertion failure in translateType()
for arrays of non-matrices.
Also refreshed SPIRV-Tools
When using a storage image on the lhs of a compound assignment,
we need to load the image and do the calculation with rhs first
before actually assigning back to the storage image again. That
load won't result in an OpAccessChain, actually it will result
in a rvalue.
We still want to make sure that the generated raw SPIR-V does not
have other validation errors except for allocating variables
containing pointers and returning pointers.
We need to change the type of these struct fields to have an extra
level of pointer.
A local resource always has void as its layout rule (because local
resource is not in the Uniform storage class). So in the TypeTranslator,
when we are trying to translate a structured/byte buffer resource that
has void layout rule, we know it must be a local resource. Then we
apply an extra level of pointer to it. Because of TypeTranslator is
recursive, that automatically handles both stand-alone local resources
and the ones in structs.
In the SPIRVEmitter, we need to have a way to tell whether a resource
is a local resource or not because if it is a local resource, we need to
OpLoad once to get the pointer to the aliased-to global resource.
That's why we have the containsAlias field in SpirvEvalInfo. We set it to
true in getTypeForPotentialAliasVar() for local resources. And do an
extra OpLoad to get the pointer in SPIRVEmitter if it is true.
Static variables are in the Private storage class but all methods
are generated to take pointers of the Function storage class. So
to call a method on a static variable, we need first create a
temporary variable initialized with the contents of the static
variable. After the method call, we also need to write the contents
back to the static variable in case there are side effects.