Writing GLSL for WebGL is notoriously unfriendly from a dev's perspective. Without some kind of preprocessor like glslx, there's no proper editor integration, no offline compilation or optimisation, and, worst of all, no support for importing files.
Instead of trying to go the fully custom route like glslx, this loader offloads the majority of work to HLSL, a well-established, well-supported languange and only provides the means to easily incorporate it into an existing WebGL project.
The inital HLSL file is run through the DirectX Shader Compiler (dxc), converting it to a SPIR-V binary per entrypoint. Subsequently, spriv-crosss reflection functionality is used to gather information on each program. Finally, the SPIR-V binaries are converted to GLES 3.0 via spriv-cross, renaming some interface variables to ensure compatibility. Introspection info from all entry-points is merged and exported as JSON.
Only UBOs are supported at the moment. Plain uniform arrays will be ignored, with the only exception being sampler objects. As long as you stick to cbuffers in HLSL, you should never encounter this limitation.
Currently, uniforms and attributes are limited to the following types: float, int and bool, including their vector types, and float4x4 matrices. This list is likely to be expanded in the future.
Textures and sampler states need to be specifically marked to not produce weird output. See Textures and SamplerState.
Sampler state is not represented in the introspection object and needs to be carried across manually.
Certain symbol names can lead to unexpected behaviour. It's best to avoid any names starting with gl_ or _ in general.
UBOs are forced to std140 layout.
All entry points of a file are assumed to be for the same program, which is why there's only one introspection object. Bundling multiple programs per file will lead to incorrect introspection data and undefined behaviour.
Interface variables are matched internally by location, meaning that they may be out of whack when imported from different files, even if the names match. To ensure compatibility, you should manually specify the location using the [[vk::location(n)]] attribute, or only combine shaders imported from the same file.
For a dxc specifc list of limitations, see dxcs SPIR-V docs.
Buffer attribute indices/locations can be explicitly specified using the [[vk::location(n)]] attribute. This is carried over to the exported introspection object.
HLSL ordinarily does not support combined Texture-Sampler objects the way GLES 3.0 expects them.
To work around this, we flag both Texture and SamplerState using the [[vk::combinedImageSampler]] attribute and bind them to the same unit using [[vk::binding(1)]].
This results in GLES 3.0 program expecting a uniform named g_MyTexture of type sampler2D. The binding is carried over to the exported introspection object.
stage specifies the shader model and -stage to compile to, vs and ps meaning "vertex shader" and "pixel shader" respectively, and 6_7 being the shader model version.
If you want to keep your main functions named the same across stages, you can also use dxcs predefined version macros to conditionally include them.
Motivations
Writing GLSL for WebGL is notoriously unfriendly from a dev's perspective. Without some kind of preprocessor like glslx, there's no proper editor integration, no offline compilation or optimisation, and, worst of all, no support for importing files.
Instead of trying to go the fully custom route like glslx, this loader offloads the majority of work to HLSL, a well-established, well-supported languange and only provides the means to easily incorporate it into an existing WebGL project.
Main goals
Usage
Install the loader
and add it to your webpack config:
If your config is written in Typescript, you can also grab the options type:
By default
@gdgt/hlsl-loader
will look for two functions ("entry points") per HLSL file:vsMain
will be treated as the vertex shader entry. Its GLES 3.0 source is exported asvertexShader
psMain
will be treated as the fragment shader entry. Its GLES 3.0 source is exported asfragmentShader
Example
Importing the above file will give you:
Loader options
exports
mangle
true
includeDirectories
[]
#include
d files in addition to the directory containing the imported file.logGlsl
false
May be useful during development, disabled for production builds.
generateDeclarations
true
.d.ts
files containing declarations for imported HLSL files.Disabled for production builds.
How it works
The inital HLSL file is run through the DirectX Shader Compiler (dxc), converting it to a SPIR-V binary per entrypoint. Subsequently, spriv-crosss reflection functionality is used to gather information on each program. Finally, the SPIR-V binaries are converted to GLES 3.0 via spriv-cross, renaming some interface variables to ensure compatibility. Introspection info from all entry-points is merged and exported as JSON.
Limitations
cbuffer
s in HLSL, you should never encounter this limitation.float
,int
andbool
, including their vector types, andfloat4x4
matrices. This list is likely to be expanded in the future.gl_
or_
in general.std140
layout.[[vk::location(n)]]
attribute, or only combine shaders imported from the same file.For a
dxc
specifc list of limitations, seedxc
s SPIR-V docs.Tips
Specifying the location of vertex attributes
Buffer attribute indices/locations can be explicitly specified using the
[[vk::location(n)]]
attribute. This is carried over to the exported introspection object.Textures and SamplerState
HLSL ordinarily does not support combined Texture-Sampler objects the way GLES 3.0 expects them. To work around this, we flag both Texture and SamplerState using the
[[vk::combinedImageSampler]]
attribute and bind them to the same unit using[[vk::binding(1)]]
.This results in GLES 3.0 program expecting a uniform named
g_MyTexture
of typesampler2D
. The binding is carried over to the exported introspection object.See the
dxc
SPIR-V docs for a complete list of supported attributes.Multiple entrypoints
By default,
@gdgt/hlsl-loader
will attempt to extract two entry points per HLSL file:vsMain
for the vertex stage, exported asvertexShader
psMain
for the pixel stage, exported asfragmentShader
You can change this by adding an
exports
object to the loaders config:stage
specifies the shader model and -stage to compile to,vs
andps
meaning "vertex shader" and "pixel shader" respectively, and6_7
being the shader model version.If you want to keep your main functions named the same across stages, you can also use dxcs predefined version macros to conditionally include them.
Todos
float4x4
Notice / Attribution
This loader builds on and ships binaries of the following projects:
See NOTICE for more info.
License
@gdgt/hlsl-loader
is distributed under the terms of the Apache 2.0 License. See LICENSE for more info.