Source browser
detail/runtime.hpp
Read the complete file below or download its original bytes. Line numbers provide stable links to a declaration or processing step.
File fingerprint
1#ifndef SHDR_RUNTIME_HPP // Skip this section if it was already included.
2#define SHDR_RUNTIME_HPP /* Mark this section as included. */
3
4/** @brief Major version of shdr. */
5#define SHDR_VERSION_MAJOR 1 // Identify the public API's major release series.
6/** @brief Minor version within the current major release. */
7#define SHDR_VERSION_MINOR 0 // Identify additions within the public API's release series.
8/** @brief Patch version within the current minor release. */
9#define SHDR_VERSION_PATCH 0 // Identify corrections within this minor release.
10/** @brief The library release version as a string. */
11#define SHDR_VERSION "1.0.0" // Expose the library release as a compile-time string.
12
13#include <algorithm> // Use standard searches, bounds, and resource copying.
14#include <cstddef> // Use portable pointer sizes and offsets.
15#include <cstdint> // Use integer widths that match GLSL values.
16#include <cctype> // Recognize characters in embedded shader declarations.
17#include <cstring> // Copy bytes and function addresses with std::memcpy.
18#include <functional> // Store GL resolvers and deferred cleanup callbacks.
19#include <initializer_list> // Accept concise resource initialization from application values.
20#include <limits> // Check arithmetic against the type limits.
21#include <map> // Find registered shader factories and shared kernel snapshots.
22#include <memory> // Retain resources while pipelines and queued jobs use them.
23#include <mutex> // Protect shared work queues and deferred deletion.
24#include <thread> // Track the context thread and start CPU workers.
25#include <sstream> // Build readable shader and driver diagnostics.
26#include <stdexcept> // Report invalid resources and failed execution.
27#include <string> // Keep embedded shader text and named interfaces.
28#include <type_traits> // Check shader storage and port types at compile time.
29#include <vector> // Store ordered stages, ports, and resource values.
30
31/** @brief Portable, header-only CPU/OpenGL compute pipelines. */
32namespace shdr { // Group these definitions in the shdr namespace.
33/** @brief A shader, resource, context or dispatch validation failure. */
34struct error : std::runtime_error { // Keep the error values and operations together.
35 explicit error(const std::string& s) : std::runtime_error(s) { // Preserve the supplied validation or driver diagnostic.
36 } // End this scope in runtime.
37}; // End this scope in runtime.
38
39/** @brief Three unsigned dimensions. dispatch dimensions count work groups. */
40struct extent { // Keep the extent values and operations together.
41 unsigned x, y, z; // Keep the three unsigned dispatch or local dimensions.
42 /** @brief Construct three workgroup counts, defaulting each dimension to one. */
43 explicit extent(unsigned x_ = 1, unsigned y_ = 1, unsigned z_ = 1) : x(x_), y(y_), z(z_) { // Initialize all three positive-or-zero dimensions supplied by the caller.
44 } // End this scope in runtime.
45}; // End this scope in runtime.
46/** @brief Minimum shader language selected independently of the context's actual version. */
47enum class profile { es31, es32, gl43, gl45, gl46 }; // Name the supported shader language profiles.
48/** @brief Automatic uses CPU execution for run() and GPU execution for run(device). */
49enum class backend { automatic, cpu, gpu }; // Select automatic, explicit CPU or explicit GPU execution.
50/** @brief Resource access qualifiers. readonly also prevents CPU writes at compile time. */
51enum class access { readonly, writeonly, readwrite }; // Express how a shader may access a bound resource.
52
53namespace detail { // Group these definitions in the detail namespace.
54inline bool identifier_char(char c) { // Recognize the letters, digits, and underscore allowed in an identifier.
55 return std::isalnum(static_cast<unsigned char>(c)) || c == '_'; // Recognize the letters, digits, and underscore allowed in an identifier.
56} // End this scope in identifier char.
57inline std::string trim(const std::string& text) { // Remove surrounding whitespace from one resource argument.
58 const std::size_t first = text.find_first_not_of(" \t\r\n"); // Set the first non-space character.
59 return first == std::string::npos // Remove surrounding whitespace from one resource argument.
60 ? std::string() // Select the value for this branch of the condition.
61 : text.substr(first, text.find_last_not_of(" \t\r\n") - first + 1); // Copy only the non-whitespace portion of this argument.
62} // End this scope in trim.
63 // ESSL forbids token-pasting macros. Expand only resource declarations in the host,
64 // preserving comments, string literals, shader statements and original line numbers.
65inline std::string expand_declarations(const std::string& source, bool es) { // Expand host resource declarations into native GLSL interfaces.
66 std::string out; // Keep the assembled shader text.
67 for (std::size_t i = 0; i < source.size();) { // Walk the embedded shader text while respecting its token boundaries.
68 const std::size_t start = i; // Set the beginning of the current token or range.
69 if (source.compare(i, 2, "//") == 0) { // Copy the line comment verbatim.
70 const std::size_t end = source.find('\n', i); // Set the end of the current range.
71 i = end == std::string::npos ? source.size() : end + 1; // Advance beyond the preserved comment or declaration.
72 } else if (source.compare(i, 2, "/*") == 0) { // Copy the block comment verbatim.
73 const std::size_t end = source.find("*/", i + 2); // Set the end of the current range.
74 i = end == std::string::npos ? source.size() : end + 2; // Advance beyond the preserved comment or declaration.
75 } else if (source[i] == '\"' || source[i] == '\'') { // Copy quoted shader text as one lexical unit.
76 const char quote = source[i++]; // Remember which quote terminates this shader literal.
77 while (i < source.size()) { // Walk the embedded shader text while respecting its token boundaries.
78 if (source[i] == '\\') { // Treat an escaped character as part of the quoted shader text.
79 i = (std::min)(i + 2, source.size()); // Advance past the escaped character within the source bounds.
80 continue; // Continue with the next token or work item.
81 } // End this scope in expand declarations.
82 if (source[i++] == quote) // Choose this branch when source[i++] equals quote.
83 break; // Stop this search or loop once its current item is complete.
84 } // End this scope in expand declarations.
85 } else if (identifier_char(source[i])) { // Recognize the letters, digits, and underscore allowed in an identifier.
86 while (i < source.size() && identifier_char(source[i])) // Walk the embedded shader text while respecting its token boundaries.
87 ++i; // Expand host resource declarations into native GLSL interfaces.
88 const std::string token = source.substr(start, i - start); // Set the current shader identifier.
89 if (token == "SHDR_BUFFER" || token == "SHDR_IMAGE2D") { // Choose this branch when token equals "SHDR BUFFER" or token equals "SHDR IMAGE2D".
90 std::size_t open = i; // Set the opening parenthesis of a resource declaration.
91 while (open < source.size() && // Walk the embedded shader text while respecting its token boundaries.
92 std::isspace(static_cast<unsigned char>(source[open]))) // Keep the opening parenthesis of a resource declaration.
93 ++open; // Keep the opening parenthesis of a resource declaration.
94 if (open >= source.size() || source[open] != '(') // Check resource size or driver limits before accessing storage.
95 throw error("resource declaration requires parentheses: " + token); // Report resource declaration requires parentheses: .
96 std::size_t end = open + 1, argument = end; // Set the end of the current range.
97 unsigned depth = 1; // Set the current parenthesis nesting.
98 std::vector<std::string> args; // Keep the parsed declaration arguments.
99 for (; end < source.size(); ++end) { // Walk the embedded shader text while respecting its token boundaries.
100 if (source[end] == '(') // Choose this branch when source[end] equals '('.
101 ++depth; // Keep the current parenthesis nesting.
102 if (source[end] == ')') // Choose this branch when source[end] equals ')'.
103 --depth; // Keep the current parenthesis nesting.
104 if ((source[end] == ',' && depth == 1) || depth == 0) { // Choose this branch when (source[end] equals ',' and depth equals 1) or depth equals 0.
105 args.push_back(trim(source.substr(argument, end - argument))); // Retain this entry in the ordered resource or work collection.
106 argument = end + 1; // Set the beginning of the next resource argument.
107 } // End this scope in expand declarations.
108 if (!depth) // Finish the argument list at its matching closing parenthesis.
109 break; // Stop this search or loop once its current item is complete.
110 } // End this scope in expand declarations.
111 const bool image = token == "SHDR_IMAGE2D"; // Choose image or storage-buffer declaration syntax.
112 if (depth || args.size() != (image ? 5u : 4u)) // Check resource size or driver limits before accessing storage.
113 throw error("invalid resource declaration: " + token); // Report invalid resource declaration: .
114 const std::string mode = args.back(); // Set the declared shader resource access.
115 if (mode != "readonly" && mode != "writeonly" && mode != "readwrite") // Choose the branch matching this interface or resource access.
116 throw error("invalid resource access: " + mode); // Report invalid resource access: .
117 const std::string qualifier = mode == "readwrite" ? "" : mode + " "; // Set the access qualifier emitted into GLSL.
118 if (image) { // Choose this branch when image.
119 if (es && mode == "readwrite" && args[1].compare(0, 4, "rgba") == 0) // Choose the branch matching this interface or resource access.
120 throw error( // Report the invalid operation.
121 "GLES RGBA images require readonly or writeonly declarations: " + // Append this literal GLSL source fragment.
122 args[3]); // Expand host resource declarations into native GLSL interfaces.
123 out += "layout(" + args[1] + ",binding=" + args[0] + ") " + qualifier + // Set the assembled shader text.
124 "uniform highp " + args[2] + " " + args[3] + ";"; // Append this literal GLSL source fragment.
125 } else // Handle the alternative resource or completion case.
126 out += "layout(std430,binding=" + args[0] + ") " + qualifier + // Set the assembled shader text.
127 "buffer shdr_block_" + args[2] + " { " + args[1] + " " + args[2] + // Append this literal GLSL source fragment.
128 "[]; };"; // Append this literal GLSL source fragment.
129 out.append(static_cast<std::size_t>( // Restore the original declaration's line count after expansion.
130 std::count(source.begin() + static_cast<std::ptrdiff_t>(start), // Keep the beginning of the current token or range.
131 source.begin() + static_cast<std::ptrdiff_t>(end + 1), // Stop counting at the end of the replaced resource declaration.
132 '\n')), // Count the newlines originally present in this declaration.
133 '\n'); // Append one newline for each removed original newline.
134 i = end + 1; // Advance beyond the preserved comment or declaration.
135 continue; // Continue with the next token or work item.
136 } // End this scope in expand declarations.
137 } else // Handle the alternative resource or completion case.
138 ++i; // Expand host resource declarations into native GLSL interfaces.
139 out.append(source, start, i - start); // Preserve this untouched token or comment in the exported GLSL.
140 } // End this scope in expand declarations.
141 return out; // Return the assembled shader text.
142} // End this scope in expand declarations.
143} // namespace detail
144
145/** @internal Export an embedded shader as standard GLSL, with version and declaration macros.
146 * This function supplies the version directive. Guard the body's C++ include
147 * with #ifdef __cplusplus. The exported source contains the selected version
148 * and expanded declarations.
149 */
150inline std::string glsl_source(const std::string& source, profile p) { // Add the selected GLSL version and shared-declaration definitions.
151 std::istringstream lines(source); // Read the embedded shader one line at a time.
152 std::string line; // Read each embedded source line while checking version directives.
153 while (std::getline(lines, line)) { // Inspect each source line for an unsupported version directive.
154 std::size_t start = line.find_first_not_of(" \t\r"); // Set the beginning of the current token or range.
155 if (start != std::string::npos && line[start] == '#') { // Choose this branch when start differs from std::string::npos and line[start] equals '#'.
156 start = line.find_first_not_of(" \t", start + 1); // Set the beginning of the current token or range.
157 if (start != std::string::npos && line.compare(start, 7, "version") == 0) // Choose this branch when start differs from std::string::npos and line.compare(start, 7, "version") equals 0.
158 throw error("shared shaders must omit #version; glsl_source supplies it"); // Report shared shaders must omit #version. glsl_source supplies it.
159 } // End this scope in glsl source.
160 } // End this scope in glsl source.
161 const char* version = p == profile::es31 ? "310 es" // Set the current GL version text.
162 : p == profile::es32 ? "320 es" // Select the version associated with this shader profile.
163 : p == profile::gl43 ? "430 core" // Select the version associated with this shader profile.
164 : p == profile::gl45 ? "450 core" // Select the version associated with this shader profile.
165 : "460 core"; // Select desktop GLSL 4.60 for the remaining profile.
166 std::string out = std::string("#version ") + version + "\n"; // Set the assembled shader text.
167 if (p == profile::es31 || p == profile::es32) // Choose this branch when p equals profile::es31 or p equals profile::es32.
168 out += "precision highp float;\nprecision highp int;\n"; // Set the assembled shader text.
169 out += // Set the assembled shader text.
170 "#define SHDR_BEGIN(name,x,y,z) layout(local_size_x=x,local_size_y=y,local_size_z=z) in;\n" // Emit a native workgroup declaration for this shader.
171 "#define SHDR_END()\n" // Remove the host-only shader wrapper terminator.
172 "#define SHDR_MAIN() void main()\n" // Map the shared entry-point spelling to native GLSL main.
173 "#define SHDR_UNIFORM(t,n) uniform t n;\n" // Emit a native typed uniform declaration.
174 "#define SHDR_SWIZZLE(v,s) (v).s\n" // Map shared swizzle syntax to native GLSL component selection.
175 "#define SHDR_NOT(v) not(v)\n" // Map shared boolean negation to the native GLSL builtin.
176 "#define SHDR_IN(t,n) in t n\n" // Emit a native input parameter qualifier.
177 "#define SHDR_OUT(t,n) out t n\n" // Emit a native output parameter qualifier.
178 "#define SHDR_INOUT(t,n) inout t n\n" // Emit a native input-output parameter qualifier.
179 "#line 1\n"; // Restore shader line numbering before appending the original source.
180 // Desktop GLSL accepts highp precision qualifiers from GLSL 4.10 onward.
181 return out + detail::expand_declarations(source, p == profile::es31 || p == profile::es32); // Add the selected GLSL version and shared-declaration definitions.
182} // End this scope in glsl source.
183
184namespace detail { // Group these definitions in the detail namespace.
185 // Private GL types and entry points keep the default header compatible with standard C++11.
186typedef unsigned int GLenum; // Name the representation used by this GL or shader interface.
187typedef unsigned int GLuint; // Name the representation used by this GL or shader interface.
188typedef int GLint; // Name the representation used by this GL or shader interface.
189typedef int GLsizei; // Name the representation used by this GL or shader interface.
190typedef unsigned int GLbitfield; // Name the representation used by this GL or shader interface.
191typedef unsigned char GLboolean; // Name the representation used by this GL or shader interface.
192typedef std::ptrdiff_t GLsizeiptr; // Name the representation used by this GL or shader interface.
193typedef std::ptrdiff_t GLintptr; // Name the representation used by this GL or shader interface.
194typedef std::uint64_t GLuint64; // Name the representation used by this GL or shader interface.
195typedef void* GLsync; // Name the representation used by this GL or shader interface.
196#if defined(_WIN32) // Compile this branch only when its platform or backend condition holds.
197#define SHDR_GL_CALL __stdcall /* Match the calling convention of the GL entry points. */
198#else // Use the alternative platform or backend definition.
199#define SHDR_GL_CALL /* Match the calling convention of the GL entry points. */
200#endif // Finish this conditional section.
201enum : unsigned { // Name the profile, access, or completion values accepted by this section.
202 VERSION = 0x1F02, // Use the GL numeric code for version.
203 MAJOR_VERSION = 0x821B, // Use the GL numeric code for major version.
204 MINOR_VERSION = 0x821C, // Use the GL numeric code for minor version.
205 COMPUTE_SHADER = 0x91B9, // Use the GL numeric code for compute shader.
206 COMPILE_STATUS = 0x8B81, // Use the GL numeric code for compile status.
207 LINK_STATUS = 0x8B82, // Use the GL numeric code for link status.
208 INFO_LOG_LENGTH = 0x8B84, // Use the GL numeric code for info log length.
209 COMPUTE_WORK_GROUP_SIZE = 0x8267, // Use the GL numeric code for compute work group size.
210 MAX_COMPUTE_WORK_GROUP_COUNT = 0x91BE, // Use the GL numeric code for max compute work group count.
211 MAX_COMPUTE_WORK_GROUP_SIZE = 0x91BF, // Use the GL numeric code for max compute work group size.
212 MAX_COMPUTE_WORK_GROUP_INVOCATIONS = 0x90EB, // Use the GL numeric code for max compute work group invocations.
213 MAX_SHADER_STORAGE_BUFFER_BINDINGS = 0x90DD, // Use the GL numeric code for max shader storage buffer bindings.
214 MAX_IMAGE_UNITS = 0x8F38, // Use the GL numeric code for max image units.
215 SHADER_STORAGE_BUFFER = 0x90D2, // Use the GL numeric code for shader storage buffer.
216 DYNAMIC_DRAW = 0x88E8, // Use the GL numeric code for dynamic draw.
217 MAP_READ_BIT = 1, // Use the GL numeric code for map read bit.
218 ALL_BARRIER_BITS = 0xFFFFFFFFu, // Use the GL numeric code for all barrier bits.
219 TEXTURE_2D = 0x0DE1, // Identify a two-dimensional texture target.
220 FLOAT = 0x1406, // Use the GL numeric code for float.
221 INT = 0x1404, // Use the GL numeric code for int.
222 UNSIGNED_INT = 0x1405, // Use the GL numeric code for unsigned int.
223 RED = 0x1903, // Use the GL numeric code for red.
224 RGBA = 0x1908, // Use the GL numeric code for rgba.
225 RED_INTEGER = 0x8D94, // Use the GL numeric code for red integer.
226 RGBA_INTEGER = 0x8D99, // Use the GL numeric code for rgba integer.
227 R32F = 0x822E, // Store one 32-bit floating-point component per pixel.
228 R32I = 0x8235, // Store one signed 32-bit component per pixel.
229 R32UI = 0x8236, // Store one unsigned 32-bit component per pixel.
230 RGBA32F = 0x8814, // Store four 32-bit floating-point components per pixel.
231 RGBA32I = 0x8D82, // Store four signed 32-bit components per pixel.
232 RGBA32UI = 0x8D70, // Store four unsigned 32-bit components per pixel.
233 READ_ONLY = 0x88B8, // Use the GL numeric code for read only.
234 WRITE_ONLY = 0x88B9, // Use the GL numeric code for write only.
235 READ_WRITE = 0x88BA, // Use the GL numeric code for read write.
236 UNPACK_ALIGNMENT = 0x0CF5, // Use the GL numeric code for unpack alignment.
237 UNPACK_ROW_LENGTH = 0x0CF2, // Use the GL numeric code for unpack row length.
238 UNPACK_SKIP_ROWS = 0x0CF3, // Use the GL numeric code for unpack skip rows.
239 UNPACK_SKIP_PIXELS = 0x0CF4, // Use the GL numeric code for unpack skip pixels.
240 PIXEL_UNPACK_BUFFER = 0x88EC, // Use the GL numeric code for pixel unpack buffer.
241 SYNC_GPU_COMMANDS_COMPLETE = 0x9117, // Use the GL numeric code for sync gpu commands complete.
242 ALREADY_SIGNALED = 0x911A, // Use the GL numeric code for already signaled.
243 TIMEOUT_EXPIRED = 0x911B, // Use the GL numeric code for timeout expired.
244 CONDITION_SATISFIED = 0x911C, // Use the GL numeric code for condition satisfied.
245 WAIT_FAILED = 0x911D, // Use the GL numeric code for wait failed.
246 SYNC_FLUSH_COMMANDS_BIT = 1 // Use the GL numeric code for sync flush commands bit.
247}; // End this scope in runtime.
248
249struct gl_api { // Keep the gl api values and operations together.
250#define SHDR_GL_PROC(ret, n, args) /* Declare a GL function pointer with its exact argument types. */ \
251 typedef ret(SHDR_GL_CALL* n##_type) args; /* Name the representation used by this GL or shader interface. */ \
252 n##_type n; /* Store the resolved function pointer for this GL entry point. */
253 SHDR_GL_PROC(const unsigned char*, GetString, (GLenum)) // Declare a function to read the driver version or renderer text.
254 SHDR_GL_PROC(void, GetIntegerv, (GLenum, GLint*)) // Declare a function to read a scalar device limit or current binding.
255 SHDR_GL_PROC(void, GetIntegeri_v, (GLenum, GLuint, GLint*)) // Declare a function to read the limit for a selected compute axis.
256 SHDR_GL_PROC(GLenum, GetError, ()) // Declare a function to read the pending GL error code.
257 SHDR_GL_PROC(GLuint, CreateShader, (GLenum)) // Declare a function to allocate a compute shader object.
258 SHDR_GL_PROC(void, ShaderSource, (GLuint, GLsizei, const char* const*, const GLint*)) // Declare a function to supply the embedded shader text to the driver.
259 SHDR_GL_PROC(void, CompileShader, (GLuint)) // Declare a function to ask the driver to compile the compute shader.
260 SHDR_GL_PROC(void, GetShaderiv, (GLuint, GLenum, GLint*)) // Declare a function to read shader compilation status or log length.
261 SHDR_GL_PROC(void, GetShaderInfoLog, (GLuint, GLsizei, GLsizei*, char*)) // Declare a function to read the driver explanation of a shader compilation failure.
262 SHDR_GL_PROC(void, DeleteShader, (GLuint)) // Declare a function to release the temporary shader object.
263 SHDR_GL_PROC(GLuint, CreateProgram, ()) // Declare a function to allocate the linked compute program.
264 SHDR_GL_PROC(void, AttachShader, (GLuint, GLuint)) // Declare a function to attach the compute shader before linking.
265 SHDR_GL_PROC(void, LinkProgram, (GLuint)) // Declare a function to link the compiled compute program.
266 SHDR_GL_PROC(void, GetProgramiv, (GLuint, GLenum, GLint*)) // Declare a function to read linked-program status, log length, or local dimensions.
267 SHDR_GL_PROC(void, GetProgramInfoLog, (GLuint, GLsizei, GLsizei*, char*)) // Declare a function to read the driver explanation of a program-link failure.
268 SHDR_GL_PROC(void, DeleteProgram, (GLuint)) // Declare a function to release the GL program after its owners finish using it.
269 SHDR_GL_PROC(void, UseProgram, (GLuint)) // Declare a function to select the compute program for the next dispatch.
270 SHDR_GL_PROC(GLint, GetUniformLocation, (GLuint, const char*)) // Declare a function to find a named uniform in the linked program.
271 SHDR_GL_PROC(void, Uniform1f, (GLint, float)) // Declare a function to assign a float uniform.
272 SHDR_GL_PROC(void, Uniform1i, (GLint, GLint)) // Declare a function to assign an integer or boolean uniform.
273 SHDR_GL_PROC(void, Uniform1ui, (GLint, GLuint)) // Declare a function to assign an unsigned integer uniform.
274#define SHDR_GL_UNIFORM(n) /* Define the SHDR_GL_UNIFORM helper for this group of declarations. */ \
275 SHDR_GL_PROC(void, Uniform##n##fv, (GLint, GLsizei, const float*)) /* Declare a function to send this typed uniform to GL. */ \
276 SHDR_GL_PROC(void, Uniform##n##iv, (GLint, GLsizei, const GLint*)) /* Declare a function to send this typed uniform to GL. */ \
277 SHDR_GL_PROC(void, Uniform##n##uiv, (GLint, GLsizei, const GLuint*)) /* Declare a function to send this typed uniform to GL. */
278 SHDR_GL_UNIFORM(2) // Declare the GL functions for vectors with this component count.
279 SHDR_GL_UNIFORM(3) // Declare the GL functions for vectors with this component count.
280 SHDR_GL_UNIFORM(4) // Declare the GL functions for vectors with this component count.
281#undef SHDR_GL_UNIFORM // Remove this temporary macro after its declarations are complete.
282#define SHDR_GL_MATRIX(n) /* Define the SHDR_GL_MATRIX helper for this group of declarations. */ \
283 SHDR_GL_PROC(void, UniformMatrix##n##fv, (GLint, GLsizei, GLboolean, const float*)) /* Declare a function to send this typed uniform to GL. */
284 SHDR_GL_MATRIX(2) // Declare the GL function for this column-major matrix shape.
285 SHDR_GL_MATRIX(3) // Declare the GL function for this column-major matrix shape.
286 SHDR_GL_MATRIX(4) // Declare the GL function for this column-major matrix shape.
287 SHDR_GL_MATRIX(2x3) // Declare the GL function for this column-major matrix shape.
288 SHDR_GL_MATRIX(2x4) // Declare the GL function for this column-major matrix shape.
289 SHDR_GL_MATRIX(3x2) // Declare the GL function for this column-major matrix shape.
290 SHDR_GL_MATRIX(3x4) // Declare the GL function for this column-major matrix shape.
291 SHDR_GL_MATRIX(4x2) // Declare the GL function for this column-major matrix shape.
292 SHDR_GL_MATRIX(4x3) // Declare the GL function for this column-major matrix shape.
293#undef SHDR_GL_MATRIX // Remove this temporary macro after its declarations are complete.
294 SHDR_GL_PROC(void, GenBuffers, (GLsizei, GLuint*)) // Declare a function to allocate names for GL storage buffers.
295 SHDR_GL_PROC(void, DeleteBuffers, (GLsizei, const GLuint*)) // Declare a function to release owned GL buffer names.
296 SHDR_GL_PROC(void, BindBuffer, (GLenum, GLuint)) // Declare a function to select a buffer for allocation or transfer.
297 SHDR_GL_PROC(void, BufferData, (GLenum, GLsizeiptr, const void*, GLenum)) // Declare a function to allocate buffer storage and supply its initial host values.
298 SHDR_GL_PROC(void, BufferSubData, (GLenum, GLintptr, GLsizeiptr, const void*)) // Declare a function to upload the current host buffer values.
299 SHDR_GL_PROC(void, BindBufferBase, (GLenum, GLuint, GLuint)) // Declare a function to bind storage to the shader interface index.
300 SHDR_GL_PROC(void*, MapBufferRange, (GLenum, GLintptr, GLsizeiptr, GLbitfield)) // Declare a function to map completed GPU buffer values for a host readback.
301 SHDR_GL_PROC(GLboolean, UnmapBuffer, (GLenum)) // Declare a function to finish reading the mapped GPU buffer.
302 SHDR_GL_PROC(void, DispatchCompute, (GLuint, GLuint, GLuint)) // Declare a function to queue the shader workgroups.
303 SHDR_GL_PROC(void, MemoryBarrier, (GLbitfield)) // Declare a function to make earlier resource writes visible to following GPU commands.
304 SHDR_GL_PROC(void, Finish, ()) // Declare a function to explicitly wait until queued GPU commands complete.
305 SHDR_GL_PROC(GLsync, FenceSync, (GLenum, GLbitfield)) // Declare a function to insert a completion fence after queued compute work.
306 SHDR_GL_PROC(GLenum, ClientWaitSync, (GLsync, GLbitfield, GLuint64)) // Declare a function to check a GPU fence with the caller-selected timeout.
307 SHDR_GL_PROC(void, DeleteSync, (GLsync)) // Declare the GL completion-fence deletion entry point.
308 SHDR_GL_PROC(void, Flush, ()) // Declare the GL command-flush entry point.
309 SHDR_GL_PROC(void, GenTextures, (GLsizei, GLuint*)) // Declare a function to allocate names for owned GL images.
310 SHDR_GL_PROC(void, DeleteTextures, (GLsizei, const GLuint*)) // Declare a function to release owned GL image names.
311 SHDR_GL_PROC(void, BindTexture, (GLenum, GLuint)) // Declare a function to select the image for allocation or upload.
312 SHDR_GL_PROC(void, TexStorage2D, (GLenum, GLsizei, GLenum, GLsizei, GLsizei)) // Declare a function to allocate immutable two-dimensional image storage.
313 SHDR_GL_PROC(void, // Declare the next dynamically resolved GL function signature.
314 TexSubImage2D, // Name the function that uploads pixels into an existing texture.
315 (GLenum, GLint, GLint, GLint, GLsizei, GLsizei, GLenum, GLenum, const void*)) // Match the argument order required by this GL entry point.
316 SHDR_GL_PROC(void, BindImageTexture, (GLuint, GLuint, GLint, GLboolean, GLint, GLenum, GLenum)) // Declare a function to bind a typed image to its shader interface index.
317 SHDR_GL_PROC(void, PixelStorei, (GLenum, GLint)) // Declare a function to set or restore the application pixel-transfer layout.
318#undef SHDR_GL_PROC // Remove this temporary macro after its declarations are complete.
319 explicit gl_api(const std::function<void*(const char*)>& resolver) { // Resolve the GL calls used by resource transfers and dispatches.
320#define SHDR_LOAD(n) /* Resolve one GL entry point and reject missing functions. */ \
321 { /* Begin the protected scope for this operation. */ \
322 void* p = resolver("gl" #n); /* Resolve the address of this named GL entry point. */ \
323 if (!p) /* Reject a missing dynamically resolved GL function. */ \
324 throw error("missing GL entry point: gl" #n); /* Report missing gl entry point: gl. */ \
325 static_assert(sizeof(p) == sizeof(n), "function pointer size unsupported"); /* Require a representation that matches the shader interface. */ \
326 std::memcpy(&n, &p, sizeof(n)); /* Copy the representation with the alias-safe byte-copy operation. */ \
327 } /* End this scope in gl api. */
328 SHDR_LOAD(GetString) // Load the function to read the driver version or renderer text.
329 SHDR_LOAD(GetIntegerv) // Load the function to read a scalar device limit or current binding.
330 SHDR_LOAD(GetIntegeri_v) // Load the function to read the limit for a selected compute axis.
331 SHDR_LOAD(GetError) // Load the function to read the pending GL error code.
332 SHDR_LOAD(CreateShader) // Load the function to allocate a compute shader object.
333 SHDR_LOAD(ShaderSource) // Load the function to supply the embedded shader text to the driver.
334 SHDR_LOAD(CompileShader) // Load the function to ask the driver to compile the compute shader.
335 SHDR_LOAD(GetShaderiv) // Load the function to read shader compilation status or log length.
336 SHDR_LOAD(GetShaderInfoLog) // Load the function to read the driver explanation of a shader compilation failure.
337 SHDR_LOAD(DeleteShader) // Load the function to release the temporary shader object.
338 SHDR_LOAD(CreateProgram) // Load the function to allocate the linked compute program.
339 SHDR_LOAD(AttachShader) // Load the function to attach the compute shader before linking.
340 SHDR_LOAD(LinkProgram) // Load the function to link the compiled compute program.
341 SHDR_LOAD(GetProgramiv) // Load the function to read linked-program status, log length, or local dimensions.
342 SHDR_LOAD(GetProgramInfoLog) // Load the function to read the driver explanation of a program-link failure.
343 SHDR_LOAD(DeleteProgram) // Load the function to release the GL program after its owners finish using it.
344 SHDR_LOAD(UseProgram) // Load the function to select the compute program for the next dispatch.
345 SHDR_LOAD(GetUniformLocation) // Load the function to find a named uniform in the linked program.
346 SHDR_LOAD(Uniform1f) // Load the function to assign a float uniform.
347 SHDR_LOAD(Uniform1i) // Load the function to assign an integer or boolean uniform.
348 SHDR_LOAD(Uniform1ui) // Load the function to assign an unsigned integer uniform.
349#define SHDR_LOAD_UNIFORM(n) /* Load the float, signed and unsigned uniform functions for this vector width. */ \
350 SHDR_LOAD(Uniform##n##fv) /* Load the function to send this typed uniform to GL. */ \
351 SHDR_LOAD(Uniform##n##iv) /* Load the function to send this typed uniform to GL. */ \
352 SHDR_LOAD(Uniform##n##uiv) /* Load the function to send this typed uniform to GL. */
353 SHDR_LOAD_UNIFORM(2) // Load the float, signed and unsigned uniform functions for this vector width.
354 SHDR_LOAD_UNIFORM(3) // Load the float, signed and unsigned uniform functions for this vector width.
355 SHDR_LOAD_UNIFORM(4) // Load the float, signed and unsigned uniform functions for this vector width.
356#undef SHDR_LOAD_UNIFORM // Remove this temporary macro after its declarations are complete.
357 SHDR_LOAD(UniformMatrix2fv) // Load the function to send this typed uniform to GL.
358 SHDR_LOAD(UniformMatrix3fv) // Load the function to send this typed uniform to GL.
359 SHDR_LOAD(UniformMatrix4fv) // Load the function to send this typed uniform to GL.
360 SHDR_LOAD(UniformMatrix2x3fv) // Load the function to send this typed uniform to GL.
361 SHDR_LOAD(UniformMatrix2x4fv) // Load the function to send this typed uniform to GL.
362 SHDR_LOAD(UniformMatrix3x2fv) // Load the function to send this typed uniform to GL.
363 SHDR_LOAD(UniformMatrix3x4fv) // Load the function to send this typed uniform to GL.
364 SHDR_LOAD(UniformMatrix4x2fv) // Load the function to send this typed uniform to GL.
365 SHDR_LOAD(UniformMatrix4x3fv) // Load the function to send this typed uniform to GL.
366 SHDR_LOAD(GenBuffers) // Load the function to allocate names for GL storage buffers.
367 SHDR_LOAD(DeleteBuffers) // Load the function to release owned GL buffer names.
368 SHDR_LOAD(BindBuffer) // Load the function to select a buffer for allocation or transfer.
369 SHDR_LOAD(BufferData) // Load the function to allocate buffer storage and supply its initial host values.
370 SHDR_LOAD(BufferSubData) // Load the function to upload the current host buffer values.
371 SHDR_LOAD(BindBufferBase) // Load the function to bind storage to the shader interface index.
372 SHDR_LOAD(MapBufferRange) // Load the function to map completed GPU buffer values for a host readback.
373 SHDR_LOAD(UnmapBuffer) // Load the function to finish reading the mapped GPU buffer.
374 SHDR_LOAD(DispatchCompute) // Load the function to queue the shader workgroups.
375 SHDR_LOAD(MemoryBarrier) // Load the function to make earlier resource writes visible to following GPU commands.
376 SHDR_LOAD(Finish) // Load the function to explicitly wait until queued GPU commands complete.
377 SHDR_LOAD(GenTextures) // Load the function to allocate names for owned GL images.
378 SHDR_LOAD(DeleteTextures) // Load the function to release owned GL image names.
379 SHDR_LOAD(FenceSync) // Load the function to insert a completion fence after queued compute work.
380 SHDR_LOAD(ClientWaitSync) // Load the function to check a GPU fence with the caller-selected timeout.
381 SHDR_LOAD(DeleteSync) // Resolve the GL completion-fence deletion entry point.
382 SHDR_LOAD(Flush) // Resolve the GL command-flush entry point.
383 SHDR_LOAD(BindTexture) // Load the function to select the image for allocation or upload.
384 SHDR_LOAD(TexStorage2D) // Load the function to allocate immutable two-dimensional image storage.
385 SHDR_LOAD(TexSubImage2D) // Load the function to upload the current host pixel values.
386 SHDR_LOAD(BindImageTexture) // Load the function to bind a typed image to its shader interface index.
387 SHDR_LOAD(PixelStorei) // Load the function to set or restore the application pixel-transfer layout.
388#undef SHDR_LOAD // Remove this temporary macro after its declarations are complete.
389 } // End this scope in gl api.
390 void check(const std::string& operation) const { // Check for a driver error from the preceding GL operation.
391 const unsigned code = GetError(); // Read the pending GL error code.
392 if (code) { // Choose this branch when code.
393 std::ostringstream s; // Build a readable diagnostic containing the operation and GL error.
394 s << operation << ": GL error 0x" << std::hex << code; // Combine the operation name and hexadecimal driver error.
395 throw error(s.str()); // Report the invalid operation.
396 } // End this scope in runtime.
397 } // End this scope in runtime.
398}; // End this scope in runtime.
399#undef SHDR_GL_CALL // Remove this temporary macro after its declarations are complete.
400
401struct device_state { // Retain GL functions, context ownership, limits, and pending cleanup.
402 gl_api gl; // Keep the retained GL function table.
403 profile language; // Keep the selected shader profile.
404 unsigned max_groups[3], max_local[3], max_invocations, max_buffers, max_images; // Keep work group dimensions, invocation and resource binding limits.
405 std::shared_ptr<void> context_keepalive; // Keep the retained headless context.
406 std::function<void()> activate_context; // Keep the callback that makes this device context current.
407 std::thread::id context_thread; // Keep the thread allowed to issue GL commands.
408 std::mutex retirement_mutex; // Keep the lock for deferred resource cleanup.
409 std::vector<std::function<void(gl_api&)>> retirements; // Keep GL deletions requested by another thread.
410 struct deferred { // Keep the deferred values and operations together.
411 std::function<bool(gl_api&)> poll; // Keep the zero-time completion callback.
412 std::function<void(gl_api&)> cancel; // Keep the fence-deletion callback used at shutdown.
413 }; // End this scope in runtime.
414 std::vector<deferred> pending_work; // Keep discarded submissions awaiting completion.
415 bool retirements_closed; // Keep whether device teardown has stopped retaining submissions.
416 device_state(const std::function<void*(const char*)>& resolver, profile p) // Load GL entry points and validate the selected compute profile.
417 : gl(resolver), language(p), context_thread(std::this_thread::get_id()), // Resolve GL functions and record this context's profile and owning thread.
418 retirements_closed(false) { // Allow pending completion leases until the public device is destroyed.
419 const unsigned char* version = gl.GetString(VERSION); // Read the current context version before checking the requested profile.
420 if (!version) // Require a version string from the current context.
421 throw error("device requires a current GL context"); // Report device requires a current gl context.
422 const bool es = std::string(reinterpret_cast<const char*>(version)).find("OpenGL ES") != // Set whether the current context uses OpenGL ES.
423 std::string::npos; // Detect the GLES marker anywhere in the driver version string.
424 const bool requested_es = p == profile::es31 || p == profile::es32; // Set whether the requested profile uses GLES.
425 if (es != requested_es) // Choose this branch when es differs from requested es.
426 throw error("shader profile does not match current GL/ES context"); // Report a mismatch between the selected profile and the current context.
427 GLint major = 0, minor = 0; // Prepare both parts of the current GL version query.
428 gl.GetIntegerv(MAJOR_VERSION, &major); // Read a scalar device limit or current binding.
429 gl.GetIntegerv(MINOR_VERSION, &minor); // Read a scalar device limit or current binding.
430 const int required_major = es ? 3 : 4; // Set the minimum GL major version for this profile.
431 const int required_minor = p == profile::es31 ? 1 // Set the minimum GL minor version for this profile.
432 : p == profile::es32 ? 2 // Select the version associated with this shader profile.
433 : p == profile::gl43 ? 3 // Select the version associated with this shader profile.
434 : p == profile::gl45 ? 5 // Select the version associated with this shader profile.
435 : 6; // Select minor version six for the desktop GL 4.6 profile.
436 if (major < required_major || (major == required_major && minor < required_minor)) // Choose this branch when major < required major or (major equals required major and minor < required minor).
437 throw error("current context is older than requested compute profile"); // Report current context is older than requested compute profile.
438 for (unsigned i = 0; i < 3; ++i) { // Validate or query each of the three compute dimensions.
439 GLint n = 0; // Set the queried GL limit or element count.
440 gl.GetIntegeri_v(MAX_COMPUTE_WORK_GROUP_COUNT, i, &n); // Read the limit for a selected compute axis.
441 max_groups[i] = static_cast<unsigned>(n); // Set the device workgroup-count limits.
442 gl.GetIntegeri_v(MAX_COMPUTE_WORK_GROUP_SIZE, i, &n); // Read the limit for a selected compute axis.
443 max_local[i] = static_cast<unsigned>(n); // Set the device local-size limits.
444 } // End this scope in device state.
445 GLint n = 0; // Set the queried GL limit or element count.
446 gl.GetIntegerv(MAX_COMPUTE_WORK_GROUP_INVOCATIONS, &n); // Read a scalar device limit or current binding.
447 max_invocations = static_cast<unsigned>(n); // Set the maximum invocations in one workgroup.
448 gl.GetIntegerv(MAX_SHADER_STORAGE_BUFFER_BINDINGS, &n); // Read a scalar device limit or current binding.
449 max_buffers = static_cast<unsigned>(n); // Set the storage-buffer binding limit.
450 gl.GetIntegerv(MAX_IMAGE_UNITS, &n); // Read a scalar device limit or current binding.
451 max_images = static_cast<unsigned>(n); // Set the image binding limit.
452 gl.check("query compute capabilities"); // Check for a driver error from the preceding GL operation.
453 } // End this scope in device state.
454 void require_thread() { // Require the device context thread and activate its headless context.
455 if (std::this_thread::get_id() != context_thread) // Check that the operation uses the context or lifecycle thread.
456 throw error("GL operations require the device's context thread"); // Report gl operations require the device's context thread.
457 if (activate_context) // Choose this branch when activate context.
458 activate_context(); // Keep the callback that makes this device context current.
459 } // End this scope in require thread.
460 void collect() { // Run queued deletions and zero-time completion checks.
461 require_thread(); // Require the device context thread and activate its headless context.
462 std::vector<std::function<void(gl_api&)>> pending; // Keep the queued work or cleanup callbacks awaiting processing.
463 std::vector<deferred> polls, unfinished; // Keep completion polls that must be checked again.
464 { // Begin the protected scope for this operation.
465 std::lock_guard<std::mutex> lock(retirement_mutex); // Hold the mutex while this shared state is accessed.
466 pending.swap(retirements); // Take the shared entries into a local collection for unlocked processing.
467 polls.swap(pending_work); // Take the shared entries into a local collection for unlocked processing.
468 } // End this scope in collect.
469 for (std::size_t i = 0; i < pending.size(); ++i) // Process the cleanup or completion callbacks taken from the queue.
470 pending[i](gl); // Keep the retained GL function table.
471 for (std::size_t i = 0; i < polls.size(); ++i) // Process the cleanup or completion callbacks taken from the queue.
472 if (!polls[i].poll(gl)) // Retain a completion callback while its work is pending.
473 unfinished.push_back(std::move(polls[i])); // Retain this entry in the ordered resource or work collection.
474 if (!unfinished.empty()) { // Keep the remaining callbacks for the next collection pass.
475 std::lock_guard<std::mutex> lock(retirement_mutex); // Hold the mutex while this shared state is accessed.
476 pending_work.insert(pending_work.end(), // Retain this entry in the ordered resource or work collection.
477 std::make_move_iterator(unfinished.begin()), // Supply the next value for the current call or declaration.
478 std::make_move_iterator(unfinished.end())); // Keep the end of the current range.
479 } // End this scope in collect.
480 } // End this scope in collect.
481 void defer_poll(const std::function<bool(gl_api&)>& poll, // Retain a completion check until its fence signals.
482 const std::function<void(gl_api&)>& cancel = std::function<void(gl_api&)>()) { // Set the fence-deletion callback used at shutdown.
483 { // Begin the protected scope for this operation.
484 std::lock_guard<std::mutex> lock(retirement_mutex); // Hold the mutex while this shared state is accessed.
485 if (!retirements_closed) { // Check whether teardown still allows retained completion callbacks.
486 pending_work.push_back(deferred{poll, cancel}); // Retain this entry in the ordered resource or work collection.
487 return; // Finish this operation.
488 } // End this scope in defer poll.
489 } // End this scope in defer poll.
490 // Handles may outlive the public device wrapper. After wrapper teardown,
491 // schedule their fence deletion directly on the context thread.
492 if (cancel) // Choose this branch when cancel.
493 retire(cancel); // Delete on the context thread or defer deletion to that thread.
494 } // End this scope in defer poll.
495 void close_retirements() { // Stop retaining new submission bundles during device teardown.
496 { // Begin the protected scope for this operation.
497 std::lock_guard<std::mutex> lock(retirement_mutex); // Hold the mutex while this shared state is accessed.
498 retirements_closed = true; // Set whether device teardown has stopped retaining submissions.
499 } // End this scope in close retirements.
500 cancel_pending(); // Schedule deferred-fence deletion on the context thread.
501 } // End this scope in close retirements.
502 void cancel_pending() { // Schedule deferred-fence deletion on the context thread.
503 require_thread(); // Require the device context thread and activate its headless context.
504 std::vector<deferred> pending; // Keep the queued work or cleanup callbacks awaiting processing.
505 { // Begin the protected scope for this operation.
506 std::lock_guard<std::mutex> lock(retirement_mutex); // Hold the mutex while this shared state is accessed.
507 pending.swap(pending_work); // Take the shared entries into a local collection for unlocked processing.
508 } // End this scope in cancel pending.
509 for (std::size_t i = 0; i < pending.size(); ++i) // Process the cleanup or completion callbacks taken from the queue.
510 if (pending[i].cancel) // Check the queue state before accepting or executing work.
511 pending[i].cancel(gl); // Keep the retained GL function table.
512 pending.clear(); // Release the retained entries after their work or cleanup is complete.
513 collect(); // Run queued deletions and zero-time completion checks.
514 } // End this scope in cancel pending.
515 void retire(const std::function<void(gl_api&)>& deletion) { // Delete on the context thread or defer deletion to that thread.
516 if (std::this_thread::get_id() == context_thread) { // Check that the operation uses the context or lifecycle thread.
517 require_thread(); // Require the device context thread and activate its headless context.
518 deletion(gl); // Keep the retained GL function table.
519 } else { // Handle the alternative resource or completion case.
520 std::lock_guard<std::mutex> lock(retirement_mutex); // Hold the mutex while this shared state is accessed.
521 retirements.push_back(deletion); // Retain this entry in the ordered resource or work collection.
522 } // End this scope in retire.
523 } // End this scope in retire.
524 ~device_state() noexcept { // Close pending retirement leases on the context thread.
525 if (std::this_thread::get_id() == context_thread) { // Check that the operation uses the context or lifecycle thread.
526 try { // Keep cleanup available if an allocation, shader, or driver operation fails.
527 close_retirements(); // Stop retaining new submission bundles during device teardown.
528 } catch (...) { // Preserve cleanup or exception propagation for this failed operation.
529 } // End this scope in device state.
530 } // End this scope in device state.
531 } // End this scope in device state.
532 GLuint compile(const std::string& source, const std::string& name) { // Compile and link the embedded compute source with driver diagnostics.
533 if (source.size() > static_cast<std::size_t>((std::numeric_limits<GLint>::max)())) // Check resource size or driver limits before accessing storage.
534 throw error("shader source too large"); // Report shader source too large.
535 GLuint shader = gl.CreateShader(COMPUTE_SHADER), program = 0; // Allocate a compute shader object.
536 if (!shader) // Check that the driver allocated a shader object.
537 throw error("cannot create compute shader"); // Report compute-shader allocation failure.
538 try { // Keep cleanup available if an allocation, shader, or driver operation fails.
539 const char* text = source.c_str(); // Give GL a pointer to the retained shader characters.
540 GLint len = static_cast<GLint>(source.size()); // Pass the validated shader character count to GL.
541 gl.ShaderSource(shader, 1, &text, &len); // Supply the embedded shader text to the driver.
542 gl.CompileShader(shader); // Ask the driver to compile the compute shader.
543 GLint success = 0; // Prepare the compile or link status returned by GL.
544 gl.GetShaderiv(shader, COMPILE_STATUS, &success); // Read shader compilation status or log length.
545 if (!success) { // Read the driver diagnostic for this failed operation.
546 GLint size = 0; // Prepare the driver diagnostic log length.
547 gl.GetShaderiv(shader, INFO_LOG_LENGTH, &size); // Read shader compilation status or log length.
548 std::vector<char> log(static_cast<std::size_t>((std::max)(size, 1)), 0); // Allocate enough characters for the driver diagnostic and terminator.
549 gl.GetShaderInfoLog(shader, size, 0, log.data()); // Read the driver explanation of a shader compilation failure.
550 throw error(name + ": shader compilation failed\n" + log.data()); // Report : shader compilation failed\n.
551 } // End this scope in compile.
552 program = gl.CreateProgram(); // Allocate the linked compute program.
553 if (!program) // Check that the driver allocated a program object.
554 throw error("cannot create compute program"); // Report compute-program allocation failure.
555 gl.AttachShader(program, shader); // Attach the compute shader before linking.
556 gl.LinkProgram(program); // Link the compiled compute program.
557 gl.GetProgramiv(program, LINK_STATUS, &success); // Read linked-program status, log length, or local dimensions.
558 if (!success) { // Read the driver diagnostic for this failed operation.
559 GLint size = 0; // Prepare the driver diagnostic log length.
560 gl.GetProgramiv(program, INFO_LOG_LENGTH, &size); // Read linked-program status, log length, or local dimensions.
561 std::vector<char> log(static_cast<std::size_t>((std::max)(size, 1)), 0); // Allocate enough characters for the driver diagnostic and terminator.
562 gl.GetProgramInfoLog(program, size, 0, log.data()); // Read the driver explanation of a program-link failure.
563 throw error(name + ": program link failed\n" + log.data()); // Report : program link failed\n.
564 } // End this scope in compile.
565 gl.DeleteShader(shader); // Release the temporary shader object.
566 shader = 0; // Set the shader used by this dispatch.
567 gl.check("link " + name); // Keep the resource or entry-point name.
568 return program; // Compile and link the embedded compute source with driver diagnostics.
569 } catch (...) { // Preserve cleanup or exception propagation for this failed operation.
570 if (shader) // Choose this branch when shader.
571 gl.DeleteShader(shader); // Release the temporary shader object.
572 if (program) // Choose this branch when program.
573 gl.DeleteProgram(program); // Release the GL program after its owners finish using it.
574 throw; // Rethrow the original failure after arranging the necessary cleanup.
575 } // End this scope in compile.
576 } // End this scope in compile.
577}; // End this scope in runtime.
578struct program_state { // Keep the program state values and operations together.
579 std::shared_ptr<device_state> owner; // Keep the device that owns the GL resource.
580 GLuint id; // Keep the compiled GL program name.
581 program_state(const std::shared_ptr<device_state>& d, GLuint id_) : owner(d), id(id_) { // Expose the retained compiled program for asynchronous ownership.
582 } // End this scope in program state.
583 ~program_state() { // Release this object's owned resources at teardown.
584 if (id) { // Choose this branch when id.
585 const GLuint name = id; // Set the resource or entry-point name.
586 owner->retire([name](gl_api& gl) { // Delete on the context thread or defer deletion to that thread.
587 gl.DeleteProgram(name); // Release the GL program after its owners finish using it.
588 }); // End this scope in program state.
589 } // End this scope in program state.
590 } // End this scope in program state.
591}; // End this scope in runtime.
592
593inline std::size_t checked_product(std::size_t a, std::size_t b) { // Reject resource-size multiplication that would overflow.
594 if (b && a > (std::numeric_limits<std::size_t>::max)() / b) // Check resource size or driver limits before accessing storage.
595 throw error("resource size overflow"); // Report resource size overflow.
596 return a * b; // Reject resource-size multiplication that would overflow.
597} // End this scope in checked product.
598inline GLsizeiptr gl_size(std::size_t size) { // Require a byte count representable by the GL buffer API.
599 if (size > static_cast<std::size_t>((std::numeric_limits<GLsizeiptr>::max)())) // Check resource size or driver limits before accessing storage.
600 throw error("resource exceeds GLsizeiptr"); // Report resource exceeds glsizeiptr.
601 return static_cast<GLsizeiptr>(size); // Require a byte count representable by the GL buffer API.
602} // End this scope in gl size.
603enum class authority { host, gpu, both }; // Name the profile, access, or completion values accepted by this section.
604enum class resource_kind { buffer, image }; // Name the profile, access, or completion values accepted by this section.
605struct resource_state { // Track shared storage and which backend has the newest values.
606 resource_kind kind; // Keep the buffer, image, or uniform interface kind.
607 authority latest; // Keep which copy contains the latest resource values.
608 std::shared_ptr<device_state> owner; // Keep the device that owns the GL resource.
609 GLuint handle; // Keep the GL buffer or texture name.
610 bool borrowed; // Keep whether the application owns the supplied GL object.
611 unsigned width, height, components, format, scalar; // Keep the GL scalar representation.
612 resource_state(resource_kind k) // Initialize ownership and synchronization for one resource.
613 : kind(k), latest(authority::host), handle(0), borrowed(false), width(0), height(0), // Start with authoritative host data and allocate GL storage on demand.
614 components(0), format(0), scalar(0) { // Initialize image layout metadata to zero.
615 } // End this scope in resource state.
616 virtual ~resource_state() { // Release this object's owned resources at teardown.
617 if (handle && !borrowed) { // Delete the resource when its GL handle is owned by this object.
618 const GLuint name = handle; // Set the resource or entry-point name.
619 if (kind == resource_kind::buffer) // Choose the branch matching this interface or resource access.
620 owner->retire([name](gl_api& gl) { // Delete on the context thread or defer deletion to that thread.
621 gl.DeleteBuffers(1, &name); // Release owned GL buffer names.
622 }); // End this scope in resource state.
623 else // Handle the other resource, platform, or completion case.
624 owner->retire([name](gl_api& gl) { // Delete on the context thread or defer deletion to that thread.
625 gl.DeleteTextures(1, &name); // Release owned GL image names.
626 }); // End this scope in resource state.
627 } // End this scope in resource state.
628 } // End this scope in resource state.
629 virtual void* host_pointer() = 0; // Require each resource type to expose its host storage.
630 virtual std::size_t bytes() const = 0; // Require each resource type to report its byte count.
631 void ensure_host(); // Download newer GPU values only when host access needs them.
632 void ensure_gpu(const std::shared_ptr<device_state>& d) { // Allocate or upload GPU storage when its host copy is newer.
633 d->collect(); // Run queued deletions and zero-time completion checks.
634 if (owner && owner != d) // Check that this storage belongs to the requested device.
635 throw error("resource belongs to a different device"); // Report resource belongs to a different device.
636 owner = d; // Set the device that owns the GL resource.
637 const gl_api& gl = d->gl; // Set the retained GL function table.
638 if (!handle) { // Allocate GPU storage on the first device access.
639 if (kind == resource_kind::buffer) { // Choose the branch matching this interface or resource access.
640 gl.GenBuffers(1, &handle); // Allocate names for GL storage buffers.
641 gl.BindBuffer(SHADER_STORAGE_BUFFER, handle); // Select a buffer for allocation or transfer.
642 gl.BufferData(SHADER_STORAGE_BUFFER, // Allocate buffer storage and supply its initial host values.
643 gl_size(bytes()), // Require a byte count representable by the GL buffer API.
644 bytes() ? host_pointer() : 0, // Supply the next value for the current call or declaration.
645 DYNAMIC_DRAW); // Require a byte count representable by the GL buffer API.
646 } else { // Handle the alternative resource or completion case.
647 gl.GenTextures(1, &handle); // Allocate names for owned GL images.
648 gl.BindTexture(TEXTURE_2D, handle); // Select the image for allocation or upload.
649 gl.TexStorage2D(TEXTURE_2D, // Allocate immutable two-dimensional image storage.
650 1, // Supply the next value for the current call or declaration.
651 format, // Visit the values or interfaces needed by ensure gpu.
652 static_cast<GLsizei>(width), // Keep the image width in pixels.
653 static_cast<GLsizei>(height)); // Keep the image height in pixels.
654 } // End this scope in ensure gpu.
655 gl.check("allocate compute resource"); // Check for a driver error from the preceding GL operation.
656 if (kind == resource_kind::buffer) // Choose the branch matching this interface or resource access.
657 latest = authority::both; // Record which backend now owns the newest resource values.
658 } // End this scope in ensure gpu.
659 if (latest == authority::host) { // Check which resource copy must be synchronized.
660 if (kind == resource_kind::buffer) { // Choose the branch matching this interface or resource access.
661 gl.BindBuffer(SHADER_STORAGE_BUFFER, handle); // Select a buffer for allocation or transfer.
662 if (bytes()) // Check resource size or driver limits before accessing storage.
663 gl.BufferSubData(SHADER_STORAGE_BUFFER, 0, gl_size(bytes()), host_pointer()); // Upload the current host buffer values.
664 } else { // Handle the alternative resource or completion case.
665 // Make upload independent of caller pixel unpack state and restore that state.
666 GLint alignment = 0, row = 0, skip_rows = 0, skip_pixels = 0, pbo = 0; // Save the complete pixel-unpack layout and bound transfer buffer.
667 gl.GetIntegerv(UNPACK_ALIGNMENT, &alignment); // Read a scalar device limit or current binding.
668 gl.GetIntegerv(UNPACK_ROW_LENGTH, &row); // Read a scalar device limit or current binding.
669 gl.GetIntegerv(UNPACK_SKIP_ROWS, &skip_rows); // Read a scalar device limit or current binding.
670 gl.GetIntegerv(UNPACK_SKIP_PIXELS, &skip_pixels); // Read a scalar device limit or current binding.
671 gl.GetIntegerv(0x88EF, &pbo); // GL_PIXEL_UNPACK_BUFFER_BINDING
672 gl.BindBuffer(PIXEL_UNPACK_BUFFER, 0); // Select a buffer for allocation or transfer.
673 gl.PixelStorei(UNPACK_ALIGNMENT, 1); // Set or restore the application pixel-transfer layout.
674 gl.PixelStorei(UNPACK_ROW_LENGTH, 0); // Set or restore the application pixel-transfer layout.
675 gl.PixelStorei(UNPACK_SKIP_ROWS, 0); // Set or restore the application pixel-transfer layout.
676 gl.PixelStorei(UNPACK_SKIP_PIXELS, 0); // Set or restore the application pixel-transfer layout.
677 gl.BindTexture(TEXTURE_2D, handle); // Select the image for allocation or upload.
678 const GLenum channels = scalar == FLOAT // Choose the float or integer pixel-transfer channel representation.
679 ? (components == 1 ? RED : RGBA) // Set the number of scalar values per pixel.
680 : (components == 1 ? RED_INTEGER : RGBA_INTEGER); // Choose the upload pixel format matching integer channels.
681 gl.TexSubImage2D(TEXTURE_2D, // Upload the current host pixel values.
682 0, // Supply the next value for the current call or declaration.
683 0, // Supply the next value for the current call or declaration.
684 0, // Supply the next value for the current call or declaration.
685 static_cast<GLsizei>(width), // Keep the image width in pixels.
686 static_cast<GLsizei>(height), // Keep the image height in pixels.
687 channels, // Supply the next value for the current call or declaration.
688 scalar, // Keep the GL scalar representation.
689 host_pointer()); // Expose this resource's borrowed or owned host storage.
690 gl.PixelStorei(UNPACK_ALIGNMENT, alignment); // Set or restore the application pixel-transfer layout.
691 gl.PixelStorei(UNPACK_ROW_LENGTH, row); // Set or restore the application pixel-transfer layout.
692 gl.PixelStorei(UNPACK_SKIP_ROWS, skip_rows); // Set or restore the application pixel-transfer layout.
693 gl.PixelStorei(UNPACK_SKIP_PIXELS, skip_pixels); // Set or restore the application pixel-transfer layout.
694 gl.BindBuffer(PIXEL_UNPACK_BUFFER, static_cast<GLuint>(pbo)); // Select a buffer for allocation or transfer.
695 } // End this scope in ensure gpu.
696 gl.check("upload compute resource"); // Check for a driver error from the preceding GL operation.
697 latest = authority::both; // Record which backend now owns the newest resource values.
698 } // End this scope in ensure gpu.
699 } // End this scope in ensure gpu.
700}; // End this scope in runtime.
701
702 // Image readback uses a compute copy on both desktop GL and GLES.
703inline void resource_state::ensure_host() { // Download newer GPU values only when host access needs them.
704 if (latest != authority::gpu) // Check which resource copy must be synchronized.
705 return; // Finish this operation.
706 owner->collect(); // Run queued deletions and zero-time completion checks.
707 const gl_api& gl = owner->gl; // Set the retained GL function table.
708 if (!bytes()) { // Check resource size or driver limits before accessing storage.
709 latest = authority::both; // Record which backend now owns the newest resource values.
710 return; // Finish this operation.
711 } // End this scope in ensure host.
712 GLuint staging = 0, program = 0; // Track the temporary readback buffer and copy program for cleanup.
713 try { // Keep cleanup available if an allocation, shader, or driver operation fails.
714 gl.MemoryBarrier(ALL_BARRIER_BITS); // Make earlier resource writes visible to following GPU commands.
715 if (kind == resource_kind::buffer) // Choose the branch matching this interface or resource access.
716 gl.BindBuffer(SHADER_STORAGE_BUFFER, handle); // Select a buffer for allocation or transfer.
717 else { // Handle the other resource, platform, or completion case.
718 const char* scalar_name = scalar == FLOAT ? "float" : scalar == INT ? "int" : "uint"; // Choose the GLSL scalar spelling matching these image pixels.
719 const char* vector_name = scalar == FLOAT ? "vec4" : scalar == INT ? "ivec4" : "uvec4"; // Choose the four-component GLSL value used by imageLoad.
720 const char* image_name = scalar == FLOAT ? "image2D" // Choose the matching float, signed or unsigned storage-image type.
721 : scalar == INT ? "iimage2D" // Choose the shader image type matching the resource's scalar representation.
722 : "uimage2D"; // Choose the shader image type matching the resource's scalar representation.
723 const char* format_name = components == 1 ? (scalar == FLOAT ? "r32f" // Choose the declared storage-image format for the copy shader.
724 : scalar == INT ? "r32i" // Choose the shader format matching scalar type and channel count.
725 : "r32ui") // Choose the shader format matching scalar type and channel count.
726 : (scalar == FLOAT ? "rgba32f" // Choose the shader format matching scalar type and channel count.
727 : scalar == INT ? "rgba32i" // Choose the shader format matching scalar type and channel count.
728 : "rgba32ui"); // Choose the shader format matching scalar type and channel count.
729 std::ostringstream body; // Download newer GPU values only when host access needs them.
730 body << "SHDR_BEGIN(shdr_readback,8,8,1)\nSHDR_IMAGE2D(0," << format_name << ',' // Download newer GPU values only when host access needs them.
731 << image_name << ",src,readonly)\n" // Download newer GPU values only when host access needs them.
732 << "SHDR_BUFFER(0," << scalar_name // Download newer GPU values only when host access needs them.
733 << ",dst,writeonly)\nSHDR_MAIN(){uvec2 p=gl_GlobalInvocationID.xy;" // Download newer GPU values only when host access needs them.
734 << "ivec2 s=imageSize(src);if(p.x>=uint(s.x)||p.y>=uint(s.y))return;" // Download newer GPU values only when host access needs them.
735 << vector_name << " v=imageLoad(src,ivec2(p));uint i=(p.y*uint(s.x)+p.x)*" // Download newer GPU values only when host access needs them.
736 << components << "u;"; // Download newer GPU values only when host access needs them.
737 for (unsigned c = 0; c < components; ++c) // Visit the values or interfaces needed by ensure host.
738 body << "dst[i+" << c << "u]=v[" << c << "];"; // Download newer GPU values only when host access needs them.
739 body << "} SHDR_END()\n"; // Download newer GPU values only when host access needs them.
740 program = owner->compile(glsl_source(body.str(), owner->language), "image readback"); // Compile the portable image-to-buffer readback shader.
741 gl.GenBuffers(1, &staging); // Allocate names for GL storage buffers.
742 gl.BindBuffer(SHADER_STORAGE_BUFFER, staging); // Select a buffer for allocation or transfer.
743 gl.BufferData(SHADER_STORAGE_BUFFER, gl_size(bytes()), 0, DYNAMIC_DRAW); // Allocate buffer storage and supply its initial host values.
744 gl.UseProgram(program); // Select the compute program for the next dispatch.
745 gl.BindBufferBase(SHADER_STORAGE_BUFFER, 0, staging); // Bind storage to the shader interface index.
746 gl.BindImageTexture(0, handle, 0, 0, 0, READ_ONLY, format); // Bind a typed image to its shader interface index.
747 gl.DispatchCompute((width + 7) / 8, (height + 7) / 8, 1); // Queue the shader workgroups.
748 gl.MemoryBarrier(ALL_BARRIER_BITS); // Make earlier resource writes visible to following GPU commands.
749 gl.check("copy image to host staging buffer"); // Check for a driver error from the preceding GL operation.
750 gl.BindBuffer(SHADER_STORAGE_BUFFER, staging); // Select a buffer for allocation or transfer.
751 } // End this scope in ensure host.
752 void* mapped = gl.MapBufferRange(SHADER_STORAGE_BUFFER, 0, gl_size(bytes()), MAP_READ_BIT); // Map completed GPU buffer values for a host readback.
753 if (!mapped) { // Check that GL returned a readable mapped range.
754 gl.check("map compute readback"); // Check for a driver error from the preceding GL operation.
755 throw error("cannot map compute readback"); // Report readback mapping failure.
756 } // End this scope in ensure host.
757 std::memcpy(host_pointer(), mapped, bytes()); // Copy the representation with the alias-safe byte-copy operation.
758 if (!gl.UnmapBuffer(SHADER_STORAGE_BUFFER)) // Finish reading the mapped GPU buffer.
759 throw error("compute readback buffer contents corrupted"); // Report compute readback buffer contents corrupted.
760 gl.check("read back compute resource"); // Check for a driver error from the preceding GL operation.
761 latest = authority::both; // Record which backend now owns the newest resource values.
762 if (staging) // Choose this branch when staging.
763 gl.DeleteBuffers(1, &staging); // Release owned GL buffer names.
764 if (program) // Choose this branch when program.
765 gl.DeleteProgram(program); // Release the GL program after its owners finish using it.
766 } catch (...) { // Preserve cleanup or exception propagation for this failed operation.
767 if (staging) // Choose this branch when staging.
768 gl.DeleteBuffers(1, &staging); // Release owned GL buffer names.
769 if (program) // Choose this branch when program.
770 gl.DeleteProgram(program); // Release the GL program after its owners finish using it.
771 throw; // Rethrow the original failure after arranging the necessary cleanup.
772 } // End this scope in ensure host.
773} // End this scope in ensure host.
774
775 // Only layouts identical to std430 arrays are accepted. vec3, bool and arbitrary structs need explicit packing.
776template <class T> // Select the scalar, dimension, or access type at compile time.
777struct storage_type // Keep the storage type values and operations together.
778 : std::integral_constant<bool, // Expose the following storage-layout checks as a compile-time boolean.
779 std::is_same<T, float>::value || std::is_same<T, int>::value || // Accept the supported 32-bit float and signed integer storage types.
780 std::is_same<T, unsigned>::value> {}; // Also accept the supported 32-bit unsigned storage type.
781template <class T, std::size_t N> // Select the scalar, dimension, or access type at compile time.
782struct storage_type<glsl::vec<T, N>> // Keep the storage type values and operations together.
783 : std::integral_constant<bool, // Expose the following storage-layout checks as a compile-time boolean.
784 storage_type<T>::value && (N == 2 || N == 4) && // Require a supported scalar and a two- or four-component vector.
785 sizeof(glsl::vec<T, N>) == sizeof(T) * N> {}; // Require a tightly packed vector that matches its std430 array stride.
786template <class T> struct buffer_state : resource_state { // Select the scalar, dimension, or access type at compile time.
787 std::vector<T> owned; // Keep host storage allocated by this resource.
788 T* external; // Point to application-owned host memory.
789 std::size_t count; // Keep the element or configuration count.
790 explicit buffer_state(std::size_t n) // Allocate owned host storage for this number of elements.
791 : resource_state(resource_kind::buffer), owned(n), external(0), count(n) { // Initialize ownership and synchronization for one resource.
792 } // End this scope in runtime.
793 buffer_state(T* p, std::size_t n) // Retain a reference to an application-owned host range.
794 : resource_state(resource_kind::buffer), external(p), count(n) { // Initialize ownership and synchronization for one resource.
795 if (!p && n) // Require a valid pointer for a nonempty borrowed range.
796 throw error("null borrowed CPU buffer"); // Report null borrowed cpu buffer.
797 } // End this scope in runtime.
798 void* host_pointer() override { // Expose this resource's borrowed or owned host storage.
799 return external ? external : owned.data(); // Use borrowed host memory when present and owned memory otherwise.
800 } // End this scope in runtime.
801 std::size_t bytes() const override { // Report this resource's byte count with overflow checking.
802 return checked_product(count, sizeof(T)); // Calculate the byte count with checked size arithmetic.
803 } // End this scope in runtime.
804}; // End this scope in runtime.
805template <class T> struct image_state : resource_state { // Select the scalar, dimension, or access type at compile time.
806 std::vector<T> owned; // Keep host storage allocated by this resource.
807 image_state(unsigned w, unsigned h, unsigned c) : resource_state(resource_kind::image) { // Initialize ownership and synchronization for one resource.
808 if (!w || !h || w > static_cast<unsigned>((std::numeric_limits<GLsizei>::max)()) || // Check resource size or driver limits before accessing storage.
809 h > static_cast<unsigned>((std::numeric_limits<GLsizei>::max)())) // Require the image height to fit the GL signed dimension type.
810 throw error("image dimensions must be positive GLsizei values"); // Report image dimensions must be positive glsizei values.
811 width = w; // Set the image width in pixels.
812 height = h; // Set the image height in pixels.
813 components = c; // Set the number of scalar values per pixel.
814 scalar = std::is_same<T, float>::value ? FLOAT // Set the GL scalar representation.
815 : std::is_same<T, int>::value ? INT // Choose the GL scalar type matching the host pixel representation.
816 : UNSIGNED_INT; // Choose the GL scalar type matching the host pixel representation.
817 format = c == 1 ? (scalar == FLOAT ? R32F // Visit the values or interfaces needed by runtime.
818 : scalar == INT ? R32I // Choose the GL internal image format matching scalar type and channel count.
819 : R32UI) // Choose the GL internal image format matching scalar type and channel count.
820 : (scalar == FLOAT ? RGBA32F // Choose the GL internal image format matching scalar type and channel count.
821 : scalar == INT ? RGBA32I // Choose the GL internal image format matching scalar type and channel count.
822 : RGBA32UI); // Choose the GL internal image format matching scalar type and channel count.
823 owned.resize(checked_product(checked_product(w, h), c)); // Reject resource-size multiplication that would overflow.
824 } // End this scope in runtime.
825 void* host_pointer() override { // Expose this resource's borrowed or owned host storage.
826 return owned.data(); // Expose the contiguous host storage owned by this resource.
827 } // End this scope in runtime.
828 std::size_t bytes() const override { // Report this resource's byte count with overflow checking.
829 return checked_product(owned.size(), sizeof(T)); // Calculate the byte count with checked size arithmetic.
830 } // End this scope in runtime.
831}; // End this scope in runtime.
832} // namespace detail
833
834/** @brief An application's current GL context, accessed through its function resolver.
835 * The context must remain current on this thread during operations and destruction of GL objects.
836 * The default header uses standard C++11 declarations. The optional
837 * headless_device supplies EGL context creation when SHDR_ENABLE_EGL is enabled.
838 */
839class device { // Keep the device values and operations together.
840 std::shared_ptr<detail::device_state> state_; // Keep the state shared by this wrapper and its retained owners.
841
842 public: // Expose the operations callers can use.
843 /** @brief Load GL entry points and validate the current context against a profile.
844 * @param resolver Callable returning the address of a named GL function, or null.
845 * @param p Minimum GLSL and GL/ES profile. defaults to ES 3.1.
846 * @throws error If function resolution or current-context validation fails.
847 */
848 explicit device(const std::function<void*(const char*)>& resolver, profile p = profile::es31) // Create a compute device using the caller's current context and function resolver.
849 : state_(std::make_shared<detail::device_state>(resolver, p)) { // Retain the resolved GL functions, profile and device limits.
850 } // End this scope in device.
851 /** @brief Release deferred completion handles through context-thread cleanup.
852 * Destroy devices on their context thread after CPU workers have stopped.
853 */
854 virtual ~device() noexcept { // Close pending retirement leases on the context thread.
855 if (std::this_thread::get_id() == state_->context_thread) { // Check that the operation uses the context or lifecycle thread.
856 try { // Keep cleanup available if an allocation, shader, or driver operation fails.
857 state_->close_retirements(); // Stop retaining new submission bundles during device teardown.
858 } catch (...) { // Preserve cleanup or exception propagation for this failed operation.
859 } // End this scope in device.
860 } // End this scope in device.
861 } // End this scope in device.
862 device(const device&) = delete; // Prevent copying this object and duplicating its retained identity.
863 device& operator=(const device&) = delete; // Prevent copying this object and duplicating its retained identity.
864 /** @brief Return the shader language profile selected for this device. */
865 profile shader_profile() const { // Return the device profile used to generate shader source.
866 return state_->language; // Return the device profile used to generate shader source.
867 } // End this scope in shader profile.
868 /** @brief Wait for submitted GPU work to complete, including for accurate wall-clock benchmarks. */
869 void synchronize() { // Explicitly wait for completed GPU commands.
870 state_->collect(); // Run queued deletions and zero-time completion checks.
871 state_->gl.Finish(); // Explicitly wait until queued GPU commands complete.
872 state_->gl.check("wait for GPU completion"); // Check for a driver error from the preceding GL operation.
873 } // End this scope in synchronize.
874 /** @brief Delete GL objects retired by CPU workers and poll pending completions. */
875 void collect_garbage() { // Collect deferred GL deletions and poll completion fences.
876 state_->collect(); // Run queued deletions and zero-time completion checks.
877 } // End this scope in collect garbage.
878 /** @brief Current GL renderer name, for diagnostics and reproducible benchmark reports. */
879 std::string renderer() const { // Read the current renderer name for reproducible diagnostics.
880 state_->require_thread(); // Require the device context thread and activate its headless context.
881 const unsigned char* s = state_->gl.GetString(0x1F01); // Read the driver version or renderer text.
882 if (!s) // Validate the renderer query from the current context.
883 throw error("no current GL context"); // Report that the renderer query requires a current GL context.
884 return reinterpret_cast<const char*>(s); // Read the current renderer name for reproducible diagnostics.
885 } // End this scope in renderer.
886 /** @internal Runtime access to the retained GL function table and device limits. */
887 const std::shared_ptr<detail::device_state>& state() const { // Expose retained runtime state to the resource and backend helpers.
888 return state_; // Return the storage or execution state retained by this wrapper.
889 } // End this scope in state.
890}; // End this scope in runtime.
891
892/** @brief Shared typed std430 storage, owned or borrowed, with automatic CPU/GPU synchronization.
893 * Copying a buffer shares its storage. Mutable data() marks host data authoritative.
894 * Obtain a fresh data() pointer after a GPU dispatch before accessing host values.
895 * GPU handles belong to one device.
896 */
897template <class T> class buffer { // Select the scalar, dimension, or access type at compile time.
898 static_assert(detail::storage_type<T>::value, // Require a representation that matches the shader interface.
899 "buffer requires float/int/uint or matching vec2/vec4 std430 layout"); // Explain which element types match the supported storage-buffer layout.
900 static_assert(sizeof(float) == 4 && sizeof(int) == 4 && sizeof(unsigned) == 4, // Require a representation that matches the shader interface.
901 "shdr requires 32-bit GLSL scalars"); // Require the host scalar representation to match GLSL storage.
902 std::shared_ptr<detail::buffer_state<T>> state_; // Keep the state shared by this wrapper and its retained owners.
903
904 public: // Expose the operations callers can use.
905 /** @brief Allocate a zero-initialized host buffer containing n elements.
906 * GPU storage is allocated lazily. Zero-length buffers are valid on the CPU.
907 * GPU storage-buffer bindings require at least one element.
908 */
909 explicit buffer(std::size_t n = 0) : state_(std::make_shared<detail::buffer_state<T>>(n)) { // Create a typed buffer with owned host storage.
910 if (n > static_cast<std::size_t>((std::numeric_limits<unsigned>::max)())) // Check resource size or driver limits before accessing storage.
911 throw error("buffer length exceeds GLSL uint"); // Report buffer length exceeds glsl uint.
912 detail::gl_size(state_->bytes()); // Require a byte count representable by the GL buffer API.
913 } // End this scope in buffer.
914 /** @brief Borrow n elements stored in application-owned host memory.
915 * Keep the memory alive while any resource copy or shader port retains it.
916 * Call mark_host_modified() after writing through the original pointer.
917 */
918 buffer(T* p, std::size_t n) : state_(std::make_shared<detail::buffer_state<T>>(p, n)) { // Initialize the typed resource storage and validate its size.
919 if (n > static_cast<std::size_t>((std::numeric_limits<unsigned>::max)())) // Check resource size or driver limits before accessing storage.
920 throw error("buffer length exceeds GLSL uint"); // Report buffer length exceeds glsl uint.
921 detail::gl_size(state_->bytes()); // Require a byte count representable by the GL buffer API.
922 } // End this scope in buffer.
923 /** @brief Allocate owned host storage and copy an initializer list into it. */
924 buffer(std::initializer_list<T> values) : buffer(values.size()) { // Initialize the typed resource storage and validate its size.
925 std::copy(values.begin(), values.end(), data()); // Access host values after bringing their resource copy up to date.
926 } // End this scope in buffer.
927 /** @brief Return the stored element count. */
928 std::size_t size() const { // Read the stored element or stage count.
929 return state_->count; // Return the result of this check or operation.
930 } // End this scope in runtime.
931 /** @brief Synchronize newer GPU contents and return mutable host elements.
932 * Marks host data modified. Reacquire this pointer after GPU work before using it.
933 */
934 T* data() { // Access host values after bringing their resource copy up to date.
935 state_->ensure_host(); // Download newer GPU values only when host access needs them.
936 state_->latest = detail::authority::host; // Record which backend now owns the newest resource values.
937 return static_cast<T*>(state_->host_pointer()); // Access host values after bringing their resource copy up to date.
938 } // End this scope in data.
939 /** @brief Synchronize newer GPU contents and return readonly host elements. */
940 const T* data() const { // Access host values after bringing their resource copy up to date.
941 state_->ensure_host(); // Download newer GPU values only when host access needs them.
942 return static_cast<const T*>(state_->host_pointer()); // Access host values after bringing their resource copy up to date.
943 } // End this scope in data.
944 /** @brief Access one host element after synchronization. reject an out-of-range index. */
945 T& operator[](std::size_t i) { // Bounds-check this host element before returning its typed reference.
946 if (i >= size()) // Check resource size or driver limits before accessing storage.
947 throw error("host buffer index out of range"); // Report host buffer index out of range.
948 return data()[i]; // Return the result of this check or operation.
949 } // End this scope in runtime.
950 /** @brief Read one host element after synchronization. reject an out-of-range index. */
951 const T& operator[](std::size_t i) const { // Bounds-check this host element before returning its typed reference.
952 if (i >= size()) // Check resource size or driver limits before accessing storage.
953 throw error("host buffer index out of range"); // Report host buffer index out of range.
954 return data()[i]; // Return the result of this check or operation.
955 } // End this scope in runtime.
956 /** @brief Ensure this device has the latest contents, allocating or uploading as needed. */
957 void upload(device& d) { // Explicitly make current host values available to the GPU.
958 state_->ensure_gpu(d.state()); // Allocate or upload GPU storage when its host copy is newer.
959 } // End this scope in upload.
960 /** @brief Download newer GPU contents. reject a different owning device. */
961 void download(device& d) { // Explicitly bring newer GPU values back to host storage.
962 if (state_->owner && state_->owner != d.state()) // Check that this storage belongs to the requested device.
963 throw error("resource belongs to a different device"); // Report resource belongs to a different device.
964 state_->ensure_host(); // Download newer GPU values only when host access needs them.
965 } // End this scope in download.
966 /** @brief Obtain the synchronized GL SSBO handle for application interop. */
967 unsigned gl_handle(device& d) { // Expose a synchronized GL resource handle for interoperation.
968 upload(d); // Explicitly make current host values available to the GPU.
969 return state_->handle; // Expose a synchronized GL resource handle for interoperation.
970 } // End this scope in gl handle.
971 /** @brief Notify shdr after the application writes the GL buffer. its context must be current. */
972 void mark_gpu_modified(device& d) { // Record a write performed by application GL commands.
973 if (state_->owner != d.state() || !state_->handle) // Check that this storage belongs to the requested device.
974 throw error("resource has no handle on this device"); // Report a mismatch between the resource handle and the device.
975 state_->latest = detail::authority::gpu; // Record which backend now owns the newest resource values.
976 } // End this scope in mark gpu modified.
977 /** @brief Notify shdr after changing previously obtained or borrowed host memory.
978 * Call data() before editing if GPU data must first be preserved.
979 */
980 void mark_host_modified() { // Record a write through retained or borrowed host memory.
981 state_->latest = detail::authority::host; // Record which backend now owns the newest resource values.
982 } // End this scope in mark host modified.
983 /** @brief Borrow an existing SSBO. Its contents initially belong to the GPU. caller retains ownership. */
984 static buffer from_gl(device& d, unsigned handle, std::size_t count) { // Borrow an application-owned GL object.
985 if (!handle) // Validate the resource handle before using it.
986 throw error("cannot borrow GL buffer zero"); // Require a nonzero GL buffer handle.
987 buffer result(count); // Keep the element or configuration count.
988 result.state_->owner = d.state(); // Set the device that owns the GL resource.
989 result.state_->handle = handle; // Set the GL buffer or texture name.
990 result.state_->borrowed = true; // Set whether the application owns the supplied GL object.
991 result.state_->latest = detail::authority::gpu; // Record which backend now owns the newest resource values.
992 return result; // Return the value returned by this operation.
993 } // End this scope in from gl.
994 /** @internal Runtime access to shared resource storage and synchronization state. */
995 const std::shared_ptr<detail::buffer_state<T>>& state() const { // Expose retained runtime state to the resource and backend helpers.
996 return state_; // Return the storage or execution state retained by this wrapper.
997 } // End this scope in state.
998}; // End this scope in runtime.
999
1000/** @brief Interleaved 32-bit scalar or RGBA image shared between CPU and GPU.
1001 * C is 1 or 4. T is float, int, or unsigned. Use matching r32f/r32i/r32ui or
1002 * rgba32f/rgba32i/rgba32ui GLSL declarations. Copies and bound ports alias storage.
1003 */
1004template <class T, unsigned C = 1> class image2d { // Select the scalar, dimension, or access type at compile time.
1005 static_assert(detail::storage_type<T>::value && std::is_arithmetic<T>::value, // Require a representation that matches the shader interface.
1006 "image requires float/int/uint"); // Require an image scalar type supported by the runtime.
1007 static_assert(C == 1 || C == 4, "portable image formats have one or four components"); // Require a representation that matches the shader interface.
1008 std::shared_ptr<detail::image_state<T>> state_; // Keep the state shared by this wrapper and its retained owners.
1009
1010 public: // Expose the operations callers can use.
1011 /** @brief Allocate a zero-initialized image with positive width and height in pixels. */
1012 image2d(unsigned w, unsigned h) : state_(std::make_shared<detail::image_state<T>>(w, h, C)) { // Initialize the image geometry and typed pixel storage.
1013 } // End this scope in image2d.
1014 /** @brief Return the stored number of pixels in each row. */
1015 unsigned width() const { // Return the image width in pixels.
1016 return state_->width; // Return the result of this check or operation.
1017 } // End this scope in runtime.
1018 /** @brief Return the stored number of pixel rows. */
1019 unsigned height() const { // Return the image height in pixels.
1020 return state_->height; // Return the result of this check or operation.
1021 } // End this scope in runtime.
1022 /** @brief Synchronize newer GPU contents and return mutable interleaved host pixels.
1023 * Index component c at (x,y) as data()[(y*width()+x)*C+c]. Marks host data
1024 * modified. reacquire the pointer after GPU work before using it.
1025 */
1026 T* data() { // Access host values after bringing their resource copy up to date.
1027 state_->ensure_host(); // Download newer GPU values only when host access needs them.
1028 state_->latest = detail::authority::host; // Record which backend now owns the newest resource values.
1029 return static_cast<T*>(state_->host_pointer()); // Access host values after bringing their resource copy up to date.
1030 } // End this scope in data.
1031 /** @brief Synchronize newer GPU contents and return readonly interleaved host pixels. */
1032 const T* data() const { // Access host values after bringing their resource copy up to date.
1033 state_->ensure_host(); // Download newer GPU values only when host access needs them.
1034 return static_cast<const T*>(state_->host_pointer()); // Access host values after bringing their resource copy up to date.
1035 } // End this scope in data.
1036 /** @brief Ensure this device has the latest pixels, allocating or uploading as needed. */
1037 void upload(device& d) { // Explicitly make current host values available to the GPU.
1038 state_->ensure_gpu(d.state()); // Allocate or upload GPU storage when its host copy is newer.
1039 } // End this scope in upload.
1040 /** @brief Download newer GPU pixels through a portable compute-to-buffer copy. */
1041 void download(device& d) { // Explicitly bring newer GPU values back to host storage.
1042 if (state_->owner && state_->owner != d.state()) // Check that this storage belongs to the requested device.
1043 throw error("resource belongs to a different device"); // Report resource belongs to a different device.
1044 state_->ensure_host(); // Download newer GPU values only when host access needs them.
1045 } // End this scope in download.
1046 /** @brief Obtain the synchronized level-zero GL texture handle for application interop. */
1047 unsigned gl_handle(device& d) { // Expose a synchronized GL resource handle for interoperation.
1048 upload(d); // Explicitly make current host values available to the GPU.
1049 return state_->handle; // Expose a synchronized GL resource handle for interoperation.
1050 } // End this scope in gl handle.
1051 /** @brief Notify shdr after the application writes the GL texture. its context must be current. */
1052 void mark_gpu_modified(device& d) { // Record a write performed by application GL commands.
1053 if (state_->owner != d.state() || !state_->handle) // Check that this storage belongs to the requested device.
1054 throw error("resource has no handle on this device"); // Report a mismatch between the resource handle and the device.
1055 state_->latest = detail::authority::gpu; // Record which backend now owns the newest resource values.
1056 } // End this scope in mark gpu modified.
1057 /** @brief Notify shdr after changing a retained host pixel pointer. */
1058 void mark_host_modified() { // Record a write through retained or borrowed host memory.
1059 state_->latest = detail::authority::host; // Record which backend now owns the newest resource values.
1060 } // End this scope in mark host modified.
1061 /** @brief Borrow a matching immutable-format, level-zero 2D GL texture. caller retains ownership. */
1062 static image2d from_gl(device& d, unsigned handle, unsigned w, unsigned h) { // Borrow an application-owned GL object.
1063 if (!handle) // Validate the resource handle before using it.
1064 throw error("cannot borrow GL texture zero"); // Require a nonzero GL texture handle.
1065 image2d result(w, h); // Allocate host pixel storage before attaching the borrowed texture.
1066 result.state_->owner = d.state(); // Set the device that owns the GL resource.
1067 result.state_->handle = handle; // Set the GL buffer or texture name.
1068 result.state_->borrowed = true; // Set whether the application owns the supplied GL object.
1069 result.state_->latest = detail::authority::gpu; // Record which backend now owns the newest resource values.
1070 return result; // Return the value returned by this operation.
1071 } // End this scope in from gl.
1072 /** @internal Runtime access to shared image storage and synchronization state. */
1073 const std::shared_ptr<detail::image_state<T>>& state() const { // Expose retained runtime state to the resource and backend helpers.
1074 return state_; // Return the storage or execution state retained by this wrapper.
1075 } // End this scope in state.
1076}; // End this scope in runtime.
1077
1078class kernel; // Keep the kernel values and operations together.
1079namespace detail { // Group these definitions in the detail namespace.
1080enum class port_kind { buffer, image, uniform }; // Name the profile, access, or completion values accepted by this section.
1081struct port_base { // Keep the port base values and operations together.
1082 kernel& parent; // Keep the shader instance that owns this interface.
1083 std::string name; // Keep the resource or entry-point name.
1084 unsigned binding; // Keep the numeric shader resource binding.
1085 access mode; // Keep the declared shader resource access.
1086 port_kind kind; // Keep the buffer, image, or uniform interface kind.
1087 port_base(kernel&, const char*, unsigned, access, port_kind); // Register the named interface with its owning shader.
1088 virtual ~port_base() { // Allow typed interfaces to be destroyed through this base interface.
1089 } // End this scope in runtime.
1090 virtual std::shared_ptr<resource_state> resource() const { // Expose the retained storage used by this port.
1091 return std::shared_ptr<resource_state>(); // Represent a uniform-only interface with an empty resource reference.
1092 } // End this scope in runtime.
1093 virtual void bind_gpu(device_state&, GLuint) { // Bind this interface or assign its uniform before dispatch.
1094 } // End this scope in bind gpu.
1095 virtual void validate() const { // Check that this resource binding satisfies the shader declaration.
1096 } // End this scope in validate.
1097 virtual void copy_to(port_base&) const { // Copy the typed port into a matching snapshot interface.
1098 throw error("shader port does not support snapshots"); // Report that this port requires snapshot support.
1099 } // End this scope in copy to.
1100}; // End this scope in runtime.
1101} // namespace detail
1102
1103/** @brief Base for compiled shader objects, owning resource declarations and invocation builtins.
1104 * Shader objects are neither copyable nor movable because their ports refer to their instance.
1105 * CMake-generated adapters embed their native GLSL source automatically.
1106 */
1107class kernel { // Keep the kernel values and operations together.
1108 std::string name_, source_; // Keep the diagnostic shader name and its embedded GLSL text.
1109 extent local_; // Keep the fixed workgroup dimensions.
1110 std::vector<detail::port_base*> ports_; // Keep the registered resource and uniform interfaces.
1111 std::shared_ptr<detail::program_state> program_; // Keep the retained compiled compute program.
1112
1113 protected: // Allow derived kernels to initialize their embedded source.
1114 // Generated shader constructors install their build-embedded GLSL through this method.
1115 void set_embedded_source(const char* text) { // Retain the build-generated GLSL text inside the shader.
1116 source_ = text; // Set the automatically embedded shader text.
1117 } // End this scope in runtime.
1118
1119 public: // Expose the operations callers can use.
1120 glsl::uvec3 gl_GlobalInvocationID, gl_LocalInvocationID, gl_WorkGroupID, gl_NumWorkGroups, // Keep CPU global, local, group and dispatch coordinates for GLSL builtins.
1121 gl_WorkGroupSize; // Keep the fixed local dimensions exposed to CPU shader code.
1122 unsigned gl_LocalInvocationIndex; // Keep the flattened index within the current CPU work group.
1123 /** @brief Construct a kernel identity and positive local workgroup dimensions.
1124 * SHDR_BEGIN emits this constructor for shared shader classes.
1125 */
1126 kernel(const std::string& name, extent local) // Initialize the shader name, local dimensions, and invocation values.
1127 : name_(name), local_(local), gl_LocalInvocationIndex(0) { // Remember the shader name and local size with initially zero CPU invocation state.
1128 if (!local.x || !local.y || !local.z) // Require positive local dimensions on every axis.
1129 throw error("local work group dimensions must be positive"); // Report local work group dimensions must be positive.
1130 gl_WorkGroupSize = glsl::uvec3(local.x, local.y, local.z); // Publish the fixed local dimensions to CPU shader builtins.
1131 } // End this scope in kernel.
1132 virtual ~kernel() { // Allow derived shader instances to be destroyed through the base interface.
1133 } // End this scope in kernel.
1134 kernel(const kernel&) = delete; // Prevent copying this object and duplicating its retained identity.
1135 kernel& operator=(const kernel&) = delete; // Prevent copying this object and duplicating its retained identity.
1136 /** @brief Execute one CPU invocation. SHDR_MAIN supplies the override.
1137 * Dispatch through a stage or pipeline to initialize builtins and synchronize resources.
1138 */
1139 virtual void main() = 0; // Require a compiled shader to supply its CPU invocation method.
1140 /** @brief Make an independent shader instance with a snapshot of its resource bindings and uniforms. */
1141 virtual std::unique_ptr<kernel> clone() const { // Create an independent shader instance for deferred CPU execution.
1142 throw error("shader does not support snapshots"); // Report that this shader requires snapshot support.
1143 } // End this scope in runtime.
1144 /** @internal Copy registered interfaces into a newly constructed instance. */
1145 void copy_interfaces(kernel& destination) const { // Copy resource bindings and uniforms into a fresh shader clone.
1146 if (ports_.size() != destination.ports_.size()) // Check resource size or driver limits before accessing storage.
1147 throw error("shader snapshot interface mismatch"); // Report shader snapshot interface mismatch.
1148 for (std::size_t i = 0; i < ports_.size(); ++i) // Visit every registered resource and uniform interface.
1149 ports_[i]->copy_to(*destination.ports_[i]); // Copy the typed port into a matching snapshot interface.
1150 } // End this scope in copy interfaces.
1151 /** @internal Retain all resources used by an enqueued dispatch. */
1152 std::vector<std::shared_ptr<detail::resource_state>> resources() const { // Collect the shared storage used by this shader.
1153 std::vector<std::shared_ptr<detail::resource_state>> result; // Keep the value returned by this operation.
1154 for (std::size_t i = 0; i < ports_.size(); ++i) { // Visit every registered resource and uniform interface.
1155 auto r = ports_[i]->resource(); // Get the shared storage behind this resource interface.
1156 if (r) // Choose this branch when r.
1157 result.push_back(r); // Retain this entry in the ordered resource or work collection.
1158 } // End this scope in resources.
1159 return result; // Return the value returned by this operation.
1160 } // End this scope in resources.
1161 /** @internal Retain a compiled program during asynchronous execution. */
1162 const std::shared_ptr<detail::program_state>& program_state() const { // Expose the retained compiled program for asynchronous ownership.
1163 return program_; // Return the retained compiled compute program.
1164 } // End this scope in program state.
1165 /** @brief Return the kernel's diagnostic name. */
1166 const std::string& name() const { // Return the diagnostic shader name.
1167 return name_; // Return the kernel or registration name.
1168 } // End this scope in runtime.
1169 /** @brief Return the fixed local workgroup dimensions declared by SHDR_BEGIN. */
1170 extent local_size() const { // Return the fixed local work group dimensions.
1171 return local_; // Return the fixed workgroup dimensions.
1172 } // End this scope in runtime.
1173 /** @brief Export this shader's automatically embedded source for compiler diagnostics or tooling. */
1174 std::string glsl(profile p) const { // Export the embedded shader for the requested language profile.
1175 if (source_.empty()) // Choose this branch when source .empty().
1176 throw error("shader has no embedded GPU source: " + name_); // Report that this shader requires an embedded GPU program.
1177 return glsl_source(source_, p); // Return the result of this check or operation.
1178 } // End this scope in runtime.
1179 /** @internal Register declarations emitted by the shared shader macros. */
1180 void register_port(detail::port_base& port) { // Register one named resource or uniform with duplicate checks.
1181 for (std::size_t i = 0; i < ports_.size(); ++i) { // Visit every registered resource and uniform interface.
1182 if (ports_[i]->name == port.name) // Choose this branch when ports [i]->name equals port.name.
1183 throw error("duplicate shader port: " + port.name); // Report duplicate shader port: .
1184 if (port.kind != detail::port_kind::uniform && ports_[i]->kind == port.kind && // Choose the branch matching this interface or resource access.
1185 ports_[i]->binding == port.binding) // Set the numeric shader resource binding.
1186 throw error("duplicate shader resource binding: " + port.name); // Report duplicate shader resource binding: .
1187 } // End this scope in register port.
1188 ports_.push_back(&port); // Retain this entry in the ordered resource or work collection.
1189 } // End this scope in register port.
1190 /** @brief Bind by name, for runtime-selected shader modules. Type mismatches throw error. */
1191 template <class T> kernel& bind(const std::string& name, const buffer<T>& resource); // Select the scalar, dimension, or access type at compile time.
1192 /** @brief Bind an image by name. scalar type and component count must match its port. */
1193 template <class T, unsigned C> // Select the scalar, dimension, or access type at compile time.
1194 kernel& bind(const std::string& name, const image2d<T, C>& resource); // Rebind a named resource port while preserving its declared type.
1195 /** @brief Assign a uniform by name. T must exactly match its declared shader type. */
1196 template <class T> kernel& set(const std::string& name, const T& value); // Select the scalar, dimension, or access type at compile time.
1197 /** @internal Find a registered port for runtime binding and uniform assignment. */
1198 detail::port_base& find_port(const std::string& name) { // Resolve the named shader interface.
1199 for (std::size_t i = 0; i < ports_.size(); ++i) // Visit every registered resource and uniform interface.
1200 if (ports_[i]->name == name) // Choose this branch when ports [i]->name equals name.
1201 return *ports_[i]; // Resolve the named shader interface.
1202 throw error("unknown shader port: " + name); // Report unknown shader port: .
1203 } // End this scope in find port.
1204 /** @brief Execute one dispatch with workgroup counts on a device, or on the CPU if null.
1205 * Normally called by pipeline.run(). GPU work uses the embedded shader and a current context.
1206 */
1207 void run(extent groups, device* d); // Keep the target device.
1208 /** @brief Compile and allocate GPU resources before entering a latency-sensitive dispatch loop. */
1209 void prepare(device& d); // Compile the program and prepare its bound GPU resources.
1210}; // End this scope in runtime.
1211
1212namespace detail { // Group these definitions in the detail namespace.
1213inline port_base::port_base(kernel& p, const char* n, unsigned b, access a, port_kind k) // Register the named interface with its owning shader.
1214 : parent(p), name(n), binding(b), mode(a), kind(k) { // Record the owning kernel, interface name, binding, access and resource kind.
1215 parent.register_port(*this); // Register one named resource or uniform with duplicate checks.
1216} // End this scope in runtime.
1217} // namespace detail
1218
1219/** @brief Typed shader SSBO declaration. assign a buffer to bind or rebind it. */
1220template <class T, access A = access::readwrite> class buffer_port : public detail::port_base { // Select the scalar, dimension, or access type at compile time.
1221 std::shared_ptr<detail::buffer_state<T>> state_; // Keep the state shared by this wrapper and its retained owners.
1222 unsigned fixed_length_; // Keep the fixed array length declared by the shader.
1223
1224 public: // Expose the operations callers can use.
1225 /** @brief Declare a typed buffer port on a kernel, usually through SHDR_BUFFER. */
1226 buffer_port(kernel& p, unsigned b, const char* n, unsigned fixed = 0) // Register a typed buffer interface and any fixed shader array length.
1227 : port_base(p, n, b, A, detail::port_kind::buffer), fixed_length_(fixed) { // Record the buffer interface and its optional fixed shader length.
1228 } // End this scope in runtime.
1229 /** @brief Bind or rebind this port to the buffer's retained shared storage. */
1230 buffer_port& operator=(const buffer<T>& b) { // Retain the newly bound typed resource in this shader interface.
1231 state_ = b.state(); // Set the state shared by this wrapper and its retained owners.
1232 return *this; // Allow another operation to follow in the same expression.
1233 } // End this scope in runtime.
1234 void copy_to(detail::port_base& destination) const override { // Copy the typed port into a matching snapshot interface.
1235 buffer_port* p = dynamic_cast<buffer_port*>(&destination); // Require a snapshot port with the same buffer type and access qualifier.
1236 if (!p) // Validate the typed interface pointer before accessing it.
1237 throw error("buffer snapshot type mismatch: " + name); // Report buffer snapshot type mismatch: .
1238 p->state_ = state_; // Set the state shared by this wrapper and its retained owners.
1239 } // End this scope in copy to.
1240 /** @brief Return the bound buffer's element count, matching GLSL array length(). */
1241 unsigned length() const { // Return the GLSL-visible array length after validating its binding.
1242 validate(); // Check that this resource binding satisfies the shader declaration.
1243 return fixed_length_ ? fixed_length_ : static_cast<unsigned>(state_->count); // Return the GLSL-visible array length after validating its binding.
1244 } // End this scope in length.
1245 /** @brief Access an element during CPU execution, checking bounds and declared access.
1246 * Readonly ports return const references. Pipeline execution synchronizes host contents.
1247 */
1248 typename std::conditional<A == access::readonly, const T&, T&>::type operator[](std::size_t i) { // Expose a const component for readonly ports and a mutable one otherwise.
1249 validate(); // Check that this resource binding satisfies the shader declaration.
1250 const std::size_t limit = fixed_length_ ? fixed_length_ : state_->count; // Use the declared array size or the full bound buffer length.
1251 if (i >= limit) // Choose this branch when i >= limit.
1252 throw error("CPU SSBO index out of bounds: " + name); // Report cpu ssbo index out of bounds: .
1253 return static_cast<T*>(state_->host_pointer())[i]; // Return the result of this check or operation.
1254 } // End this scope in runtime.
1255 void validate() const override { // Check that this resource binding satisfies the shader declaration.
1256 if (!state_) // Require retained storage for this interface.
1257 throw error("unbound shader buffer: " + name); // Report unbound shader buffer: .
1258 if (fixed_length_ && state_->count < fixed_length_) // Check resource size or driver limits before accessing storage.
1259 throw error("buffer is smaller than the shader's fixed array: " + name); // Report buffer is smaller than the shader's fixed array: .
1260 } // End this scope in validate.
1261 std::shared_ptr<detail::resource_state> resource() const override { // Expose the retained storage used by this port.
1262 return state_; // Return the storage or execution state retained by this wrapper.
1263 } // End this scope in runtime.
1264 void bind_gpu(detail::device_state& d, unsigned) override { // Bind this interface or assign its uniform before dispatch.
1265 if (binding >= d.max_buffers) // Choose this branch when binding >= d.max buffers.
1266 throw error("SSBO binding exceeds device limit: " + name); // Report ssbo binding exceeds device limit: .
1267 if (!state_->bytes()) // Check resource size or driver limits before accessing storage.
1268 throw error("GPU SSBO bindings require a nonempty buffer: " + name); // Report gpu ssbo bindings require a nonempty buffer: .
1269 d.gl.BindBufferBase(detail::SHADER_STORAGE_BUFFER, binding, state_->handle); // Bind storage to the shader interface index.
1270 } // End this scope in bind gpu.
1271}; // End this scope in runtime.
1272
1273namespace detail { // Group these definitions in the detail namespace.
1274inline void uniform_value(gl_api& gl, GLint loc, float v) { // Send the typed uniform value through its matching GL call.
1275 gl.Uniform1f(loc, v); // Assign a float uniform.
1276} // End this scope in uniform value.
1277inline void uniform_value(gl_api& gl, GLint loc, int v) { // Send the typed uniform value through its matching GL call.
1278 gl.Uniform1i(loc, v); // Assign an integer or boolean uniform.
1279} // End this scope in uniform value.
1280inline void uniform_value(gl_api& gl, GLint loc, unsigned v) { // Send the typed uniform value through its matching GL call.
1281 gl.Uniform1ui(loc, v); // Assign an unsigned integer uniform.
1282} // End this scope in uniform value.
1283inline void uniform_value(gl_api& gl, GLint loc, bool v) { // Send the typed uniform value through its matching GL call.
1284 gl.Uniform1i(loc, v ? 1 : 0); // Assign an integer or boolean uniform.
1285} // End this scope in uniform value.
1286#define SHDR_UNIFORM_VEC(n, t, suffix) /* Define the SHDR_UNIFORM_VEC helper for this group of declarations. */ \
1287 inline void uniform_value(gl_api& gl, GLint loc, const glsl::vec<t, n>& v) { /* Send the typed uniform value through its matching GL call. */ \
1288 t packed[n]; /* Pack the vector components contiguously for the GL uniform call. */ \
1289 for (unsigned i = 0; i < n; ++i) /* Visit the values or interfaces needed by uniform value. */ \
1290 packed[i] = v[i]; /* Copy the component into the contiguous GL uniform payload. */ \
1291 gl.Uniform##n##suffix(loc, 1, packed); /* Send the packed vector components through their matching GL function. */ \
1292 } /* End this scope in uniform value. */
1293SHDR_UNIFORM_VEC(2, float, fv) // Define the listed float, signed or unsigned vector uniform overloads.
1294SHDR_UNIFORM_VEC(3, float, fv) SHDR_UNIFORM_VEC(4, float, fv) SHDR_UNIFORM_VEC(2, int, iv) // Define the listed float, signed or unsigned vector uniform overloads.
1295 SHDR_UNIFORM_VEC(3, int, iv) SHDR_UNIFORM_VEC(4, int, iv) SHDR_UNIFORM_VEC(2, unsigned, uiv) // Define the listed float, signed or unsigned vector uniform overloads.
1296 SHDR_UNIFORM_VEC(3, unsigned, uiv) SHDR_UNIFORM_VEC(4, unsigned, uiv) // Define the listed float, signed or unsigned vector uniform overloads.
1297#undef SHDR_UNIFORM_VEC // Remove this temporary macro after its declarations are complete.
1298 template <std::size_t N> // Select the scalar, dimension, or access type at compile time.
1299 inline void uniform_value(gl_api& gl, GLint loc, const glsl::vec<bool, N>& v) { // Send the typed uniform value through its matching GL call.
1300 GLint packed[N]; // Pack boolean components as the integer values expected by GL.
1301 for (unsigned i = 0; i < N; ++i) // Visit the values or interfaces needed by uniform value.
1302 packed[i] = v[i] ? 1 : 0; // Represent a boolean uniform component as a GL integer.
1303 if (N == 2) // Choose this branch when N equals 2.
1304 gl.Uniform2iv(loc, 1, packed); // Send two integer-represented boolean components to GL.
1305 else if (N == 3) // Choose this branch when N equals 3.
1306 gl.Uniform3iv(loc, 1, packed); // Send three integer-represented boolean components to GL.
1307 else // Handle the other resource, platform, or completion case.
1308 gl.Uniform4iv(loc, 1, packed); // Send four integer-represented boolean components to GL.
1309} // End this scope in uniform value.
1310#define SHDR_UNIFORM_MATRIX(c, r, n) /* Define the SHDR_UNIFORM_MATRIX helper for this group of declarations. */ \
1311 inline void uniform_value(gl_api& gl, GLint loc, const glsl::mat<float, c, r>& m) { /* Send the typed uniform value through its matching GL call. */ \
1312 float packed[c * r]; /* Send the typed uniform value through its matching GL call. */ \
1313 for (unsigned i = 0; i < c; ++i) /* Visit the values or interfaces needed by uniform value. */ \
1314 for (unsigned j = 0; j < r; ++j) /* Visit the values or interfaces needed by uniform value. */ \
1315 packed[i * r + j] = m[i][j]; /* Flatten the matrix in GLSL column order. */ \
1316 gl.UniformMatrix##n##fv(loc, 1, 0, packed); /* Upload the column-major matrix with the transpose flag set to false. */ \
1317 } /* End this scope in uniform value. */
1318SHDR_UNIFORM_MATRIX(2, 2, 2) // Define the listed column-major matrix uniform overloads.
1319SHDR_UNIFORM_MATRIX(3, 3, 3) SHDR_UNIFORM_MATRIX(4, 4, 4) SHDR_UNIFORM_MATRIX(2, 3, 2x3) // Define the listed column-major matrix uniform overloads.
1320 SHDR_UNIFORM_MATRIX(2, 4, 2x4) SHDR_UNIFORM_MATRIX(3, 2, 3x2) SHDR_UNIFORM_MATRIX(3, 4, 3x4) // Define the listed column-major matrix uniform overloads.
1321 SHDR_UNIFORM_MATRIX(4, 2, 4x2) SHDR_UNIFORM_MATRIX(4, 3, 4x3) // Define the listed column-major matrix uniform overloads.
1322#undef SHDR_UNIFORM_MATRIX // Remove this temporary macro after its declarations are complete.
1323} // namespace detail
1324
1325/** @brief A zero-initialized typed shader uniform, assigned with ordinary C++ syntax. */
1326template <class T> class uniform_port : public detail::port_base { // Select the scalar, dimension, or access type at compile time.
1327 T owned_; // Keep the uniform value stored by the port.
1328 T* value_; // Keep the current uniform field.
1329
1330 public: // Expose the operations callers can use.
1331 /** @brief Declare a zero-initialized uniform backed by this port's own value. */
1332 uniform_port(kernel& p, const char* n) // Register a typed uniform backed by this port's own zero-initialized value.
1333 : port_base(p, n, 0, access::readonly, detail::port_kind::uniform), owned_(), // Keep the uniform value stored by the port.
1334 value_(&owned_) { // Keep the uniform value stored by the port.
1335 } // End this scope in runtime.
1336 /** @brief Register an existing typed kernel field as a uniform. emitted by SHDR_UNIFORM. */
1337 uniform_port(kernel& p, const char* n, T& value) // Register a uniform and refer to its shader member value.
1338 : port_base(p, n, 0, access::readonly, detail::port_kind::uniform), owned_(), // Keep the uniform value stored by the port.
1339 value_(&value) { // Refer to the uniform member owned by this shader instance.
1340 } // End this scope in runtime.
1341 /** @brief Set the uniform value uploaded at the next dispatch. */
1342 uniform_port& operator=(const T& value) { // Assign the shader member referred to by this uniform interface.
1343 *value_ = value; // Set the current uniform field.
1344 return *this; // Allow another operation to follow in the same expression.
1345 } // End this scope in runtime.
1346 /** @brief Read the uniform as its declared shader type during CPU execution. */
1347 operator T() const { // Read the uniform member as an ordinary value of its declared type.
1348 return *value_; // Return the result of this check or operation.
1349 } // End this scope in runtime.
1350 /** @brief Return the stored uniform value. */
1351 const T& value() const { // Read the shader member referred to by this uniform port.
1352 return *value_; // Return the result of this check or operation.
1353 } // End this scope in runtime.
1354 void copy_to(detail::port_base& destination) const override { // Copy the typed port into a matching snapshot interface.
1355 uniform_port* p = dynamic_cast<uniform_port*>(&destination); // Require a snapshot port with the same uniform type.
1356 if (!p) // Validate the typed interface pointer before accessing it.
1357 throw error("uniform snapshot type mismatch: " + name); // Report uniform snapshot type mismatch: .
1358 *p->value_ = *value_; // Set the current uniform field.
1359 } // End this scope in copy to.
1360 void bind_gpu(detail::device_state& d, unsigned program) override { // Bind this interface or assign its uniform before dispatch.
1361 const int loc = d.gl.GetUniformLocation(program, name.c_str()); // Find a named uniform in the linked program.
1362 if (loc >= 0) // Choose this branch when loc >= 0.
1363 detail::uniform_value(d.gl, loc, *value_); // Send the typed uniform value through its matching GL call.
1364 } // End this scope in bind gpu.
1365}; // End this scope in runtime.
1366
1367/** @brief A typed image declaration with format/access matching its shared shader. */
1368template <class T, unsigned C, access A> class image_port : public detail::port_base { // Select the scalar, dimension, or access type at compile time.
1369 std::shared_ptr<detail::image_state<T>> state_; // Keep the state shared by this wrapper and its retained owners.
1370
1371 public: // Expose the operations callers can use.
1372 /** @brief Declare an image port, usually through SHDR_IMAGE2D. */
1373 image_port(kernel& p, unsigned b, const char* n) // Register a typed image interface at the specified binding.
1374 : port_base(p, n, b, A, detail::port_kind::image) { // Record the image interface and its declared access qualifier.
1375 } // End this scope in runtime.
1376 /** @brief Bind or rebind this port to the image's retained shared storage. */
1377 image_port& operator=(const image2d<T, C>& image) { // Retain the newly bound typed resource in this shader interface.
1378 state_ = image.state(); // Set the state shared by this wrapper and its retained owners.
1379 return *this; // Allow another operation to follow in the same expression.
1380 } // End this scope in runtime.
1381 void copy_to(detail::port_base& destination) const override { // Copy the typed port into a matching snapshot interface.
1382 image_port* p = dynamic_cast<image_port*>(&destination); // Require a snapshot port with the same image type and access qualifier.
1383 if (!p) // Validate the typed interface pointer before accessing it.
1384 throw error("image snapshot type mismatch: " + name); // Report image snapshot type mismatch: .
1385 p->state_ = state_; // Set the state shared by this wrapper and its retained owners.
1386 } // End this scope in copy to.
1387 void validate() const override { // Check that this resource binding satisfies the shader declaration.
1388 if (!state_) // Require retained storage for this interface.
1389 throw error("unbound shader image: " + name); // Report unbound shader image: .
1390 } // End this scope in validate.
1391 std::shared_ptr<detail::resource_state> resource() const override { // Expose the retained storage used by this port.
1392 return state_; // Return the storage or execution state retained by this wrapper.
1393 } // End this scope in runtime.
1394 /** @internal Read image state for the CPU imageLoad/imageStore/imageSize helpers. */
1395 const std::shared_ptr<detail::image_state<T>>& image_state() const { // Expose the validated image storage to CPU image helpers.
1396 validate(); // Check that this resource binding satisfies the shader declaration.
1397 return state_; // Return the storage or execution state retained by this wrapper.
1398 } // End this scope in runtime.
1399 void bind_gpu(detail::device_state& d, unsigned) override { // Bind this interface or assign its uniform before dispatch.
1400 if (binding >= d.max_images) // Choose this branch when binding >= d.max images.
1401 throw error("image binding exceeds device limit: " + name); // Report image binding exceeds device limit: .
1402 d.gl.BindImageTexture(binding, // Bind a typed image to its shader interface index.
1403 state_->handle, // Keep the GL buffer or texture name.
1404 0, // Supply the next value for the current call or declaration.
1405 0, // Supply the next value for the current call or declaration.
1406 0, // Supply the next value for the current call or declaration.
1407 A == access::readonly ? detail::READ_ONLY // Select the GL read or write qualifier matching this typed image port.
1408 : A == access::writeonly ? detail::WRITE_ONLY // Choose the GL image access matching the shader qualifier.
1409 : detail::READ_WRITE, // Choose the GL image access matching the shader qualifier.
1410 state_->format); // Keep the image storage format.
1411 } // End this scope in bind gpu.
1412}; // End this scope in runtime.
1413
1414/** @brief GLSL image dimensions in pixels. */
1415template <class T, unsigned C, access A> // Select the scalar, dimension, or access type at compile time.
1416inline glsl::ivec2 imageSize(const image_port<T, C, A>& image) { // Return the bound image dimensions as GLSL integers.
1417 return glsl::ivec2(image.image_state()->width, image.image_state()->height); // Return the bound image dimensions as GLSL integers.
1418} // End this scope in imageSize.
1419/** @brief Load one texel. Missing RGB components are zero and missing alpha is one.
1420 * Out-of-range coordinates return zero. loading a writeonly image is a compile error.
1421 */
1422template <class T, unsigned C, access A> // Select the scalar, dimension, or access type at compile time.
1423inline glsl::vec<T, 4> imageLoad(const image_port<T, C, A>& image, glsl::ivec2 pos) { // Load one pixel while enforcing readonly access and image bounds.
1424 static_assert(A != access::writeonly, "cannot imageLoad a writeonly image"); // Require a representation that matches the shader interface.
1425 const std::shared_ptr<detail::image_state<T>>& s = image.image_state(); // Access the retained image geometry and host pixel storage.
1426 if (pos.x < 0 || pos.y < 0 || static_cast<unsigned>(pos.x) >= s->width || // Choose this branch when pos.x < 0 or pos.y < 0 or static cast<unsigned>(pos.x) >= s->width or .
1427 static_cast<unsigned>(pos.y) >= s->height) // Reject an image row outside the allocated pixel storage.
1428 return glsl::vec<T, 4>(T(0)); // Load one pixel while enforcing readonly access and image bounds.
1429 glsl::vec<T, 4> result(T(0)); // Start the loaded texel with zero components before filling its channels.
1430 result[3] = T(1); // Set the value returned by this operation.
1431 const std::size_t start = // Set the beginning of the current token or range.
1432 (static_cast<std::size_t>(pos.y) * s->width + static_cast<unsigned>(pos.x)) * C; // Load one pixel while enforcing readonly access and image bounds.
1433 for (unsigned c = 0; c < C; ++c) // Visit the values or interfaces needed by imageLoad.
1434 result[c] = s->owned[start + c]; // Set the value returned by this operation.
1435 return result; // Return the value returned by this operation.
1436} // End this scope in imageLoad.
1437/** @brief Store a texel, ignoring out-of-range coordinates. Readonly stores are a compile error. */
1438template <class T, unsigned C, access A> // Select the scalar, dimension, or access type at compile time.
1439inline void imageStore(image_port<T, C, A>& image, glsl::ivec2 pos, const glsl::vec<T, 4>& value) { // Store one pixel while enforcing writable access and image bounds.
1440 static_assert(A != access::readonly, "cannot imageStore a readonly image"); // Require a representation that matches the shader interface.
1441 const std::shared_ptr<detail::image_state<T>>& s = image.image_state(); // Access the retained image geometry and host pixel storage.
1442 if (pos.x < 0 || pos.y < 0 || static_cast<unsigned>(pos.x) >= s->width || // Choose this branch when pos.x < 0 or pos.y < 0 or static cast<unsigned>(pos.x) >= s->width or .
1443 static_cast<unsigned>(pos.y) >= s->height) // Reject an image row outside the allocated pixel storage.
1444 return; // Finish this operation.
1445 const std::size_t start = // Set the beginning of the current token or range.
1446 (static_cast<std::size_t>(pos.y) * s->width + static_cast<unsigned>(pos.x)) * C; // Store one pixel while enforcing writable access and image bounds.
1447 for (unsigned c = 0; c < C; ++c) // Visit the values or interfaces needed by imageStore.
1448 s->owned[start + c] = value[c]; // Set host storage allocated by this resource.
1449} // End this scope in imageStore.
1450
1451template <class T> inline kernel& kernel::bind(const std::string& name, const buffer<T>& b) { // Select the scalar, dimension, or access type at compile time.
1452 detail::port_base& p = find_port(name); // Find the declared shader interface with this resource name.
1453 if (buffer_port<T, access::readonly>* v = dynamic_cast<buffer_port<T, access::readonly>*>(&p)) // Choose this branch when buffer port<T, access::readonly>* v = dynamic cast<buffer port<T, access::readonly>*>(&p).
1454 *v = b; // Store the current v value for this operation.
1455 else if (buffer_port<T, access::writeonly>* v = // Choose this branch when buffer port<T, access::writeonly>* v =.
1456 dynamic_cast<buffer_port<T, access::writeonly>*>(&p)) // Try the matching typed write-only buffer interface.
1457 *v = b; // Store the current v value for this operation.
1458 else if (buffer_port<T, access::readwrite>* v = // Choose this branch when buffer port<T, access::readwrite>* v =.
1459 dynamic_cast<buffer_port<T, access::readwrite>*>(&p)) // Try the matching typed read-write buffer interface.
1460 *v = b; // Store the current v value for this operation.
1461 else // Handle the other resource, platform, or completion case.
1462 throw error("buffer type mismatch: " + name); // Report buffer type mismatch: .
1463 return *this; // Allow another operation to follow in the same expression.
1464} // End this scope in runtime.
1465template <class T, unsigned C> // Select the scalar, dimension, or access type at compile time.
1466inline kernel& kernel::bind(const std::string& name, const image2d<T, C>& b) { // Rebind a named resource port while preserving its declared type.
1467 detail::port_base& p = find_port(name); // Find the declared shader interface with this resource name.
1468 if (image_port<T, C, access::readonly>* v = // Choose this branch when image port<T, C, access::readonly>* v =.
1469 dynamic_cast<image_port<T, C, access::readonly>*>(&p)) // Try the matching typed read-only image interface.
1470 *v = b; // Store the current v value for this operation.
1471 else if (image_port<T, C, access::writeonly>* v = // Choose this branch when image port<T, C, access::writeonly>* v =.
1472 dynamic_cast<image_port<T, C, access::writeonly>*>(&p)) // Try the matching typed write-only image interface.
1473 *v = b; // Store the current v value for this operation.
1474 else if (image_port<T, C, access::readwrite>* v = // Choose this branch when image port<T, C, access::readwrite>* v =.
1475 dynamic_cast<image_port<T, C, access::readwrite>*>(&p)) // Try the matching typed read-write image interface.
1476 *v = b; // Store the current v value for this operation.
1477 else // Handle the other resource, platform, or completion case.
1478 throw error("image type mismatch: " + name); // Report image type mismatch: .
1479 return *this; // Allow another operation to follow in the same expression.
1480} // End this scope in runtime.
1481template <class T> inline kernel& kernel::set(const std::string& name, const T& value) { // Select the scalar, dimension, or access type at compile time.
1482 uniform_port<T>* p = dynamic_cast<uniform_port<T>*>(&find_port(name)); // Require the named uniform to have the supplied value's type.
1483 if (!p) // Validate the typed interface pointer before accessing it.
1484 throw error("uniform type mismatch: " + name); // Report uniform type mismatch: .
1485 *p = value; // Update the typed uniform value for subsequent dispatches.
1486 return *this; // Allow another operation to follow in the same expression.
1487} // End this scope in runtime.
1488
1489inline void kernel::prepare(device& d) { // Compile the program and prepare its bound GPU resources.
1490 const auto& state = d.state(); // Set the shared device or worker state.
1491 state->collect(); // Run queued deletions and zero-time completion checks.
1492 for (std::size_t i = 0; i < ports_.size(); ++i) // Visit every registered resource and uniform interface.
1493 ports_[i]->validate(); // Check that this resource binding satisfies the shader declaration.
1494 if (!program_) { // Compile the shader when its cached program is empty.
1495 if (source_.empty()) // Choose this branch when source .empty().
1496 throw error("shader has no embedded GPU source: " + name_); // Report that this shader requires an embedded GPU program.
1497 program_ = std::make_shared<detail::program_state>( // Set the retained compiled compute program.
1498 state, state->compile(glsl_source(source_, state->language), name_)); // Add the selected GLSL version and shared-declaration definitions.
1499 detail::GLint actual[3] = {0, 0, 0}; // Prepare all three local dimensions reported by the linked program.
1500 state->gl.GetProgramiv(program_->id, detail::COMPUTE_WORK_GROUP_SIZE, actual); // Read linked-program status, log length, or local dimensions.
1501 if (actual[0] != static_cast<int>(local_.x) || actual[1] != static_cast<int>(local_.y) || // Choose this branch when actual[0] differs from static cast<int>(local .x) or actual[1] differs from static cast<int>(local .y) or .
1502 actual[2] != static_cast<int>(local_.z)) { // Require the linked program's third local dimension to match the CPU shader.
1503 program_.reset(); // Release or replace the owned object at this lifecycle boundary.
1504 throw error("GPU source and C++ local sizes differ: " + name_); // Report gpu source and c++ local sizes differ: .
1505 } // End this scope in prepare.
1506 } // End this scope in prepare.
1507 if (program_->owner != state) // Check that this storage belongs to the requested device.
1508 throw error("shader program belongs to a different device; create a new shader instance"); // Report shader program belongs to a different device. create a new shader instance.
1509 for (std::size_t i = 0; i < ports_.size(); ++i) { // Visit every registered resource and uniform interface.
1510 auto r = ports_[i]->resource(); // Get the shared storage behind this resource interface.
1511 if (r) // Choose this branch when r.
1512 r->ensure_gpu(state); // Allocate or upload GPU storage when its host copy is newer.
1513 } // End this scope in prepare.
1514} // End this scope in prepare.
1515
1516inline void kernel::run(extent groups, device* d) { // Keep the target device.
1517 if (!groups.x || !groups.y || !groups.z) // Finish an empty dispatch before submitting GL commands.
1518 return; // Finish this operation.
1519 const unsigned g[3] = {groups.x, groups.y, groups.z}, l[3] = {local_.x, local_.y, local_.z}; // Keep dispatch counts and local dimensions for validation and CPU indexing.
1520 for (unsigned i = 0; i < 3; ++i) // Validate or query each of the three compute dimensions.
1521 if (g[i] > (std::numeric_limits<unsigned>::max)() / l[i]) // Choose this branch when g[i] > (std::numeric limits<unsigned>::max)() / l[i].
1522 throw error("global invocation index overflow"); // Report global invocation index overflow.
1523 std::vector<std::shared_ptr<detail::resource_state>> outputs; // Retain the writable resources whose authority changes after this dispatch.
1524 for (std::size_t i = 0; i < ports_.size(); ++i) { // Visit every registered resource and uniform interface.
1525 ports_[i]->validate(); // Check that this resource binding satisfies the shader declaration.
1526 std::shared_ptr<detail::resource_state> r = ports_[i]->resource(); // Get the shared storage behind this resource interface.
1527 if (r && ports_[i]->mode != access::readonly) // Choose the branch matching this interface or resource access.
1528 outputs.push_back(r); // Retain this entry in the ordered resource or work collection.
1529 } // End this scope in runtime.
1530 if (d) { // Choose this branch when d.
1531 const std::shared_ptr<detail::device_state>& state = d->state(); // Set the shared device or worker state.
1532 for (unsigned i = 0; i < 3; ++i) { // Validate or query each of the three compute dimensions.
1533 if (g[i] > state->max_groups[i]) // Choose this branch when g[i] > state->max groups[i].
1534 throw error("dispatch exceeds device work group count"); // Report dispatch exceeds device work group count.
1535 if (l[i] > state->max_local[i]) // Choose this branch when l[i] > state->max local[i].
1536 throw error("local work group dimension exceeds device limit"); // Report local work group dimension exceeds device limit.
1537 } // End this scope in runtime.
1538 const std::size_t invocations = // Count the shader invocations in one local work group.
1539 detail::checked_product(detail::checked_product(l[0], l[1]), l[2]); // Reject resource-size multiplication that would overflow.
1540 if (invocations > state->max_invocations) // Choose this branch when invocations > state->max invocations.
1541 throw error("local invocation count exceeds device limit"); // Report local invocation count exceeds device limit.
1542 prepare(*d); // Compile the program and prepare its bound GPU resources.
1543 state->gl.UseProgram(program_->id); // Select the compute program for the next dispatch.
1544 for (std::size_t i = 0; i < ports_.size(); ++i) // Visit every registered resource and uniform interface.
1545 ports_[i]->bind_gpu(*state, program_->id); // Bind this interface or assign its uniform before dispatch.
1546 state->gl.check("bind shader " + name_); // Keep the kernel or registration name.
1547 state->gl.DispatchCompute(groups.x, groups.y, groups.z); // Queue the shader workgroups.
1548 state->gl.MemoryBarrier(detail::ALL_BARRIER_BITS); // Make earlier resource writes visible to following GPU commands.
1549 // Conservatively record GPU authority even if an error follows a submitted dispatch.
1550 for (std::size_t i = 0; i < outputs.size(); ++i) // Visit this axis of the dispatch or current workgroup.
1551 outputs[i]->latest = detail::authority::gpu; // Record which backend now owns the newest resource values.
1552 state->gl.check("dispatch shader " + name_); // Keep the kernel or registration name.
1553 } else { // Handle the alternative resource or completion case.
1554 for (std::size_t i = 0; i < ports_.size(); ++i) { // Visit every registered resource and uniform interface.
1555 std::shared_ptr<detail::resource_state> r = ports_[i]->resource(); // Get the shared storage behind this resource interface.
1556 if (r) // Choose this branch when r.
1557 r->ensure_host(); // Download newer GPU values only when host access needs them.
1558 } // End this scope in runtime.
1559 gl_NumWorkGroups = glsl::uvec3(groups.x, groups.y, groups.z); // Publish the dispatch dimensions to CPU shader builtins.
1560 try { // Keep cleanup available if an allocation, shader, or driver operation fails.
1561 for (unsigned gz = 0; gz < groups.z; ++gz) // Visit this axis of the dispatch or current workgroup.
1562 for (unsigned gy = 0; gy < groups.y; ++gy) // Visit this axis of the dispatch or current workgroup.
1563 for (unsigned gx = 0; gx < groups.x; ++gx) { // Visit this axis of the dispatch or current workgroup.
1564 gl_WorkGroupID = glsl::uvec3(gx, gy, gz); // Publish the CPU work group currently being executed.
1565 for (unsigned z = 0; z < local_.z; ++z) // Visit this axis of the dispatch or current workgroup.
1566 for (unsigned y = 0; y < local_.y; ++y) // Visit this axis of the dispatch or current workgroup.
1567 for (unsigned x = 0; x < local_.x; ++x) { // Visit this axis of the dispatch or current workgroup.
1568 gl_LocalInvocationID = glsl::uvec3(x, y, z); // Publish the invocation coordinates inside this work group.
1569 gl_GlobalInvocationID = glsl::uvec3( // Combine work group and local coordinates into global indices.
1570 gx * local_.x + x, gy * local_.y + y, gz * local_.z + z); // Use the local size to place this invocation in the complete dispatch.
1571 gl_LocalInvocationIndex = (z * local_.y + y) * local_.x + x; // Flatten the local coordinates with X changing fastest.
1572 main(); // Execute the shader with all CPU invocation builtins initialized.
1573 } // End this scope in runtime.
1574 } // End this scope in runtime.
1575 } catch (...) { // Preserve cleanup or exception propagation for this failed operation.
1576 for (std::size_t i = 0; i < outputs.size(); ++i) // Visit this axis of the dispatch or current workgroup.
1577 outputs[i]->latest = detail::authority::host; // Record which backend now owns the newest resource values.
1578 throw; // Rethrow the original failure after arranging the necessary cleanup.
1579 } // End this scope in runtime.
1580 for (std::size_t i = 0; i < outputs.size(); ++i) // Visit this axis of the dispatch or current workgroup.
1581 outputs[i]->latest = detail::authority::host; // Record which backend now owns the newest resource values.
1582 } // End this scope in runtime.
1583} // End this scope in runtime.
1584
1585/** @brief One dispatch retaining a reference to its shader. Keep that shader alive during execution. */
1586struct stage { // Keep the stage values and operations together.
1587 kernel* shader; // Keep the shader used by this dispatch.
1588 extent groups; // Keep the number of workgroups to dispatch.
1589 backend target; // Keep the chosen CPU or GPU backend.
1590 std::shared_ptr<kernel> owner; // Retain an owned kernel while this stage uses it.
1591 /** @brief Retain a kernel reference with workgroup counts and a backend override. */
1592 stage(kernel& k, extent g, backend b) : shader(&k), groups(g), target(b) { // Keep the shader reference, work group counts and backend choice.
1593 } // End this scope in runtime.
1594 stage(std::shared_ptr<kernel> k, extent g, backend b) // Keep the shader reference, work group counts and backend choice.
1595 : shader(k.get()), groups(g), target(b), owner(k) { // Retain the kernel owner together with dispatch dimensions and backend choice.
1596 if (!shader) // Require a retained kernel for this dispatch.
1597 throw error("cannot dispatch a null shader"); // Require a valid kernel for this dispatch.
1598 } // End this scope in runtime.
1599}; // End this scope in runtime.
1600/** @brief Build a dispatch using work group counts, with an optional per-stage backend override. */
1601inline stage dispatch(kernel& shader, extent groups, backend b = backend::automatic) { // Build one ordered dispatch with the requested backend choice.
1602 return stage(shader, groups, b); // Return the result of this check or operation.
1603} // End this scope in runtime.
1604/** @brief Build a dispatch that also retains ownership of its shader. */
1605inline stage // Return a dispatch value that retains its shader owner.
1606dispatch(std::shared_ptr<kernel> shader, extent groups, backend b = backend::automatic) { // Build one ordered dispatch with the requested backend choice.
1607 return stage(shader, groups, b); // Return the result of this check or operation.
1608} // End this scope in runtime.
1609
1610class submission; // Keep the submission values and operations together.
1611
1612/** @brief An ordered pipeline, composed with >> or +=. Resources link stages by shared storage.
1613 * GPU stages issue memory barriers. CPU/GPU transitions transfer authoritative resource contents.
1614 * Running again observes updated uniforms and resource bindings. CPU execution
1615 * completes each stage before returning. GPU execution queues stages in order.
1616 * Request host data or use an explicit wait to observe GPU completion.
1617 */
1618class pipeline { // Keep the pipeline values and operations together.
1619 std::vector<stage> stages_; // Keep the dispatches in execution order.
1620 void execute(device* d) const { // Run stages in order while honoring their backend choices.
1621 for (std::size_t i = 0; i < stages_.size(); ++i) { // Visit every dispatch in processing order.
1622 const stage& s = stages_[i]; // Inspect the next dispatch, its dimensions and backend choice.
1623 if (s.target == backend::gpu && !d) // Require a device for an explicitly GPU stage.
1624 throw error("GPU stage requires a device"); // Report gpu stage requires a device.
1625 s.shader->run(s.groups, s.target == backend::cpu ? 0 : d); // Dispatch this stage on its selected CPU or GPU backend.
1626 } // End this scope in execute.
1627 } // End this scope in execute.
1628
1629 public: // Expose the operations callers can use.
1630 /** @brief Construct an empty pipeline. append dispatches with operator+=. */
1631 pipeline() { // Create the ordered dispatch container.
1632 } // End this scope in runtime.
1633 /** @brief Construct a pipeline containing one dispatch. */
1634 pipeline(stage s) { // Create a pipeline containing this initial dispatch.
1635 stages_.push_back(s); // Keep this dispatch at the end of the processing order.
1636 } // End this scope in runtime.
1637 /** @brief Append a dispatch in execution order and return this pipeline. */
1638 pipeline& operator+=(stage s) { // Append one dispatch while retaining its optional kernel owner.
1639 stages_.push_back(s); // Keep this dispatch at the end of the processing order.
1640 return *this; // Allow another operation to follow in the same expression.
1641 } // End this scope in runtime.
1642 /** @brief Append every dispatch from another pipeline, including a safe copy of this pipeline. */
1643 pipeline& operator+=(const pipeline& other) { // Compose complete processing blocks in their original stage order.
1644 const std::vector<stage> additions(other.stages_); // Preserve the source range before self-append can reallocate it.
1645 stages_.insert(stages_.end(), additions.begin(), additions.end()); // Retain each dispatch's kernel ownership and backend choice.
1646 return *this; // Allow another stage or pipeline to follow in the same expression.
1647 } // Appending copies dispatch references and preserves the stages for later execution.
1648 /** @brief Return the number of stages. */
1649 std::size_t size() const { // Read the stored element or stage count.
1650 return stages_.size(); // Return the result of this check or operation.
1651 } // End this scope in runtime.
1652 /** @brief Run automatic and CPU stages on the CPU. explicit GPU stages need a device. */
1653 void run() const { // Execute the pipeline using the selected CPU or GPU backend.
1654 execute(0); // Run stages in order while honoring their backend choices.
1655 } // End this scope in runtime.
1656 /** @brief Run automatic and GPU stages on the device, respecting explicit CPU stages.
1657 * Host readback completes queued GPU writes when resource data is requested.
1658 */
1659 void run(device& d) const { // Execute the pipeline using the selected CPU or GPU backend.
1660 execute(&d); // Run stages in order while honoring their backend choices.
1661 } // End this scope in runtime.
1662 /** @brief Compile GPU programs and upload resources before steady-state submission. */
1663 void prepare(device& d) const { // Compile the program and prepare its bound GPU resources.
1664 for (std::size_t i = 0; i < stages_.size(); ++i) // Visit every dispatch in processing order.
1665 if (stages_[i].target != backend::cpu) // Precompile and upload stages that can execute on the GPU.
1666 stages_[i].shader->prepare(d); // Compile the program and prepare its bound GPU resources.
1667 } // End this scope in prepare.
1668 /** @brief Enqueue GPU stages and return a fence for zero-timeout completion polls. */
1669 submission submit(device& d) const; // Queue compute work and return its completion handle.
1670 /** @internal Access dispatches for submission and snapshot handling. */
1671 const std::vector<stage>& stages() const { // Expose the ordered dispatches for submission and snapshot handling.
1672 return stages_; // Return the dispatches in execution order.
1673 } // End this scope in runtime.
1674 /** @brief Snapshot shader instances, uniforms and bindings for deferred CPU execution. */
1675 pipeline snapshot() const { // Clone kernels and retain the current binding and uniform values.
1676 pipeline result; // Keep the value returned by this operation.
1677 std::map<kernel*, std::shared_ptr<kernel>> copies; // Keep the shader clones reused by repeated stages.
1678 for (std::size_t i = 0; i < stages_.size(); ++i) { // Visit every dispatch in processing order.
1679 const stage& s = stages_[i]; // Inspect the next dispatch, its dimensions and backend choice.
1680 auto& copy = copies[s.shader]; // Reuse one clone for every occurrence of the same source kernel.
1681 if (!copy) // Create a clone only on the source kernel's first occurrence.
1682 copy = std::shared_ptr<kernel>(s.shader->clone().release()); // Own an isolated clone of this kernel's current interfaces.
1683 result += stage(copy, s.groups, s.target); // Retain the cloned kernel with its original dimensions and backend choice.
1684 } // End this scope in snapshot.
1685 return result; // Return the value returned by this operation.
1686 } // End this scope in snapshot.
1687 /** @brief Validate CPU execution support and current host data for a deferred job. */
1688 void require_host_ready() const { // Validate CPU stages and host-ready resources.
1689 for (std::size_t i = 0; i < stages_.size(); ++i) { // Visit every dispatch in processing order.
1690 if (stages_[i].target == backend::gpu) // Reject GPU-only stages before submitting work to a CPU worker.
1691 throw error("CPU executor cannot execute a GPU-only stage"); // Require CPU-compatible stages for the CPU executor.
1692 auto resources = stages_[i].shader->resources(); // Inspect the resources bound to this stage.
1693 for (std::size_t r = 0; r < resources.size(); ++r) // Check every bound resource for data requiring GPU readback.
1694 if (resources[r]->latest == detail::authority::gpu) // Require the caller to download newer GPU contents on the context thread.
1695 throw error( // Report the invalid operation.
1696 "download GPU data on its context thread before submitting CPU work"); // Explain how to make this resource ready for CPU execution.
1697 } // End this scope in require host ready.
1698 } // End this scope in require host ready.
1699}; // End this scope in runtime.
1700/** @brief Compose ordered stages into a reusable pipeline. */
1701inline pipeline operator>>(stage a, stage b) { // Compose these dispatches in their execution order.
1702 pipeline p(a); // Start the pipeline with the first dispatch.
1703 p += b; // Append the following dispatch to the ordered pipeline.
1704 return p; // Return the composed pipeline for later execution.
1705} // End this scope in runtime.
1706/** @brief Append a stage to a pipeline value, retaining references to the same kernels. */
1707inline pipeline operator>>(pipeline p, stage b) { // Compose these dispatches in their execution order.
1708 p += b; // Append the following dispatch to the ordered pipeline.
1709 return p; // Return the composed pipeline for later execution.
1710} // End this scope in runtime.
1711/** @brief Prepend a stage to a pipeline while retaining the same linked shader resources. */
1712inline pipeline operator>>(stage a, const pipeline& b) { // Place the independent dispatch before all existing processing stages.
1713 pipeline result(a); // Start with the supplied dispatch and its retained kernel owner.
1714 result += b; // Append the complete processing block in its original order.
1715 return result; // Return the reusable pipeline for later execution.
1716} // The source pipeline remains available for further composition or execution.
1717/** @brief Join two complete processing pipelines in execution order. */
1718inline pipeline operator>>(pipeline a, const pipeline& b) { // Compose reusable algorithm blocks with ordinary shader dispatches.
1719 a += b; // Append a copy of the second block's dispatch references.
1720 return a; // Return the combined ordered processing plan.
1721} // Resource sharing follows the same rules as individual stage composition.
1722
1723namespace detail { // Group these definitions in the detail namespace.
1724typedef kernel* (*kernel_factory)(); // Name the representation used by this GL or shader interface.
1725inline std::map<std::string, std::vector<kernel_factory>>& shader_registry() { // Share the compiled shader factories discovered during startup.
1726 static std::map<std::string, std::vector<kernel_factory>> registry; // Keep the shared display lifetime registry.
1727 return registry; // Return the shared display lifetime registry.
1728} // End this scope in shader registry.
1729 // Generated shader translation units register factories directly in their target.
1730 // Unregistering makes plugin unload safe for future lookups. existing shader objects
1731 // still require the plugin to stay loaded through destruction.
1732class shader_registration { // Keep the shader registration values and operations together.
1733 std::string name_; // Keep the kernel or registration name.
1734 kernel_factory factory_; // Keep the compiled shader construction callback.
1735
1736 public: // Expose the operations callers can use.
1737 shader_registration(const char* name, kernel_factory factory) : name_(name), factory_(factory) { // Keep the callback used to create a registered shader.
1738 shader_registry()[name_].push_back(factory_); // Retain this entry in the ordered resource or work collection.
1739 } // End this scope in runtime.
1740 ~shader_registration() { // Unregister this factory before its dynamic module is unloaded.
1741 std::vector<kernel_factory>& entries = shader_registry()[name_]; // Find the registered factories using this shader name.
1742 const std::vector<kernel_factory>::iterator it = // Find this module's factory among the registrations.
1743 std::find(entries.begin(), entries.end(), factory_); // Keep the compiled shader construction callback.
1744 if (it != entries.end()) // Choose this branch when it differs from entries.end().
1745 entries.erase(it); // Remove this module's registration and preserve the other factories.
1746 if (entries.empty()) // Choose this branch when entries.empty().
1747 shader_registry().erase(name_); // Share the compiled shader factories discovered during startup.
1748 } // End this scope in runtime.
1749 shader_registration(const shader_registration&) = delete; // Prevent copying this object and duplicating its retained identity.
1750 shader_registration& operator=(const shader_registration&) = delete; // Prevent copying this object and duplicating its retained identity.
1751}; // End this scope in runtime.
1752} // namespace detail
1753
1754/** @brief Create a shader compiled and embedded by shdr_target_shaders during the CMake build.
1755 * A file named scale.cpp registers scale_kernel. The returned instance owns its CPU/GPU program
1756 * and exposes resource binding through bind() and set(). Both programs are part of the binary.
1757 * Unknown or ambiguous names throw error. Call from one application thread after startup.
1758 */
1759inline std::unique_ptr<kernel> make_kernel(const std::string& name) { // Create one automatically registered shader by its build-time name.
1760 const std::map<std::string, std::vector<detail::kernel_factory>>& registry = // Set the shared display lifetime registry.
1761 detail::shader_registry(); // Share the compiled shader factories discovered during startup.
1762 const std::map<std::string, std::vector<detail::kernel_factory>>::const_iterator it = // Find the unique compiled factory requested by the application.
1763 registry.find(name); // Keep the resource or entry-point name.
1764 if (it == registry.end()) // Choose this branch when it equals registry.end().
1765 throw error("unknown compiled shader: " + name); // Report unknown compiled shader: .
1766 if (it->second.size() != 1) // Check resource size or driver limits before accessing storage.
1767 throw error("ambiguous compiled shader name: " + name); // Report ambiguous compiled shader name: .
1768 return std::unique_ptr<kernel>(it->second.front()()); // Create one automatically registered shader by its build-time name.
1769} // End this scope in make kernel.
1770} // namespace shdr
1771
1772 // A short namespace alias keeps host-side mathematical expressions readable.
1773namespace glsl = shdr::glsl; // Group these definitions in the glsl namespace.
1774#endif // SHDR_RUNTIME_HPP