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