Cogs.Core
ShaderBuilderHelpersWebGPU.cpp
1#include "ShaderBuilderHelpersWebGPU.h"
2#include "Rendering/IBindGroups.h"
3#include "Context.h"
4#include "Utilities/Strings.h"
5#include "Utilities/Preprocessor.h"
6#include "ResourceStore.h"
7#include "Renderer/EnginePermutations.h"
8#include "Rendering/IGraphicsDevice.h"
9
10#include "Foundation/Logging/Logger.h"
11
12#include <array>
13#include <sstream>
14#include <map>
15
16namespace
17{
18 using Cogs::Core::BindGroup;
19 const Cogs::Logging::Log logger = Cogs::Logging::getLogger("ShaderBuilderHelpersWebGPU");
20
21 std::vector<std::string> sceneGetters = {
22 "getClipFromViewMatrix",
23 "getClipFromWorldMatrix",
24 "getViewFromWorldMatrix",
25 "getViewFromClipMatrix",
26 "getWorldFromViewMatrix",
27 "getInverseViewMatrix",
28 "getViewFromViewportMatrix",
29 "getViewportFromViewMatrix",
30 "getPeriodicWorldPosAndCell",
31 "getPeriodicWorldPos"
32 };
33
34 std::vector<std::string> objectGetters = {
35 "getWorldFromLocalMatrix",
36 "getObjectId"
37 };
38
39 struct EngineBufferDesc {
40 std::string name;
41 BindGroup group;
42 std::vector<std::string> getters;
43 };
44
45
46 const std::array engineBuffers = {
47 EngineBufferDesc{"SceneBuffer", BindGroup::Scene, sceneGetters},
48 EngineBufferDesc{"ObjectBuffer", BindGroup::Object, objectGetters},
49 };
50
51 struct EngineTexture {
52 std::string name;
53 Cogs::ResourceDimensions dimensions;
54 bool isDepthTexture = false;
55 BindGroup group = BindGroup::Default;
56 };
57
58 const std::array engineTextures = {
59 EngineTexture{.name = "environmentSky", .dimensions = Cogs::ResourceDimensions::TextureCube, .group = BindGroup::LightTextures},
60 EngineTexture{.name = "environmentRadiance", .dimensions = Cogs::ResourceDimensions::TextureCube, .group = BindGroup::LightTextures},
61 EngineTexture{.name = "environmentIrradiance", .dimensions = Cogs::ResourceDimensions::TextureCube, .group = BindGroup::LightTextures},
62 EngineTexture{.name = "ambientIrradiance", .dimensions = Cogs::ResourceDimensions::TextureCube, .group = BindGroup::LightTextures},
63 EngineTexture{.name = "brdfLUT", .dimensions = Cogs::ResourceDimensions::Texture2D, .group = BindGroup::LightTextures},
64 EngineTexture{.name = "cascadedShadowMap", .dimensions = Cogs::ResourceDimensions::Texture2DArray, .isDepthTexture = true, .group = BindGroup::LightTextures},
65 EngineTexture{.name = "cubeShadowMap", .dimensions = Cogs::ResourceDimensions::TextureCube, .isDepthTexture = true, .group = BindGroup::LightTextures},
66 };
67
68 struct EngineSampler {
69 std::string name;
70 BindGroup group = BindGroup::Default;
71 bool isDepthTexture = false;
72 };
73
74 const std::array engineSamplers = {
75 EngineSampler{.name = "linearSampler", .group = BindGroup::LightTextures},
76 EngineSampler{.name = "linearClampSampler", .group = BindGroup::LightTextures},
77 EngineSampler{.name = "pointSampler", .group = BindGroup::LightTextures},
78 EngineSampler{.name = "pointClampSampler", .group = BindGroup::LightTextures},
79 EngineSampler{.name = "shadowSampler", .group = BindGroup::LightTextures, .isDepthTexture = true},
80 };
81
82
83 const uint32_t SceneGlobalVisibility = (Cogs::BindingVisibilityVertex | Cogs::BindingVisibilityFragment | Cogs::BindingVisibilityCompute);
84
85 Cogs::BindGroupEntryDescription makeBufferEntry(const uint32_t binding, const char* name)
86 {
88 entry.binding = binding;
89 entry.nameHash = Cogs::hash(name);
90 entry.resourceType = Cogs::BindingResourceType::UniformBuffer;
91 entry.visibility = SceneGlobalVisibility;
92 return entry;
93 }
94
95 Cogs::BindGroupEntryDescription makeTextureEntry(
96 const uint32_t binding,
97 const char* name,
98 const Cogs::ResourceDimensions dimension,
99 const bool isDepthTexture)
100 {
102 entry.binding = binding;
103 entry.nameHash = Cogs::hash(name);
104 entry.resourceType = Cogs::BindingResourceType::Texture;
105 entry.visibility = SceneGlobalVisibility;
106 entry.textureDimension = dimension;
107 entry.isDepthTexture = isDepthTexture;
108 return entry;
109 }
110
111 Cogs::BindGroupEntryDescription makeSamplerEntry(const uint32_t binding, const char* name, bool isDepthTexture)
112 {
114 entry.binding = binding;
115 entry.nameHash = Cogs::hash(name);
116 entry.resourceType = Cogs::BindingResourceType::Sampler;
117 entry.samplerBindingType = isDepthTexture ? Cogs::BindingSamplerBindingType::Comparison : Cogs::BindingSamplerBindingType::Filtering;
118 entry.visibility = SceneGlobalVisibility;
119 return entry;
120 }
121
122 [[nodiscard]]
123 std::string textureDataType(Cogs::BindingTextureSampleType type)
124 {
125 switch (type) {
126 case Cogs::BindingTextureSampleType::Unknown: // Defaults to f32
127 case Cogs::BindingTextureSampleType::Float:
128 case Cogs::BindingTextureSampleType::UnfilterableFloat:
129 case Cogs::BindingTextureSampleType::Depth:
130 return "f32"; break;
131 case Cogs::BindingTextureSampleType::Sint:
132 return "i32"; break;
133 case Cogs::BindingTextureSampleType::Uint:
134 return "u32"; break;
135 default:
136 LOG_ERROR(logger, "Unsupported texture datatype %d", int(type));
137 return "<illegal>";
138 break;
139 }
140 }
141
142}
143
144namespace Cogs::Core
145{
147 {
148 size_t nameHash = Cogs::hash(name);
149 for (uint16_t i = 0; i < group.numEntries; ++i) {
150 const Cogs::BindGroupEntryDescription& entry = group.entries[i];
151 if (entry.nameHash == nameHash) {
152 return &entry;
153 }
154 }
155 return nullptr;
156 }
157
158 void addBufferToSource(std::string& source, const std::string& name, BindGroup group, const Cogs::BindGroupEntryDescription& entry)
159 {
160 source.append("#include \"Engine/");
161 source.append(name);
162 source.append(".wgsl\"\n");
163 source.append("@group(");
164 source.append(std::to_string(static_cast<size_t>(group)));
165 source.append(") @binding(");
166 source.append(std::to_string(entry.binding));
167 source.append(") var<uniform> ");
168 source.append(name);
169 source.append(" : ");
170 source.append(name);
171 source.append("_t;\n");
172 }
173
174 void addSamplerToSource(std::string& source, const std::string& name, BindGroup group, const Cogs::BindGroupEntryDescription& entry)
175 {
176 source.append("@group(");
177 source.append(std::to_string(static_cast<size_t>(group)));
178 source.append(") @binding(");
179 source.append(std::to_string(entry.binding));
180 source.append(") var ");
181 source.append(name);
182 source.append(" : ");
183 source.append(entry.samplerBindingType == Cogs::BindingSamplerBindingType::Comparison ? "sampler_comparison" : "sampler");
184 source.append(";\n");
185 }
186
187 void addTextureToSource(std::string& source, const std::string& name, BindGroup group, const Cogs::BindGroupEntryDescription& entry)
188 {
189 std::string type;
190 type.reserve(50);
191 type += "texture";
192
193 if (entry.isDepthTexture) {
194 type += "_depth";
195 }
196
197 switch (entry.textureDimension) {
198 case Cogs::ResourceDimensions::Texture2D:
199 type += "_2d";
200 break;
201 case Cogs::ResourceDimensions::TextureCube:
202 type += "_cube";
203 break;
204 case Cogs::ResourceDimensions::Texture2DArray:
205 type += "_2d_array";
206 break;
207 case Cogs::ResourceDimensions::Texture3D:
208 type += "_3d";
209 break;
210 default:
211 LOG_ERROR(logger, "Unsupported texture dimension %d", int(entry.textureDimension));
212 break;
213 }
214
215 if (!entry.isDepthTexture) {
216 std::string dataType = textureDataType(entry.textureSampleType);
217 type += "<" + dataType + ">";
218 }
219
220 source.append("@group(");
221 source.append(std::to_string(static_cast<size_t>(group)));
222 source.append(") @binding(");
223 source.append(std::to_string(entry.binding));
224 source.append(") var");
225 source.append(" ");
226 source.append(name);
227 source.append(" : ");
228 source.append(type);
229 source.append(";\n");
230 }
231
232
233 const Cogs::BindGroupSetDescription& getEngineBindGroupDescription()
234 {
235 static Cogs::BindGroupSetDescription staticDesc = {};
236
237 if (staticDesc.numGroups == 0) {
239 groups.numGroups = static_cast<uint16_t>(BindGroup::Count);
240 for (const auto& buffer : engineBuffers) {
241 Cogs::BindGroupDescription& g = groups.groups[static_cast<size_t>(buffer.group)];
242 uint16_t binding = g.numEntries;
243 g.entries[g.numEntries++] = makeBufferEntry(binding, buffer.name.c_str());
244 ;
245 }
246
247 for (const auto& texture : engineTextures) {
248 Cogs::BindGroupDescription& g = groups.groups[static_cast<size_t>(texture.group)];
249 uint16_t binding = g.numEntries;
250 g.entries[g.numEntries++] = makeTextureEntry(binding, texture.name.c_str(), texture.dimensions, texture.isDepthTexture);
251 }
252
253 for (const auto& sampler : engineSamplers) {
254 Cogs::BindGroupDescription& g = groups.groups[static_cast<size_t>(sampler.group)];
255 uint16_t binding = g.numEntries;
256 g.entries[g.numEntries++] = makeSamplerEntry(binding, sampler.name.c_str(), sampler.isDepthTexture);
257 }
258 staticDesc = groups;
259 }
260 return staticDesc;
261 }
262
263 void addEngineUniformsToSourceWebGPU(std::string& source, const std::unordered_set<Cogs::Core::StringRef>& identifiersSeen, std::vector<bool>& bindGroupUsed) {
264 bindGroupUsed.resize(static_cast<size_t>(BindGroup::Count), false);
265 const Cogs::BindGroupSetDescription& desc = getEngineBindGroupDescription();
266
267 for (const auto& buffer : engineBuffers) {
268 bool add = false;
269 for (auto& get : buffer.getters) {
270 if (identifiersSeen.contains(Strings::add(get))) {
271 add = true;
272 break;
273 }
274 }
275 if (identifiersSeen.contains(Cogs::Core::Strings::add(buffer.name))) {
276 add = true;
277 }
278 if (!add) continue;
279 bindGroupUsed[static_cast<size_t>(buffer.group)] = true;
280 const Cogs::BindGroupEntryDescription* entry = findEntryByName(desc.groups[static_cast<size_t>(buffer.group)], buffer.name);
281 if (entry) {
282 addBufferToSource(source, buffer.name, buffer.group, *entry);
283 }
284 }
285
286 for (const auto& texture : engineTextures) {
287 if (!identifiersSeen.contains(Cogs::Core::Strings::add(texture.name))) {
288 continue;
289 }
290
291 const Cogs::BindGroupEntryDescription* entry = findEntryByName(desc.groups[static_cast<size_t>(texture.group)], texture.name);
292 if (entry) {
293 addTextureToSource(source, texture.name, texture.group, *entry);
294 bindGroupUsed[static_cast<size_t>(texture.group)] = true;
295 }
296 }
297
298 for (const auto& sampler : engineSamplers) {
299 if (!identifiersSeen.contains(Cogs::Core::Strings::add(sampler.name))) {
300 continue;
301 }
302 const Cogs::BindGroupEntryDescription* entry = findEntryByName(desc.groups[static_cast<size_t>(sampler.group)], sampler.name);
303 if (entry) {
304 bindGroupUsed[static_cast<size_t>(sampler.group)] = true;
305 addSamplerToSource(source, sampler.name, sampler.group, *entry);
306 }
307 }
308 }
309
310}
Log implementation class.
Definition: LogManager.h:140
Contains the Engine, Renderer, resource managers and other systems needed to run Cogs....
void addBufferToSource(std::string &source, const std::string &name, BindGroup group, const Cogs::BindGroupEntryDescription &entry)
Emits a valid WGSL uniform buffer declaration ("#include" + "@group()@binding() var<uniform>") for an...
const Cogs::BindGroupEntryDescription * findEntryByName(const Cogs::BindGroupDescription &group, std::string_view name)
Finds an entry by name within a single bind group's already-populated entries (bound by numEntries).
void addSamplerToSource(std::string &source, const std::string &name, BindGroup group, const Cogs::BindGroupEntryDescription &entry)
Emits a valid WGSL sampler declaration ("@group()@binding() var") for an engine-provided sampler entr...
void addTextureToSource(std::string &source, const std::string &name, BindGroup group, const Cogs::BindGroupEntryDescription &entry)
Emits a valid WGSL texture declaration ("@group()@binding() var") for an engine-provided texture entr...
constexpr Log getLogger(const char(&name)[LEN]) noexcept
Definition: LogManager.h:181
constexpr size_t hash() noexcept
Simple getter function that returns the initial value for fnv1a hashing.
Definition: HashFunctions.h:62