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