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 "src/gpu/vk/GrVkUniformHandler.h" 9 10#include "src/gpu/GrTexture.h" 11#include "src/gpu/glsl/GrGLSLProgramBuilder.h" 12#include "src/gpu/vk/GrVkGpu.h" 13#include "src/gpu/vk/GrVkPipelineStateBuilder.h" 14#include "src/gpu/vk/GrVkTexture.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 24static uint32_t grsltype_to_alignment_mask(GrSLType type) { 25 switch(type) { 26 case kShort_GrSLType: // fall through 27 case kUShort_GrSLType: 28 return 0x1; 29 case kShort2_GrSLType: // fall through 30 case kUShort2_GrSLType: 31 return 0x3; 32 case kShort3_GrSLType: // fall through 33 case kShort4_GrSLType: 34 case kUShort3_GrSLType: 35 case kUShort4_GrSLType: 36 return 0x7; 37 case kInt_GrSLType: 38 case kUInt_GrSLType: 39 return 0x3; 40 case kInt2_GrSLType: 41 case kUInt2_GrSLType: 42 return 0x7; 43 case kInt3_GrSLType: 44 case kUInt3_GrSLType: 45 case kInt4_GrSLType: 46 case kUInt4_GrSLType: 47 return 0xF; 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 kHalf2x2_GrSLType: // fall through 61 case kFloat2x2_GrSLType: 62 return 0x7; 63 case kHalf3x3_GrSLType: // fall through 64 case kFloat3x3_GrSLType: 65 return 0xF; 66 case kHalf4x4_GrSLType: // fall through 67 case kFloat4x4_GrSLType: 68 return 0xF; 69 70 // This query is only valid for certain types. 71 case kVoid_GrSLType: 72 case kBool_GrSLType: 73 case kBool2_GrSLType: 74 case kBool3_GrSLType: 75 case kBool4_GrSLType: 76 case kTexture2DSampler_GrSLType: 77 case kTextureExternalSampler_GrSLType: 78 case kTexture2DRectSampler_GrSLType: 79 case kSampler_GrSLType: 80 case kTexture2D_GrSLType: 81 case kInput_GrSLType: 82 break; 83 } 84 SK_ABORT("Unexpected type"); 85} 86 87/** Returns the size in bytes taken up in vulkanbuffers for GrSLTypes. */ 88static inline uint32_t grsltype_to_vk_size(GrSLType type, int layout) { 89 switch(type) { 90 case kShort_GrSLType: 91 return sizeof(int16_t); 92 case kShort2_GrSLType: 93 return 2 * sizeof(int16_t); 94 case kShort3_GrSLType: 95 return 3 * sizeof(int16_t); 96 case kShort4_GrSLType: 97 return 4 * sizeof(int16_t); 98 case kUShort_GrSLType: 99 return sizeof(uint16_t); 100 case kUShort2_GrSLType: 101 return 2 * sizeof(uint16_t); 102 case kUShort3_GrSLType: 103 return 3 * sizeof(uint16_t); 104 case kUShort4_GrSLType: 105 return 4 * sizeof(uint16_t); 106 case kHalf_GrSLType: // fall through 107 case kFloat_GrSLType: 108 return sizeof(float); 109 case kHalf2_GrSLType: // fall through 110 case kFloat2_GrSLType: 111 return 2 * sizeof(float); 112 case kHalf3_GrSLType: // fall through 113 case kFloat3_GrSLType: 114 return 3 * sizeof(float); 115 case kHalf4_GrSLType: // fall through 116 case kFloat4_GrSLType: 117 return 4 * sizeof(float); 118 case kInt_GrSLType: // fall through 119 case kUInt_GrSLType: 120 return sizeof(int32_t); 121 case kInt2_GrSLType: // fall through 122 case kUInt2_GrSLType: 123 return 2 * sizeof(int32_t); 124 case kInt3_GrSLType: // fall through 125 case kUInt3_GrSLType: 126 return 3 * sizeof(int32_t); 127 case kInt4_GrSLType: // fall through 128 case kUInt4_GrSLType: 129 return 4 * sizeof(int32_t); 130 case kHalf2x2_GrSLType: // fall through 131 case kFloat2x2_GrSLType: 132 if (layout == GrVkUniformHandler::kStd430Layout) { 133 return 4 * sizeof(float); 134 } else { 135 return 8 * sizeof(float); 136 } 137 case kHalf3x3_GrSLType: // fall through 138 case kFloat3x3_GrSLType: 139 return 12 * sizeof(float); 140 case kHalf4x4_GrSLType: // fall through 141 case kFloat4x4_GrSLType: 142 return 16 * sizeof(float); 143 144 // This query is only valid for certain types. 145 case kVoid_GrSLType: 146 case kBool_GrSLType: 147 case kBool2_GrSLType: 148 case kBool3_GrSLType: 149 case kBool4_GrSLType: 150 case kTexture2DSampler_GrSLType: 151 case kTextureExternalSampler_GrSLType: 152 case kTexture2DRectSampler_GrSLType: 153 case kSampler_GrSLType: 154 case kTexture2D_GrSLType: 155 case kInput_GrSLType: 156 break; 157 } 158 SK_ABORT("Unexpected type"); 159} 160 161// Given the current offset into the ubo data, calculate the offset for the uniform we're trying to 162// add taking into consideration all alignment requirements. The uniformOffset is set to the offset 163// for the new uniform, and currentOffset is updated to be the offset to the end of the new uniform. 164static uint32_t get_aligned_offset(uint32_t* currentOffset, 165 GrSLType type, 166 int arrayCount, 167 int layout) { 168 uint32_t alignmentMask = grsltype_to_alignment_mask(type); 169 // For std140 layout we must make arrays align to 16 bytes. 170 if (layout == GrVkUniformHandler::kStd140Layout && (arrayCount || type == kFloat2x2_GrSLType)) { 171 alignmentMask = 0xF; 172 } 173 uint32_t offsetDiff = *currentOffset & alignmentMask; 174 if (offsetDiff != 0) { 175 offsetDiff = alignmentMask - offsetDiff + 1; 176 } 177 int32_t uniformOffset = *currentOffset + offsetDiff; 178 SkASSERT(sizeof(float) == 4); 179 if (arrayCount) { 180 // TODO: this shouldn't be necessary for std430 181 uint32_t elementSize = std::max<uint32_t>(16, grsltype_to_vk_size(type, layout)); 182 SkASSERT(0 == (elementSize & 0xF)); 183 *currentOffset = uniformOffset + elementSize * arrayCount; 184 } else { 185 *currentOffset = uniformOffset + grsltype_to_vk_size(type, layout); 186 } 187 return uniformOffset; 188} 189 190GrVkUniformHandler::~GrVkUniformHandler() { 191 for (VkUniformInfo& sampler : fSamplers.items()) { 192 if (sampler.fImmutableSampler) { 193 sampler.fImmutableSampler->unref(); 194 sampler.fImmutableSampler = nullptr; 195 } 196 } 197} 198 199GrGLSLUniformHandler::UniformHandle GrVkUniformHandler::internalAddUniformArray( 200 const GrFragmentProcessor* owner, 201 uint32_t visibility, 202 GrSLType type, 203 const char* name, 204 bool mangleName, 205 int arrayCount, 206 const char** outName) { 207 SkASSERT(name && strlen(name)); 208 SkASSERT(GrSLTypeCanBeUniformValue(type)); 209 210 // TODO this is a bit hacky, lets think of a better way. Basically we need to be able to use 211 // the uniform view matrix name in the GP, and the GP is immutable so it has to tell the PB 212 // exactly what name it wants to use for the uniform view matrix. If we prefix anythings, then 213 // the names will mismatch. I think the correct solution is to have all GPs which need the 214 // uniform view matrix, they should upload the view matrix in their setData along with regular 215 // uniforms. 216 char prefix = 'u'; 217 if ('u' == name[0] || !strncmp(name, GR_NO_MANGLE_PREFIX, strlen(GR_NO_MANGLE_PREFIX))) { 218 prefix = '\0'; 219 } 220 SkString resolvedName = fProgramBuilder->nameVariable(prefix, name, mangleName); 221 222 VkUniformInfo tempInfo; 223 tempInfo.fVariable = GrShaderVar{std::move(resolvedName), 224 type, 225 GrShaderVar::TypeModifier::None, 226 arrayCount}; 227 228 tempInfo.fVisibility = visibility; 229 tempInfo.fOwner = owner; 230 tempInfo.fRawName = SkString(name); 231 232 for (int layout = 0; layout < kLayoutCount; ++layout) { 233 tempInfo.fOffsets[layout] = get_aligned_offset(&fCurrentOffsets[layout], 234 type, 235 arrayCount, 236 layout); 237 } 238 239 fUniforms.push_back(tempInfo); 240 241 if (outName) { 242 *outName = fUniforms.back().fVariable.c_str(); 243 } 244 245 return GrGLSLUniformHandler::UniformHandle(fUniforms.count() - 1); 246} 247 248GrGLSLUniformHandler::SamplerHandle GrVkUniformHandler::addSampler( 249 const GrBackendFormat& backendFormat, GrSamplerState state, const GrSwizzle& swizzle, 250 const char* name, const GrShaderCaps* shaderCaps) { 251 SkASSERT(name && strlen(name)); 252 253 const char prefix = 'u'; 254 SkString mangleName = fProgramBuilder->nameVariable(prefix, name, /*mangle=*/true); 255 256 SkString layoutQualifier; 257 layoutQualifier.appendf("set=%d, binding=%d", kSamplerDescSet, fSamplers.count()); 258 259 VkUniformInfo tempInfo; 260 tempInfo.fVariable = 261 GrShaderVar{std::move(mangleName), 262 GrSLCombinedSamplerTypeForTextureType(backendFormat.textureType()), 263 GrShaderVar::TypeModifier::Uniform, 264 GrShaderVar::kNonArray, 265 std::move(layoutQualifier), 266 SkString()}; 267 268 tempInfo.fVisibility = kFragment_GrShaderFlag; 269 tempInfo.fOwner = nullptr; 270 tempInfo.fRawName = SkString(name); 271 tempInfo.fOffsets[0] = 0; 272 tempInfo.fOffsets[1] = 0; 273 274 fSamplers.push_back(tempInfo); 275 276 // Check if we are dealing with an external texture and store the needed information if so. 277 auto ycbcrInfo = backendFormat.getVkYcbcrConversionInfo(); 278 if (ycbcrInfo && ycbcrInfo->isValid()) { 279 GrVkGpu* gpu = static_cast<GrVkPipelineStateBuilder*>(fProgramBuilder)->gpu(); 280 GrVkSampler* sampler = gpu->resourceProvider().findOrCreateCompatibleSampler(state, 281 *ycbcrInfo); 282 fSamplers.back().fImmutableSampler = sampler; 283 if (!sampler) { 284 return {}; 285 } 286 } 287 288 fSamplerSwizzles.push_back(swizzle); 289 SkASSERT(fSamplerSwizzles.count() == fSamplers.count()); 290 return GrGLSLUniformHandler::SamplerHandle(fSamplers.count() - 1); 291} 292 293GrGLSLUniformHandler::SamplerHandle GrVkUniformHandler::addInputSampler(const GrSwizzle& swizzle, 294 const char* name) { 295 SkASSERT(name && strlen(name)); 296 SkASSERT(fInputUniform.fVariable.getType() == kVoid_GrSLType); 297 298 const char prefix = 'u'; 299 SkString mangleName = fProgramBuilder->nameVariable(prefix, name, /*mangle=*/true); 300 301 SkString layoutQualifier; 302 layoutQualifier.appendf("input_attachment_index=%d, set=%d, binding=%d", 303 kDstInputAttachmentIndex, kInputDescSet, kInputBinding); 304 305 fInputUniform = { 306 GrShaderVar{std::move(mangleName), kInput_GrSLType, GrShaderVar::TypeModifier::Uniform, 307 GrShaderVar::kNonArray, std::move(layoutQualifier), SkString()}, 308 kFragment_GrShaderFlag, nullptr, SkString(name)}; 309 fInputSwizzle = swizzle; 310 return GrGLSLUniformHandler::SamplerHandle(0); 311} 312 313void GrVkUniformHandler::appendUniformDecls(GrShaderFlags visibility, SkString* out) const { 314 for (const VkUniformInfo& sampler : fSamplers.items()) { 315 SkASSERT(sampler.fVariable.getType() == kTexture2DSampler_GrSLType || 316 sampler.fVariable.getType() == kTextureExternalSampler_GrSLType); 317 if (visibility == sampler.fVisibility) { 318 sampler.fVariable.appendDecl(fProgramBuilder->shaderCaps(), out); 319 out->append(";\n"); 320 } 321 } 322 if (fInputUniform.fVariable.getType() == kInput_GrSLType) { 323 if (visibility == fInputUniform.fVisibility) { 324 SkASSERT(visibility == kFragment_GrShaderFlag); 325 fInputUniform.fVariable.appendDecl(fProgramBuilder->shaderCaps(), out); 326 out->append(";\n"); 327 } 328 } 329 330#ifdef SK_DEBUG 331 bool firstOffsetCheck = false; 332 for (const VkUniformInfo& localUniform : fUniforms.items()) { 333 if (!firstOffsetCheck) { 334 // Check to make sure we are starting our offset at 0 so the offset qualifier we 335 // set on each variable in the uniform block is valid. 336 SkASSERT(0 == localUniform.fOffsets[kStd140Layout] && 337 0 == localUniform.fOffsets[kStd430Layout]); 338 firstOffsetCheck = true; 339 } 340 } 341#endif 342 343 // At this point we determine whether we'll be using push constants based on the 344 // uniforms set so far. Later checks will use the internal bool we set here to 345 // keep things consistent. 346 this->determineIfUsePushConstants(); 347 SkString uniformsString; 348 for (const VkUniformInfo& localUniform : fUniforms.items()) { 349 if (visibility & localUniform.fVisibility) { 350 if (GrSLTypeCanBeUniformValue(localUniform.fVariable.getType())) { 351 Layout layout = fUsePushConstants ? kStd430Layout : kStd140Layout; 352 uniformsString.appendf("layout(offset=%d) ", localUniform.fOffsets[layout]); 353 localUniform.fVariable.appendDecl(fProgramBuilder->shaderCaps(), &uniformsString); 354 uniformsString.append(";\n"); 355 } 356 } 357 } 358 359 if (!uniformsString.isEmpty()) { 360 if (fUsePushConstants) { 361 out->append("layout (push_constant) "); 362 } else { 363 out->appendf("layout (set=%d, binding=%d) ", 364 kUniformBufferDescSet, kUniformBinding); 365 } 366 out->append("uniform uniformBuffer\n{\n"); 367 out->appendf("%s\n};\n", uniformsString.c_str()); 368 } 369} 370 371uint32_t GrVkUniformHandler::getRTFlipOffset() const { 372 Layout layout = fUsePushConstants ? kStd430Layout : kStd140Layout; 373 uint32_t currentOffset = fCurrentOffsets[layout]; 374 return get_aligned_offset(¤tOffset, kFloat2_GrSLType, 0, layout); 375} 376 377void GrVkUniformHandler::determineIfUsePushConstants() const { 378 // We may insert a uniform for flipping origin-sensitive language features (e.g. sk_FragCoord). 379 // We won't know that for sure until then but we need to make this determination now, 380 // so assume we will need it. 381 static constexpr uint32_t kPad = 2*sizeof(float); 382 fUsePushConstants = 383 fCurrentOffsets[kStd430Layout] > 0 && 384 fCurrentOffsets[kStd430Layout] + kPad <= fProgramBuilder->caps()->maxPushConstantsSize(); 385} 386