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 "spirv_cross_helpers_gles.h"
17 
18 #include <cmath>
19 
20 #include "gles/device_gles.h"
21 #include "gles/gl_functions.h"
22 #include "util/log.h"
23 
24 using namespace BASE_NS;
25 
26 RENDER_BEGIN_NAMESPACE()
27 namespace Gles {
FindConstant(const array_view<const PushConstantReflection> reflections,const PushConstantReflection & reflection)28 int32_t FindConstant(
29     const array_view<const PushConstantReflection> reflections, const PushConstantReflection& reflection)
30 {
31     for (size_t i = 0; i < reflections.size(); i++) {
32         if (reflection.name == reflections[i].name) {
33             // Check that it's actually same and not a conflict!.
34             if (reflection.type != reflections[i].type) {
35                 return -2;
36             }
37             if (reflection.offset != reflections[i].offset) {
38                 return -2;
39             }
40             if (reflection.size != reflections[i].size) {
41                 return -2;
42             }
43             if (reflection.arraySize != reflections[i].arraySize) {
44                 return -2;
45             }
46             if (reflection.arrayStride != reflections[i].arrayStride) {
47                 return -2;
48             }
49             if (reflection.matrixStride != reflections[i].matrixStride) {
50                 return -2;
51             }
52             return (int32_t)i;
53         }
54     }
55     return -1;
56 }
57 
DefineForSpec(const array_view<const ShaderSpecialization::Constant> reflectionInfo,const uint32_t spcid,const uintptr_t offset,string & result)58 bool DefineForSpec(const array_view<const ShaderSpecialization::Constant> reflectionInfo, const uint32_t spcid,
59     const uintptr_t offset, string& result)
60 {
61     // "#define SPIRV_CROSS_CONSTANT_ID_4294967295 4294967295\n" //worst case for bool
62     // "#define SPIRV_CROSS_CONSTANT_ID_4294967295 4294967295\n" //worst case for uint32
63     // "#define SPIRV_CROSS_CONSTANT_ID_4294967295 -2147483648\n"//worst case for int32
64     // and floats can be REALLY long..
65     char buf[1024];
66     bool ok = false;
67     for (const auto& c : reflectionInfo) {
68         if (c.id == spcid) {
69             // The constant_id can only be applied to a scalar *int*, a scalar *float* or a scalar *bool*.
70             // https://www.khronos.org/registry/OpenGL/extensions/ARB/ARB_gl_spirv.txt
71             switch (c.type) {
72                 default:
73                     [[fallthrough]];
74                 case ShaderSpecialization::Constant::Type::INVALID:
75                     PLUGIN_ASSERT_MSG(false, "Unhandled specialization constant type");
76                     break;
77 
78                 case ShaderSpecialization::Constant::Type::BOOL:
79                     [[fallthrough]];
80                 case ShaderSpecialization::Constant::Type::UINT32: {
81                     const uint32_t value = *reinterpret_cast<uint32_t*>(offset);
82                     const int len = sprintf_s(buf, sizeof(buf), "%u %uu\n", c.id, value);
83                     ok = len > 0;
84                     break;
85                 }
86 
87                 case ShaderSpecialization::Constant::Type::INT32: {
88                     const int32_t value = *reinterpret_cast<int32_t*>(offset);
89                     const int len = sprintf_s(buf, sizeof(buf), "%u %d\n", c.id, value);
90                     ok = len > 0;
91                     break;
92                 }
93 
94                 case ShaderSpecialization::Constant::Type::FLOAT: {
95                     const float value = *reinterpret_cast<float_t*>(offset);
96                     // NOTE: resulting constant might not be the same. due to float -> string -> float conversions.
97                     const int len = sprintf_s(buf, sizeof(buf), "%u %f\n", c.id, value);
98                     ok = len > 0;
99                     break;
100                 }
101             }
102             if (ok) {
103                 result.append("#define SPIRV_CROSS_CONSTANT_ID_");
104                 result.append(buf);
105             }
106             break;
107         }
108     }
109     return ok;
110 }
111 
InsertDefines(const string_view shaderIn,const string_view Defines)112 string InsertDefines(const string_view shaderIn, const string_view Defines)
113 {
114     string shaderOut;
115     // Create defines..
116     if (!shaderIn.empty()) {
117         const size_t voff = shaderIn.find_first_of('\n');
118         shaderOut.reserve(shaderIn.length() + Defines.length());
119         shaderOut.append(shaderIn.substr(0, voff + 1));
120         shaderOut.append(Defines);
121         shaderOut.append(shaderIn.substr(voff + 1));
122     } else {
123         shaderOut = Defines;
124     }
125     return shaderOut;
126 }
127 
Specialize(ShaderStageFlags mask,const string_view shaderTemplate,const array_view<const ShaderSpecialization::Constant> info,const ShaderSpecializationConstantDataView & data)128 string Specialize(ShaderStageFlags mask, const string_view shaderTemplate,
129     const array_view<const ShaderSpecialization::Constant> info, const ShaderSpecializationConstantDataView& data)
130 {
131     if (shaderTemplate.empty()) {
132         return {};
133     }
134     if (data.data.empty()) {
135         // missing specialization constant values
136         return string(shaderTemplate);
137     }
138     bool ok = false;
139     for (const auto& spc : data.constants) {
140         if (spc.shaderStage & mask) {
141             ok = true;
142             break;
143         }
144     }
145     if (!ok) {
146         // nothing to specialize
147         return string(shaderTemplate);
148     }
149     // Create defines..
150     const uintptr_t base = (uintptr_t)data.data.data();
151     string defines;
152     defines.reserve(256);
153     for (const auto& spc : data.constants) {
154         if (spc.shaderStage & mask) {
155             const uintptr_t offset = base + spc.offset;
156             DefineForSpec(info, spc.id, offset, defines);
157         }
158     }
159     // inject defines to shader source.
160     return InsertDefines(shaderTemplate, defines);
161 }
162 } // namespace Gles
163 RENDER_END_NAMESPACE()
164