1#include "ShaderBuilderPostProcess.h"
3#include "Utilities/Strings.h"
4#include "Utilities/Preprocessor.h"
5#include "ResourceStore.h"
6#include "Renderer/EnginePermutations.h"
7#include "Renderer/Renderer.h"
8#include "Rendering/IGraphicsDevice.h"
9#include "Renderer/Tasks/PostProcessTask.h"
10#include "Foundation/Logging/Logger.h"
11#include "Rendering/IContext.h"
12#include "Rendering/IBuffers.h"
13#include "Renderer/IRenderer.h"
14#include "Renderer/RenderTarget.h"
16#include <glm/gtc/type_ptr.hpp>
24 constexpr size_t InitialBufferCapasity = 4096u;
28 constexpr std::array engineSamplers
36 void changeSuffix(std::string& dst,
const std::string_view& from,
const std::string_view& to)
38 auto pos = dst.find(from);
39 if (pos == std::string::npos)
return;
40 dst.replace(pos, to.length(), to);
43 void removeSuffix(std::string& dst,
const std::string_view& suffix)
45 auto pos = dst.find(suffix);
46 if (pos == std::string::npos)
return;
50 [[nodiscard]] std::string_view parameterType(
const ParsedDataType type)
53 case ParsedDataType::Float:
return "f32";
break;
54 case ParsedDataType::Float2:
return "vec2f";
break;
55 case ParsedDataType::Float3:
return "vec3f";
break;
56 case ParsedDataType::Float4:
return "vec4f";
break;
57 case ParsedDataType::Float4x4:
return "mat4x4f";
break;
58 case ParsedDataType::Int:
return "i32";
break;
59 case ParsedDataType::Int2:
return "vec2i";
break;
60 case ParsedDataType::Int3:
return "vec3i";
break;
61 case ParsedDataType::Int4:
return "vec4i";
break;
62 case ParsedDataType::UInt:
return "u32";
break;
63 case ParsedDataType::UInt2:
return "vec2u";
break;
64 case ParsedDataType::UInt3:
return "vec3u";
break;
65 case ParsedDataType::UInt4:
return "vec4u";
break;
67 LOG_ERROR(logger,
"Unsupported attribute type %d",
int(type));
74 std::string convertDefinesToConstExpressions(
const std::string& s) {
75 std::map<std::string, std::string> addedDefines;
77 result.reserve(s.size());
79 std::istringstream iss(s);
81 std::string identifier;
82 std::string replacement;
83 for (std::string line; std::getline(iss, line); )
88 std::string_view sv(line);
89 auto pos = sv.find_first_not_of(
" \t");
90 if (pos == std::string_view::npos || sv.substr(pos, 7) !=
"#define") {
91 result += line +
"\n";
94 sv = sv.substr(pos + 7);
96 pos = sv.find_first_not_of(
" \t");
97 if (pos == std::string_view::npos) {
98 result += line +
"\n";
103 auto end = sv.find_first_of(
" \t");
104 if (end == std::string_view::npos) {
108 identifier = sv.substr(0, end);
110 pos = sv.find_first_not_of(
" \t");
111 if (pos == std::string_view::npos) {
114 auto last = sv.find_last_not_of(
" \t");
115 replacement = sv.substr(pos, last - pos + 1);
119 auto it = addedDefines.find(identifier);
120 if (it != addedDefines.end()) {
121 if (replacement != (*it).second) {
122 LOG_WARNING(logger,
"Shader generation with inconsistent preprosessor define %s set to %s conflicts with previous define %s.", identifier.c_str(), replacement.c_str(), (*it).second.c_str());
126 result +=
"const " + identifier +
" = " + replacement +
";\n";
127 addedDefines.try_emplace(identifier, replacement);
132 void addInterface(std::string& src) {
136 @builtin(position) position: vec4f,
137 @location(0) TexCoords: vec2f,
138 @location(1) NormalizedCoords: vec2f,
144 void addDefine(std::string& output,
const std::string &name,
const std::string &value)
146 output.append(
"#define ");
149 output.append(value);
153 void addDefines(std::string& output,
const std::vector<std::pair<std::string, std::string>>& definitions)
155 for (
const std::pair<std::string, std::string>& define : definitions) {
156 addDefine(output, define.first, define.second);
160 void addUniforms(std::string& src,
const std::vector<ProcessTaskProperty>& properties,
int group,
int& bindings) {
161 std::string parameterUBOsrc;
163 bool hasParameters =
false;
164 parameterUBOsrc.append(
"");
165 parameterUBOsrc.append(
"struct EffectParameters_t {\n");
168 switch (p.definition->type) {
169 case ParsedDataType::Float:
170 case ParsedDataType::Float2:
171 case ParsedDataType::Float3:
172 case ParsedDataType::Float4:
173 case ParsedDataType::Float4x4:
174 case ParsedDataType::Int:
175 case ParsedDataType::Int2:
176 case ParsedDataType::Int3:
177 case ParsedDataType::Int4:
178 case ParsedDataType::UInt:
179 case ParsedDataType::UInt2:
180 case ParsedDataType::UInt3:
181 case ParsedDataType::UInt4:
182 parameterUBOsrc.append(
" " + p.definition->key +
" : ");
183 parameterUBOsrc.append(parameterType(p.definition->type));
184 parameterUBOsrc.append(
",\n");
185 hasParameters =
true;
188 case ParsedDataType::Texture2D:
190 std::string texType =
"texture";
191 if ((p.definition->texture.flags & ParsedValueTextureFlags::DepthTexture) != 0) {
192 texType.append(
"_depth");
194 if (p.definition->texture.samples > 1) {
195 texType.append(
"_multisampled");
196 LOG_ERROR(logger,
"Sampling from multisampled textures is not allowed on this platform, well, it is, just not implemented");
198 texType.append(
"_2d");
199 std::string_view texture_template =
"<f32>";
200 switch (p.definition->texture.dataType) {
201 case ParsedDataType::Int:
202 case ParsedDataType::Int2:
203 case ParsedDataType::Int3:
204 case ParsedDataType::Int4:
205 texture_template =
"<i32>";
207 case ParsedDataType::UInt:
208 case ParsedDataType::UInt2:
209 case ParsedDataType::UInt3:
210 case ParsedDataType::UInt4:
211 texture_template =
"<u32>";
216 if ((p.definition->texture.flags & ParsedValueTextureFlags::DepthTexture) == 0) {
217 texType.append(texture_template);
219 src.append(
"@group(");
220 src.append(std::to_string(group));
221 src.append(
") @binding(");
222 src.append(std::to_string(++bindings));
225 src.append(
"<uniform>");
228 src.append(p.definition->key);
229 src.append(
" : " + texType);
233 src.append(
"@group(");
234 src.append(std::to_string(group));
235 src.append(
") @binding(");
236 src.append(std::to_string(++bindings));
239 src.append(
"<uniform>");
242 src.append(p.definition->key);
243 src.append(
"Sampler : sampler");
247 case ParsedDataType::Buffer:
248 LOG_ERROR(logger,
"Render buffers not supported on this platform");
250 case ParsedDataType::ConstantBuffer:
251 if (p.definition->key ==
"SceneBuffer") {
252 src.append(
"@group(" + std::to_string(group) +
") @binding(" + std::to_string(++bindings) +
") var<uniform> SceneBuffer : SceneBuffer_t; \n");
253 src.append(
"#define COGS_VIEWGETTERS_REFERENCED 1\n");
255 else if (p.definition->value.starts_with(
"Cogs.")) {
256 std::string bufferName = p.definition->value.substr(5);
257 src.append(
"#include \"Engine/" + bufferName +
".wgsl\"\n");
258 src.append(
"@group(" + std::to_string(group) +
") @binding(" + std::to_string(++bindings) +
") var<uniform> " + p.definition->key +
" : " + bufferName +
"_t; \n");
265 parameterUBOsrc.append(
"};\n");
267 src.append(parameterUBOsrc);
268 src.append(
"@group(" + std::to_string(group) +
") @binding(" + std::to_string(++bindings) +
") var<uniform> EffectParameters : EffectParameters_t;\n");
272 void addEngineSamplers(std::string& src,
int group,
int& bindings) {
273 for (
auto sampler : engineSamplers) {
274 src.append(
"@group(" + std::to_string(group) +
") @binding(" + std::to_string(++bindings) +
") var " + sampler +
" : sampler;\n");
278 void addOutputStruct(std::string& src,
const PipelineOptions& options,
bool writeDepth) {
279 bool hasCustomTargets =
false;
280 std::string prefix =
"target_";
281 src.append(
"struct FragmentOut {\n");
283 src.append(
" @builtin(frag_depth) fragDepth: f32,\n");
285 for (
auto o : options) {
288 for (
auto& c : key) {
289 c =
static_cast<decltype(key)::value_type
>(std::tolower(c));
291 if (key.substr(0, prefix.size()) == prefix) {
292 if (!hasCustomTargets) {
293 hasCustomTargets =
true;
295 int location = std::stoi(key.substr(prefix.size()));
297 Cogs::Core::split(o.second,
" ", tokens);
298 if (tokens.size() != 2) {
299 LOG_ERROR(logger,
"Unable to parse pipeline output option %s", o.second.c_str());
302 std::string_view datatype =
"Undefined";
305 datatype = parameterType(ParsedDataType::Float);
308 datatype = parameterType(ParsedDataType::Float2);
311 datatype = parameterType(ParsedDataType::Float3);
314 datatype = parameterType(ParsedDataType::Float4);
317 datatype = parameterType(ParsedDataType::UInt);
320 datatype = parameterType(ParsedDataType::UInt2);
323 datatype = parameterType(ParsedDataType::UInt3);
326 datatype = parameterType(ParsedDataType::UInt4);
329 datatype = parameterType(ParsedDataType::Int);
332 datatype = parameterType(ParsedDataType::Int2);
335 datatype = parameterType(ParsedDataType::Int3);
338 datatype = parameterType(ParsedDataType::Int4);
341 src.append(
" @location(" + std::to_string(location) +
") ");
342 src.append(tokens[0]);
344 src.append(datatype);
348 if (!hasCustomTargets) {
349 src.append(
" @location(0) fragColor : vec4f,\n");
351 src.append(
"};\n\n");
365 addDefine(src,
"COGS_MAX_LIGHTS", std::to_string(context->renderer->
getMaxLights()));
366 addDefines(src, desc.definitions);
368 bool SceneBufferRef =
false;
370 if (p.definition->key ==
"SceneBuffer") {
371 SceneBufferRef =
true;
375 addUniforms(src, task->properties, bindGroup, bindings);
376 addEngineSamplers(src, bindGroup, bindings);
377 addOutputStruct(src, task->options, task->writeDepth || task->depthTest);
379 src.append(
"#include \"Engine/SceneBuffer.wgsl\"\n\n");
382 std::string prefix_ps = desc.ps;
383 removeSuffix(prefix_ps,
".hlsl");
384 src.append(
"#include \"" + prefix_ps +
".wgsl\"\n");
385 auto code =
hash(src);
386 std::string file_prefix = prefix_ps +
"_" + std::to_string(code);
389 pp.
processed.reserve(::InitialBufferCapasity);
391 if (!pp.
process(context->context, src))
return false;
394 src = convertDefinesToConstExpressions(src);
395 context->context->
resourceStore->addResource(file_prefix +
".wgsl", src);
396 desc.ps = file_prefix +
".hlsl";
397 desc.definitions.clear();
std::unique_ptr< class ResourceStore > resourceStore
ResourceStore service instance.
unsigned getMaxLights() const override
Get the maximum number of lights.
Log implementation class.
Contains the Engine, Renderer, resource managers and other systems needed to run Cogs....
constexpr Log getLogger(const char(&name)[LEN]) noexcept
Contains all Cogs related functionality.
constexpr size_t hash() noexcept
Simple getter function that returns the initial value for fnv1a hashing.
COGSFOUNDATION_API size_t hashLowercase(std::string_view str, size_t hashValue=Cogs::hash()) noexcept
Get the hash code of the string converted to lowercase.
bool process(Context *context, const StringView input)
Run a text block through the preprocessor.
std::string processed
Resulting processed text.