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 GrTexture* 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->texturePriv().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