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