You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
SPIR-V has two main types of execution models: Kernels and shaders. Kernels are intended for OpenCL-ingestion with clCreateProgramWithIL. Shaders, which includes the execution models Vertex, Fragment, and GLCompute (Vulkan/OpenGL compute shaders) are intended for use with Vulkan (and OpenGL). Each execution model in SPIR-V comes with different rules, but the most notable changes are between Kernels and non-Kernels.
One of the main differences is that non-kernels do, by default, not allow (useful) pointers. Pointers may be declared and used, however, they must always be resolvable to a static location. This is a consequence of the Logical addressing model (see OpMemoryModel), which is only address models that shaders are allowed to use without extensions (more on this later). Quoting SPIR-V spec; 2.18 "Memory Model":
The Logical addressing model means pointers are abstract, having no physical size or numeric value. In this mode, pointers must be created only from existing objects, and they must not be stored into an object, unless additional capabilities, e.g., VariablePointers, are declared to add such functionality.
This also means no pointers casting, no pointer arithmetic, etc.
This is quite a limitation for a general-purpose language like Zig, where the use of pointers is pretty normal. For this reason, the SPIR-V backend currently focusses on OpenCL, where all these operations are allowed.
Another problem related to this is is that shaders do not support "generic" pointers: All pointers must be ascribed with the real storage class that they are placed in, and the equivalent of @addrSpaceCast is not allowed. I think that it would be best if there are no surprises regarding the result of taking the address of variables that don't have an explicit address space: these should all result in a generic pointer, because too much existing code (mainly hinting at support code in the standard library etc) would break otherwise. Related: #15232.
In order for Zig to support shaders for Vulkan or OpenGL, there needs to be some way to work with or around these limitations. I can see a few ways forward.
Forbidding "dynamic pointers" pointers at runtime
This option consists of pulling in the SPIR-V limitations into Zig semantics: During semantic analysis, Zig would check whether we are "confusing" the origin location of any pointers, and emit an error if so. One positive aspect of this is that it may integrate well with some comptime processing for the other backends (for example, comptime memory islands?).
If code that does not use dynamic pointers can be made to adhere to the SPIR-V semantics, I think that this solution is not so bad as it may sound. Basically "just don't use pointers".
One thing that should be noted here is that any comptime processing should not interfere with these dynamic pointers at runtime. The goal here is to prevent as little existing Zig code to break as possible.
VK_KHR_buffer_device_address
This Vulkan extension (and corresponding SPIR-V extension) allows emitting some pointer operations, specifically those which affect pointers placed in Physical Storage Buffers. Pointer arithmetic, casting, etc, are all allowed for these pointers. Note that this only partially mitigates the problem, as for example function locals, global variables, and (instance-) private variables will not be affected by this. This would essentially be an extension to the above.
Lifting pointers during code generation
Most pointer operations can be partially lifted out, and we can essentially pretend that these operations are supported while they are implemented using integer operations instead. The original object that a pointer points to can be found by inlining functions. This would be a little bit impractical to the size of generated code (though can be partially mitigated by monomorphizing), but most GPU code is inlined anyway and so should in principle have little effect on the final shader binary. I believe that this is the approach that clspv takes. This would still have its limitations, of course: This type of processing can cause combinatoric explosion, and does not handle all situations. It also means applying for example the following transformations:
SPIR-V has two main types of execution models: Kernels and shaders. Kernels are intended for OpenCL-ingestion with
clCreateProgramWithIL
. Shaders, which includes the execution modelsVertex
,Fragment
, andGLCompute
(Vulkan/OpenGL compute shaders) are intended for use with Vulkan (and OpenGL). Each execution model in SPIR-V comes with different rules, but the most notable changes are between Kernels and non-Kernels.One of the main differences is that non-kernels do, by default, not allow (useful) pointers. Pointers may be declared and used, however, they must always be resolvable to a static location. This is a consequence of the
Logical
addressing model (see OpMemoryModel), which is only address models that shaders are allowed to use without extensions (more on this later). Quoting SPIR-V spec; 2.18 "Memory Model":This also means no pointers casting, no pointer arithmetic, etc.
This is quite a limitation for a general-purpose language like Zig, where the use of pointers is pretty normal. For this reason, the SPIR-V backend currently focusses on OpenCL, where all these operations are allowed.
Another problem related to this is is that shaders do not support "generic" pointers: All pointers must be ascribed with the real storage class that they are placed in, and the equivalent of
@addrSpaceCast
is not allowed. I think that it would be best if there are no surprises regarding the result of taking the address of variables that don't have an explicit address space: these should all result in a generic pointer, because too much existing code (mainly hinting at support code in the standard library etc) would break otherwise. Related: #15232.In order for Zig to support shaders for Vulkan or OpenGL, there needs to be some way to work with or around these limitations. I can see a few ways forward.
Forbidding "dynamic pointers" pointers at runtime
This option consists of pulling in the SPIR-V limitations into Zig semantics: During semantic analysis, Zig would check whether we are "confusing" the origin location of any pointers, and emit an error if so. One positive aspect of this is that it may integrate well with some comptime processing for the other backends (for example, comptime memory islands?).
If code that does not use dynamic pointers can be made to adhere to the SPIR-V semantics, I think that this solution is not so bad as it may sound. Basically "just don't use pointers".
One thing that should be noted here is that any comptime processing should not interfere with these dynamic pointers at runtime. The goal here is to prevent as little existing Zig code to break as possible.
VK_KHR_buffer_device_address
This Vulkan extension (and corresponding SPIR-V extension) allows emitting some pointer operations, specifically those which affect pointers placed in Physical Storage Buffers. Pointer arithmetic, casting, etc, are all allowed for these pointers. Note that this only partially mitigates the problem, as for example function locals, global variables, and (instance-) private variables will not be affected by this. This would essentially be an extension to the above.
Lifting pointers during code generation
Most pointer operations can be partially lifted out, and we can essentially pretend that these operations are supported while they are implemented using integer operations instead. The original object that a pointer points to can be found by inlining functions. This would be a little bit impractical to the size of generated code (though can be partially mitigated by monomorphizing), but most GPU code is inlined anyway and so should in principle have little effect on the final shader binary. I believe that this is the approach that clspv takes. This would still have its limitations, of course: This type of processing can cause combinatoric explosion, and does not handle all situations. It also means applying for example the following transformations:
to
Needless to say this solution is relatively complicated to implement.
The text was updated successfully, but these errors were encountered: