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