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). 21static 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. */ 91static 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. 175static void get_ubo_aligned_offset(uint32_t* uniformOffset, 176 uint32_t* currentOffset, 177 uint32_t* maxAlignment, 178 GrSLType type, 179 int arrayCount) { 180 uint32_t alignmentMask = grsltype_to_alignment_mask(type); 181 if (alignmentMask > *maxAlignment) { 182 *maxAlignment = alignmentMask; 183 } 184 uint32_t offsetDiff = *currentOffset & alignmentMask; 185 if (offsetDiff != 0) { 186 offsetDiff = alignmentMask - offsetDiff + 1; 187 } 188 *uniformOffset = *currentOffset + offsetDiff; 189 SkASSERT(sizeof(float) == 4); 190 if (arrayCount) { 191 uint32_t elementSize = SkTMax<uint32_t>(16, grsltype_to_mtl_size(type)); 192 SkASSERT(0 == (elementSize & 0xF)); 193 *currentOffset = *uniformOffset + elementSize * arrayCount; 194 } else { 195 *currentOffset = *uniformOffset + grsltype_to_mtl_size(type); 196 } 197} 198 199GrGLSLUniformHandler::UniformHandle GrMtlUniformHandler::internalAddUniformArray( 200 uint32_t visibility, 201 GrSLType type, 202 const char* name, 203 bool mangleName, 204 int arrayCount, 205 const char** outName) { 206 SkASSERT(name && strlen(name)); 207 // For now asserting the the visibility is either geometry types (vertex, tesselation, geometry, 208 // etc.) or only fragment. 209 SkASSERT(kVertex_GrShaderFlag == visibility || 210 kGeometry_GrShaderFlag == visibility || 211 (kVertex_GrShaderFlag | kGeometry_GrShaderFlag) == visibility || 212 kFragment_GrShaderFlag == visibility); 213 GrSLTypeIsFloatType(type); 214 215 UniformInfo& uni = fUniforms.push_back(); 216 uni.fVariable.setType(type); 217 // TODO this is a bit hacky, lets think of a better way. Basically we need to be able to use 218 // the uniform view matrix name in the GP, and the GP is immutable so it has to tell the PB 219 // exactly what name it wants to use for the uniform view matrix. If we prefix anythings, then 220 // the names will mismatch. I think the correct solution is to have all GPs which need the 221 // uniform view matrix, they should upload the view matrix in their setData along with regular 222 // uniforms. 223 char prefix = 'u'; 224 if ('u' == name[0] || !strncmp(name, GR_NO_MANGLE_PREFIX, strlen(GR_NO_MANGLE_PREFIX))) { 225 prefix = '\0'; 226 } 227 fProgramBuilder->nameVariable(uni.fVariable.accessName(), prefix, name, mangleName); 228 uni.fVariable.setArrayCount(arrayCount); 229 uni.fVisibility = visibility; 230 // When outputing the GLSL, only the outer uniform block will get the Uniform modifier. Thus 231 // we set the modifier to none for all uniforms declared inside the block. 232 uni.fVariable.setTypeModifier(GrShaderVar::kNone_TypeModifier); 233 234 uint32_t* currentOffset; 235 uint32_t* maxAlignment; 236 uint32_t geomStages = kVertex_GrShaderFlag | kGeometry_GrShaderFlag; 237 if (geomStages & visibility) { 238 currentOffset = &fCurrentGeometryUBOOffset; 239 maxAlignment = &fCurrentGeometryUBOMaxAlignment; 240 } else { 241 SkASSERT(kFragment_GrShaderFlag == visibility); 242 currentOffset = &fCurrentFragmentUBOOffset; 243 maxAlignment = &fCurrentFragmentUBOMaxAlignment; 244 } 245 get_ubo_aligned_offset(&uni.fUBOffset, currentOffset, maxAlignment, type, arrayCount); 246 247 SkString layoutQualifier; 248 layoutQualifier.appendf("offset=%d", uni.fUBOffset); 249 uni.fVariable.addLayoutQualifier(layoutQualifier.c_str()); 250 251 if (outName) { 252 *outName = uni.fVariable.c_str(); 253 } 254 255 return GrGLSLUniformHandler::UniformHandle(fUniforms.count() - 1); 256} 257 258GrGLSLUniformHandler::SamplerHandle GrMtlUniformHandler::addSampler(const GrTexture* texture, 259 const GrSamplerState&, 260 const char* name, 261 const GrShaderCaps* caps) { 262 SkASSERT(name && strlen(name)); 263 SkString mangleName; 264 char prefix = 'u'; 265 fProgramBuilder->nameVariable(&mangleName, prefix, name, true); 266 267 GrSwizzle swizzle = caps->configTextureSwizzle(texture->config()); 268 GrTextureType type = texture->texturePriv().textureType(); 269 270 UniformInfo& info = fSamplers.push_back(); 271 info.fVariable.setType(GrSLCombinedSamplerTypeForTextureType(type)); 272 info.fVariable.setTypeModifier(GrShaderVar::kUniform_TypeModifier); 273 info.fVariable.setName(mangleName); 274 SkString layoutQualifier; 275 layoutQualifier.appendf("binding=%d", fSamplers.count() - 1); 276 info.fVariable.addLayoutQualifier(layoutQualifier.c_str()); 277 info.fVisibility = kFragment_GrShaderFlag; 278 info.fUBOffset = 0; 279 fSamplerSwizzles.push_back(swizzle); 280 SkASSERT(fSamplerSwizzles.count() == fSamplers.count()); 281 return GrGLSLUniformHandler::SamplerHandle(fSamplers.count() - 1); 282} 283 284void GrMtlUniformHandler::appendUniformDecls(GrShaderFlags visibility, SkString* out) const { 285 SkASSERT(kVertex_GrShaderFlag == visibility || 286 kGeometry_GrShaderFlag == visibility || 287 kFragment_GrShaderFlag == visibility); 288 289 for (int i = 0; i < fSamplers.count(); ++i) { 290 const UniformInfo& sampler = fSamplers[i]; 291 SkASSERT(sampler.fVariable.getType() == kTexture2DSampler_GrSLType); 292 if (visibility == sampler.fVisibility) { 293 sampler.fVariable.appendDecl(fProgramBuilder->shaderCaps(), out); 294 out->append(";\n"); 295 } 296 } 297 298#ifdef SK_DEBUG 299 bool firstGeomOffsetCheck = false; 300 bool firstFragOffsetCheck = false; 301 for (int i = 0; i < fUniforms.count(); ++i) { 302 const UniformInfo& localUniform = fUniforms[i]; 303 if (kVertex_GrShaderFlag == localUniform.fVisibility || 304 kGeometry_GrShaderFlag == localUniform.fVisibility || 305 (kVertex_GrShaderFlag | kGeometry_GrShaderFlag) == localUniform.fVisibility) { 306 if (!firstGeomOffsetCheck) { 307 // Check to make sure we are starting our offset at 0 so the offset qualifier we 308 // set on each variable in the uniform block is valid. 309 SkASSERT(0 == localUniform.fUBOffset); 310 firstGeomOffsetCheck = true; 311 } 312 } else { 313 SkASSERT(kFragment_GrShaderFlag == localUniform.fVisibility); 314 if (!firstFragOffsetCheck) { 315 // Check to make sure we are starting our offset at 0 so the offset qualifier we 316 // set on each variable in the uniform block is valid. 317 SkASSERT(0 == localUniform.fUBOffset); 318 firstFragOffsetCheck = true; 319 } 320 } 321 } 322#endif 323 324 SkString uniformsString; 325 for (int i = 0; i < fUniforms.count(); ++i) { 326 const UniformInfo& localUniform = fUniforms[i]; 327 if (visibility & localUniform.fVisibility) { 328 if (GrSLTypeIsFloatType(localUniform.fVariable.getType())) { 329 localUniform.fVariable.appendDecl(fProgramBuilder->shaderCaps(), &uniformsString); 330 uniformsString.append(";\n"); 331 } 332 } 333 } 334 335 if (!uniformsString.isEmpty()) { 336 uint32_t uniformBinding; 337 const char* stage; 338 if (kVertex_GrShaderFlag == visibility) { 339 uniformBinding = kGeometryBinding; 340 stage = "vertex"; 341 } else if (kGeometry_GrShaderFlag == visibility) { 342 uniformBinding = kGeometryBinding; 343 stage = "geometry"; 344 } else { 345 SkASSERT(kFragment_GrShaderFlag == visibility); 346 uniformBinding = kFragBinding; 347 stage = "fragment"; 348 } 349 out->appendf("layout (binding=%d) uniform %sUniformBuffer\n{\n", uniformBinding, stage); 350 out->appendf("%s\n};\n", uniformsString.c_str()); 351 } 352} 353