1/*
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 "include/gpu/GrTexture.h"
9#include "src/gpu/GrTexturePriv.h"
10#include "src/gpu/glsl/GrGLSLProgramBuilder.h"
11#include "src/gpu/mtl/GrMtlUniformHandler.h"
12
13#if !__has_feature(objc_arc)
14#error This file must be compiled with Arc. Use -fobjc-arc flag
15#endif
16
17// TODO: this class is basically copy and pasted from GrVklUniformHandler so that we can have
18// some shaders working. The SkSL Metal code generator was written to work with GLSL generated for
19// the Ganesh Vulkan backend, so it should all work. There might be better ways to do things in
20// Metal and/or some Vulkan GLSLisms left in.
21
22// To determine whether a current offset is aligned, we can just 'and' the lowest bits with the
23// alignment mask. A value of 0 means aligned, any other value is how many bytes past alignment we
24// are. This works since all alignments are powers of 2. The mask is always (alignment - 1).
25static uint32_t grsltype_to_alignment_mask(GrSLType type) {
26    switch(type) {
27        case kByte_GrSLType: // fall through
28        case kUByte_GrSLType:
29            return 0x0;
30        case kByte2_GrSLType: // fall through
31        case kUByte2_GrSLType:
32            return 0x1;
33        case kByte3_GrSLType: // fall through
34        case kByte4_GrSLType:
35        case kUByte3_GrSLType:
36        case kUByte4_GrSLType:
37            return 0x3;
38        case kShort_GrSLType: // fall through
39        case kUShort_GrSLType:
40            return 0x1;
41        case kShort2_GrSLType: // fall through
42        case kUShort2_GrSLType:
43            return 0x3;
44        case kShort3_GrSLType: // fall through
45        case kShort4_GrSLType:
46        case kUShort3_GrSLType:
47        case kUShort4_GrSLType:
48            return 0x7;
49        case kInt_GrSLType:
50        case kUint_GrSLType:
51            return 0x3;
52        case kHalf_GrSLType: // fall through
53        case kFloat_GrSLType:
54            return 0x3;
55        case kHalf2_GrSLType: // fall through
56        case kFloat2_GrSLType:
57            return 0x7;
58        case kHalf3_GrSLType: // fall through
59        case kFloat3_GrSLType:
60            return 0xF;
61        case kHalf4_GrSLType: // fall through
62        case kFloat4_GrSLType:
63            return 0xF;
64        case kUint2_GrSLType:
65            return 0x7;
66        case kInt2_GrSLType:
67            return 0x7;
68        case kInt3_GrSLType:
69            return 0xF;
70        case kInt4_GrSLType:
71            return 0xF;
72        case kHalf2x2_GrSLType: // fall through
73        case kFloat2x2_GrSLType:
74            return 0x7;
75        case kHalf3x3_GrSLType: // fall through
76        case kFloat3x3_GrSLType:
77            return 0xF;
78        case kHalf4x4_GrSLType: // fall through
79        case kFloat4x4_GrSLType:
80            return 0xF;
81
82        // This query is only valid for certain types.
83        case kVoid_GrSLType:
84        case kBool_GrSLType:
85        case kTexture2DSampler_GrSLType:
86        case kTextureExternalSampler_GrSLType:
87        case kTexture2DRectSampler_GrSLType:
88        case kSampler_GrSLType:
89        case kTexture2D_GrSLType:
90            break;
91    }
92    SK_ABORT("Unexpected type");
93}
94
95/** Returns the size in bytes taken up in Metal buffers for GrSLTypes. */
96static inline uint32_t grsltype_to_mtl_size(GrSLType type) {
97    switch(type) {
98        case kByte_GrSLType:
99            return sizeof(int8_t);
100        case kByte2_GrSLType:
101            return 2 * sizeof(int8_t);
102        case kByte3_GrSLType:
103            return 4 * sizeof(int8_t);
104        case kByte4_GrSLType:
105            return 4 * sizeof(int8_t);
106        case kUByte_GrSLType:
107            return sizeof(uint8_t);
108        case kUByte2_GrSLType:
109            return 2 * sizeof(uint8_t);
110        case kUByte3_GrSLType:
111            return 4 * sizeof(uint8_t);
112        case kUByte4_GrSLType:
113            return 4 * sizeof(uint8_t);
114        case kShort_GrSLType:
115            return sizeof(int16_t);
116        case kShort2_GrSLType:
117            return 2 * sizeof(int16_t);
118        case kShort3_GrSLType:
119            return 4 * sizeof(int16_t);
120        case kShort4_GrSLType:
121            return 4 * sizeof(int16_t);
122        case kUShort_GrSLType:
123            return sizeof(uint16_t);
124        case kUShort2_GrSLType:
125            return 2 * sizeof(uint16_t);
126        case kUShort3_GrSLType:
127            return 4 * sizeof(uint16_t);
128        case kUShort4_GrSLType:
129            return 4 * sizeof(uint16_t);
130        case kInt_GrSLType:
131            return sizeof(int32_t);
132        case kUint_GrSLType:
133            return sizeof(int32_t);
134        case kHalf_GrSLType: // fall through
135        case kFloat_GrSLType:
136            return sizeof(float);
137        case kHalf2_GrSLType: // fall through
138        case kFloat2_GrSLType:
139            return 2 * sizeof(float);
140        case kHalf3_GrSLType: // fall through
141        case kFloat3_GrSLType:
142            return 4 * sizeof(float);
143        case kHalf4_GrSLType: // fall through
144        case kFloat4_GrSLType:
145            return 4 * sizeof(float);
146        case kUint2_GrSLType:
147            return 2 * sizeof(uint32_t);
148        case kInt2_GrSLType:
149            return 2 * sizeof(int32_t);
150        case kInt3_GrSLType:
151            return 4 * sizeof(int32_t);
152        case kInt4_GrSLType:
153            return 4 * sizeof(int32_t);
154        case kHalf2x2_GrSLType: // fall through
155        case kFloat2x2_GrSLType:
156            return 4 * sizeof(float);
157        case kHalf3x3_GrSLType: // fall through
158        case kFloat3x3_GrSLType:
159            return 12 * sizeof(float);
160        case kHalf4x4_GrSLType: // fall through
161        case kFloat4x4_GrSLType:
162            return 16 * sizeof(float);
163
164        // This query is only valid for certain types.
165        case kVoid_GrSLType:
166        case kBool_GrSLType:
167        case kTexture2DSampler_GrSLType:
168        case kTextureExternalSampler_GrSLType:
169        case kTexture2DRectSampler_GrSLType:
170        case kSampler_GrSLType:
171        case kTexture2D_GrSLType:
172            break;
173    }
174    SK_ABORT("Unexpected type");
175}
176
177// Given the current offset into the ubo, calculate the offset for the uniform we're trying to add
178// taking into consideration all alignment requirements. The uniformOffset is set to the offset for
179// the new uniform, and currentOffset is updated to be the offset to the end of the new uniform.
180static void get_ubo_aligned_offset(uint32_t* uniformOffset,
181                                   uint32_t* currentOffset,
182                                   uint32_t* maxAlignment,
183                                   GrSLType type,
184                                   int arrayCount) {
185    uint32_t alignmentMask = grsltype_to_alignment_mask(type);
186    if (alignmentMask > *maxAlignment) {
187        *maxAlignment = alignmentMask;
188    }
189    uint32_t offsetDiff = *currentOffset & alignmentMask;
190    if (offsetDiff != 0) {
191        offsetDiff = alignmentMask - offsetDiff + 1;
192    }
193    *uniformOffset = *currentOffset + offsetDiff;
194    SkASSERT(sizeof(float) == 4);
195    if (arrayCount) {
196        *currentOffset = *uniformOffset + grsltype_to_mtl_size(type) * arrayCount;
197    } else {
198        *currentOffset = *uniformOffset + grsltype_to_mtl_size(type);
199    }
200}
201
202GrGLSLUniformHandler::UniformHandle GrMtlUniformHandler::internalAddUniformArray(
203                                                                            uint32_t visibility,
204                                                                            GrSLType type,
205                                                                            const char* name,
206                                                                            bool mangleName,
207                                                                            int arrayCount,
208                                                                            const char** outName) {
209    SkASSERT(name && strlen(name));
210    GrSLTypeIsFloatType(type);
211
212    UniformInfo& uni = fUniforms.push_back();
213    uni.fVariable.setType(type);
214    // TODO this is a bit hacky, lets think of a better way.  Basically we need to be able to use
215    // the uniform view matrix name in the GP, and the GP is immutable so it has to tell the PB
216    // exactly what name it wants to use for the uniform view matrix.  If we prefix anythings, then
217    // the names will mismatch.  I think the correct solution is to have all GPs which need the
218    // uniform view matrix, they should upload the view matrix in their setData along with regular
219    // uniforms.
220    char prefix = 'u';
221    if ('u' == name[0] || !strncmp(name, GR_NO_MANGLE_PREFIX, strlen(GR_NO_MANGLE_PREFIX))) {
222        prefix = '\0';
223    }
224    fProgramBuilder->nameVariable(uni.fVariable.accessName(), prefix, name, mangleName);
225    uni.fVariable.setArrayCount(arrayCount);
226    uni.fVisibility = kFragment_GrShaderFlag | kVertex_GrShaderFlag;
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    get_ubo_aligned_offset(&uni.fUBOffset, &fCurrentUBOOffset, &fCurrentUBOMaxAlignment, type,
232                           arrayCount);
233
234    SkString layoutQualifier;
235    layoutQualifier.appendf("offset=%d", uni.fUBOffset);
236    uni.fVariable.addLayoutQualifier(layoutQualifier.c_str());
237
238    if (outName) {
239        *outName = uni.fVariable.c_str();
240    }
241
242    return GrGLSLUniformHandler::UniformHandle(fUniforms.count() - 1);
243}
244
245GrGLSLUniformHandler::SamplerHandle GrMtlUniformHandler::addSampler(const GrTextureProxy* texture,
246                                                                    const GrSamplerState&,
247                                                                    const GrSwizzle& swizzle,
248                                                                    const char* name,
249                                                                    const GrShaderCaps* caps) {
250    SkASSERT(name && strlen(name));
251    SkString mangleName;
252    char prefix = 'u';
253    fProgramBuilder->nameVariable(&mangleName, prefix, name, true);
254
255    GrTextureType type = texture->textureType();
256
257    UniformInfo& info = fSamplers.push_back();
258    info.fVariable.setType(GrSLCombinedSamplerTypeForTextureType(type));
259    info.fVariable.setTypeModifier(GrShaderVar::kUniform_TypeModifier);
260    info.fVariable.setName(mangleName);
261    SkString layoutQualifier;
262    layoutQualifier.appendf("binding=%d", fSamplers.count() - 1);
263    info.fVariable.addLayoutQualifier(layoutQualifier.c_str());
264    info.fVisibility = kFragment_GrShaderFlag;
265    info.fUBOffset = 0;
266    SkASSERT(caps->textureSwizzleAppliedInShader());
267    fSamplerSwizzles.push_back(swizzle);
268    SkASSERT(fSamplerSwizzles.count() == fSamplers.count());
269    return GrGLSLUniformHandler::SamplerHandle(fSamplers.count() - 1);
270}
271
272void GrMtlUniformHandler::appendUniformDecls(GrShaderFlags visibility, SkString* out) const {
273    for (int i = 0; i < fSamplers.count(); ++i) {
274        const UniformInfo& sampler = fSamplers[i];
275        SkASSERT(sampler.fVariable.getType() == kTexture2DSampler_GrSLType);
276        if (visibility == sampler.fVisibility) {
277            sampler.fVariable.appendDecl(fProgramBuilder->shaderCaps(), out);
278            out->append(";\n");
279        }
280    }
281
282#ifdef SK_DEBUG
283    bool firstOffsetCheck = false;
284    for (int i = 0; i < fUniforms.count(); ++i) {
285        const UniformInfo& localUniform = fUniforms[i];
286        if (!firstOffsetCheck) {
287            // Check to make sure we are starting our offset at 0 so the offset qualifier we
288            // set on each variable in the uniform block is valid.
289            SkASSERT(0 == localUniform.fUBOffset);
290            firstOffsetCheck = true;
291        }
292    }
293#endif
294
295    SkString uniformsString;
296    for (int i = 0; i < fUniforms.count(); ++i) {
297        const UniformInfo& localUniform = fUniforms[i];
298        if (visibility & localUniform.fVisibility) {
299            if (GrSLTypeIsFloatType(localUniform.fVariable.getType())) {
300                localUniform.fVariable.appendDecl(fProgramBuilder->shaderCaps(), &uniformsString);
301                uniformsString.append(";\n");
302            }
303        }
304    }
305
306    if (!uniformsString.isEmpty()) {
307        out->appendf("layout (binding=%d) uniform uniformBuffer\n{\n", kUniformBinding);
308        out->appendf("%s\n};\n", uniformsString.c_str());
309    }
310}
311