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