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 "Rendering/IGraphicsDevice.h"
8#include "Renderer/Tasks/PostProcessTask.h"
9#include "Foundation/Logging/Logger.h"
10#include "Rendering/IContext.h"
11#include "Rendering/IBuffers.h"
12#include "Renderer/IRenderer.h"
13#include "Renderer/RenderTarget.h"
15#include <glm/gtc/type_ptr.hpp>
23 constexpr size_t InitialBufferCapasity = 4096u;
27 constexpr std::array engineSamplers
35 void changeSuffix(std::string& dst,
const std::string_view& from,
const std::string_view& to)
37 auto pos = dst.find(from);
38 if (pos == std::string::npos)
return;
39 dst.replace(pos, to.length(), to);
42 void removeSuffix(std::string& dst,
const std::string_view& suffix)
44 auto pos = dst.find(suffix);
45 if (pos == std::string::npos)
return;
49 [[nodiscard]] std::string_view parameterType(
const ParsedDataType type)
52 case ParsedDataType::Float:
return "f32";
break;
53 case ParsedDataType::Float2:
return "vec2f";
break;
54 case ParsedDataType::Float3:
return "vec3f";
break;
55 case ParsedDataType::Float4:
return "vec4f";
break;
56 case ParsedDataType::Float4x4:
return "mat4x4f";
break;
57 case ParsedDataType::Int:
return "i32";
break;
58 case ParsedDataType::Int2:
return "vec2i";
break;
59 case ParsedDataType::Int3:
return "vec3i";
break;
60 case ParsedDataType::Int4:
return "vec4i";
break;
61 case ParsedDataType::UInt:
return "u32";
break;
62 case ParsedDataType::UInt2:
return "vec2u";
break;
63 case ParsedDataType::UInt3:
return "vec3u";
break;
64 case ParsedDataType::UInt4:
return "vec4u";
break;
66 LOG_ERROR(logger,
"Unsupported attribute type %d",
int(type));
73 std::string convertDefinesToConstExpressions(
const std::string& s) {
74 std::map<std::string, std::string> addedDefines;
76 result.reserve(s.size());
78 std::istringstream iss(s);
80 std::string identifier;
81 std::string replacement;
82 for (std::string line; std::getline(iss, line); )
87 std::string_view sv(line);
88 auto pos = sv.find_first_not_of(
" \t");
89 if (pos == std::string_view::npos || sv.substr(pos, 7) !=
"#define") {
90 result += line +
"\n";
93 sv = sv.substr(pos + 7);
95 pos = sv.find_first_not_of(
" \t");
96 if (pos == std::string_view::npos) {
97 result += line +
"\n";
102 auto end = sv.find_first_of(
" \t");
103 if (end == std::string_view::npos) {
107 identifier = sv.substr(0, end);
109 pos = sv.find_first_not_of(
" \t");
110 if (pos == std::string_view::npos) {
113 auto last = sv.find_last_not_of(
" \t");
114 replacement = sv.substr(pos, last - pos + 1);
118 auto it = addedDefines.find(identifier);
119 if (it != addedDefines.end()) {
120 if (replacement != (*it).second) {
121 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());
125 result +=
"const " + identifier +
" = " + replacement +
";\n";
126 addedDefines.try_emplace(identifier, replacement);
131 void addInterface(std::string& src) {
135 @builtin(position) position: vec4f,
136 @location(0) TexCoords: vec2f,
137 @location(1) NormalizedCoords: vec2f,
143 void addDefines(std::string& output,
144 const std::vector<std::pair<std::string, std::string>>& definitions)
146 for (
const std::pair<std::string, std::string>& define : definitions) {
147 output.append(
"#define ");
148 output.append(define.first);
150 output.append(define.second);
155 void addUniforms(std::string& src,
const std::vector<ProcessTaskProperty>& properties,
int group,
int& bindings) {
156 std::string parameterUBOsrc;
158 bool hasParameters =
false;
159 parameterUBOsrc.append(
"");
160 parameterUBOsrc.append(
"struct EffectParameters_t {\n");
163 switch (p.definition->type) {
164 case ParsedDataType::Float:
165 case ParsedDataType::Float2:
166 case ParsedDataType::Float3:
167 case ParsedDataType::Float4:
168 case ParsedDataType::Float4x4:
169 case ParsedDataType::Int:
170 case ParsedDataType::Int2:
171 case ParsedDataType::Int3:
172 case ParsedDataType::Int4:
173 case ParsedDataType::UInt:
174 case ParsedDataType::UInt2:
175 case ParsedDataType::UInt3:
176 case ParsedDataType::UInt4:
177 parameterUBOsrc.append(
" " + p.definition->key +
" : ");
178 parameterUBOsrc.append(parameterType(p.definition->type));
179 parameterUBOsrc.append(
",\n");
180 hasParameters =
true;
183 case ParsedDataType::Texture2D:
185 std::string texType =
"texture";
186 if ((p.definition->texture.flags & ParsedValueTextureFlags::DepthTexture) != 0) {
187 texType.append(
"_depth");
189 if (p.definition->texture.samples > 1) {
190 texType.append(
"_multisampled");
191 LOG_ERROR(logger,
"Sampling from multisampled textures is not allowed on this platform, well, it is, just not implemented");
193 texType.append(
"_2d");
194 std::string_view texture_template =
"<f32>";
195 switch (p.definition->texture.dataType) {
196 case ParsedDataType::Int:
197 case ParsedDataType::Int2:
198 case ParsedDataType::Int3:
199 case ParsedDataType::Int4:
200 texture_template =
"<i32>";
202 case ParsedDataType::UInt:
203 case ParsedDataType::UInt2:
204 case ParsedDataType::UInt3:
205 case ParsedDataType::UInt4:
206 texture_template =
"<u32>";
211 if ((p.definition->texture.flags & ParsedValueTextureFlags::DepthTexture) == 0) {
212 texType.append(texture_template);
214 src.append(
"@group(");
215 src.append(std::to_string(group));
216 src.append(
") @binding(");
217 src.append(std::to_string(++bindings));
220 src.append(
"<uniform>");
223 src.append(p.definition->key);
224 src.append(
" : " + texType);
228 src.append(
"@group(");
229 src.append(std::to_string(group));
230 src.append(
") @binding(");
231 src.append(std::to_string(++bindings));
234 src.append(
"<uniform>");
237 src.append(p.definition->key);
238 src.append(
"Sampler : sampler");
242 case ParsedDataType::Buffer:
243 LOG_ERROR(logger,
"Render buffers not supported on this platform");
245 case ParsedDataType::ConstantBuffer:
246 if (p.definition->key ==
"SceneBuffer") {
247 src.append(
"@group(" + std::to_string(group) +
") @binding(" + std::to_string(++bindings) +
") var<uniform> SceneBuffer : SceneBuffer_t; \n");
248 src.append(
"#define COGS_VIEWGETTERS_REFERENCED 1\n");
250 else if (p.definition->key ==
"ShadowBuffer") {
251 src.append(
"@group(" + std::to_string(group) +
") @binding(" + std::to_string(++bindings) +
") var<uniform> ShadowBuffer : ShadowBuffer_t; \n");
253 else if (p.definition->value.starts_with(
"Cogs.")) {
254 std::string bufferName = p.definition->value.substr(5);
255 src.append(
"#include \"Engine/" + bufferName +
".wgsl\"\n");
256 src.append(
"@group(" + std::to_string(group) +
") @binding(" + std::to_string(++bindings) +
") var<uniform> " + p.definition->key +
" : " + bufferName +
"_t; \n");
263 parameterUBOsrc.append(
"};\n");
265 src.append(parameterUBOsrc);
266 src.append(
"@group(" + std::to_string(group) +
") @binding(" + std::to_string(++bindings) +
") var<uniform> EffectParameters : EffectParameters_t;\n");
270 void addEngineSamplers(std::string& src,
int group,
int& bindings) {
271 for (
auto sampler : engineSamplers) {
272 src.append(
"@group(" + std::to_string(group) +
") @binding(" + std::to_string(++bindings) +
") var " + sampler +
" : sampler;\n");
276 void addOutputStruct(std::string& src,
const PipelineOptions& options,
bool writeDepth) {
277 bool hasCustomTargets =
false;
278 std::string prefix =
"target_";
279 src.append(
"struct FragmentOut {\n");
281 src.append(
" @builtin(frag_depth) fragDepth: f32,\n");
283 for (
auto o : options) {
286 for (
auto& c : key) {
287 c =
static_cast<decltype(key)::value_type
>(std::tolower(c));
289 if (key.substr(0, prefix.size()) == prefix) {
290 if (!hasCustomTargets) {
291 hasCustomTargets =
true;
293 int location = std::stoi(key.substr(prefix.size()));
295 Cogs::Core::split(o.second,
" ", tokens);
296 if (tokens.size() != 2) {
297 LOG_ERROR(logger,
"Unable to parse pipeline output option %s", o.second.c_str());
300 std::string_view datatype =
"Undefined";
303 datatype = parameterType(ParsedDataType::Float);
306 datatype = parameterType(ParsedDataType::Float2);
309 datatype = parameterType(ParsedDataType::Float3);
312 datatype = parameterType(ParsedDataType::Float4);
315 datatype = parameterType(ParsedDataType::UInt);
318 datatype = parameterType(ParsedDataType::UInt2);
321 datatype = parameterType(ParsedDataType::UInt3);
324 datatype = parameterType(ParsedDataType::UInt4);
327 datatype = parameterType(ParsedDataType::Int);
330 datatype = parameterType(ParsedDataType::Int2);
333 datatype = parameterType(ParsedDataType::Int3);
336 datatype = parameterType(ParsedDataType::Int4);
339 src.append(
" @location(" + std::to_string(location) +
") ");
340 src.append(tokens[0]);
342 src.append(datatype);
346 if (!hasCustomTargets) {
347 src.append(
" @location(0) fragColor : vec4f,\n");
349 src.append(
"};\n\n");
363 addDefines(src, desc.definitions);
365 bool SceneBufferRef =
false;
367 if (p.definition->key ==
"SceneBuffer") {
368 SceneBufferRef =
true;
372 addUniforms(src, task->properties, bindGroup, bindings);
373 addEngineSamplers(src, bindGroup, bindings);
374 addOutputStruct(src, task->options, task->writeDepth || task->depthTest);
376 src.append(
"#include \"Engine/SceneBuffer.wgsl\"\n\n");
379 std::string prefix_ps = desc.ps;
380 removeSuffix(prefix_ps,
".hlsl");
381 src.append(
"#include \"" + prefix_ps +
".wgsl\"\n");
382 auto code =
hash(src);
383 std::string file_prefix = prefix_ps +
"_" + std::to_string(code);
386 pp.
processed.reserve(::InitialBufferCapasity);
388 if (!pp.
process(context->context, src))
return false;
391 src = convertDefinesToConstExpressions(src);
392 context->context->
resourceStore->addResource(file_prefix +
".wgsl", src);
393 desc.ps = file_prefix +
".hlsl";
394 desc.definitions.clear();
std::unique_ptr< class ResourceStore > resourceStore
ResourceStore service instance.
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.