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>
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::regex e1(
"^\\s*#define\\s+([^\\s]+)\\s+([^\\s]+)");
82 std::regex e2(
"^\\s*#define\\s+([^\\s]+)\\s*$");
83 std::string identifier;
84 std::string replacement;
85 for (std::string line; std::getline(iss, line); )
91 if (std::regex_search(line, match, e1))
93 identifier += match.str(1);
94 replacement += match.str(2);
96 else if (std::regex_search(line, match, e2)) {
97 identifier += match.str(1);
100 if (identifier.empty()) {
101 result += line +
"\n";
104 auto it = addedDefines.find(identifier);
105 if (it != addedDefines.end()) {
106 if (replacement != (*it).second) {
107 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());
111 result +=
"const " + identifier +
" = " + replacement +
";\n";
112 addedDefines.try_emplace(identifier, replacement);
117 void addInterface(std::string& src) {
121 @builtin(position) position: vec4f,
122 @location(0) TexCoords: vec2f,
123 @location(1) NormalizedCoords: vec2f,
129 void addDefines(std::string& output,
130 const std::vector<std::pair<std::string, std::string>>& definitions)
132 for (
const std::pair<std::string, std::string>& define : definitions) {
133 output.append(
"#define ");
134 output.append(define.first);
136 output.append(define.second);
141 void addUniforms(std::string& src,
const std::vector<ProcessTaskProperty>& properties,
int group,
int& bindings) {
142 std::string parameterUBOsrc;
144 bool hasParameters =
false;
145 parameterUBOsrc.append(
"");
146 parameterUBOsrc.append(
"struct EffectParameters_t {\n");
149 switch (p.definition->type) {
150 case ParsedDataType::Float:
151 case ParsedDataType::Float2:
152 case ParsedDataType::Float3:
153 case ParsedDataType::Float4:
154 case ParsedDataType::Float4x4:
155 case ParsedDataType::Int:
156 case ParsedDataType::Int2:
157 case ParsedDataType::Int3:
158 case ParsedDataType::Int4:
159 case ParsedDataType::UInt:
160 case ParsedDataType::UInt2:
161 case ParsedDataType::UInt3:
162 case ParsedDataType::UInt4:
163 parameterUBOsrc.append(
" " + p.definition->key +
" : ");
164 parameterUBOsrc.append(parameterType(p.definition->type));
165 parameterUBOsrc.append(
",\n");
166 hasParameters =
true;
169 case ParsedDataType::Texture2D:
171 std::string texType =
"texture";
172 if ((p.definition->texture.flags & ParsedValueTextureFlags::DepthTexture) != 0) {
173 texType.append(
"_depth");
175 if (p.definition->texture.samples > 1) {
176 texType.append(
"_multisampled");
177 LOG_ERROR(logger,
"Sampling from multisampled textures is not allowed on this platform, well, it is, just not implemented");
179 texType.append(
"_2d");
180 std::string_view texture_template =
"<f32>";
181 switch (p.definition->texture.dataType) {
182 case ParsedDataType::Int:
183 case ParsedDataType::Int2:
184 case ParsedDataType::Int3:
185 case ParsedDataType::Int4:
186 texture_template =
"<i32>";
188 case ParsedDataType::UInt:
189 case ParsedDataType::UInt2:
190 case ParsedDataType::UInt3:
191 case ParsedDataType::UInt4:
192 texture_template =
"<u32>";
197 if ((p.definition->texture.flags & ParsedValueTextureFlags::DepthTexture) == 0) {
198 texType.append(texture_template);
200 src.append(
"@group(");
201 src.append(std::to_string(group));
202 src.append(
") @binding(");
203 src.append(std::to_string(++bindings));
206 src.append(
"<uniform>");
209 src.append(p.definition->key);
210 src.append(
" : " + texType);
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(
"Sampler : sampler");
228 case ParsedDataType::Buffer:
229 LOG_ERROR(logger,
"Render buffers not supported on this platform");
231 case ParsedDataType::ConstantBuffer:
232 if (p.definition->key ==
"SceneBuffer") {
233 src.append(
"@group(" + std::to_string(group) +
") @binding(" + std::to_string(++bindings) +
") var<uniform> SceneBuffer : SceneBuffer_t; \n");
234 src.append(
"#define COGS_VIEWGETTERS_REFERENCED 1\n");
236 else if (p.definition->key ==
"ShadowBuffer") {
237 src.append(
"@group(" + std::to_string(group) +
") @binding(" + std::to_string(++bindings) +
") var<uniform> ShadowBuffer : ShadowBuffer_t; \n");
239 else if (p.definition->value.starts_with(
"Cogs.")) {
240 std::string bufferName = p.definition->value.substr(5);
241 src.append(
"#include \"Engine/" + bufferName +
".wgsl\"\n");
242 src.append(
"@group(" + std::to_string(group) +
") @binding(" + std::to_string(++bindings) +
") var<uniform> " + p.definition->key +
" : " + bufferName +
"_t; \n");
249 parameterUBOsrc.append(
"};\n");
251 src.append(parameterUBOsrc);
252 src.append(
"@group(" + std::to_string(group) +
") @binding(" + std::to_string(++bindings) +
") var<uniform> EffectParameters : EffectParameters_t;\n");
256 void addEngineSamplers(std::string& src,
int group,
int& bindings) {
257 for (
auto sampler : engineSamplers) {
258 src.append(
"@group(" + std::to_string(group) +
") @binding(" + std::to_string(++bindings) +
") var " + sampler +
" : sampler;\n");
262 void addOutputStruct(std::string& src,
const PipelineOptions& options,
bool writeDepth) {
263 bool hasCustomTargets =
false;
264 std::string prefix =
"target_";
265 src.append(
"struct FragmentOut {\n");
267 src.append(
" @builtin(frag_depth) fragDepth: f32,\n");
269 for (
auto o : options) {
272 for (
auto& c : key) {
273 c =
static_cast<decltype(key)::value_type
>(std::tolower(c));
275 if (key.substr(0, prefix.size()) == prefix) {
276 if (!hasCustomTargets) {
277 hasCustomTargets =
true;
279 int location = std::stoi(key.substr(prefix.size()));
281 Cogs::Core::split(o.second,
" ", tokens);
282 if (tokens.size() != 2) {
283 LOG_ERROR(logger,
"Unable to parse pipeline output option %s", o.second.c_str());
286 std::string_view datatype =
"Undefined";
289 datatype = parameterType(ParsedDataType::Float);
292 datatype = parameterType(ParsedDataType::Float2);
295 datatype = parameterType(ParsedDataType::Float3);
298 datatype = parameterType(ParsedDataType::Float4);
301 datatype = parameterType(ParsedDataType::UInt);
304 datatype = parameterType(ParsedDataType::UInt2);
307 datatype = parameterType(ParsedDataType::UInt3);
310 datatype = parameterType(ParsedDataType::UInt4);
313 datatype = parameterType(ParsedDataType::Int);
316 datatype = parameterType(ParsedDataType::Int2);
319 datatype = parameterType(ParsedDataType::Int3);
322 datatype = parameterType(ParsedDataType::Int4);
325 src.append(
" @location(" + std::to_string(location) +
") ");
326 src.append(tokens[0]);
328 src.append(datatype);
332 if (!hasCustomTargets) {
333 src.append(
" @location(0) fragColor : vec4f,\n");
335 src.append(
"};\n\n");
349 addDefines(src, desc.definitions);
351 addUniforms(src, task->properties, bindGroup, bindings);
352 addEngineSamplers(src, bindGroup, bindings);
353 addOutputStruct(src, task->options, task->writeDepth);
354 src.append(
"#include \"Engine/SceneBuffer.wgsl\"\n\n");
356 std::string prefix_ps = desc.ps;
357 removeSuffix(prefix_ps,
".hlsl");
358 src.append(
"#include \"" + prefix_ps +
".wgsl\"\n");
359 auto code =
hash(src);
360 std::string file_prefix = prefix_ps +
"_" + std::to_string(code);
363 pp.
processed.reserve(::InitialBufferCapasity);
365 if (!pp.
process(context->context, src))
return false;
368 src = convertDefinesToConstExpressions(src);
369 context->context->
resourceStore->addResource(file_prefix +
".wgsl", src);
370 desc.ps = file_prefix +
".hlsl";
371 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.