Loading...
Searching...
No Matches
ShaderProgram.hpp
1#pragma once
2#include <Gfx/Graph/RenderState.hpp>
3#include <Gfx/Hashes.hpp>
4
5#include <score/tools/Debug.hpp>
6
7#include <ossia/detail/hash.hpp>
8#include <ossia/detail/hash_map.hpp>
9
10#include <QDebug>
11#include <QObject>
12#include <QString>
13#include <QtGui/private/qshader_p.h>
14
15#include <isf.hpp>
16#include <score_plugin_gfx_export.h>
17
18#include <array>
19#include <optional>
20#include <verdigris>
21namespace isf
22{
23struct descriptor;
24}
25
26namespace Gfx
27{
28struct SCORE_PLUGIN_GFX_EXPORT ShaderSource
29{
30 using ProgramType = isf::parser::ShaderType;
31 ShaderSource() = default;
32 ~ShaderSource() = default;
33 ShaderSource(const ShaderSource&) = default;
34 ShaderSource(ShaderSource&&) = default;
35
36 ShaderSource(const QString& vert, const QString& frag)
37 : type{isf::parser::ShaderType::ISF}
38 , vertex{vert}
39 , fragment{frag}
40 {
41 }
42
43 ShaderSource(const std::vector<QString>& vec)
44 {
45 SCORE_ASSERT(vec.size() == 2);
46 type = isf::parser::ShaderType::ISF;
47 fragment = vec[0];
48 vertex = vec[1];
49 }
50 ShaderSource(ProgramType tp, const QString& vert, const QString& frag)
51 : type{tp}
52 , vertex{vert}
53 , fragment{frag}
54 {
55 }
56
57 ShaderSource(ProgramType tp, const std::vector<QString>& vec)
58 {
59 SCORE_ASSERT(vec.size() == 2);
60 type = tp;
61 fragment = vec[0];
62 vertex = vec[1];
63 }
64
65 ShaderSource(ProgramType tp, std::vector<QString>&& vec)
66 {
67 SCORE_ASSERT(vec.size() == 2);
68 type = tp;
69 fragment = std::move(vec[0]);
70 vertex = std::move(vec[1]);
71 }
72
73 ShaderSource& operator=(const ShaderSource&) = default;
74 ShaderSource& operator=(ShaderSource&&) = default;
75
76 ProgramType type{};
77 QString vertex;
78 QString fragment;
79
81 {
82 const QString name;
83 const QString ShaderSource::*pointer;
84 const std::string_view language{};
85 };
86
87 static const inline std::array<MemberSpec, 2> specification{
88 MemberSpec{QObject::tr("Fragment"), &ShaderSource::fragment, "GLSL"},
89 MemberSpec{QObject::tr("Vertex"), &ShaderSource::vertex, "GLSL"},
90 };
91
92 friend QDebug& operator<<(QDebug& d, const ShaderSource& sp)
93 {
94 return (d << sp.vertex << sp.fragment);
95 }
96 friend bool operator==(const ShaderSource& lhs, const ShaderSource& rhs) noexcept
97 {
98 // `type` MUST be part of equality: std::hash<ShaderSource> seeds with
99 // `type`, so two sources differing only by type hash differently. If ==
100 // ignored type they'd be "equal but unequal-hash", breaking the
101 // unordered-container invariant for ProgramCache / ProgramCacheKey.
102 return lhs.type == rhs.type && lhs.vertex == rhs.vertex
103 && lhs.fragment == rhs.fragment;
104 }
105 friend bool operator!=(const ShaderSource& lhs, const ShaderSource& rhs) noexcept
106 {
107 return !(lhs == rhs);
108 }
109
110 friend bool
111 operator==(const std::vector<QString>& lhs, const ShaderSource& rhs) noexcept
112 {
113 SCORE_ASSERT(lhs.size() == 2);
114 return lhs[0] == rhs.*(ShaderSource::specification[0].pointer)
115 && lhs[1] == rhs.*(ShaderSource::specification[1].pointer);
116 }
117 friend bool
118 operator!=(const std::vector<QString>& lhs, const ShaderSource& rhs) noexcept
119 {
120 return !(lhs == rhs);
121 }
122};
123
124SCORE_PLUGIN_GFX_EXPORT ShaderSource
125programFromISFFragmentShaderPath(
126 const QString& fsFilename, QByteArray fsData,
127 ShaderSource::ProgramType type = ShaderSource::ProgramType::ISF);
128SCORE_PLUGIN_GFX_EXPORT ShaderSource
129programFromVSAVertexShaderPath(const QString& vertexFilename, QByteArray vertexData);
130
131// Textual `#include` resolution for a single GLSL buffer. Used by
132// callers that want include support without going through the full
133// ProgramCache ISF pipeline — compute shaders are the current use case.
134// Returns the expanded source and a non-empty error string on failure
135// (missing header, include cycle, depth limit, …). The returned
136// QByteArray is empty iff the error is non-empty.
137SCORE_PLUGIN_GFX_EXPORT
138std::pair<QByteArray, QString>
139preprocessShaderIncludes(QByteArray source, const QString& originPath = {}) noexcept;
140}
141
142namespace std
143{
144template <>
145struct hash<Gfx::ShaderSource>
146{
147 std::size_t operator()(const Gfx::ShaderSource& program) const noexcept
148 {
149 // rapidhash via the gfx Qt-aware adapters; same primitive that
150 // produces content_hash values throughout the gfx pipeline.
151 std::size_t seed{(std::size_t)program.type};
152 ossia::hash_combine(seed, score::gfx::hash_qstring(program.vertex));
153 ossia::hash_combine(seed, score::gfx::hash_qstring(program.fragment));
154 return seed;
155 }
156};
157}
158
159namespace Gfx
160{
162{
163 isf::descriptor descriptor;
164};
165
166// Cache key. `originDir` is the *canonical directory* the shader was
167// loaded from (derived by the cache from the caller-supplied origin
168// path). Keying on both means two models loading the same source text
169// from different directories don't collide — include resolution against
170// each shader's own sibling dir stays correct.
172{
173 ShaderSource source;
174 QString originDir;
175
176 friend bool
177 operator==(const ProgramCacheKey& a, const ProgramCacheKey& b) noexcept
178 {
179 return a.source == b.source && a.originDir == b.originDir;
180 }
181};
182}
183
184namespace std
185{
186template <>
187struct hash<Gfx::ProgramCacheKey>
188{
189 std::size_t operator()(const Gfx::ProgramCacheKey& k) const noexcept
190 {
191 std::size_t seed = std::hash<Gfx::ShaderSource>{}(k.source);
192 ossia::hash_combine(seed, score::gfx::hash_qstring(k.originDir));
193 return seed;
194 }
195};
196}
197
198namespace Gfx
199{
200struct SCORE_PLUGIN_GFX_EXPORT ProgramCache
201{
202 static ProgramCache& instance() noexcept;
203
204 // `originPath` is the absolute path of the shader file the source was
205 // loaded from, used as the base for quoted `#include "..."` resolution
206 // and as part of the cache key. Empty when the source is in-memory
207 // with no associated file.
208 std::pair<std::optional<ProcessedProgram>, QString>
209 get(const ShaderSource& program, const QString& originPath = {}) noexcept;
210
211 ossia::hash_map<ProgramCacheKey, ProcessedProgram> programs;
212};
213
214}
215
216Q_DECLARE_METATYPE(Gfx::ShaderSource)
217W_REGISTER_ARGTYPE(Gfx::ShaderSource)
218Q_DECLARE_METATYPE(Gfx::ProcessedProgram)
219W_REGISTER_ARGTYPE(Gfx::ProcessedProgram)
Binds the rendering pipeline to ossia processes.
Definition AssetTable.cpp:7
STL namespace.
Definition ShaderProgram.hpp:162
Definition ShaderProgram.hpp:201
Definition ShaderProgram.hpp:172
Definition ShaderProgram.hpp:81
Definition ShaderProgram.hpp:29