-
Notifications
You must be signed in to change notification settings - Fork 14
/
Copy pathprogram.h
349 lines (313 loc) Β· 11.8 KB
/
program.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
// Copyright (C) 2020-2021 Sami VΓ€isΓ€nen
// Copyright (C) 2020-2021 Ensisoft http://www.ensisoft.com
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.
#pragma once
#include "config.h"
#include "warnpush.h"
# include <glm/vec2.hpp>
# include <glm/vec3.hpp>
# include <glm/vec4.hpp>
# include <glm/mat4x4.hpp>
# include <glm/mat3x3.hpp>
# include <glm/mat2x2.hpp>
#include "warnpop.h"
#include <cstring>
#include <vector>
#include <variant>
#include <memory>
#include <string>
#include "device/uniform.h"
#include "graphics/color4f.h"
#include "graphics/uniform.h"
namespace gfx
{
class Shader;
class Texture;
class ProgramState
{
public:
struct Sampler {
std::string name;
unsigned unit = 0;
const Texture* texture = nullptr;
};
using Uniform = dev::Uniform;
template<typename T>
inline void SetUniformBlock(std::string name, UniformBlockData<T>&& uniform_data)
{
UniformBlock block(std::move(name, std::move(uniform_data)));
mUniformBlocks.push_back(std::move(block));
}
template<typename T>
inline void SetUniformBlock(std::string name, const UniformBlockData<T>& uniform_data)
{
UniformBlock block(std::move(name, std::move(uniform_data)));
mUniformBlocks.push_back(block);
}
inline void SetUniformBlock(UniformBlock&& block)
{
mUniformBlocks.push_back(std::move(block));
}
inline void SetUniformBlock(const UniformBlock& block)
{
mUniformBlocks.push_back(block);
}
inline void SetUniform(const char* name, unsigned x)
{
mUniforms.push_back({ name, x, });
}
inline void SetUniform(const char* name, unsigned x, unsigned y)
{
mUniforms.push_back({ name, glm::uvec2{ x, y} });
}
inline void SetUniform(const char* name, unsigned x, unsigned y, unsigned z)
{
mUniforms.push_back({ name, glm::uvec3{ x, y, z} });
}
inline void SetUniform(const char* name, unsigned x, unsigned y, unsigned z, unsigned w)
{
mUniforms.push_back({ name, glm::uvec4{ x, y, z, w} });
}
// Set scalar uniform.
inline void SetUniform(const char* name, int x)
{
mUniforms.push_back({ name, x });
}
inline void SetUniform(const char* name, int x, int y)
{
mUniforms.push_back({ name, glm::ivec2{x, y} });
}
inline void SetUniform(const char* name, int x, int y, int z)
{
mUniforms.push_back({ name, glm::ivec3{x, y, z} });
}
inline void SetUniform(const char* name, int x, int y, int z, int w)
{
mUniforms.push_back({ name, glm::ivec4{x, y, z, w} });
}
// Set scalar uniform.
inline void SetUniform(const char* name, float x)
{
mUniforms.push_back({ name, x });
}
// Set vec2 uniform.
inline void SetUniform(const char* name, float x, float y)
{
mUniforms.push_back({ name, glm::vec2{x, y} });
}
// Set vec3 uniform.
inline void SetUniform(const char* name, float x, float y, float z)
{
mUniforms.push_back({ name, glm::vec3{x, y, z} });
}
// Set vec4 uniform.
inline void SetUniform(const char* name, float x, float y, float z, float w)
{
mUniforms.push_back({ name, glm::vec4{x, y, z, w} });
}
// set Color uniform
inline void SetUniform(const char* name, const Color4f& color)
{
mUniforms.push_back({name, color});
}
inline void SetUniform(const char* name, const glm::vec2& vector)
{
mUniforms.push_back({name, vector});
}
inline void SetUniform(const char* name, const glm::vec3& vector)
{
mUniforms.push_back({name, vector});
}
inline void SetUniform(const char* name, const glm::vec4& vector)
{
mUniforms.push_back({name, vector});
}
// Matrix memory layout is as follows:
// {xx xy xz}
// {yx yy yz}
// {zx zy zz}
// or {{xx xy xz}, {yx yy yz}, {zx zy zz}}
// In other words the first 3 floats are the X vector followed by the
// Y vector followed by the Z vector.
// set 2x2 matrix uniform.
inline void SetUniform(const char* name, const glm::mat2& matrix)
{
mUniforms.push_back({name, matrix });
}
// Set 3x3 matrix uniform.
inline void SetUniform(const char* name, const glm::mat3& matrix)
{
mUniforms.push_back({name, matrix });
}
// Set 4x4 matrix uniform.
inline void SetUniform(const char* name, const glm::mat4& matrix)
{
mUniforms.push_back({name, matrix });
}
// Set a texture sampler.
// Sampler is the name of the texture sampler in the shader.
// It's possible to sample multiple textures in the program by setting each
// texture to a different texture unit.
void SetTexture(const char* sampler, unsigned unit, const Texture& texture)
{
// in OpenGL the expected memory layout of texture data that
// is given to glTexImage2D doesn't match the "typical" layout
// that is used by many toolkits/libraries. The order of scan lines
// is reversed and glTexImage expects the first scanline (in memory)
// to be the bottom scanline of the image.
// There are several ways to deal with this dilemma:
// - flip all images on the horizontal axis before calling glTexImage2D.
// - use a matrix to transform the texture coordinates to counter this.
// - flip texture coordinates and assume that Y=0.0f means the top of
// the texture (first scan row) and Y=1.0f means the bottom of the
// texture (last scan row).
//
//
// this comment and the below kDeviceTextureMatrix is here only for posterity.
// Currently, we have flipped the texture coordinates like explained above.
//
// If the program being used to render stuff is using a texture
// we helpfully set up here a "device texture matrix" that will be provided
// for the program and should be used to transform the texture coordinates
// before sampling textures.
// This will avoid having to do any image flips which is especially
// handy when dealing with data that gets often (re)uploaded, e.g.
// dynamically changing/procedural texture data.
//
// It should also be possible to use the device texture matrix for example
// in cases where the device would bake some often used textures into an atlas
// and implicitly alter the texture coordinates.
//
/* no longer used.
static const float kDeviceTextureMatrix[3][3] = {
{1.0f, 0.0f, 0.0f},
{0.0f, -1.0f, 0.0f},
{0.0f, 1.0f, 0.0f}
};
SetUniform("kDeviceTextureMatrix", kDeviceTextureMatrix);
*/
if (unit >= mSamplers.size())
mSamplers.resize(unit + 1);
mSamplers[unit].texture = &texture;
mSamplers[unit].name = sampler;
mSamplers[unit].unit = unit;
}
// Set the number of textures used by the next draw.
// Todo: this api and the SetTexture are potentially bug prone, it'd be better to
// combine both into a single api call that takes the whole array of textures.
inline void SetTextureCount(unsigned count)
{
mSamplers.resize(count);
}
inline auto GetTextureCount() const noexcept
{
return mSamplers.size();
}
inline size_t GetUniformCount() const noexcept
{
return mUniforms.size();
}
inline size_t GetSamplerCount() const noexcept
{
return mSamplers.size();
}
inline size_t GetUniformBlockCount() const noexcept
{
return mUniformBlocks.size();
}
inline const UniformBlock& GetUniformBlock(size_t index) const noexcept
{
return mUniformBlocks[index];
}
inline const Sampler& GetSamplerSetting(size_t index) const noexcept
{
return mSamplers[index];
}
inline const Uniform& GetUniformSetting(size_t index) const noexcept
{
return mUniforms[index];
}
template<typename T>
bool GetUniform(const char* name, T* value) noexcept
{
for (const auto& u : mUniforms)
{
if (u.name != name)
continue;
ASSERT(std::holds_alternative<T>(u.value));
*value = std::get<T>(u.value);
return true;
}
return false;
}
inline bool HasUniform(const char* name) noexcept
{
for (const auto& u : mUniforms)
{
if (u.name == name)
return true;
}
return false;
}
inline void Clear() noexcept
{
mUniforms.clear();
mSamplers.clear();
mUniformBlocks.clear();
}
const Sampler* FindTextureBinding(const char* name) const
{
for (const auto& s : mSamplers)
{
if (s.name == name)
return &s;
}
return nullptr;
}
private:
std::vector<Sampler> mSamplers;
std::vector<Uniform> mUniforms;
std::vector<UniformBlock> mUniformBlocks;
};
// Program object interface. Program objects are device
// specific graphics programs that are built from shaders
// and then uploaded and executed on the device.
class Program
{
public:
struct CreateArgs {
// The program state that is applied initially on the program
// once when created. Note that this only applies to uniforms!
ProgramState state;
// The program human-readable debug name
std::string name;
// mandatory fragment shader. Must be valid
std::shared_ptr<const Shader> fragment_shader;
// mandatory vertex shader. Must be valid.
std::shared_ptr<const Shader> vertex_shader;
};
virtual ~Program() = default;
// Returns true if the program is valid or not I.e. it has been
// successfully build and can be used for drawing.
virtual bool IsValid() const = 0;
// Get the human readable (debug) program name.
virtual std::string GetName() const = 0;
// Get the program GPU resource ID that was used when program
// was first created.
virtual std::string GetId() const = 0;
private:
};
using ProgramPtr = std::shared_ptr<const Program>;
} //