1 /*
2 * This program is free software; you can redistribute it and/or
3 * modify it under the terms of the GNU General Public License
4 * as published by the Free Software Foundation; either version 2
5 * of the License, or (at your option) any later version.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU General Public License
13 * along with this program; if not, write to the Free Software Foundation,
14 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15 *
16 * The Original Code is Copyright (C) 2016 by Mike Erwin.
17 * All rights reserved.
18 */
19
20 /** \file
21 * \ingroup gpu
22 *
23 * GPU shader interface (C --> GLSL)
24 *
25 * Structure detailing needed vertex inputs and resources for a specific shader.
26 * A shader interface can be shared between two similar shaders.
27 */
28
29 #pragma once
30
31 #include <cstring> /* required for STREQ later on. */
32
33 #include "BLI_hash.h"
34 #include "BLI_utildefines.h"
35
36 #include "GPU_shader.h"
37
38 namespace blender::gpu {
39
40 typedef struct ShaderInput {
41 uint32_t name_offset;
42 uint32_t name_hash;
43 int32_t location;
44 /** Defined at interface creation or in shader. Only for Samplers, UBOs and Vertex Attribs. */
45 int32_t binding;
46 } ShaderInput;
47
48 /**
49 * Implementation of Shader interface.
50 * Base class which is then specialized for each implementation (GL, VK, ...).
51 **/
52 class ShaderInterface {
53 /* TODO(fclem): should be protected. */
54 public:
55 /** Flat array. In this order: Attributes, Ubos, Uniforms. */
56 ShaderInput *inputs_ = NULL;
57 /** Buffer containing all inputs names separated by '\0'. */
58 char *name_buffer_ = NULL;
59 /** Input counts inside input array. */
60 uint attr_len_ = 0;
61 uint ubo_len_ = 0;
62 uint uniform_len_ = 0;
63 /** Enabled bindpoints that needs to be fed with data. */
64 uint16_t enabled_attr_mask_ = 0;
65 uint16_t enabled_ubo_mask_ = 0;
66 uint8_t enabled_ima_mask_ = 0;
67 uint64_t enabled_tex_mask_ = 0;
68 /** Location of builtin uniforms. Fast access, no lookup needed. */
69 int32_t builtins_[GPU_NUM_UNIFORMS];
70 int32_t builtin_blocks_[GPU_NUM_UNIFORM_BLOCKS];
71
72 public:
73 ShaderInterface();
74 virtual ~ShaderInterface();
75
76 void debug_print(void);
77
attr_get(const char * name) const78 inline const ShaderInput *attr_get(const char *name) const
79 {
80 return input_lookup(inputs_, attr_len_, name);
81 }
82
ubo_get(const char * name) const83 inline const ShaderInput *ubo_get(const char *name) const
84 {
85 return input_lookup(inputs_ + attr_len_, ubo_len_, name);
86 }
ubo_get(const int binding) const87 inline const ShaderInput *ubo_get(const int binding) const
88 {
89 return input_lookup(inputs_ + attr_len_, ubo_len_, binding);
90 }
91
uniform_get(const char * name) const92 inline const ShaderInput *uniform_get(const char *name) const
93 {
94 return input_lookup(inputs_ + attr_len_ + ubo_len_, uniform_len_, name);
95 }
96
texture_get(const int binding) const97 inline const ShaderInput *texture_get(const int binding) const
98 {
99 return input_lookup(inputs_ + attr_len_ + ubo_len_, uniform_len_, binding);
100 }
101
input_name_get(const ShaderInput * input) const102 inline const char *input_name_get(const ShaderInput *input) const
103 {
104 return name_buffer_ + input->name_offset;
105 }
106
107 /* Returns uniform location. */
uniform_builtin(const GPUUniformBuiltin builtin) const108 inline int32_t uniform_builtin(const GPUUniformBuiltin builtin) const
109 {
110 BLI_assert(builtin >= 0 && builtin < GPU_NUM_UNIFORMS);
111 return builtins_[builtin];
112 }
113
114 /* Returns binding position. */
ubo_builtin(const GPUUniformBlockBuiltin builtin) const115 inline int32_t ubo_builtin(const GPUUniformBlockBuiltin builtin) const
116 {
117 BLI_assert(builtin >= 0 && builtin < GPU_NUM_UNIFORM_BLOCKS);
118 return builtin_blocks_[builtin];
119 }
120
121 protected:
122 static inline const char *builtin_uniform_name(GPUUniformBuiltin u);
123 static inline const char *builtin_uniform_block_name(GPUUniformBlockBuiltin u);
124
125 inline uint32_t set_input_name(ShaderInput *input, char *name, uint32_t name_len) const;
126
127 /* Finalize interface construction by sorting the ShaderInputs for faster lookups. */
128 void sort_inputs(void);
129
130 private:
131 inline const ShaderInput *input_lookup(const ShaderInput *const inputs,
132 const uint inputs_len,
133 const char *name) const;
134
135 inline const ShaderInput *input_lookup(const ShaderInput *const inputs,
136 const uint inputs_len,
137 const int binding) const;
138 };
139
builtin_uniform_name(GPUUniformBuiltin u)140 inline const char *ShaderInterface::builtin_uniform_name(GPUUniformBuiltin u)
141 {
142 switch (u) {
143 case GPU_UNIFORM_MODEL:
144 return "ModelMatrix";
145 case GPU_UNIFORM_VIEW:
146 return "ViewMatrix";
147 case GPU_UNIFORM_MODELVIEW:
148 return "ModelViewMatrix";
149 case GPU_UNIFORM_PROJECTION:
150 return "ProjectionMatrix";
151 case GPU_UNIFORM_VIEWPROJECTION:
152 return "ViewProjectionMatrix";
153 case GPU_UNIFORM_MVP:
154 return "ModelViewProjectionMatrix";
155
156 case GPU_UNIFORM_MODEL_INV:
157 return "ModelMatrixInverse";
158 case GPU_UNIFORM_VIEW_INV:
159 return "ViewMatrixInverse";
160 case GPU_UNIFORM_MODELVIEW_INV:
161 return "ModelViewMatrixInverse";
162 case GPU_UNIFORM_PROJECTION_INV:
163 return "ProjectionMatrixInverse";
164 case GPU_UNIFORM_VIEWPROJECTION_INV:
165 return "ViewProjectionMatrixInverse";
166
167 case GPU_UNIFORM_NORMAL:
168 return "NormalMatrix";
169 case GPU_UNIFORM_ORCO:
170 return "OrcoTexCoFactors";
171 case GPU_UNIFORM_CLIPPLANES:
172 return "WorldClipPlanes";
173
174 case GPU_UNIFORM_COLOR:
175 return "color";
176 case GPU_UNIFORM_BASE_INSTANCE:
177 return "baseInstance";
178 case GPU_UNIFORM_RESOURCE_CHUNK:
179 return "resourceChunk";
180 case GPU_UNIFORM_RESOURCE_ID:
181 return "resourceId";
182 case GPU_UNIFORM_SRGB_TRANSFORM:
183 return "srgbTarget";
184
185 default:
186 return NULL;
187 }
188 }
189
builtin_uniform_block_name(GPUUniformBlockBuiltin u)190 inline const char *ShaderInterface::builtin_uniform_block_name(GPUUniformBlockBuiltin u)
191 {
192 switch (u) {
193 case GPU_UNIFORM_BLOCK_VIEW:
194 return "viewBlock";
195 case GPU_UNIFORM_BLOCK_MODEL:
196 return "modelBlock";
197 case GPU_UNIFORM_BLOCK_INFO:
198 return "infoBlock";
199 default:
200 return NULL;
201 }
202 }
203
204 /* Returns string length including '\0' terminator. */
set_input_name(ShaderInput * input,char * name,uint32_t name_len) const205 inline uint32_t ShaderInterface::set_input_name(ShaderInput *input,
206 char *name,
207 uint32_t name_len) const
208 {
209 /* remove "[0]" from array name */
210 if (name[name_len - 1] == ']') {
211 name[name_len - 3] = '\0';
212 name_len -= 3;
213 }
214
215 input->name_offset = (uint32_t)(name - name_buffer_);
216 input->name_hash = BLI_hash_string(name);
217 return name_len + 1; /* include NULL terminator */
218 }
219
input_lookup(const ShaderInput * const inputs,const uint inputs_len,const char * name) const220 inline const ShaderInput *ShaderInterface::input_lookup(const ShaderInput *const inputs,
221 const uint inputs_len,
222 const char *name) const
223 {
224 const uint name_hash = BLI_hash_string(name);
225 /* Simple linear search for now. */
226 for (int i = inputs_len - 1; i >= 0; i--) {
227 if (inputs[i].name_hash == name_hash) {
228 if ((i > 0) && UNLIKELY(inputs[i - 1].name_hash == name_hash)) {
229 /* Hash colision resolve. */
230 for (; i >= 0 && inputs[i].name_hash == name_hash; i--) {
231 if (STREQ(name, name_buffer_ + inputs[i].name_offset)) {
232 return inputs + i; /* not found */
233 }
234 }
235 return NULL; /* not found */
236 }
237
238 /* This is a bit dangerous since we could have a hash collision.
239 * where the asked uniform that does not exist has the same hash
240 * as a real uniform. */
241 BLI_assert(STREQ(name, name_buffer_ + inputs[i].name_offset));
242 return inputs + i;
243 }
244 }
245 return NULL; /* not found */
246 }
247
input_lookup(const ShaderInput * const inputs,const uint inputs_len,const int binding) const248 inline const ShaderInput *ShaderInterface::input_lookup(const ShaderInput *const inputs,
249 const uint inputs_len,
250 const int binding) const
251 {
252 /* Simple linear search for now. */
253 for (int i = inputs_len - 1; i >= 0; i--) {
254 if (inputs[i].binding == binding) {
255 return inputs + i;
256 }
257 }
258 return NULL; /* not found */
259 }
260
261 } // namespace blender::gpu
262