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