Home | History | Annotate | Download | only in vk
      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 "GrVkPipelineState.h"
      9 
     10 #include "GrPipeline.h"
     11 #include "GrTexturePriv.h"
     12 #include "GrVkCommandBuffer.h"
     13 #include "GrVkDescriptorPool.h"
     14 #include "GrVkDescriptorSet.h"
     15 #include "GrVkGpu.h"
     16 #include "GrVkImageView.h"
     17 #include "GrVkMemory.h"
     18 #include "GrVkPipeline.h"
     19 #include "GrVkRenderTarget.h"
     20 #include "GrVkSampler.h"
     21 #include "GrVkTexture.h"
     22 #include "GrVkUniformBuffer.h"
     23 #include "glsl/GrGLSLFragmentProcessor.h"
     24 #include "glsl/GrGLSLGeometryProcessor.h"
     25 #include "glsl/GrGLSLXferProcessor.h"
     26 #include "SkMipMap.h"
     27 
     28 GrVkPipelineState::GrVkPipelineState(GrVkGpu* gpu,
     29                                      const GrVkPipelineState::Desc& desc,
     30                                      GrVkPipeline* pipeline,
     31                                      VkPipelineLayout layout,
     32                                      const GrVkDescriptorSetManager::Handle& samplerDSHandle,
     33                                      const BuiltinUniformHandles& builtinUniformHandles,
     34                                      const UniformInfoArray& uniforms,
     35                                      uint32_t vertexUniformSize,
     36                                      uint32_t fragmentUniformSize,
     37                                      uint32_t numSamplers,
     38                                      GrGLSLPrimitiveProcessor* geometryProcessor,
     39                                      GrGLSLXferProcessor* xferProcessor,
     40                                      const GrGLSLFragProcs& fragmentProcessors)
     41     : fPipeline(pipeline)
     42     , fPipelineLayout(layout)
     43     , fUniformDescriptorSet(nullptr)
     44     , fSamplerDescriptorSet(nullptr)
     45     , fSamplerDSHandle(samplerDSHandle)
     46     , fStartDS(SK_MaxS32)
     47     , fDSCount(0)
     48     , fBuiltinUniformHandles(builtinUniformHandles)
     49     , fGeometryProcessor(geometryProcessor)
     50     , fXferProcessor(xferProcessor)
     51     , fFragmentProcessors(fragmentProcessors)
     52     , fDesc(desc)
     53     , fDataManager(uniforms, vertexUniformSize, fragmentUniformSize) {
     54     fSamplers.setReserve(numSamplers);
     55     fTextureViews.setReserve(numSamplers);
     56     fTextures.setReserve(numSamplers);
     57 
     58     fDescriptorSets[0] = VK_NULL_HANDLE;
     59     fDescriptorSets[1] = VK_NULL_HANDLE;
     60 
     61     // Currently we are always binding a descriptor set for uniform buffers.
     62     if (vertexUniformSize || fragmentUniformSize) {
     63         fDSCount++;
     64         fStartDS = GrVkUniformHandler::kUniformBufferDescSet;
     65     }
     66     if (numSamplers) {
     67         fDSCount++;
     68         fStartDS = SkTMin(fStartDS, (int)GrVkUniformHandler::kSamplerDescSet);
     69     }
     70 
     71     fVertexUniformBuffer.reset(GrVkUniformBuffer::Create(gpu, vertexUniformSize));
     72     fFragmentUniformBuffer.reset(GrVkUniformBuffer::Create(gpu, fragmentUniformSize));
     73 
     74     fNumSamplers = numSamplers;
     75 }
     76 
     77 GrVkPipelineState::~GrVkPipelineState() {
     78     // Must have freed all GPU resources before this is destroyed
     79     SkASSERT(!fPipeline);
     80     SkASSERT(!fPipelineLayout);
     81     SkASSERT(!fSamplers.count());
     82     SkASSERT(!fTextureViews.count());
     83     SkASSERT(!fTextures.count());
     84     for (int i = 0; i < fFragmentProcessors.count(); ++i) {
     85         delete fFragmentProcessors[i];
     86     }
     87 }
     88 
     89 void GrVkPipelineState::freeTempResources(const GrVkGpu* gpu) {
     90     for (int i = 0; i < fSamplers.count(); ++i) {
     91         fSamplers[i]->unref(gpu);
     92     }
     93     fSamplers.rewind();
     94 
     95     for (int i = 0; i < fTextureViews.count(); ++i) {
     96             fTextureViews[i]->unref(gpu);
     97     }
     98     fTextureViews.rewind();
     99 
    100     for (int i = 0; i < fTextures.count(); ++i) {
    101             fTextures[i]->unref(gpu);
    102     }
    103     fTextures.rewind();
    104 }
    105 
    106 void GrVkPipelineState::freeGPUResources(const GrVkGpu* gpu) {
    107     if (fPipeline) {
    108         fPipeline->unref(gpu);
    109         fPipeline = nullptr;
    110     }
    111 
    112     if (fPipelineLayout) {
    113         GR_VK_CALL(gpu->vkInterface(), DestroyPipelineLayout(gpu->device(),
    114                                                              fPipelineLayout,
    115                                                              nullptr));
    116         fPipelineLayout = VK_NULL_HANDLE;
    117     }
    118 
    119     if (fVertexUniformBuffer) {
    120         fVertexUniformBuffer->release(gpu);
    121     }
    122 
    123     if (fFragmentUniformBuffer) {
    124         fFragmentUniformBuffer->release(gpu);
    125     }
    126 
    127     if (fUniformDescriptorSet) {
    128         fUniformDescriptorSet->recycle(const_cast<GrVkGpu*>(gpu));
    129         fUniformDescriptorSet = nullptr;
    130     }
    131 
    132     if (fSamplerDescriptorSet) {
    133         fSamplerDescriptorSet->recycle(const_cast<GrVkGpu*>(gpu));
    134         fSamplerDescriptorSet = nullptr;
    135     }
    136 
    137     this->freeTempResources(gpu);
    138 }
    139 
    140 void GrVkPipelineState::abandonGPUResources() {
    141     fPipeline->unrefAndAbandon();
    142     fPipeline = nullptr;
    143 
    144     fPipelineLayout = VK_NULL_HANDLE;
    145 
    146     fVertexUniformBuffer->abandon();
    147     fFragmentUniformBuffer->abandon();
    148 
    149     for (int i = 0; i < fSamplers.count(); ++i) {
    150         fSamplers[i]->unrefAndAbandon();
    151     }
    152     fSamplers.rewind();
    153 
    154     for (int i = 0; i < fTextureViews.count(); ++i) {
    155         fTextureViews[i]->unrefAndAbandon();
    156     }
    157     fTextureViews.rewind();
    158 
    159     for (int i = 0; i < fTextures.count(); ++i) {
    160         fTextures[i]->unrefAndAbandon();
    161     }
    162     fTextures.rewind();
    163 
    164     if (fUniformDescriptorSet) {
    165         fUniformDescriptorSet->unrefAndAbandon();
    166         fUniformDescriptorSet = nullptr;
    167     }
    168 
    169     if (fSamplerDescriptorSet) {
    170         fSamplerDescriptorSet->unrefAndAbandon();
    171         fSamplerDescriptorSet = nullptr;
    172     }
    173 }
    174 
    175 static void append_texture_bindings(const GrProcessor& processor,
    176                                     SkTArray<const GrProcessor::TextureSampler*>* textureBindings) {
    177     // We don't support image storages in VK.
    178     SkASSERT(!processor.numImageStorages());
    179     if (int numTextureSamplers = processor.numTextureSamplers()) {
    180         const GrProcessor::TextureSampler** bindings =
    181                 textureBindings->push_back_n(numTextureSamplers);
    182         int i = 0;
    183         do {
    184             bindings[i] = &processor.textureSampler(i);
    185         } while (++i < numTextureSamplers);
    186     }
    187 }
    188 
    189 void GrVkPipelineState::setData(GrVkGpu* gpu,
    190                                 const GrPrimitiveProcessor& primProc,
    191                                 const GrPipeline& pipeline) {
    192     // This is here to protect against someone calling setData multiple times in a row without
    193     // freeing the tempData between calls.
    194     this->freeTempResources(gpu);
    195 
    196     this->setRenderTargetState(pipeline);
    197 
    198     SkSTArray<8, const GrProcessor::TextureSampler*> textureBindings;
    199 
    200     fGeometryProcessor->setData(fDataManager, primProc,
    201                                 GrFragmentProcessor::CoordTransformIter(pipeline));
    202     append_texture_bindings(primProc, &textureBindings);
    203 
    204     GrFragmentProcessor::Iter iter(pipeline);
    205     GrGLSLFragmentProcessor::Iter glslIter(fFragmentProcessors.begin(),
    206                                            fFragmentProcessors.count());
    207     const GrFragmentProcessor* fp = iter.next();
    208     GrGLSLFragmentProcessor* glslFP = glslIter.next();
    209     while (fp && glslFP) {
    210         glslFP->setData(fDataManager, *fp);
    211         append_texture_bindings(*fp, &textureBindings);
    212         fp = iter.next();
    213         glslFP = glslIter.next();
    214     }
    215     SkASSERT(!fp && !glslFP);
    216 
    217     fXferProcessor->setData(fDataManager, pipeline.getXferProcessor());
    218     append_texture_bindings(pipeline.getXferProcessor(), &textureBindings);
    219 
    220     // Get new descriptor sets
    221     if (fNumSamplers) {
    222         if (fSamplerDescriptorSet) {
    223             fSamplerDescriptorSet->recycle(gpu);
    224         }
    225         fSamplerDescriptorSet = gpu->resourceProvider().getSamplerDescriptorSet(fSamplerDSHandle);
    226         int samplerDSIdx = GrVkUniformHandler::kSamplerDescSet;
    227         fDescriptorSets[samplerDSIdx] = fSamplerDescriptorSet->descriptorSet();
    228         this->writeSamplers(gpu, textureBindings, pipeline.getAllowSRGBInputs());
    229     }
    230 
    231     if (fVertexUniformBuffer.get() || fFragmentUniformBuffer.get()) {
    232         if (fDataManager.uploadUniformBuffers(gpu,
    233                                               fVertexUniformBuffer.get(),
    234                                               fFragmentUniformBuffer.get())
    235             || !fUniformDescriptorSet)
    236         {
    237             if (fUniformDescriptorSet) {
    238                 fUniformDescriptorSet->recycle(gpu);
    239             }
    240             fUniformDescriptorSet = gpu->resourceProvider().getUniformDescriptorSet();
    241             int uniformDSIdx = GrVkUniformHandler::kUniformBufferDescSet;
    242             fDescriptorSets[uniformDSIdx] = fUniformDescriptorSet->descriptorSet();
    243             this->writeUniformBuffers(gpu);
    244         }
    245     }
    246 }
    247 
    248 void GrVkPipelineState::writeUniformBuffers(const GrVkGpu* gpu) {
    249     VkWriteDescriptorSet descriptorWrites[2];
    250     memset(descriptorWrites, 0, 2 * sizeof(VkWriteDescriptorSet));
    251 
    252     uint32_t firstUniformWrite = 0;
    253     uint32_t uniformBindingUpdateCount = 0;
    254 
    255     VkDescriptorBufferInfo vertBufferInfo;
    256     // Vertex Uniform Buffer
    257     if (fVertexUniformBuffer.get()) {
    258         ++uniformBindingUpdateCount;
    259         memset(&vertBufferInfo, 0, sizeof(VkDescriptorBufferInfo));
    260         vertBufferInfo.buffer = fVertexUniformBuffer->buffer();
    261         vertBufferInfo.offset = fVertexUniformBuffer->offset();
    262         vertBufferInfo.range = fVertexUniformBuffer->size();
    263 
    264         descriptorWrites[0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
    265         descriptorWrites[0].pNext = nullptr;
    266         descriptorWrites[0].dstSet = fDescriptorSets[GrVkUniformHandler::kUniformBufferDescSet];
    267         descriptorWrites[0].dstBinding = GrVkUniformHandler::kVertexBinding;
    268         descriptorWrites[0].dstArrayElement = 0;
    269         descriptorWrites[0].descriptorCount = 1;
    270         descriptorWrites[0].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
    271         descriptorWrites[0].pImageInfo = nullptr;
    272         descriptorWrites[0].pBufferInfo = &vertBufferInfo;
    273         descriptorWrites[0].pTexelBufferView = nullptr;
    274     }
    275 
    276     VkDescriptorBufferInfo fragBufferInfo;
    277     // Fragment Uniform Buffer
    278     if (fFragmentUniformBuffer.get()) {
    279         if (0 == uniformBindingUpdateCount) {
    280             firstUniformWrite = 1;
    281         }
    282         ++uniformBindingUpdateCount;
    283         memset(&fragBufferInfo, 0, sizeof(VkDescriptorBufferInfo));
    284         fragBufferInfo.buffer = fFragmentUniformBuffer->buffer();
    285         fragBufferInfo.offset = fFragmentUniformBuffer->offset();
    286         fragBufferInfo.range = fFragmentUniformBuffer->size();
    287 
    288         descriptorWrites[1].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
    289         descriptorWrites[1].pNext = nullptr;
    290         descriptorWrites[1].dstSet = fDescriptorSets[GrVkUniformHandler::kUniformBufferDescSet];
    291         descriptorWrites[1].dstBinding = GrVkUniformHandler::kFragBinding;;
    292         descriptorWrites[1].dstArrayElement = 0;
    293         descriptorWrites[1].descriptorCount = 1;
    294         descriptorWrites[1].descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
    295         descriptorWrites[1].pImageInfo = nullptr;
    296         descriptorWrites[1].pBufferInfo = &fragBufferInfo;
    297         descriptorWrites[1].pTexelBufferView = nullptr;
    298     }
    299 
    300     if (uniformBindingUpdateCount) {
    301         GR_VK_CALL(gpu->vkInterface(), UpdateDescriptorSets(gpu->device(),
    302                                                             uniformBindingUpdateCount,
    303                                                             &descriptorWrites[firstUniformWrite],
    304                                                             0, nullptr));
    305     }
    306 }
    307 
    308 void GrVkPipelineState::writeSamplers(
    309         GrVkGpu* gpu,
    310         const SkTArray<const GrProcessor::TextureSampler*>& textureBindings,
    311         bool allowSRGBInputs) {
    312     SkASSERT(fNumSamplers == textureBindings.count());
    313 
    314     for (int i = 0; i < textureBindings.count(); ++i) {
    315         const GrSamplerParams& params = textureBindings[i]->params();
    316 
    317         GrVkTexture* texture = static_cast<GrVkTexture*>(textureBindings[i]->texture());
    318 
    319         fSamplers.push(gpu->resourceProvider().findOrCreateCompatibleSampler(params,
    320                                                           texture->texturePriv().maxMipMapLevel()));
    321 
    322         const GrVkResource* textureResource = texture->resource();
    323         textureResource->ref();
    324         fTextures.push(textureResource);
    325 
    326         const GrVkImageView* textureView = texture->textureView(allowSRGBInputs);
    327         textureView->ref();
    328         fTextureViews.push(textureView);
    329 
    330         VkDescriptorImageInfo imageInfo;
    331         memset(&imageInfo, 0, sizeof(VkDescriptorImageInfo));
    332         imageInfo.sampler = fSamplers[i]->sampler();
    333         imageInfo.imageView = textureView->imageView();
    334         imageInfo.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
    335 
    336         VkWriteDescriptorSet writeInfo;
    337         memset(&writeInfo, 0, sizeof(VkWriteDescriptorSet));
    338         writeInfo.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
    339         writeInfo.pNext = nullptr;
    340         writeInfo.dstSet = fDescriptorSets[GrVkUniformHandler::kSamplerDescSet];
    341         writeInfo.dstBinding = i;
    342         writeInfo.dstArrayElement = 0;
    343         writeInfo.descriptorCount = 1;
    344         writeInfo.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
    345         writeInfo.pImageInfo = &imageInfo;
    346         writeInfo.pBufferInfo = nullptr;
    347         writeInfo.pTexelBufferView = nullptr;
    348 
    349         GR_VK_CALL(gpu->vkInterface(), UpdateDescriptorSets(gpu->device(),
    350                                                             1,
    351                                                             &writeInfo,
    352                                                             0,
    353                                                             nullptr));
    354     }
    355 }
    356 
    357 void GrVkPipelineState::setRenderTargetState(const GrPipeline& pipeline) {
    358     // Load the RT height uniform if it is needed to y-flip gl_FragCoord.
    359     if (fBuiltinUniformHandles.fRTHeightUni.isValid() &&
    360         fRenderTargetState.fRenderTargetSize.fHeight != pipeline.getRenderTarget()->height()) {
    361         fDataManager.set1f(fBuiltinUniformHandles.fRTHeightUni,
    362                                   SkIntToScalar(pipeline.getRenderTarget()->height()));
    363     }
    364 
    365     // set RT adjustment
    366     const GrRenderTarget* rt = pipeline.getRenderTarget();
    367     SkISize size;
    368     size.set(rt->width(), rt->height());
    369     SkASSERT(fBuiltinUniformHandles.fRTAdjustmentUni.isValid());
    370     if (fRenderTargetState.fRenderTargetOrigin != rt->origin() ||
    371         fRenderTargetState.fRenderTargetSize != size) {
    372         fRenderTargetState.fRenderTargetSize = size;
    373         fRenderTargetState.fRenderTargetOrigin = rt->origin();
    374 
    375         float rtAdjustmentVec[4];
    376         fRenderTargetState.getRTAdjustmentVec(rtAdjustmentVec);
    377         fDataManager.set4fv(fBuiltinUniformHandles.fRTAdjustmentUni, 1, rtAdjustmentVec);
    378     }
    379 }
    380 
    381 void GrVkPipelineState::bind(const GrVkGpu* gpu, GrVkCommandBuffer* commandBuffer) {
    382     commandBuffer->bindPipeline(gpu, fPipeline);
    383 
    384     if (fDSCount) {
    385         commandBuffer->bindDescriptorSets(gpu, this, fPipelineLayout, fStartDS, fDSCount,
    386                                           &fDescriptorSets[fStartDS], 0, nullptr);
    387     }
    388 }
    389 
    390 void GrVkPipelineState::addUniformResources(GrVkCommandBuffer& commandBuffer) {
    391     if (fUniformDescriptorSet) {
    392         commandBuffer.addRecycledResource(fUniformDescriptorSet);
    393     }
    394     if (fSamplerDescriptorSet) {
    395         commandBuffer.addRecycledResource(fSamplerDescriptorSet);
    396     }
    397 
    398     if (fVertexUniformBuffer.get()) {
    399         commandBuffer.addRecycledResource(fVertexUniformBuffer->resource());
    400     }
    401     if (fFragmentUniformBuffer.get()) {
    402         commandBuffer.addRecycledResource(fFragmentUniformBuffer->resource());
    403     }
    404 
    405     for (int i = 0; i < fSamplers.count(); ++i) {
    406         commandBuffer.addResource(fSamplers[i]);
    407     }
    408 
    409     for (int i = 0; i < fTextureViews.count(); ++i) {
    410         commandBuffer.addResource(fTextureViews[i]);
    411     }
    412 
    413     for (int i = 0; i < fTextures.count(); ++i) {
    414         commandBuffer.addResource(fTextures[i]);
    415     }
    416 }
    417 
    418 ////////////////////////////////////////////////////////////////////////////////
    419 
    420 void GrVkPipelineState::DescriptorPoolManager::getNewPool(GrVkGpu* gpu) {
    421     if (fPool) {
    422         fPool->unref(gpu);
    423         uint32_t newPoolSize = fMaxDescriptors + ((fMaxDescriptors + 1) >> 1);
    424         if (newPoolSize < kMaxDescLimit) {
    425             fMaxDescriptors = newPoolSize;
    426         } else {
    427             fMaxDescriptors = kMaxDescLimit;
    428         }
    429 
    430     }
    431     if (fMaxDescriptors) {
    432         fPool = gpu->resourceProvider().findOrCreateCompatibleDescriptorPool(fDescType,
    433                                                                              fMaxDescriptors);
    434     }
    435     SkASSERT(fPool || !fMaxDescriptors);
    436 }
    437 
    438 void GrVkPipelineState::DescriptorPoolManager::getNewDescriptorSet(GrVkGpu* gpu,
    439                                                                    VkDescriptorSet* ds) {
    440     if (!fMaxDescriptors) {
    441         return;
    442     }
    443     fCurrentDescriptorCount += fDescCountPerSet;
    444     if (fCurrentDescriptorCount > fMaxDescriptors) {
    445         this->getNewPool(gpu);
    446         fCurrentDescriptorCount = fDescCountPerSet;
    447     }
    448 
    449     VkDescriptorSetAllocateInfo dsAllocateInfo;
    450     memset(&dsAllocateInfo, 0, sizeof(VkDescriptorSetAllocateInfo));
    451     dsAllocateInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
    452     dsAllocateInfo.pNext = nullptr;
    453     dsAllocateInfo.descriptorPool = fPool->descPool();
    454     dsAllocateInfo.descriptorSetCount = 1;
    455     dsAllocateInfo.pSetLayouts = &fDescLayout;
    456     GR_VK_CALL_ERRCHECK(gpu->vkInterface(), AllocateDescriptorSets(gpu->device(),
    457                                                                    &dsAllocateInfo,
    458                                                                    ds));
    459 }
    460 
    461 void GrVkPipelineState::DescriptorPoolManager::freeGPUResources(const GrVkGpu* gpu) {
    462     if (fDescLayout) {
    463         GR_VK_CALL(gpu->vkInterface(), DestroyDescriptorSetLayout(gpu->device(), fDescLayout,
    464                                                                   nullptr));
    465         fDescLayout = VK_NULL_HANDLE;
    466     }
    467 
    468     if (fPool) {
    469         fPool->unref(gpu);
    470         fPool = nullptr;
    471     }
    472 }
    473 
    474 void GrVkPipelineState::DescriptorPoolManager::abandonGPUResources() {
    475     fDescLayout = VK_NULL_HANDLE;
    476     if (fPool) {
    477         fPool->unrefAndAbandon();
    478         fPool = nullptr;
    479     }
    480 }
    481 
    482 uint32_t get_blend_info_key(const GrPipeline& pipeline) {
    483     GrXferProcessor::BlendInfo blendInfo;
    484     pipeline.getXferProcessor().getBlendInfo(&blendInfo);
    485 
    486     static const uint32_t kBlendWriteShift = 1;
    487     static const uint32_t kBlendCoeffShift = 5;
    488     GR_STATIC_ASSERT(kLast_GrBlendCoeff < (1 << kBlendCoeffShift));
    489     GR_STATIC_ASSERT(kFirstAdvancedGrBlendEquation - 1 < 4);
    490 
    491     uint32_t key = blendInfo.fWriteColor;
    492     key |= (blendInfo.fSrcBlend << kBlendWriteShift);
    493     key |= (blendInfo.fDstBlend << (kBlendWriteShift + kBlendCoeffShift));
    494     key |= (blendInfo.fEquation << (kBlendWriteShift + 2 * kBlendCoeffShift));
    495 
    496     return key;
    497 }
    498 
    499 bool GrVkPipelineState::Desc::Build(Desc* desc,
    500                                     const GrPrimitiveProcessor& primProc,
    501                                     const GrPipeline& pipeline,
    502                                     const GrStencilSettings& stencil,
    503                                     GrPrimitiveType primitiveType,
    504                                     const GrShaderCaps& caps) {
    505     if (!INHERITED::Build(desc, primProc, primitiveType == kPoints_GrPrimitiveType, pipeline,
    506                           caps)) {
    507         return false;
    508     }
    509 
    510     GrProcessorKeyBuilder b(&desc->key());
    511     GrVkRenderTarget* vkRT = (GrVkRenderTarget*)pipeline.getRenderTarget();
    512     vkRT->simpleRenderPass()->genKey(&b);
    513 
    514     stencil.genKey(&b);
    515 
    516     SkASSERT(sizeof(GrDrawFace) <= sizeof(uint32_t));
    517     b.add32((int32_t)pipeline.getDrawFace());
    518 
    519     b.add32(get_blend_info_key(pipeline));
    520 
    521     b.add32(primitiveType);
    522 
    523     return true;
    524 }
    525