Loading...
Searching...
No Matches
isf_glsl_prelude.hpp
1#pragma once
2// The GLSL text libisf puts around every shader it generates: the #version
3// line, the vertex / fragment preludes, the renderer and process uniform
4// blocks, the image and storage macros (IMG_PIXEL, IMG_NORM_PIXEL, IMG_LOAD,
5// ISF_READ / ISF_WRITE...) and the helper functions. Kept apart from the
6// parser so the generated code reads as one place (shader board U31).
7// Included by isf.cpp only.
8
9namespace isf
10{
11namespace
12{
13static constexpr struct glsl45_t
14{
15 // Identifiers the target languages reserve (quad / operator / new on Metal,
16 // lerp / frac on D3D...) are renamed per target at bake time, for every
17 // shader: score::gfx::renameReservedIdentifiers in ShaderCache.
18 static constexpr auto versionPrelude = R"_(#version 460
19)_";
20
21 static constexpr auto vertexPrelude = R"_(
22layout(location = 0) in vec2 position;
23layout(location = 1) in vec2 texcoord;
24layout(location = 0) out vec2 isf_FragNormCoord;
25
26)_";
27 // The vertex stage works in OpenGL's clip space, as in VVISF: init sets
28 // gl_Position to the quad in [-1, 1] with z = 0 and w = 1, and
29 // isf_FragNormCoord to the same position in [0, 1]. Whatever the shader then
30 // does to gl_Position (a rotation, a projection of its own) is the same math
31 // on every backend: isf_vertShaderApplyClipSpace maps the result to the
32 // backend's clip space once, after the shader's main has returned. The
33 // generated entry point calls it, so a shader never does.
34 static constexpr auto vertexInitFunc = R"_(
35void isf_vertShaderInit()
36{
37 gl_Position = vec4(position, 0.0, 1.0);
38 isf_FragNormCoord = vec2((position.x+1.0)/2.0, (position.y+1.0)/2.0);
39#if defined(ISF_THIS_PIXEL_VARYING)
40 isf_ThisPixelNormCoord = isf_FragNormCoord;
41#endif
42}
43
44// Kept for the ISF vertex shaders that end their main with it; the clip-space
45// mapping is isf_vertShaderApplyClipSpace, run by the entry point.
46void isf_vertShaderFinish()
47{
48}
49
50// OpenGL clip space to the backend's (isf_vertShaderApplyClipSpace), so that
51// OpenGL's y = +1 lands on the picture's top row in the row order of
52// RowOrder.hpp: clipSpaceCorrMatrix (Z to [0, 1] on Vulkan, Metal and D3D; Y
53// negated on Vulkan, whose render targets are top row first), then a Y
54// negation on Metal and D3D, whose render targets are bottom row first behind
55// a top-left framebuffer origin (isf_vertShaderApplyFramebufferY). Both are
56// linear in homogeneous coordinates, so they hold for any w.
57void isf_vertShaderApplyFramebufferY()
58{
59#if defined(QSHADER_HLSL) || defined(QSHADER_MSL)
60 gl_Position.y = -gl_Position.y;
61#endif
62}
63void isf_vertShaderApplyClipSpace()
64{
65 gl_Position = clipSpaceCorrMatrix * gl_Position;
66 isf_vertShaderApplyFramebufferY();
67}
68)_";
69
70 static constexpr auto vertexDefaultMain = R"_(
71void main()
72{
73 isf_vertShaderInit();
74 isf_vertShaderApplyClipSpace();
75}
76)_";
77
78 // Entry point around a shader's own vertex main, renamed by
79 // vertexUserMainRename.
80 static constexpr auto vertexUserMainRename = "#define main isf_user_vertex_main\n";
81 static constexpr auto vertexUserMainWrapper = R"_(
82#undef main
83void main()
84{
85 isf_user_vertex_main();
86 isf_vertShaderApplyClipSpace();
87}
88)_";
89
90 // The entry point for a shader that multiplies by clipSpaceCorrMatrix
91 // itself: what is left of isf_vertShaderApplyClipSpace.
92 static constexpr auto vertexUserMainWrapperOwnClipSpace = R"_(
93#undef main
94void main()
95{
96 isf_user_vertex_main();
97 isf_vertShaderApplyFramebufferY();
98}
99)_";
100
101 static constexpr auto fragmentPrelude = R"_(
102layout(location = 0) in vec2 isf_FragNormCoord;
103layout(location = 0) out vec4 isf_FragColor;
104)_";
105
106 static constexpr auto defaultUniforms = R"_(
107// Shared uniform buffer for the whole render window
108layout(std140, binding = 0) uniform renderer_t {
109 mat4 clipSpaceCorrMatrix_;
110
111 vec2 RENDERSIZE_;
112 // MSAA sample count of the active output target (1 when MSAA is off).
113 // Mirrors RenderList::samples(); needed because glslang strips
114 // gl_NumSamples under SPIR-V. _pad0 keeps the struct vec4-aligned.
115 int MSAA_SAMPLES_;
116 int _renderer_pad0_;
117} isf_renderer_uniforms;
118
119// This dance is needed because otherwise
120// spirv-cross may generate different struct names in the vertex & fragment, causing crashes..
121// but we have to keep compat with ISF
122#define clipSpaceCorrMatrix isf_renderer_uniforms.clipSpaceCorrMatrix_
123#define MSAA_SAMPLES isf_renderer_uniforms.MSAA_SAMPLES_
124
125// Time-dependent uniforms, only relevant during execution
126layout(std140, binding = 1) uniform process_t {
127 float TIME_;
128 float TIMEDELTA_;
129 float PROGRESS_;
130 float SAMPLERATE_;
131
132 int PASSINDEX_;
133 int FRAMEINDEX_;
134
135 vec2 RENDERSIZE_;
136 vec4 DATE_;
137 // Mirrors gl_NumWorkGroups for compute shaders. SPIRV-Cross's HLSL
138 // backend refuses to emit code for the NumWorkgroups built-in unless
139 // remap_num_workgroups_builtin() is set up on both the cross-compiler
140 // and the QRhi side; QShaderBaker exposes neither, so any compute
141 // shader using gl_NumWorkGroups silently fails to bake to HLSL on
142 // D3D11/D3D12. We sidestep that by routing references through this
143 // uniform — populated host-side just before each dispatch — and
144 // textually shadowing the built-in via the #define below.
145 uvec3 NUMWORKGROUPS_;
146 // The view a multiview shader renders where the backend cannot amplify
147 // (MULTIVIEW lowered to one pass per view, see ShaderCache): gl_ViewIndex
148 // reads this. Kept apart from PASSINDEX, which PER_MIP already spends on
149 // the mip level.
150 int VIEWINDEX_;
151} isf_process_uniforms;
152
153#define TIME isf_process_uniforms.TIME_
154#define TIMEDELTA isf_process_uniforms.TIMEDELTA_
155#define PROGRESS isf_process_uniforms.PROGRESS_
156#define PASSINDEX isf_process_uniforms.PASSINDEX_
157#define FRAMEINDEX isf_process_uniforms.FRAMEINDEX_
158#define RENDERSIZE isf_process_uniforms.RENDERSIZE_
159#define DATE isf_process_uniforms.DATE_
160#define SAMPLERATE isf_process_uniforms.SAMPLERATE_
161#define gl_NumWorkGroups isf_process_uniforms.NUMWORKGROUPS_
162#define isf_NumWorkGroups isf_process_uniforms.NUMWORKGROUPS_
163)_";
164
165 // Storage-image access for compute shaders, in the author's coordinate system.
166 //
167 // imageStore() addresses a raw texel index, where row 0 is first in memory on
168 // every backend, while the render target the result is copied into is
169 // bottom-up on OpenGL and top-down everywhere else -- what
170 // QRhi::isYUpInFramebuffer() reports. The two spaces are therefore mirrored
171 // against each other on OpenGL, and a compute shader that only stores comes
172 // out upside down there.
173 //
174 // The gate is the inverse of the fragment macros above: a fragment shader
175 // needs its correction on Vulkan, a compute shader needs it everywhere else.
176 // Read and store are corrected TOGETHER, so a shader that samples an input and
177 // stores it at the matching index keeps agreeing with itself.
178 //
179 // A cube face is addressed by direction, so no Y convention applies to it.
180 static constexpr auto computeImageMacros =
181 R"_(
182// Everything except Vulkan, measured rather than derived. The framebuffer
183// origin does not predict this: Direct3D and Metal put it at the top like
184// Vulkan, yet a compute storage image reaches the delivered picture mirrored on
185// them exactly as it does on OpenGL. Gating this on OpenGL alone leaves D3D11,
186// D3D12 and Metal upside down -- csf_orient_macros reports green=255 where 0
187// is expected at row 0 on all three.
188#if defined(QSHADER_SPIRV)
189#define ISF_STORE_COORD(img, coord) ivec2(coord)
190#define ISF_STORE_COORD_LAYER(img, coord) ivec3(coord)
191#define ISF_FIXUP_COMPUTE_TEXCOORD(coord) (coord)
192#define IMG_TEXEL_PREMULTIPLIED(tex, coord) texelFetch(tex, ivec2(coord), 0)
193#else
194#define ISF_STORE_COORD(img, coord) ivec2((coord).x, imageSize(img).y - 1 - (coord).y)
195#define ISF_STORE_COORD_LAYER(img, coord) ivec3((coord).x, imageSize(img).y - 1 - (coord).y, (coord).z)
196#define ISF_FIXUP_COMPUTE_TEXCOORD(coord) vec2((coord).x, 1. - (coord).y)
197#define IMG_TEXEL_PREMULTIPLIED(tex, coord) texelFetch(tex, ivec2(ivec2(coord).x, textureSize(tex, 0).y - 1 - ivec2(coord).y), 0)
198#endif
199
200#define IMG_STORE(img, coord, val) imageStore(img, ISF_STORE_COORD(img, coord), val)
201#define IMG_STORE_LAYER(img, coord, val) imageStore(img, ISF_STORE_COORD_LAYER(img, coord), val)
202#define IMG_LOAD(img, coord) imageLoad(img, ISF_STORE_COORD(img, coord))
203#define IMG_STORE_CUBE(img, coord, val) imageStore(img, ivec3(coord), val)
204#define IMG_LOAD_CUBE(img, coord) imageLoad(img, ivec3(coord))
205
206// Face size of a writable cubemap, as ivec2, on every backend.
207//
208// A cube storage image is declared `image2DArray` when the target is HLSL --
209// Direct3D has no writable cube texture type, so `imageCube` cannot be
210// translated at all (isf_emit_cube_image_decl). imageStore / imageLoad take the
211// same ivec3 (x, y, face) coordinate for both spellings, so nothing else about
212// the author's source changes; imageSize() is the single exception, returning
213// ivec2 for a cube and ivec3 for a 2D array. Take the size through this macro
214// (or write `imageSize(img).xy`, which is what it expands to) and the shader is
215// identical on Direct3D and everywhere else.
216#define IMG_SIZE_CUBE(img) (imageSize(img).xy)
217
218vec4 isf_unpremultiply(vec4 c) { return c.a > 0.0 ? vec4(c.rgb / c.a, c.a) : vec4(0.0); }
219#define IMG_NORM_PIXEL_PREMULTIPLIED(tex, coord) texture(tex, ISF_FIXUP_COMPUTE_TEXCOORD(coord))
220#define IMG_PIXEL_PREMULTIPLIED(tex, coord) texture(tex, ISF_FIXUP_COMPUTE_TEXCOORD((coord) / vec2(textureSize(tex, 0))))
221#define IMG_NORM_PIXEL(tex, coord) IMG_NORM_PIXEL_PREMULTIPLIED(tex, coord)
222#define IMG_PIXEL(tex, coord) IMG_PIXEL_PREMULTIPLIED(tex, coord)
223#define ISF_STRAIGHT_NORM_PIXEL(tex, coord) isf_unpremultiply(IMG_NORM_PIXEL_PREMULTIPLIED(tex, coord))
224#define ISF_STRAIGHT_PIXEL(tex, coord) isf_unpremultiply(IMG_PIXEL_PREMULTIPLIED(tex, coord))
225#define IMG_TEXEL(tex, coord) IMG_TEXEL_PREMULTIPLIED(tex, coord)
226#define ISF_STRAIGHT_TEXEL(tex, coord) isf_unpremultiply(IMG_TEXEL_PREMULTIPLIED(tex, coord))
227#define IMG_CUBE(tex, dir) texture(tex, dir)
228)_";
229
230 static constexpr auto defaultFunctions =
231 R"_(
232// GLSL's textureSize is overloaded by sampler dimensionality — sampler2D
233// returns ivec2, sampler3D returns ivec3. Authors typically reach for
234// TEX_DIMENSIONS regardless of 2D/3D; the *_2D / *_3D aliases below make
235// the intended dimensionality explicit in shader source.
236#define TEX_DIMENSIONS(tex) textureSize(tex, 0)
237#define TEX_DIMENSIONS_2D(tex) textureSize(tex, 0)
238#define TEX_DIMENSIONS_3D(tex) textureSize(tex, 0)
239#define IMG_SIZE(tex) textureSize(tex, 0)
240#define IMG_SIZE_3D(tex) textureSize(tex, 0)
241
242// IMG_CUBE(tex, dir) — canonical colour-cube read; same in both coord systems
243// since a direction vector has no Y-flip. IMG_CUBE_DEPTH(tex, dir) —
244// canonical depth-cube read for inputs declared DEPTH: true on a cubemap,
245// hides the internal `_depth` companion binding.
246#define IMG_CUBE(tex, dir) texture(tex, dir)
247#define IMG_CUBE_DEPTH(tex, dir) textureLod(tex##_depth, dir, 0.0).r
248
249vec4 isf_unpremultiply(vec4 c) { return c.a > 0.0 ? vec4(c.rgb / c.a, c.a) : vec4(0.0); }
250
251// Pixel-unit reads are in the sampled image's own pixels, as in the ISF spec:
252// IMG_PIXEL(image, coord) is coord / size(image), IMG_THIS_PIXEL(image) is
253// IMG_PIXEL(image, isf_ThisPixelCoord), pixel-aligned. IMG_THIS_NORM_PIXEL is
254// the one that stretches the image over the frame.
255//
256// isf_ThisPixelCoord is the quad's position in output pixels, in the ISF
257// (Y-up) convention. Where the vertex stage is isf_vertShaderInit, it comes
258// from the quad position init writes, interpolated: the image then follows
259// a warped or rotated quad, as in VVISF. That is isf_FragNormCoord, or, when
260// the shader's vertex code touches isf_FragNormCoord after init, the copy
261// init also writes into isf_ThisPixelNormCoord (ISF_THIS_PIXEL_VARYING), so
262// that the shader's later writes do not move the image. On an unwarped quad
263// it is isf_FragCoord.xy on every backend: the vertices map to 0 and
264// RENDERSIZE, so the value interpolated at a pixel centre is that centre,
265// (i + 0.5). Stages without either varying (RAW_RASTER, its resolve pass, a
266// vertex shader that does not call isf_vertShaderInit) use the fragment's
267// position.
268#if defined(ISF_THIS_PIXEL_VARYING)
269#define isf_ThisPixelCoord (isf_ThisPixelNormCoord * RENDERSIZE)
270#elif defined(ISF_THIS_PIXEL_FROM_NORM_COORD)
271#define isf_ThisPixelCoord (isf_FragNormCoord * RENDERSIZE)
272#else
273#define isf_ThisPixelCoord (isf_FragCoord.xy)
274#endif
275
276#if defined(QSHADER_SPIRV)
277#define isf_FragCoord vec4(gl_FragCoord.x, RENDERSIZE.y - gl_FragCoord.y, gl_FragCoord.z, gl_FragCoord.w)
278#define ISF_FIXUP_TEXCOORD(coord) vec2((coord).x, 1. - (coord).y)
279#define IMG_THIS_PIXEL_PREMULTIPLIED(tex) IMG_PIXEL_PREMULTIPLIED(tex, isf_ThisPixelCoord)
280#define IMG_THIS_NORM_PIXEL_PREMULTIPLIED(tex) texture(tex, ISF_FIXUP_TEXCOORD(isf_FragNormCoord))
281#define IMG_PIXEL_PREMULTIPLIED(tex, coord) texture(tex, ISF_FIXUP_TEXCOORD((coord) / vec2(textureSize(tex, 0))))
282#define IMG_NORM_PIXEL_PREMULTIPLIED(tex, coord) texture(tex, ISF_FIXUP_TEXCOORD(coord))
283#define IMG_THIS_DEPTH(tex) textureLod(tex##_depth, ISF_FIXUP_TEXCOORD(isf_FragNormCoord), 0.0).r
284#define IMG_DEPTH_PIXEL(tex, coord) textureLod(tex##_depth, ISF_FIXUP_TEXCOORD((coord) / vec2(textureSize(tex##_depth, 0))), 0.0).r
285#define IMG_DEPTH_NORM_PIXEL(tex, coord) textureLod(tex##_depth, ISF_FIXUP_TEXCOORD(coord), 0.0).r
286#define IMG_TEXEL_PREMULTIPLIED(tex, coord) texelFetch(tex, ivec2(ivec2(coord).x, textureSize(tex, 0).y - 1 - ivec2(coord).y), 0)
287#define IMG_NORM_PIXEL_LOD_PREMULTIPLIED(tex, coord, lod) textureLod(tex, ISF_FIXUP_TEXCOORD(coord), lod)
288#else
289#define isf_FragCoord gl_FragCoord
290#define IMG_THIS_PIXEL_PREMULTIPLIED(tex) IMG_PIXEL_PREMULTIPLIED(tex, isf_ThisPixelCoord)
291#define IMG_THIS_NORM_PIXEL_PREMULTIPLIED(tex) texture(tex, isf_FragNormCoord)
292#define IMG_PIXEL_PREMULTIPLIED(tex, coord) texture(tex, (coord) / vec2(textureSize(tex, 0)))
293#define IMG_NORM_PIXEL_PREMULTIPLIED(tex, coord) texture(tex, coord)
294#define IMG_THIS_DEPTH(tex) textureLod(tex##_depth, isf_FragNormCoord, 0.0).r
295#define IMG_DEPTH_PIXEL(tex, coord) textureLod(tex##_depth, (coord) / vec2(textureSize(tex##_depth, 0)), 0.0).r
296#define IMG_DEPTH_NORM_PIXEL(tex, coord) textureLod(tex##_depth, coord, 0.0).r
297#define IMG_TEXEL_PREMULTIPLIED(tex, coord) texelFetch(tex, ivec2(coord), 0)
298#define IMG_NORM_PIXEL_LOD_PREMULTIPLIED(tex, coord, lod) textureLod(tex, coord, lod)
299#endif
300
301// IMG_NORM_PIXEL with an explicit mip level. Use it (with lod 0.0) for reads
302// inside loops whose trip count is a uniform: an implicit-gradient texture()
303// there makes fxc (D3D11/D3D12-without-dxc, ps_5_0) try to unroll the loop
304// and fail (X3570 + X3511) -- or crash outright on big shaders.
305#define IMG_NORM_PIXEL_LOD(tex, coord, lod) IMG_NORM_PIXEL_LOD_PREMULTIPLIED(tex, coord, lod)
306
307#define IMG_THIS_PIXEL(tex) IMG_THIS_PIXEL_PREMULTIPLIED(tex)
308#define IMG_THIS_NORM_PIXEL(tex) IMG_THIS_NORM_PIXEL_PREMULTIPLIED(tex)
309#define IMG_PIXEL(tex, coord) IMG_PIXEL_PREMULTIPLIED(tex, coord)
310#define IMG_NORM_PIXEL(tex, coord) IMG_NORM_PIXEL_PREMULTIPLIED(tex, coord)
311#define ISF_STRAIGHT_THIS_PIXEL(tex) isf_unpremultiply(IMG_THIS_PIXEL_PREMULTIPLIED(tex))
312#define ISF_STRAIGHT_THIS_NORM_PIXEL(tex) isf_unpremultiply(IMG_THIS_NORM_PIXEL_PREMULTIPLIED(tex))
313#define ISF_STRAIGHT_PIXEL(tex, coord) isf_unpremultiply(IMG_PIXEL_PREMULTIPLIED(tex, coord))
314#define ISF_STRAIGHT_NORM_PIXEL(tex, coord) isf_unpremultiply(IMG_NORM_PIXEL_PREMULTIPLIED(tex, coord))
315#define IMG_TEXEL(tex, coord) IMG_TEXEL_PREMULTIPLIED(tex, coord)
316#define ISF_STRAIGHT_TEXEL(tex, coord) isf_unpremultiply(IMG_TEXEL_PREMULTIPLIED(tex, coord))
317)_";
318
319} GLSL45;
320}
321}