• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2024 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "gles/gpu_program_gles.h"
17 
18 #include <algorithm>
19 
20 #include <base/containers/fixed_string.h>
21 #include <base/containers/unordered_map.h>
22 
23 #include "device/gpu_program_util.h"
24 #include "gles/device_gles.h"
25 #include "gles/gl_functions.h"
26 #include "gles/shader_module_gles.h"
27 #include "util/log.h"
28 #include "util/string_util.h"
29 
30 using namespace BASE_NS;
31 
32 RENDER_BEGIN_NAMESPACE()
33 namespace {
34 // although max set is 4...
35 #define BIND_MAP_4_4(a, b) (((a) << 4) | (b))
36 
37 // 16 samplers and 16 textures = 256 combinations
38 // this is actually not enugh. since there can be 32 sampled images (16 in vertex and 16 in fragment) + 32 samplers (16
39 // in vertex and 16 in fragment)
40 constexpr uint32_t MAX_FINAL_BIND_MAP_A { 16 };
41 constexpr uint32_t MAX_FINAL_BIND_MAP_B { 16 };
42 constexpr uint32_t MAX_FINAL_BINDS { MAX_FINAL_BIND_MAP_A * MAX_FINAL_BIND_MAP_B };
43 
44 struct BindMaps {
45     uint8_t maxImageBinding { 0 };   // next free imagetexture unit
46     uint8_t maxUniformBinding { 0 }; // next free uniform block binding
47     uint8_t maxStorageBinding { 0 }; // next free storege buffer binding
48 
49     uint8_t maxSamplerBinding { 0 }; // next sampler binding
50     uint8_t maxTextureBinding { 0 }; // next texture binding
51     uint8_t maxFinalBinding {
52         0
53     }; // "real" last combined sampler binding (last generated combination + maxCSamplerBinding)
54 
55     uint8_t map[Gles::ResourceLimits::MAX_BINDS] { 0 }; // mapping from set/binding -> "unit/binding"
56     uint8_t finalMap[MAX_FINAL_BINDS] { 0 }; // mapping from sampler/texture combination -> texture sampler unit
57     vector<Gles::PushConstantReflection> pushConstants;
58 };
59 
ProcessPushConstants(GLuint program,const ShaderModulePlatformDataGLES & plat,GLenum flag,BindMaps & map)60 void ProcessPushConstants(GLuint program, const ShaderModulePlatformDataGLES& plat, GLenum flag, BindMaps& map)
61 {
62     GLsizei len;
63     const GLenum uniform_props[] = { flag, GL_LOCATION };
64     GLint inUse[BASE_NS::countof(uniform_props)] { 0 };
65     for (auto& info : plat.infos) {
66         // check for duplicates.. (there can be dupes since vertex and fragment both can use the same constant..)
67         if (auto pos = std::find_if(map.pushConstants.begin(), map.pushConstants.end(),
68                 [&name = info.name](auto& info2) { return info2.name == name; });
69             pos != map.pushConstants.end()) {
70             pos->stage |= info.stage;
71         } else {
72             const GLuint index = glGetProgramResourceIndex(program, GL_UNIFORM, info.name.c_str());
73             if (index != GL_INVALID_INDEX) {
74                 glGetProgramResourceiv(program, GL_UNIFORM, index, (GLsizei)countof(uniform_props), uniform_props,
75                     (GLsizei)sizeof(inUse), &len, inUse);
76                 if (inUse[0]) {
77                     map.pushConstants.push_back(info);
78                     map.pushConstants.back().location = inUse[1];
79                 }
80             }
81         }
82     }
83 }
84 
ProcessStorageBlocks(GLuint program,const ShaderModulePlatformDataGLES & plat,GLenum flag,const BindMaps & map)85 void ProcessStorageBlocks(GLuint program, const ShaderModulePlatformDataGLES& plat, GLenum flag, const BindMaps& map)
86 {
87     GLsizei len;
88     const GLenum block_props[] = { flag, GL_BUFFER_BINDING };
89     GLint inUse[BASE_NS::countof(block_props)] { 0 };
90     for (const auto& t : plat.sbSets) {
91         PLUGIN_ASSERT(t.iSet < Gles::ResourceLimits::MAX_SETS);
92         PLUGIN_ASSERT(t.iBind < Gles::ResourceLimits::MAX_BIND_IN_SET);
93         const GLuint index = glGetProgramResourceIndex(program, GL_SHADER_STORAGE_BLOCK, t.name.c_str());
94         if (index != GL_INVALID_INDEX) {
95             glGetProgramResourceiv(program, GL_SHADER_STORAGE_BLOCK, index, (GLsizei)countof(block_props), block_props,
96                 (GLsizei)sizeof(inUse), &len, inUse);
97             if (inUse[0]) {
98                 const uint8_t fi = map.map[BIND_MAP_4_4(t.iSet, t.iBind)];
99                 PLUGIN_UNUSED(fi);
100                 PLUGIN_ASSERT(inUse[1] == (fi - 1));
101             }
102         }
103     }
104 }
105 
ProcessUniformBlocks(GLuint program,const ShaderModulePlatformDataGLES & plat,GLenum flag,BindMaps & map)106 void ProcessUniformBlocks(GLuint program, const ShaderModulePlatformDataGLES& plat, GLenum flag, BindMaps& map)
107 {
108     GLsizei len;
109     const GLenum block_props[] = { flag, GL_BUFFER_BINDING };
110     GLint inUse[BASE_NS::countof(block_props)] { 0 };
111     for (const auto& t : plat.ubSets) {
112         PLUGIN_ASSERT(t.iSet < Gles::ResourceLimits::MAX_SETS);
113         PLUGIN_ASSERT(t.iBind < Gles::ResourceLimits::MAX_BIND_IN_SET);
114         if (t.arrayElements > 1) {
115             GLint inUse2[BASE_NS::countof(block_props)] { 0 };
116             // need to handle arrays separately, (since each index is a separate resource..)
117             uint8_t& fi = map.map[BIND_MAP_4_4(t.iSet, t.iBind)];
118             fi = (uint8_t)(map.maxUniformBinding + 1);
119             string tmp;
120             for (uint32_t i = 0; i < t.arrayElements; i++) {
121                 const auto iStr = BASE_NS::to_string(i);
122                 tmp.reserve(t.name.size() + iStr.size() + 2U);
123                 tmp = t.name;
124                 tmp += '[';
125                 tmp += iStr;
126                 tmp += ']';
127                 const GLuint elementIndex = glGetProgramResourceIndex(program, GL_UNIFORM_BLOCK, tmp.c_str());
128                 glGetProgramResourceiv(program, GL_UNIFORM_BLOCK, elementIndex, (GLsizei)countof(block_props),
129                     block_props, (GLsizei)sizeof(inUse2), &len, inUse2);
130                 /*
131                  * we could do this optimization, but currently no way to signal backend that array element is not in
132                  * use.
133                  * if (inUse2[0]) {
134                  *     uint8_t& fi = map.uniformBindingSets.map[BIND_MAP_4_4(t.iSet, t.iBind)];
135                  *     fi = ++map.uniformBindingSets.maxBind;
136                  *     glUniformBlockBinding(program, i, (GLuint)(fi - 1));
137                  * } else {
138                  *     mark as unused. no need to bind.
139                  * }
140                  */
141                 glUniformBlockBinding(program, i, (GLuint)(fi - 1 + i));
142                 ++map.maxUniformBinding;
143             }
144         } else {
145             const GLuint index = glGetProgramResourceIndex(program, GL_UNIFORM_BLOCK, t.name.c_str());
146             if (index != GL_INVALID_INDEX) {
147                 glGetProgramResourceiv(program, GL_UNIFORM_BLOCK, index, (GLsizei)countof(block_props), block_props,
148                     (GLsizei)sizeof(inUse), &len, inUse);
149                 if (inUse[0]) {
150                     uint8_t& fi = map.map[BIND_MAP_4_4(t.iSet, t.iBind)];
151                     if (fi == 0) {
152                         fi = ++map.maxUniformBinding;
153                         glUniformBlockBinding(program, index, (GLuint)(fi - 1));
154                     }
155                 }
156             }
157         }
158     }
159 }
160 
ProcessImageTextures(GLuint program,const ShaderModulePlatformDataGLES & plat,GLenum flag,const BindMaps & map)161 void ProcessImageTextures(GLuint program, const ShaderModulePlatformDataGLES& plat, GLenum flag, const BindMaps& map)
162 {
163     GLsizei len;
164     const GLenum uniform_props[] = { flag, GL_LOCATION };
165     GLint inUse[BASE_NS::countof(uniform_props)] { 0 };
166     for (const auto& t : plat.ciSets) {
167         PLUGIN_ASSERT(t.iSet < Gles::ResourceLimits::MAX_SETS);
168         PLUGIN_ASSERT(t.iBind < Gles::ResourceLimits::MAX_BIND_IN_SET);
169         const GLuint index = glGetProgramResourceIndex(program, GL_UNIFORM, t.name.c_str());
170         if (index != GL_INVALID_INDEX) {
171             glGetProgramResourceiv(program, GL_UNIFORM, index, (GLsizei)countof(uniform_props), uniform_props,
172                 (GLsizei)sizeof(inUse), &len, inUse);
173             if (inUse[0]) {
174                 const uint8_t& fi = map.map[BIND_MAP_4_4(t.iSet, t.iBind)];
175                 GLint unit = 0;
176                 glGetUniformiv(program, inUse[1], &unit);
177                 PLUGIN_UNUSED(fi);
178                 PLUGIN_ASSERT(unit == (fi - 1));
179             }
180         }
181     }
182 }
183 
ProcessCombinedSamplers(GLuint program,const ShaderModulePlatformDataGLES & plat,GLenum flag,BindMaps & map)184 void ProcessCombinedSamplers(GLuint program, const ShaderModulePlatformDataGLES& plat, GLenum flag, BindMaps& map)
185 {
186     GLsizei len;
187     const GLenum props[] = { flag, GL_LOCATION };
188     GLint inUse[BASE_NS::countof(props)] { 0 };
189     for (const auto& t : plat.combSets) {
190         PLUGIN_ASSERT(t.iSet < Gles::ResourceLimits::MAX_SETS);
191         PLUGIN_ASSERT(t.iBind < Gles::ResourceLimits::MAX_BIND_IN_SET);
192         PLUGIN_ASSERT(t.sSet < Gles::ResourceLimits::MAX_SETS);
193         PLUGIN_ASSERT(t.sBind < Gles::ResourceLimits::MAX_BIND_IN_SET);
194         const GLuint index = glGetProgramResourceIndex(program, GL_UNIFORM, t.name.c_str());
195         if (index != GL_INVALID_INDEX) {
196             glGetProgramResourceiv(
197                 program, GL_UNIFORM, index, (GLsizei)countof(props), props, (GLsizei)sizeof(inUse), &len, inUse);
198             if (inUse[0]) {
199                 uint8_t& ii = map.map[BIND_MAP_4_4(t.iSet, t.iBind)];
200                 if (ii == 0) {
201                     ii = ++map.maxTextureBinding;
202                 }
203                 uint8_t& si = map.map[BIND_MAP_4_4(t.sSet, t.sBind)];
204                 if (si == 0) {
205                     si = ++map.maxSamplerBinding;
206                 }
207                 PLUGIN_ASSERT(si < MAX_FINAL_BIND_MAP_A);
208                 PLUGIN_ASSERT(ii < MAX_FINAL_BIND_MAP_B);
209                 uint8_t& fi = map.finalMap[BIND_MAP_4_4(si, ii)];
210                 if (fi == 0) {
211                     fi = ++map.maxFinalBinding;
212                     // assign texture unit for 'sampler'
213                     glProgramUniform1i(program, inUse[1], fi - 1);
214                 }
215             }
216         }
217     }
218 }
219 
ProcessSubPassInputs(GLuint program,const ShaderModulePlatformDataGLES & plat,GLenum flag,BindMaps & map)220 void ProcessSubPassInputs(GLuint program, const ShaderModulePlatformDataGLES& plat, GLenum flag, BindMaps& map)
221 {
222     GLsizei len;
223     const GLenum uniform_props[] = { flag, GL_LOCATION };
224     GLint inUse[BASE_NS::countof(uniform_props)] { 0 };
225     for (const auto& t : plat.siSets) {
226         PLUGIN_ASSERT(t.iSet < Gles::ResourceLimits::MAX_SETS);
227         PLUGIN_ASSERT(t.iBind < Gles::ResourceLimits::MAX_BIND_IN_SET);
228         const GLuint index = glGetProgramResourceIndex(program, GL_UNIFORM, t.name.c_str());
229         if (index != GL_INVALID_INDEX) {
230             glGetProgramResourceiv(program, GL_UNIFORM, index, (GLsizei)countof(uniform_props), uniform_props,
231                 (GLsizei)sizeof(inUse), &len, inUse);
232             if (inUse[0]) {
233                 uint8_t& fi = map.map[BIND_MAP_4_4(t.iSet, t.iBind)];
234                 if (fi == 0) {
235                     fi = ++map.maxFinalBinding;
236                     glProgramUniform1i(program, inUse[1], (fi - 1));
237                 }
238             }
239         }
240     }
241 }
242 
ProcessSamplers(GLuint program,const ShaderModulePlatformDataGLES & plat,GLenum flag,BindMaps & map)243 void ProcessSamplers(GLuint program, const ShaderModulePlatformDataGLES& plat, GLenum flag, BindMaps& map)
244 {
245     GLsizei len;
246     const GLenum uniform_props[] = { flag, GL_LOCATION, GL_ARRAY_SIZE };
247     GLint inUse[BASE_NS::countof(uniform_props)] { 0 };
248     for (const auto& t : plat.cbSets) {
249         PLUGIN_ASSERT(t.iSet < Gles::ResourceLimits::MAX_SETS);
250         PLUGIN_ASSERT(t.iBind < Gles::ResourceLimits::MAX_BIND_IN_SET);
251         const GLuint index = glGetProgramResourceIndex(program, GL_UNIFORM, t.name.c_str());
252         if (index != GL_INVALID_INDEX) {
253             glGetProgramResourceiv(program, GL_UNIFORM, index, (GLsizei)countof(uniform_props), uniform_props,
254                 (GLsizei)sizeof(inUse), &len, inUse);
255             if (inUse[0]) {
256                 uint8_t& fi = map.map[BIND_MAP_4_4(t.iSet, t.iBind)];
257                 if (fi == 0) {
258                     // Assign texture units to each "sampler".
259                     GLint units[Gles::ResourceLimits::MAX_SAMPLERS_IN_STAGE];
260                     fi = (uint8_t)(map.maxFinalBinding + 1);
261                     for (int i = 0; i < inUse[2]; i++) {
262                         // (we have NO WAY of knowing if the index is actually used..)
263                         units[i] = fi + i - 1;
264                     }
265                     map.maxFinalBinding += (uint8_t)inUse[2];
266                     glProgramUniform1iv(program, inUse[1], inUse[2], units);
267                 }
268             }
269         }
270     }
271 }
272 
ProcessProgram(GLuint program,const ShaderModulePlatformDataGLES & plat,GLenum flag,BindMaps & map)273 void ProcessProgram(GLuint program, const ShaderModulePlatformDataGLES& plat, GLenum flag, BindMaps& map)
274 {
275     // Collect used resources, and create mapping from set/bind(vulkan) to bind(gl)
276     // Process push constants..
277     ProcessPushConstants(program, plat, flag, map);
278     // Process shader storage blocks. (these have been bound in source, so just filter actually unused ones.)
279     ProcessStorageBlocks(program, plat, flag, map);
280     // Process "imagetexture":s (these have been bound in source, so just filter actually unused ones.)
281     ProcessImageTextures(program, plat, flag, map);
282 
283     // Process combined samplers (actual sampler2D etc.  allocates binds from combinedSamplers.finalBind)..
284     ProcessSamplers(program, plat, flag, map);
285 
286     // Process combined samplers (create map from image / sampler binds to single combined texture)..
287     ProcessCombinedSamplers(program, plat, flag, map);
288     // Process uniform blocks
289     ProcessUniformBlocks(program, plat, flag, map);
290     // Process sub-pass inputs (allocate bind from combinedSamplers.finalBind)..
291     ProcessSubPassInputs(program, plat, flag, map);
292 }
293 
SetValue(char * source,uint32_t final)294 void SetValue(char* source, uint32_t final)
295 {
296     const auto tmp = final - 1;
297     PLUGIN_ASSERT(tmp < 99);
298     const auto div = static_cast<char>((tmp / 10u) + '0');
299     const auto mod = static_cast<char>((tmp % 10u) + '0');
300     source[10u] = (tmp > 10u) ? div : ' ';
301     source[11u] = mod;
302 }
303 
304 struct binder {
305     uint8_t& maxBind;
306     uint8_t* map;
307     vector<size_t>& bindingIndices;
308 };
309 
310 template<typename T, size_t N, typename TypeOfOther>
FixBindings(T (& types)[N],binder & map,const TypeOfOther & sbSets,string & source)311 void FixBindings(T (&types)[N], binder& map, const TypeOfOther& sbSets, string& source)
312 {
313     // SetValue does inplace replacements so the string's data addess is constant and a string_view can be used while
314     // going through the idices, types, and names.
315     const auto data = source.data();
316     auto view = string_view(source);
317     // remove patched bindings so they are not considered for other type/name combinations.
318     auto& indices = map.bindingIndices;
319     indices.erase(std::remove_if(indices.begin(), indices.end(),
320                       [&view, &types, &sbSets, &map, data](const size_t bindingI) {
321                           for (const auto& type : types) {
322                               const auto eol = view.find('\n', bindingI);
323                               const auto typeI = view.find(type, bindingI);
324                               if ((typeI == string_view::npos) || (typeI > eol)) {
325                                   continue;
326                               }
327                               const auto afterType = view.substr(typeI + type.size());
328                               for (const auto& t : sbSets) {
329                                   PLUGIN_ASSERT(t.iSet < Gles::ResourceLimits::MAX_SETS);
330                                   PLUGIN_ASSERT(t.iBind < Gles::ResourceLimits::MAX_BIND_IN_SET);
331                                   if (!afterType.starts_with(t.name)) {
332                                       continue;
333                                   }
334                                   // expect to find end of declaration, end of line, or _number (case: multiple uniforms
335                                   // with same binding, not actually checking for the number)
336                                   if (const char ch = afterType[t.name.size()];
337                                       (ch != ';') && (ch != '\n') && (ch != '_')) {
338                                       continue;
339                                   }
340                                   // okay.. do it then..
341                                   uint8_t& final = map.map[BIND_MAP_4_4(t.iSet, t.iBind)];
342                                   if (final == 0) {
343                                       final = ++map.maxBind;
344                                   }
345                                   PLUGIN_ASSERT(final < Gles::ResourceLimits::MAX_BIND_IN_SET);
346                                   // replace the binding point...
347                                   SetValue(data + bindingI, final);
348                                   return true;
349                               }
350                           }
351                           return false;
352                       }),
353         indices.cend());
354 }
355 
356 constexpr const string_view SSBO_KEYS[] = { " buffer " };
357 constexpr const string_view IMAGE_KEYS[] = { " image2D ", " iimage2D ", " uimage2D ", " image2DArray ",
358     " iimage2DArray ", " uimage2DArray ", " image3D ", " iimage3D ", " uimage3D ", " imageCube ", " iimageCube ",
359     " uimageCube ", " imageCubeArray ", " iimageCubeArray ", " uimageCubeArray ", " imageBuffer ", " iimageBuffer ",
360     " uimageBuffer " };
361 constexpr const string_view SPECIAL_BINDING = "binding = 11";
362 
PostProcessSource(BindMaps & map,const ShaderModulePlatformDataGLES & modPlat,string & source)363 void PostProcessSource(BindMaps& map, const ShaderModulePlatformDataGLES& modPlat, string& source)
364 {
365     // Special handling for shader storage blocks and images...
366     // We expect spirv_cross to generate them with certain pattern..
367     // layout(std430, set = 3, binding = 2) buffer MyBuffer
368     // ->   (note: we force binding = 11 during spirv-cross compile.
369     // layout(std430, binding = 11) buffer MyBuffer
370     if (modPlat.sbSets.empty() && modPlat.ciSets.empty()) {
371         // no need if there are no SSBOs or image stores.
372         return;
373     }
374 
375     // go through the source and gather all the special bindings. FixBindings needs to then consider only the found
376     // locations instead of scanning the whole source for every type/name combination.
377     vector<size_t> bindings;
378     const auto view = string_view(source);
379     for (auto pos = view.find(SPECIAL_BINDING); pos != string_view::npos;
380          pos = view.find(SPECIAL_BINDING, pos + SPECIAL_BINDING.size())) {
381         bindings.push_back(pos);
382     }
383     if (!bindings.empty()) {
384         if (!modPlat.sbSets.empty()) {
385             binder storageBindings { map.maxStorageBinding, map.map, bindings };
386             FixBindings(SSBO_KEYS, storageBindings, modPlat.sbSets, source);
387         }
388         if (!modPlat.ciSets.empty()) {
389             binder imageBindings { map.maxImageBinding, map.map, bindings };
390             FixBindings(IMAGE_KEYS, imageBindings, modPlat.ciSets, source);
391         }
392         // after patching the list should be empty
393 #if (RENDER_VALIDATION_ENABLED == 1)
394         if (!bindings.empty()) {
395             PLUGIN_LOG_E("RENDER_VALIDATION: GL(ES) program bindings not empty.");
396         }
397 #endif
398     }
399 }
400 
BuildBindInfos(vector<Binder> & bindinfos,const PipelineLayout & pipelineLayout,BindMaps & map,const vector<ShaderModulePlatformDataGLES::DoubleBind> & combSets)401 void BuildBindInfos(vector<Binder>& bindinfos, const PipelineLayout& pipelineLayout, BindMaps& map,
402     const vector<ShaderModulePlatformDataGLES::DoubleBind>& combSets)
403 {
404     vector<Binder> samplers;
405     vector<Binder> others;
406     for (uint32_t set = 0; set < PipelineLayoutConstants::MAX_DESCRIPTOR_SET_COUNT; set++) {
407         const auto& s = pipelineLayout.descriptorSetLayouts[set];
408         if (s.set == PipelineLayoutConstants::INVALID_INDEX) {
409             continue;
410         }
411         PLUGIN_ASSERT(set == s.set);
412         for (const auto& b : s.bindings) {
413             Binder tmp;
414             tmp.set = s.set;
415             tmp.bind = b.binding;
416             tmp.type = b.descriptorType;
417             tmp.id.resize(b.descriptorCount);
418             bool add = false;
419             for (size_t index = 0; index < b.descriptorCount; index++) {
420                 switch (b.descriptorType) {
421                     case CORE_DESCRIPTOR_TYPE_SAMPLER: {
422                         const uint32_t sid = map.map[BIND_MAP_4_4(s.set, b.binding)];
423                         if (sid) {
424                             for (const auto& cs : combSets) {
425                                 if ((cs.sSet == s.set) && (cs.sBind == b.binding)) {
426                                     const uint32_t iid = map.map[BIND_MAP_4_4(cs.iSet, cs.iBind)];
427                                     if (iid) {
428                                         uint32_t final = map.finalMap[BIND_MAP_4_4(sid, iid)];
429                                         if (final) {
430                                             tmp.id[index].push_back(final - 1);
431                                             add = true;
432                                         }
433                                     }
434                                 }
435                             }
436                         }
437                         break;
438                     }
439                     case CORE_DESCRIPTOR_TYPE_SAMPLED_IMAGE: {
440                         const uint32_t iid = map.map[BIND_MAP_4_4(s.set, b.binding)];
441                         if (iid) {
442                             for (const auto& cs : combSets) {
443                                 if ((cs.iSet == s.set) && (cs.iBind == b.binding)) {
444                                     const uint32_t sid = map.map[BIND_MAP_4_4(cs.sSet, cs.sBind)];
445                                     if (sid) {
446                                         uint32_t final = map.finalMap[BIND_MAP_4_4(sid, iid)];
447                                         if (final) {
448                                             tmp.id[index].push_back(final - 1);
449                                             add = true;
450                                         }
451                                     }
452                                 }
453                             }
454                         }
455                         break;
456                     }
457                     case CORE_DESCRIPTOR_TYPE_STORAGE_IMAGE:
458                     case CORE_DESCRIPTOR_TYPE_INPUT_ATTACHMENT:
459                     case CORE_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER:
460                     case CORE_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC:
461                     case CORE_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC:
462                     case CORE_DESCRIPTOR_TYPE_UNIFORM_BUFFER:
463                     case CORE_DESCRIPTOR_TYPE_STORAGE_BUFFER: {
464                         uint32_t id = map.map[BIND_MAP_4_4(s.set, b.binding)];
465                         if (id) {
466                             tmp.id[index].push_back(id - 1);
467                             add = true;
468                         }
469                         break;
470                     }
471                     case CORE_DESCRIPTOR_TYPE_MAX_ENUM:
472                     default:
473                         PLUGIN_ASSERT_MSG(false, "Unhandled descriptor type");
474                         break;
475                 }
476             }
477             if (add) {
478                 if (b.descriptorType == CORE_DESCRIPTOR_TYPE_SAMPLER) {
479                     samplers.push_back(move(tmp));
480                 } else {
481                     others.push_back(move(tmp));
482                 }
483             }
484         }
485     }
486     // add samplers first. helps with oes.
487     bindinfos.reserve(samplers.size() + others.size());
488     for (auto& s : samplers) {
489         bindinfos.push_back(move(s));
490     }
491     for (auto& o : others) {
492         bindinfos.push_back(move(o));
493     }
494 }
495 
PatchOesBinds(const array_view<const OES_Bind> & oesBinds,const ShaderModulePlatformDataGLES & fragPlat,string & fragSource)496 void PatchOesBinds(
497     const array_view<const OES_Bind>& oesBinds, const ShaderModulePlatformDataGLES& fragPlat, string& fragSource)
498 {
499     if (!oesBinds.empty()) {
500         // find names of oes binds (we only patch Sampler2D's)
501         auto p = fragSource.find("\n", 0);
502         const string_view extension("#extension GL_OES_EGL_image_external_essl3 : enable\n");
503         fragSource.insert(p + 1, extension.data(), extension.size());
504         for (const auto& bnd : oesBinds) {
505             for (const auto& t : fragPlat.cbSets) {
506                 if ((t.iSet != bnd.set) || (t.iBind != bnd.bind)) {
507                     continue;
508                 }
509                 // We expect to find one matching declaration of the sampler in the source.
510                 StringUtil::FindAndReplaceOne(
511                     fragSource, " sampler2D " + t.name + ";", " samplerExternalOES " + t.name + ";");
512             }
513             for (const auto& t : fragPlat.combSets) {
514                 if ((t.iSet != bnd.set) || (t.iBind != bnd.bind)) {
515                     continue;
516                 }
517                 // We expect to find one matching declaration of the sampler in the source.
518                 StringUtil::FindAndReplaceOne(
519                     fragSource, " sampler2D " + t.name + ";", " samplerExternalOES " + t.name + ";");
520             }
521         }
522     }
523 }
524 
PatchMultiview(uint32_t views,string & vertSource)525 void PatchMultiview(uint32_t views, string& vertSource)
526 {
527     if (views) {
528         static constexpr const string_view numViews("layout(num_views = 1)");
529         if (auto pos = vertSource.find(numViews); pos != std::string::npos) {
530             const auto value = vertSource.cbegin() + static_cast<ptrdiff_t>(pos + numViews.size() - 2U);
531             vertSource.replace(value, value + 1, to_string(views));
532         }
533     }
534 }
535 } // namespace
536 
GpuShaderProgramGLES(Device & device)537 GpuShaderProgramGLES::GpuShaderProgramGLES(Device& device) : GpuShaderProgram(), device_((DeviceGLES&)device) {}
538 
~GpuShaderProgramGLES()539 GpuShaderProgramGLES::~GpuShaderProgramGLES()
540 {
541     if (plat_.program) {
542         PLUGIN_ASSERT(device_.IsActive());
543         device_.ReleaseProgram(plat_.program);
544     }
545 }
546 
GpuShaderProgramGLES(Device & device,const GpuShaderProgramCreateData & createData)547 GpuShaderProgramGLES::GpuShaderProgramGLES(Device& device, const GpuShaderProgramCreateData& createData)
548     : GpuShaderProgram(), device_((DeviceGLES&)device)
549 {
550     // combine vertex and fragment shader data
551     if (createData.vertShaderModule && createData.fragShaderModule) {
552         plat_.vertShaderModule_ = static_cast<const ShaderModuleGLES*>(createData.vertShaderModule);
553         plat_.fragShaderModule_ = static_cast<const ShaderModuleGLES*>(createData.fragShaderModule);
554         auto& pipelineLayout = reflection_.pipelineLayout;
555         // vert
556         pipelineLayout = plat_.vertShaderModule_->GetPipelineLayout();
557         const auto& sscv = plat_.vertShaderModule_->GetSpecilization();
558         // has sort inside
559         GpuProgramUtil::CombineSpecializationConstants(sscv.constants, constants_);
560         // not owned, directly reflected from vertex shader module
561         const auto& vidv = plat_.vertShaderModule_->GetVertexInputDeclaration();
562         reflection_.vertexInputDeclarationView = vidv;
563         // frag
564         const auto& reflPl = plat_.fragShaderModule_->GetPipelineLayout();
565         // has sort inside
566         GpuProgramUtil::CombinePipelineLayouts({ &reflPl, 1u }, pipelineLayout);
567 
568         const auto& fsscv = plat_.fragShaderModule_->GetSpecilization();
569         // has sort inside
570         GpuProgramUtil::CombineSpecializationConstants(fsscv.constants, constants_);
571         reflection_.shaderSpecializationConstantView.constants = constants_;
572     }
573 }
574 
FilterInputs(GpuShaderProgramGLES & ret)575 void GpuShaderProgramGLES::FilterInputs(GpuShaderProgramGLES& ret)
576 {
577     GLint inputs;
578     uint32_t inputLocations[Gles::ResourceLimits::MAX_VERTEXINPUT_ATTRIBUTES];
579     enum propertyIndices { LOCATION = 0, VERTEX_REF = 1, FRAGMENT_REF = 2, MAX_INDEX };
580     const GLenum inputProps[] = { GL_LOCATION, GL_REFERENCED_BY_VERTEX_SHADER, GL_REFERENCED_BY_FRAGMENT_SHADER };
581     constexpr auto PropertyCount = static_cast<GLsizei>(countof(inputProps));
582     static_assert(PropertyCount == MAX_INDEX);
583     GLint values[PropertyCount];
584     const auto program = static_cast<GLuint>(ret.plat_.program);
585     glGetProgramInterfaceiv(program, GL_PROGRAM_INPUT, GL_ACTIVE_RESOURCES, &inputs);
586     uint32_t inputsInUse = 0;
587     for (GLint i = 0; i < inputs; i++) {
588         GLsizei wrote;
589         glGetProgramResourceiv(program, GL_PROGRAM_INPUT, static_cast<GLuint>(i), PropertyCount, inputProps,
590             PropertyCount, &wrote, values);
591         if ((values[LOCATION] != Gles::INVALID_LOCATION) &&
592             ((values[VERTEX_REF] == GL_TRUE) || (values[FRAGMENT_REF] == GL_TRUE))) {
593             inputLocations[inputsInUse] = static_cast<uint32_t>((values[LOCATION]));
594             inputsInUse++;
595         }
596     }
597 
598     for (auto& input : ret.plat_.inputs) {
599         input = Gles::INVALID_LOCATION;
600     }
601 
602     const auto& list = ret.reflection_.vertexInputDeclarationView.attributeDescriptions;
603     PLUGIN_ASSERT(list.size() < Gles::ResourceLimits::MAX_VERTEXINPUT_ATTRIBUTES);
604     for (size_t i = 0; i < list.size(); i++) {
605         for (uint32_t j = 0; j < inputsInUse; j++) {
606             if (list[i].location == inputLocations[j]) {
607                 ret.plat_.inputs[i] = static_cast<int32_t>(i);
608                 break;
609             }
610         }
611     }
612 }
613 
Specialize(const ShaderSpecializationConstantDataView & specData,uint32_t views) const614 unique_ptr<GpuShaderProgramGLES> GpuShaderProgramGLES::Specialize(
615     const ShaderSpecializationConstantDataView& specData, uint32_t views) const
616 {
617     return Specialize(specData, {}, views);
618 }
619 
OesPatch(const array_view<const OES_Bind> & binds,uint32_t views) const620 unique_ptr<GpuShaderProgramGLES> GpuShaderProgramGLES::OesPatch(
621     const array_view<const OES_Bind>& binds, uint32_t views) const
622 {
623     ShaderSpecializationConstantDataView specData;
624     specData.constants = constants_;
625     specData.data = specializedWith;
626     return Specialize(specData, binds, views);
627 }
628 
Specialize(const ShaderSpecializationConstantDataView & specData,const array_view<const OES_Bind> & oesBinds,uint32_t views) const629 unique_ptr<GpuShaderProgramGLES> GpuShaderProgramGLES::Specialize(const ShaderSpecializationConstantDataView& specData,
630     const array_view<const OES_Bind>& oesBinds, uint32_t views) const
631 {
632     PLUGIN_ASSERT(device_.IsActive());
633     unique_ptr<GpuShaderProgramGLES> ret { new GpuShaderProgramGLES(device_) };
634     ret->specializedWith = { specData.data.begin(), specData.data.end() };
635     ret->plat_.vertShaderModule_ = plat_.vertShaderModule_;
636     ret->plat_.fragShaderModule_ = plat_.fragShaderModule_;
637     ret->reflection_ = reflection_;
638     ret->constants_ = constants_;
639     ret->reflection_.shaderSpecializationConstantView.constants = ret->constants_;
640 
641     // Special handling for shader storage blocks and images...
642     BindMaps map {};
643 
644     if (!(plat_.vertShaderModule_ && plat_.fragShaderModule_)) {
645         PLUGIN_LOG_E("Invalid shader module");
646         return nullptr;
647     }
648     string vertSource = plat_.vertShaderModule_->GetGLSL(specData);
649     if (vertSource.empty()) {
650         PLUGIN_LOG_W("Trying to specialize a program with no vert source");
651         return nullptr;
652     }
653     const auto& vertPlat = static_cast<const ShaderModulePlatformDataGLES&>(plat_.vertShaderModule_->GetPlatformData());
654     PostProcessSource(map, vertPlat, vertSource);
655 
656     // Patch OVR_multiview num_views
657     PatchMultiview(views, vertSource);
658 
659     string fragSource = plat_.fragShaderModule_->GetGLSL(specData);
660     if (fragSource.empty()) {
661         PLUGIN_LOG_W("Trying to specialize a program with no frag source");
662         return nullptr;
663     }
664     const auto& fragPlat = static_cast<const ShaderModulePlatformDataGLES&>(plat_.fragShaderModule_->GetPlatformData());
665     PostProcessSource(map, fragPlat, fragSource);
666 
667     // if there are oes binds, patches the string (fragSource)
668     PatchOesBinds(oesBinds, fragPlat, fragSource);
669 
670     // Compile / Cache binary
671     ret->plat_.program = device_.CacheProgram(vertSource, fragSource, string_view());
672     if (ret->plat_.program == 0) {
673         PLUGIN_LOG_E("Invalid shader shader program");
674         return nullptr;
675     }
676     // build the map tables..
677     ProcessProgram(ret->plat_.program, vertPlat, GL_REFERENCED_BY_VERTEX_SHADER, map);
678     ProcessProgram(ret->plat_.program, fragPlat, GL_REFERENCED_BY_FRAGMENT_SHADER, map);
679     ret->pushConstants = map.pushConstants;
680     ret->plat_.pushConstants = ret->pushConstants;
681     ret->plat_.flipLocation = glGetUniformLocation(ret->plat_.program, "CORE_FLIP_NDC");
682     FilterInputs(*ret);
683 
684     const auto& pipelineLayout = reflection_.pipelineLayout;
685     // create a union of the "new" combined samplers (created from separate sampler/image binds)
686     vector<ShaderModulePlatformDataGLES::DoubleBind> combSets = fragPlat.combSets;
687     for (const auto& c : vertPlat.combSets) {
688         bool newEntry = true;
689         for (const auto& b : combSets) {
690             if ((c.iSet == b.iSet) && (c.sSet == b.sSet) && (c.iBind == b.iBind) && (c.sBind == b.sBind)) {
691                 newEntry = false;
692                 break;
693             }
694         }
695         if (newEntry) {
696             combSets.push_back(c);
697         }
698     }
699     BuildBindInfos(ret->resourceList, pipelineLayout, map, combSets);
700     ret->plat_.resourceList = ret->resourceList;
701     return ret;
702 }
703 
GetPlatformData() const704 const GpuShaderProgramPlatformDataGL& GpuShaderProgramGLES::GetPlatformData() const
705 {
706     return plat_;
707 }
708 
GetReflection() const709 const ShaderReflection& GpuShaderProgramGLES::GetReflection() const
710 {
711     return reflection_;
712 }
713 
GpuComputeProgramGLES(Device & device)714 GpuComputeProgramGLES::GpuComputeProgramGLES(Device& device) : GpuComputeProgram(), device_((DeviceGLES&)device) {}
715 
GpuComputeProgramGLES(Device & device,const GpuComputeProgramCreateData & createData)716 GpuComputeProgramGLES::GpuComputeProgramGLES(Device& device, const GpuComputeProgramCreateData& createData)
717     : GpuComputeProgram(), device_((DeviceGLES&)device)
718 {
719     if (createData.compShaderModule) {
720         plat_.module_ = static_cast<const ShaderModuleGLES*>(createData.compShaderModule);
721         reflection_.pipelineLayout = plat_.module_->GetPipelineLayout();
722         const auto& tgs = plat_.module_->GetThreadGroupSize();
723         reflection_.threadGroupSizeX = Math::max(1u, tgs.x);
724         reflection_.threadGroupSizeY = Math::max(1u, tgs.y);
725         reflection_.threadGroupSizeZ = Math::max(1u, tgs.z);
726         reflection_.shaderSpecializationConstantView = plat_.module_->GetSpecilization();
727         const auto& constants = reflection_.shaderSpecializationConstantView.constants;
728         constants_ = vector<ShaderSpecialization::Constant>(constants.cbegin().ptr(), constants.cend().ptr());
729         // sorted based on offset due to offset mapping with shader combinations
730         // NOTE: id and name indexing
731         std::sort(constants_.begin(), constants_.end(),
732             [](const auto& lhs, const auto& rhs) { return (lhs.offset < rhs.offset); });
733         reflection_.shaderSpecializationConstantView.constants = constants_;
734     }
735 }
736 
~GpuComputeProgramGLES()737 GpuComputeProgramGLES::~GpuComputeProgramGLES()
738 {
739     if (plat_.program) {
740         PLUGIN_ASSERT(device_.IsActive());
741         device_.ReleaseProgram(plat_.program);
742     }
743 }
744 
GetPlatformData() const745 const GpuComputeProgramPlatformDataGL& GpuComputeProgramGLES::GetPlatformData() const
746 {
747     return plat_;
748 }
749 
GetReflection() const750 const ComputeShaderReflection& GpuComputeProgramGLES::GetReflection() const
751 {
752     return reflection_;
753 }
754 
Specialize(const ShaderSpecializationConstantDataView & specData) const755 unique_ptr<GpuComputeProgramGLES> GpuComputeProgramGLES::Specialize(
756     const ShaderSpecializationConstantDataView& specData) const
757 {
758     PLUGIN_ASSERT(device_.IsActive());
759     unique_ptr<GpuComputeProgramGLES> ret { new GpuComputeProgramGLES(device_) };
760     ret->plat_.module_ = plat_.module_;
761     ret->reflection_ = reflection_;
762     ret->constants_ = constants_;
763     ret->reflection_.shaderSpecializationConstantView.constants = ret->constants_;
764     // Special handling for shader storage blocks and images...
765     BindMaps map {};
766 
767     if (!plat_.module_) {
768         PLUGIN_LOG_E("Invalid shader module");
769         return nullptr;
770     }
771     string compSource = plat_.module_->GetGLSL(specData);
772     if (compSource.empty()) {
773         PLUGIN_LOG_W("Trying to specialize a program with no source");
774     }
775     const auto& plat = static_cast<const ShaderModulePlatformDataGLES&>(plat_.module_->GetPlatformData());
776     PostProcessSource(map, plat, compSource);
777     // Compile / Cache binary
778     ret->plat_.program = device_.CacheProgram(string_view(), string_view(), compSource);
779     if (ret->plat_.program == 0) {
780         // something went wrong.
781         PLUGIN_LOG_E("Invalid shader program");
782         return nullptr;
783     }
784     // build the map tables..
785     ProcessProgram(ret->plat_.program, plat, GL_REFERENCED_BY_COMPUTE_SHADER, map);
786     ret->pushConstants = map.pushConstants;
787     ret->plat_.pushConstants = ret->pushConstants;
788     ret->plat_.flipLocation = glGetUniformLocation(ret->plat_.program, "CORE_FLIP_NDC");
789 
790     const auto& pipelineLayout = reflection_.pipelineLayout;
791     BuildBindInfos(ret->resourceList, pipelineLayout, map, plat.combSets);
792     ret->plat_.resourceList = ret->resourceList;
793     return ret;
794 }
795 RENDER_END_NAMESPACE()
796