Cogs.Core
ShaderBuilderPostProcessES3.cpp
1#include "ShaderBuilderPostProcess.h"
2#include "Context.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"
14
15#include <glm/gtc/type_ptr.hpp>
16
17#include <array>
18#include <sstream>
19#include <regex>
20#include <map>
21
22
23namespace {
24 constexpr size_t InitialBufferCapasity = 4096u;
25
26 const Cogs::Logging::Log logger = Cogs::Logging::getLogger("ShaderBuilderPostProcessES3");
27
28 void changeSuffix(std::string& dst, const std::string_view& from, const std::string_view& to)
29 {
30 auto pos = dst.find(from);
31 if (pos == std::string::npos) return;
32 dst.replace(pos, to.length(), to);
33 }
34
35 void removeSuffix(std::string& dst, const std::string_view& suffix)
36 {
37 auto pos = dst.find(suffix);
38 if (pos == std::string::npos) return;
39 dst.erase(pos);
40 }
41 using namespace Cogs::Core;
42 [[nodiscard]] std::string_view parameterType(const ParsedDataType type)
43 {
44 switch (type) {
45 case ParsedDataType::Float: return "float "; break;
46 case ParsedDataType::Float2: return "vec2 "; break;
47 case ParsedDataType::Float3: return "vec3 "; break;
48 case ParsedDataType::Float4: return "vec4 "; break;
49 case ParsedDataType::Float4x4: return "mat4 "; break;
50 case ParsedDataType::Int: return "int "; break;
51 case ParsedDataType::Int2: return "ivec2 "; break;
52 case ParsedDataType::Int3: return "ivec3 "; break;
53 case ParsedDataType::Int4: return "ivec4 "; break;
54 case ParsedDataType::UInt: return "uint "; break;
55 case ParsedDataType::UInt2: return "uvec2 "; break;
56 case ParsedDataType::UInt3: return "uvec3 "; break;
57 case ParsedDataType::UInt4: return "uvec4 "; break;
58 default:
59 LOG_ERROR(logger, "Unsupported attribute type %d", int(type));
60 return "<illegal>";
61 break;
62 }
63 }
64
65 void addHeadersAndInterface(std::string &src) {
66 src.append(
67R"(#version 300 es
68precision highp float;
69precision highp int;
70precision highp usampler2D;
71
72in vec2 vs_NormalizedCoords;
73in vec2 vs_TexCoords;
74
75struct VertexIn
76{
77 vec2 NormalizedCoords;
78 vec2 TexCoords;
79};
80
81VertexIn importAttribs() {
82 VertexIn In;
83 In.NormalizedCoords = vs_NormalizedCoords;
84 In.TexCoords = vs_TexCoords;
85 return In;
86}
87
88)");
89 }
90
91 void addDefines(std::string& output,
92 const std::vector<std::pair<std::string, std::string>>& definitions)
93 {
94 for (const std::pair<std::string, std::string>& define : definitions) {
95 output.append("#define ");
96 output.append(define.first);
97 output.append(" ");
98 output.append(define.second);
99 output.append("\n");
100 }
101 }
102
103 void addUniforms(std::string& src, const std::vector<ProcessTaskProperty>& properties) {
104 std::string parameterUBOsrc;
105
106 bool hasParameters = false;
107 parameterUBOsrc.append("");
108 parameterUBOsrc.append("layout (std140) uniform EffectParameters {\n");
109
110 for (const ProcessTaskProperty& p : properties) {
111 switch (p.definition->type) {
112 case ParsedDataType::Float:
113 case ParsedDataType::Float2:
114 case ParsedDataType::Float3:
115 case ParsedDataType::Float4:
116 case ParsedDataType::Float4x4:
117 case ParsedDataType::Int:
118 case ParsedDataType::Int2:
119 case ParsedDataType::Int3:
120 case ParsedDataType::Int4:
121 case ParsedDataType::UInt:
122 case ParsedDataType::UInt2:
123 case ParsedDataType::UInt3:
124 case ParsedDataType::UInt4:
125 parameterUBOsrc.append(" ");
126 parameterUBOsrc.append(parameterType(p.definition->type));
127 parameterUBOsrc.append(" " + p.definition->key + ";\n");
128 hasParameters = true;
129 break;
130
131 case ParsedDataType::Texture2D:
132 {
133 if (p.definition->texture.samples > 1) {
134 LOG_ERROR(logger, "Sampling from multisampled textures is not allowed on this platform");
135 }
136 std::string_view sampler_prefix = "highp ";
137 switch (p.definition->texture.dataType) {
138 case ParsedDataType::Int:
139 case ParsedDataType::Int2:
140 case ParsedDataType::Int3:
141 case ParsedDataType::Int4:
142 sampler_prefix = "i";
143 break;
144 case ParsedDataType::UInt:
145 case ParsedDataType::UInt2:
146 case ParsedDataType::UInt3:
147 case ParsedDataType::UInt4:
148 sampler_prefix = "u";
149 break;
150 default:
151 break;
152 }
153 src.append("uniform ");
154 src.append(sampler_prefix);
155 src.append("sampler2D " + p.definition->key + "; \n");
156 }
157 break;
158 case ParsedDataType::Buffer:
159 LOG_ERROR(logger, "Render buffers not supported on this platform");
160 break;
161 case ParsedDataType::ConstantBuffer:
162 if (p.definition->key == "SceneBuffer") {
163 src.append("#define COGS_SCENEBUFFER_REFERENCED 1\n");
164 src.append("#define COGS_VIEWGETTERS_REFERENCED 1\n");
165 } else if (p.definition->value.starts_with("Cogs.")) {
166 std::string bufferName = p.definition->value.substr(5);
167 src.append("#include \"Engine/" + bufferName + ".es30.glsl\"\n");
168 }
169 break;
170 default:
171 break;
172 }
173 }
174 parameterUBOsrc.append("};\n");
175 if (hasParameters) {
176 src.append(parameterUBOsrc);
177 }
178 }
179
180 void addOutputStruct(std::string& src, const PipelineOptions& options) {
181 bool hasCustomTargets = false;
182 std::string prefix = "target_";
183 for (auto o : options) {
184 auto key = o.first;
185
186 for (auto& c : key) {
187 c = static_cast<decltype(key)::value_type>(std::tolower(c));
188 }
189 if (key.substr(0, prefix.size()) == prefix) {
190 if (!hasCustomTargets) {
191 hasCustomTargets = true;
192 src.append("struct FragmentOut {\n");
193 }
194 TokenStream tokens;
195 Cogs::Core::split(o.second, " ", tokens);
196 if (tokens.size() != 2) {
197 LOG_ERROR(logger, "Unable to parse pipeline output option %s", o.second.c_str());
198 continue;
199 }
200 std::string_view datatype = "Undefined";
201 switch (tokens[1].hashLowercase()) {
202 case Cogs::hash("float"):
203 datatype = parameterType(ParsedDataType::Float);
204 break;
205 case Cogs::hash("float2"):
206 datatype = parameterType(ParsedDataType::Float2);
207 break;
208 case Cogs::hash("float3"):
209 datatype = parameterType(ParsedDataType::Float3);
210 break;
211 case Cogs::hash("float4"):
212 datatype = parameterType(ParsedDataType::Float4);
213 break;
214 case Cogs::hash("uint"):
215 datatype = parameterType(ParsedDataType::UInt);
216 break;
217 case Cogs::hash("uint2"):
218 datatype = parameterType(ParsedDataType::UInt2);
219 break;
220 case Cogs::hash("uint3"):
221 datatype = parameterType(ParsedDataType::UInt3);
222 break;
223 case Cogs::hash("uint4"):
224 datatype = parameterType(ParsedDataType::UInt4);
225 break;
226 case Cogs::hash("int"):
227 datatype = parameterType(ParsedDataType::Int);
228 break;
229 case Cogs::hash("int2"):
230 datatype = parameterType(ParsedDataType::Int2);
231 break;
232 case Cogs::hash("int3"):
233 datatype = parameterType(ParsedDataType::Int3);
234 break;
235 case Cogs::hash("int4"):
236 datatype = parameterType(ParsedDataType::Int4);
237 break;
238 }
239 src.append(datatype);
240 src.append(" ");
241 src.append(tokens[0]);
242 src.append(";\n");
243 }
244 }
245 if (hasCustomTargets) {
246 src.append("};\n\n");
247 }
248 }
249
250 void addOutputs(std::string& src, const PipelineOptions& options) {
251 bool defaultTarget = true;
252 std::string prefix = "target_";
253 std::string outputStruct;
254 std::string outputExport;
255 outputStruct = "struct FragmentOut {\n";
256 outputExport = "void exportOutput(FragmentOut Out) {\n";
257 for (auto o : options) {
258 auto key = o.first;
259
260 for (auto& c : key) {
261 c = static_cast<decltype(key)::value_type>(std::tolower(c));
262 }
263 if (key.substr(0, prefix.size()) == prefix) {
264 defaultTarget = false;
265 int location = std::stoi(key.substr(prefix.size()));
266 TokenStream tokens;
267 Cogs::Core::split(o.second, " ", tokens);
268 if (tokens.size() != 2) {
269 // Log error
270 continue;
271 }
272 std::string_view datatype = "Undefined";
273 switch (tokens[1].hashLowercase()) {
274 case Cogs::hash("float"):
275 datatype = parameterType(ParsedDataType::Float);
276 break;
277 case Cogs::hash("float2"):
278 datatype = parameterType(ParsedDataType::Float2);
279 break;
280 case Cogs::hash("float3"):
281 datatype = parameterType(ParsedDataType::Float3);
282 break;
283 case Cogs::hash("float4"):
284 datatype = parameterType(ParsedDataType::Float4);
285 break;
286 case Cogs::hash("uint"):
287 datatype = parameterType(ParsedDataType::UInt);
288 break;
289 case Cogs::hash("uint2"):
290 datatype = parameterType(ParsedDataType::UInt2);
291 break;
292 case Cogs::hash("uint3"):
293 datatype = parameterType(ParsedDataType::UInt3);
294 break;
295 case Cogs::hash("uint4"):
296 datatype = parameterType(ParsedDataType::UInt4);
297 break;
298 case Cogs::hash("int"):
299 datatype = parameterType(ParsedDataType::Int);
300 break;
301 case Cogs::hash("int2"):
302 datatype = parameterType(ParsedDataType::Int2);
303 break;
304 case Cogs::hash("int3"):
305 datatype = parameterType(ParsedDataType::Int3);
306 break;
307 case Cogs::hash("int4"):
308 datatype = parameterType(ParsedDataType::Int4);
309 break;
310 }
311 src.append("layout(location = " + std::to_string(location) + ") out ");
312 src.append(datatype);
313 src.append(" g_");
314 src.append(tokens[0]);
315 src.append(";\n");
316 outputStruct.append(" ").append(datatype).append(" ").append(tokens[0]).append(";\n");
317 outputExport.append(" g_").append(tokens[0]).append(" = Out.").append(tokens[0]).append(";\n");
318 }
319 }
320 if (defaultTarget) {
321 src.append("layout(location=0) out vec4 g_fragColor;\n");
322 outputStruct.append(" vec4 fragColor;\n");
323 outputExport.append(" g_fragColor = Out.fragColor;\n");
324 }
325 outputStruct.append("};\n");
326 outputExport.append("}\n");
327 src.append(outputStruct + "\n");
328 src.append(outputExport + "\n");
329 }
330
331}
332
333
334namespace Cogs
335{
336 namespace Core {
337
338 bool buildPostProcessEffectES3(RenderTaskContext* context,
339 EffectDescription& desc, PostProcessTask *task) {
340 std::string src;
341 addHeadersAndInterface(src);
342 addDefines(src, desc.definitions);
343 addUniforms(src, task->properties);
344 addOutputs(src, task->options);
345 src.append("#include \"Engine/Common.es30.glsl\"\n\n");
346 std::string prefix_ps = desc.ps;
347 removeSuffix(prefix_ps, ".hlsl");
348
349 src.append("#include \"" + prefix_ps + ".es30.glsl\"\n");
350 auto code = hash(src);
351 std::string file_prefix = prefix_ps + "_" + std::to_string(code);
352
354 pp.processed.reserve(::InitialBufferCapasity);
355 if (!pp.process(context->context, src)) return false;
356 src.swap(pp.processed);
357
358 src.append(R"(
359void main()
360{
361 VertexIn In = importAttribs();
362 FragmentOut Out = effectMain(In);
363 exportOutput(Out);
364}
365)");
366
367 context->context->resourceStore->addResource(file_prefix + ".es30.glsl", src);
368 desc.ps = file_prefix + ".hlsl";
369 return true;
370 }
371 }
372}
std::unique_ptr< class ResourceStore > resourceStore
ResourceStore service instance.
Definition: Context.h:210
Log implementation class.
Definition: LogManager.h:140
Contains the Engine, Renderer, resource managers and other systems needed to run Cogs....
constexpr Log getLogger(const char(&name)[LEN]) noexcept
Definition: LogManager.h:181
Contains all Cogs related functionality.
Definition: FieldSetter.h:23
constexpr size_t hash() noexcept
Simple getter function that returns the initial value for fnv1a hashing.
Definition: HashFunctions.h:62
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.
Partial C preprocessor.
Definition: Preprocessor.h:42
bool process(Context *context, const StringView input)
Run a text block through the preprocessor.
std::string processed
Resulting processed text.
Definition: Preprocessor.h:48