Skip to content

Commit 1dfe14d

Browse files
authored
Merge pull request #1 from JuanDiegoMontoya/main
Added glsl article.
2 parents 16ec9e3 + 8275ce6 commit 1dfe14d

File tree

1 file changed

+110
-0
lines changed

1 file changed

+110
-0
lines changed
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
+++
2+
title = "GLSL Development Made Shrimple"
3+
description = "Tips and tools to make GLSL development easier"
4+
date = "2024-10-17"
5+
6+
#[taxonomies]
7+
author = ["Jaker"]
8+
tags = ["glsl", "opengl", "vulkan", "beginner", "visual studio", "visual studio code"]
9+
categories = ["article"]
10+
+++
11+
# GLSL Linters
12+
Life is so much better when you get instant feedback on the errors you make.
13+
## Visual Studio Code
14+
- [GLSL Lint](https://marketplace.visualstudio.com/items?itemName=dtoplak.vscode-glsllint): provides syntax highlighting and error detection.
15+
- Requires glslangValidator from [the Vulkan SDK](https://www.lunarg.com/vulkan-sdk/) or compiled yourself. The path to the executable must be supplied in the extension settings.
16+
- Compiler flags are supplied as a list in the extension settings.
17+
- I use `-C` (show multiple errors), `--glsl-version 460` (automatically sets the shader `#version`), and `--P #extension GL_GOOGLE_include_directive` (so I don't have to write that in every file).
18+
- OpenGL users should add `--target-env opengl` for it to use OpenGL semantics.
19+
- File extensions can be associated with specific shader stages in the extension settings. By default, it will detect common file extensions like .vert, .frag, and .comp. It also understands .frag.glsl, etc. automatically.
20+
- Supports my convoluted `#include` hierarchies.
21+
- [Error Lens](https://marketplace.visualstudio.com/items?itemName=usernamehw.errorlens): makes errors easier to read. Not specifically related to GLSL, but still useful to have.
22+
## Visual Studio
23+
- [GLSL language integration (for VS 2022)](https://marketplace.visualstudio.com/items?itemName=DanielScherzer.GLSL2022): provides syntax highlighting and error detection.
24+
- Similar to GLSL Lint for Visual Studio Code in terms of features.
25+
- Can use an external compiler, such as glslangValidator.
26+
- `%VULKAN_SDK%/Bin/glslangValidator.exe`
27+
- Compiler flags are supplied as a single string
28+
- I use these flags `-C --target-env vulkan1.3 --P "#extension GL_GOOGLE_include_directive: enable" --glsl-version 460`
29+
- I haven't had much luck with this extension understanding my setup for `#include`, but it could be (and probably is) a skill issue on my part.
30+
- [inline_glsl](https://marketplace.visualstudio.com/items?itemName=kristian-r.inlineglsl): provides syntax highlighting and error detection inside C strings containing GLSL.
31+
- Requires annotating C strings containing GLSL with a comment.
32+
- Has no extension settings.
33+
- Only supports OpenGL GLSL.
34+
- Does not use an external GLSL compiler.
35+
# Code Reuse
36+
## `#include` support
37+
OpenGL GLSL doesn't support `#include` (GL_ARB_shading_language_include barely counts), so we must find an external way to support it. There are a few viable ways to gain support for `#include` in your shaders if you're using OpenGL, in increasing effort:
38+
39+
- [stb_include.h] provides a simple interface for expanding includes in shader sources without evaluating other preprocessor directives (which means it expands `#include` directives in inactive preprocessor blocks).
40+
- [My fork of it](https://github.com/nothings/stb/pull/1336) fixes the const-incorrect API, which can annoyingly require `const_cast` to use in C++.
41+
- [My super secret forbidden fork of it](https://github.com/JuanDiegoMontoya/Frogfood/blob/main/vendor/stb_include.h) fixes nested includes and some other minor issues, but introduces a C++17 standard library include (`<filesystem>`) for implementer convenience.
42+
- Writing your own preprocessor?!?
43+
- Just make sure it supports nested includes.
44+
- [glslang](https://github.com/KhronosGroup/glslang) and [shaderc](https://github.com/google/shaderc) (whose shader compiler is essentially a glslang wrapper) provide C++ interfaces for processing, compiling, and linking shaders.
45+
- If you want to supply shaders to OpenGL with `glShaderSource` (and let's face it: you should), you can first use glslang to only preprocess the shader, which will expand `#include`s and other directives, then return a string. glslang explicitly warns against this usage as it's not officially supported, but it worked on my machine! The downside to this approach is that errors reported by the driver will be in the "wrong" place after expanding includes. This can be mitigated by using a linter (see above) or fully compiling and linking the shader with glslang, which itself will report errors in the correct places.
46+
- If you want to supply shaders to OpenGL with `glShaderBinary` (you don't), glslang can compile shaders into a SPIR-V binary with little more code than it takes to preprocess them.
47+
- Both glslang and shaderc require writing an include handler to tell the compiler where to find included files. This gets a bit hairy with nested includes. I'm surprised there isn't a default implementation, so [here's a link to my hacky one](https://github.com/JuanDiegoMontoya/Frogfood/blob/main/src/Fvog/Shader2.cpp#L70-L116).
48+
49+
### Vulkan
50+
Since Vulkan GLSL users are likely to be already using glslang in some fashion (be it the CLI or API), adding support for `#include` is straightforward. CLI users don't have to do anything. API users must write an include handler as outlined above. In either case, I suggest adding `#extension GL_GOOGLE_include_directive : enable` to the preamble to preserve your fingers (`--P` in the CLI, `TShader::setPreamble` in the API).
51+
## Sharing Code Between the Device and Host
52+
Writing the same structure definitions for buffers in both C or C++ and GLSL is annoying and prone to error.
53+
### My Solution
54+
is to abuse macros. It also requires shading language includes.
55+
56+
The first step is to write a "shared header" of common definitions that can be understood by both GLSL and C++. This file is split into sections that are only compiled in one language or the other:
57+
```c
58+
#ifndef COMMON_H
59+
#define COMMON_H
60+
61+
#ifdef __cplusplus // C++ definitions
62+
63+
#include <cstdint>
64+
#include <glm/glm.hpp> // Prefer glm/{vecM, matMxN}.hpp. I only use this for brevity
65+
#define FROG_UINT32 std::uint32_t
66+
#define FROG_VEC4 glm::vec4
67+
68+
#else // GLSL definitions
69+
70+
#define FROG_UINT32 uint
71+
#define FROG_VEC4 vec4
72+
73+
#endif
74+
75+
#endif // COMMON_H
76+
```
77+
78+
The second step is to put structure definitions in a file that both languages understand. This can be a separate header, or in the body of the shader itself. For an example of the latter:
79+
```c
80+
// Common section
81+
#include "Common.h"
82+
83+
struct Args
84+
{
85+
#ifdef __cplusplus // Simple constructor to initialize with safe defaults
86+
Args() : dimensions(0, 0), samples(1) {}
87+
#endif
88+
FROG_IVEC2 dimensions;
89+
FROG_UINT32 samples;
90+
};
91+
92+
#ifndef __cplusplus // GLSL-only section
93+
94+
layout(binding = 0) readonly buffer ArgsBuffer
95+
{
96+
Args args;
97+
};
98+
99+
void main()
100+
{
101+
// Do some shadery things
102+
}
103+
104+
#endif
105+
```
106+
107+
C++ source files can include this file to see the definition of `Args`.
108+
109+
Note that including shader sources like this only works if the `#version` directive can be omitted (e.g. if it's part of the preamble). It cannot be placed into a preprocessor block due to wacky GLSL rules:
110+
> The `#version` directive must occur in a shader before anything else, except for comments and white space.

0 commit comments

Comments
 (0)