blob: 5a7c52aa7b3dc7a3d20e8d68bc8b802a20d6be13 [file] [log] [blame]
Timothy Liang057c3902018-08-08 10:48:45 -04001/*
2* Copyright 2018 Google Inc.
3*
4* Use of this source code is governed by a BSD-style license that can be
5* found in the LICENSE file.
6*/
7
8#include "GrMtlUniformHandler.h"
9#include "glsl/GrGLSLProgramBuilder.h"
10
11// TODO: this class is basically copy and pasted from GrVklUniformHandler so that we can have
12// some shaders working. The SkSL Metal code generator was written to work with GLSL generated for
13// the Ganesh Vulkan backend, so it should all work. There might be better ways to do things in
14// Metal and/or some Vulkan GLSLisms left in.
15
16// To determine whether a current offset is aligned, we can just 'and' the lowest bits with the
17// alignment mask. A value of 0 means aligned, any other value is how many bytes past alignment we
18// are. This works since all alignments are powers of 2. The mask is always (alignment - 1).
Timothy Liang057c3902018-08-08 10:48:45 -040019uint32_t grsltype_to_alignment_mask(GrSLType type) {
20 switch(type) {
21 case kByte_GrSLType: // fall through
22 case kUByte_GrSLType:
23 return 0x0;
24 case kByte2_GrSLType: // fall through
25 case kUByte2_GrSLType:
26 return 0x1;
27 case kByte3_GrSLType: // fall through
28 case kByte4_GrSLType:
29 case kUByte3_GrSLType:
30 case kUByte4_GrSLType:
31 return 0x3;
32 case kShort_GrSLType: // fall through
33 case kUShort_GrSLType:
34 return 0x1;
35 case kShort2_GrSLType: // fall through
36 case kUShort2_GrSLType:
37 return 0x3;
38 case kShort3_GrSLType: // fall through
39 case kShort4_GrSLType:
40 case kUShort3_GrSLType:
41 case kUShort4_GrSLType:
42 return 0x7;
43 case kInt_GrSLType:
44 case kUint_GrSLType:
45 return 0x3;
46 case kHalf_GrSLType: // fall through
47 case kFloat_GrSLType:
48 return 0x3;
49 case kHalf2_GrSLType: // fall through
50 case kFloat2_GrSLType:
51 return 0x7;
52 case kHalf3_GrSLType: // fall through
53 case kFloat3_GrSLType:
54 return 0xF;
55 case kHalf4_GrSLType: // fall through
56 case kFloat4_GrSLType:
57 return 0xF;
58 case kUint2_GrSLType:
59 return 0x7;
60 case kInt2_GrSLType:
61 return 0x7;
62 case kInt3_GrSLType:
63 return 0xF;
64 case kInt4_GrSLType:
65 return 0xF;
66 case kHalf2x2_GrSLType: // fall through
67 case kFloat2x2_GrSLType:
68 return 0x7;
69 case kHalf3x3_GrSLType: // fall through
70 case kFloat3x3_GrSLType:
71 return 0xF;
72 case kHalf4x4_GrSLType: // fall through
73 case kFloat4x4_GrSLType:
74 return 0xF;
75
76 // This query is only valid for certain types.
77 case kVoid_GrSLType:
78 case kBool_GrSLType:
79 case kTexture2DSampler_GrSLType:
80 case kTextureExternalSampler_GrSLType:
81 case kTexture2DRectSampler_GrSLType:
82 break;
83 }
84 SK_ABORT("Unexpected type");
85 return 0;
86}
87
Timothy Liang609fbe32018-08-10 16:40:49 -040088/** Returns the size in bytes taken up in Metal buffers for GrSLTypes. */
Timothy Liang057c3902018-08-08 10:48:45 -040089static inline uint32_t grsltype_to_mtl_size(GrSLType type) {
90 switch(type) {
91 case kByte_GrSLType:
92 return sizeof(int8_t);
93 case kByte2_GrSLType:
94 return 2 * sizeof(int8_t);
95 case kByte3_GrSLType:
Timothy Liang609fbe32018-08-10 16:40:49 -040096 return 4 * sizeof(int8_t);
Timothy Liang057c3902018-08-08 10:48:45 -040097 case kByte4_GrSLType:
98 return 4 * sizeof(int8_t);
99 case kUByte_GrSLType:
100 return sizeof(uint8_t);
101 case kUByte2_GrSLType:
102 return 2 * sizeof(uint8_t);
103 case kUByte3_GrSLType:
Timothy Liang609fbe32018-08-10 16:40:49 -0400104 return 4 * sizeof(uint8_t);
Timothy Liang057c3902018-08-08 10:48:45 -0400105 case kUByte4_GrSLType:
106 return 4 * sizeof(uint8_t);
107 case kShort_GrSLType:
108 return sizeof(int16_t);
109 case kShort2_GrSLType:
110 return 2 * sizeof(int16_t);
111 case kShort3_GrSLType:
Timothy Liang609fbe32018-08-10 16:40:49 -0400112 return 4 * sizeof(int16_t);
Timothy Liang057c3902018-08-08 10:48:45 -0400113 case kShort4_GrSLType:
114 return 4 * sizeof(int16_t);
115 case kUShort_GrSLType:
116 return sizeof(uint16_t);
117 case kUShort2_GrSLType:
118 return 2 * sizeof(uint16_t);
119 case kUShort3_GrSLType:
Timothy Liang609fbe32018-08-10 16:40:49 -0400120 return 4 * sizeof(uint16_t);
Timothy Liang057c3902018-08-08 10:48:45 -0400121 case kUShort4_GrSLType:
122 return 4 * sizeof(uint16_t);
123 case kInt_GrSLType:
124 return sizeof(int32_t);
125 case kUint_GrSLType:
126 return sizeof(int32_t);
127 case kHalf_GrSLType: // fall through
128 case kFloat_GrSLType:
129 return sizeof(float);
130 case kHalf2_GrSLType: // fall through
131 case kFloat2_GrSLType:
132 return 2 * sizeof(float);
133 case kHalf3_GrSLType: // fall through
134 case kFloat3_GrSLType:
Timothy Liang609fbe32018-08-10 16:40:49 -0400135 return 4 * sizeof(float);
Timothy Liang057c3902018-08-08 10:48:45 -0400136 case kHalf4_GrSLType: // fall through
137 case kFloat4_GrSLType:
138 return 4 * sizeof(float);
139 case kUint2_GrSLType:
140 return 2 * sizeof(uint32_t);
141 case kInt2_GrSLType:
142 return 2 * sizeof(int32_t);
143 case kInt3_GrSLType:
Timothy Liang609fbe32018-08-10 16:40:49 -0400144 return 4 * sizeof(int32_t);
Timothy Liang057c3902018-08-08 10:48:45 -0400145 case kInt4_GrSLType:
146 return 4 * sizeof(int32_t);
147 case kHalf2x2_GrSLType: // fall through
148 case kFloat2x2_GrSLType:
149 //TODO: this will be 4 * szof(float) on std430.
150 return 8 * sizeof(float);
151 case kHalf3x3_GrSLType: // fall through
152 case kFloat3x3_GrSLType:
153 return 12 * sizeof(float);
154 case kHalf4x4_GrSLType: // fall through
155 case kFloat4x4_GrSLType:
156 return 16 * sizeof(float);
157
158 // This query is only valid for certain types.
159 case kVoid_GrSLType:
160 case kBool_GrSLType:
161 case kTexture2DSampler_GrSLType:
162 case kTextureExternalSampler_GrSLType:
163 case kTexture2DRectSampler_GrSLType:
164 break;
165 }
166 SK_ABORT("Unexpected type");
167 return 0;
168}
169
Timothy Liang057c3902018-08-08 10:48:45 -0400170// Given the current offset into the ubo, calculate the offset for the uniform we're trying to add
171// taking into consideration all alignment requirements. The uniformOffset is set to the offset for
172// the new uniform, and currentOffset is updated to be the offset to the end of the new uniform.
173void get_ubo_aligned_offset(uint32_t* uniformOffset,
174 uint32_t* currentOffset,
175 GrSLType type,
176 int arrayCount) {
177 uint32_t alignmentMask = grsltype_to_alignment_mask(type);
Timothy Liang057c3902018-08-08 10:48:45 -0400178 uint32_t offsetDiff = *currentOffset & alignmentMask;
179 if (offsetDiff != 0) {
180 offsetDiff = alignmentMask - offsetDiff + 1;
181 }
182 *uniformOffset = *currentOffset + offsetDiff;
183 SkASSERT(sizeof(float) == 4);
184 if (arrayCount) {
185 uint32_t elementSize = SkTMax<uint32_t>(16, grsltype_to_mtl_size(type));
186 SkASSERT(0 == (elementSize & 0xF));
187 *currentOffset = *uniformOffset + elementSize * arrayCount;
188 } else {
189 *currentOffset = *uniformOffset + grsltype_to_mtl_size(type);
190 }
191}
192
193GrGLSLUniformHandler::UniformHandle GrMtlUniformHandler::internalAddUniformArray(
194 uint32_t visibility,
195 GrSLType type,
196 GrSLPrecision precision,
197 const char* name,
198 bool mangleName,
199 int arrayCount,
200 const char** outName) {
201 SkASSERT(name && strlen(name));
202 // For now asserting the the visibility is either geometry types (vertex, tesselation, geometry,
203 // etc.) or only fragment.
204 SkASSERT(kVertex_GrShaderFlag == visibility ||
205 kGeometry_GrShaderFlag == visibility ||
206 (kVertex_GrShaderFlag | kGeometry_GrShaderFlag) == visibility ||
207 kFragment_GrShaderFlag == visibility);
208 SkASSERT(kDefault_GrSLPrecision == precision || GrSLTypeIsFloatType(type));
209 GrSLTypeIsFloatType(type);
210
211 UniformInfo& uni = fUniforms.push_back();
212 uni.fVariable.setType(type);
213 // TODO this is a bit hacky, lets think of a better way. Basically we need to be able to use
214 // the uniform view matrix name in the GP, and the GP is immutable so it has to tell the PB
215 // exactly what name it wants to use for the uniform view matrix. If we prefix anythings, then
216 // the names will mismatch. I think the correct solution is to have all GPs which need the
217 // uniform view matrix, they should upload the view matrix in their setData along with regular
218 // uniforms.
219 char prefix = 'u';
220 if ('u' == name[0] || !strncmp(name, GR_NO_MANGLE_PREFIX, strlen(GR_NO_MANGLE_PREFIX))) {
221 prefix = '\0';
222 }
223 fProgramBuilder->nameVariable(uni.fVariable.accessName(), prefix, name, mangleName);
224 uni.fVariable.setArrayCount(arrayCount);
225 uni.fVisibility = visibility;
226 uni.fVariable.setPrecision(precision);
227 // When outputing the GLSL, only the outer uniform block will get the Uniform modifier. Thus
228 // we set the modifier to none for all uniforms declared inside the block.
229 uni.fVariable.setTypeModifier(GrShaderVar::kNone_TypeModifier);
230
231 uint32_t* currentOffset;
232 uint32_t geomStages = kVertex_GrShaderFlag | kGeometry_GrShaderFlag;
233 if (geomStages & visibility) {
234 currentOffset = &fCurrentGeometryUBOOffset;
235 } else {
236 SkASSERT(kFragment_GrShaderFlag == visibility);
237 currentOffset = &fCurrentFragmentUBOOffset;
238 }
239 get_ubo_aligned_offset(&uni.fUBOffset, currentOffset, type, arrayCount);
240
241 SkString layoutQualifier;
242 layoutQualifier.appendf("offset=%d", uni.fUBOffset);
243 uni.fVariable.addLayoutQualifier(layoutQualifier.c_str());
244
245 if (outName) {
246 *outName = uni.fVariable.c_str();
247 }
248
249 return GrGLSLUniformHandler::UniformHandle(fUniforms.count() - 1);
250}
251
252GrGLSLUniformHandler::SamplerHandle GrMtlUniformHandler::addSampler(uint32_t visibility,
253 GrSwizzle swizzle,
254 GrTextureType type,
255 GrSLPrecision precision,
256 const char* name) {
257 SkASSERT(name && strlen(name));
258 // For now asserting the the visibility is either only vertex, geometry, or fragment
259 SkASSERT(kVertex_GrShaderFlag == visibility ||
260 kFragment_GrShaderFlag == visibility ||
261 kGeometry_GrShaderFlag == visibility);
262 SkString mangleName;
263 char prefix = 'u';
264 fProgramBuilder->nameVariable(&mangleName, prefix, name, true);
265
266 UniformInfo& info = fSamplers.push_back();
267 info.fVariable.setType(GrSLCombinedSamplerTypeForTextureType(type));
268 info.fVariable.setTypeModifier(GrShaderVar::kUniform_TypeModifier);
269 info.fVariable.setPrecision(precision);
270 info.fVariable.setName(mangleName);
271 SkString layoutQualifier;
272 layoutQualifier.appendf("binding=%d", fSamplers.count() - 1);
273 info.fVariable.addLayoutQualifier(layoutQualifier.c_str());
274 info.fVisibility = visibility;
275 info.fUBOffset = 0;
276 fSamplerSwizzles.push_back(swizzle);
277 SkASSERT(fSamplerSwizzles.count() == fSamplers.count());
278 return GrGLSLUniformHandler::SamplerHandle(fSamplers.count() - 1);
279}
280
281void GrMtlUniformHandler::appendUniformDecls(GrShaderFlags visibility, SkString* out) const {
282 SkASSERT(kVertex_GrShaderFlag == visibility ||
283 kGeometry_GrShaderFlag == visibility ||
284 kFragment_GrShaderFlag == visibility);
285
286 for (int i = 0; i < fSamplers.count(); ++i) {
287 const UniformInfo& sampler = fSamplers[i];
288 SkASSERT(sampler.fVariable.getType() == kTexture2DSampler_GrSLType);
289 if (visibility == sampler.fVisibility) {
290 sampler.fVariable.appendDecl(fProgramBuilder->shaderCaps(), out);
291 out->append(";\n");
292 }
293 }
294
295#ifdef SK_DEBUG
296 bool firstGeomOffsetCheck = false;
297 bool firstFragOffsetCheck = false;
298 for (int i = 0; i < fUniforms.count(); ++i) {
299 const UniformInfo& localUniform = fUniforms[i];
300 if (kVertex_GrShaderFlag == localUniform.fVisibility ||
301 kGeometry_GrShaderFlag == localUniform.fVisibility ||
302 (kVertex_GrShaderFlag | kGeometry_GrShaderFlag) == localUniform.fVisibility) {
303 if (!firstGeomOffsetCheck) {
304 // Check to make sure we are starting our offset at 0 so the offset qualifier we
305 // set on each variable in the uniform block is valid.
306 SkASSERT(0 == localUniform.fUBOffset);
307 firstGeomOffsetCheck = true;
308 }
309 } else {
310 SkASSERT(kFragment_GrShaderFlag == localUniform.fVisibility);
311 if (!firstFragOffsetCheck) {
312 // Check to make sure we are starting our offset at 0 so the offset qualifier we
313 // set on each variable in the uniform block is valid.
314 SkASSERT(0 == localUniform.fUBOffset);
315 firstFragOffsetCheck = true;
316 }
317 }
318 }
319#endif
320
321 SkString uniformsString;
322 for (int i = 0; i < fUniforms.count(); ++i) {
323 const UniformInfo& localUniform = fUniforms[i];
324 if (visibility & localUniform.fVisibility) {
325 if (GrSLTypeIsFloatType(localUniform.fVariable.getType())) {
326 localUniform.fVariable.appendDecl(fProgramBuilder->shaderCaps(), &uniformsString);
327 uniformsString.append(";\n");
328 }
329 }
330 }
331
332 if (!uniformsString.isEmpty()) {
333 uint32_t uniformBinding;
334 const char* stage;
335 if (kVertex_GrShaderFlag == visibility) {
336 uniformBinding = kGeometryBinding;
337 stage = "vertex";
338 } else if (kGeometry_GrShaderFlag == visibility) {
339 uniformBinding = kGeometryBinding;
340 stage = "geometry";
341 } else {
342 SkASSERT(kFragment_GrShaderFlag == visibility);
343 uniformBinding = kFragBinding;
344 stage = "fragment";
345 }
346 out->appendf("layout (binding=%d) uniform %sUniformBuffer\n{\n", uniformBinding, stage);
347 out->appendf("%s\n};\n", uniformsString.c_str());
348 }
349}