Sometimes in programming languages, the compiler determines how variables are packed into memory. It might insert padding to satisfy alignment requirements, meaning you don't always know exactly how many bytes a specific variable has.
Explicit Layout is the practice of expressing in the SPIR-V exactly where every byte of a data structure resides in memory.
Note: Explicit layout requirement is based on the client API. Vulkan uses it, but OpenCL does not.
If a type has an explicit layout, it means its size, padding, and member locations are defined by SPIR-V decorations, leaving zero ambiguity for the compiler back-end.
Depending on the type, there is a different OpDecorate (or OpMemberDecorate) to apply the explicit layout.
OpTypeStructuseOffseton each memberOpTypeArrayuseArrayStride- etc (all ways can be found in the spec)
If you are passing data between the host (CPU) and the device (GPU), SPIR-V requires an explicit layout.
This means storage classes such as Uniform, StorageBuffer, PushConstant are required to have an explicit layout.
For memory that is purely local to the shader execution, the implementation wants the freedom to pack and optimize data into registers or specialized memory banks as it sees fit.
This means storage classes such as Function, Private, Workgroup are not allowed to have an explicit layout.
With SPV_KHR_workgroup_memory_explicit_layout it is possible to use with
WorkgroupSPV_EXT_descriptor_heap also changes this (partially) for
UniformConstant(used as heaps).
Imagine the following structurally identical types:
%uint = OpTypeInt 32 0
%struct_a = OpTypeStruct %uint %uint
%struct_b = OpTypeStruct %uint %uintIf you try to load from %struct_a but have a result type of %struct_b such as
%ptr = OpTypePointer PhysicalStorageBuffer %struct_a
%ac = OpAccessChain %ptr %base %int_0
%load = OpLoad %struct_b %acyou will get an error in spirv-val.
The issue is the structs might have a different explicit layout.
OpMemberDecorate %struct_a 0 Offset 0
OpMemberDecorate %struct_a 1 Offset 4
OpMemberDecorate %struct_b 0 Offset 0
OpMemberDecorate %struct_b 1 Offset 8To prevent the compiler to have to do deep comparisons, it is the responsibility for those generating SPIR-V to make sure the same OpTypeStruct ID is used in this case.
OpCopyLogical is a variant of OpCopyObject specifically designed to handle copying aggregates (array or structure) that differ only in their layout decorations.
For example if some code is trying to copy a [Uniform, explicit layout, OpTypeStruct] over to a [Function, no explicit layout, OpTypeStruct], it previously had to load/store each component of the struct one at a time. Starting in SPIR-V 1.4, OpCopyLogical can be used.
Example of this can be easily seen in a simple GLSL example
%1 = OpAccessChain %ptr_Uniform_struct_a %var %int_0
%2 = OpLoad %struct_a %1
- %3 = OpCompositeExtract %uint %2 0
- %4 = OpAccessChain %ptr_Function_uint %new_var %int_0
- OpStore %4 %3
- %5 = OpCompositeExtract %uint %2 1
- %6 = OpAccessChain %ptr_Function_uint %new_var %int_1
- OpStore %6 %5
+ %3 = OpCopyLogical %struct_b %2
+ OpStore %new_var %3The only requirement for OpCopyLogical is that it must "logically match" (the underlying type, without the decorations, must be the same).