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