/* * Copyright 2016 Google Inc. * * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ #include "vk/GrVkPipelineStateBuilder.h" #include "GrContext.h" #include "GrContextPriv.h" #include "GrShaderCaps.h" #include "GrStencilSettings.h" #include "GrVkRenderTarget.h" #include "vk/GrVkDescriptorSetManager.h" #include "vk/GrVkGpu.h" #include "vk/GrVkRenderPass.h" typedef size_t shader_size; GrVkPipelineState* GrVkPipelineStateBuilder::CreatePipelineState( GrVkGpu* gpu, GrRenderTarget* renderTarget, GrSurfaceOrigin origin, const GrPrimitiveProcessor& primProc, const GrTextureProxy* const primProcProxies[], const GrPipeline& pipeline, const GrStencilSettings& stencil, GrPrimitiveType primitiveType, Desc* desc, VkRenderPass compatibleRenderPass) { // create a builder. This will be handed off to effects so they can use it to add // uniforms, varyings, textures, etc GrVkPipelineStateBuilder builder(gpu, renderTarget, origin, pipeline, primProc, primProcProxies, desc); if (!builder.emitAndInstallProcs()) { return nullptr; } return builder.finalize(stencil, primitiveType, compatibleRenderPass, desc); } GrVkPipelineStateBuilder::GrVkPipelineStateBuilder(GrVkGpu* gpu, GrRenderTarget* renderTarget, GrSurfaceOrigin origin, const GrPipeline& pipeline, const GrPrimitiveProcessor& primProc, const GrTextureProxy* const primProcProxies[], GrProgramDesc* desc) : INHERITED(renderTarget, origin, primProc, primProcProxies, pipeline, desc) , fGpu(gpu) , fVaryingHandler(this) , fUniformHandler(this) {} const GrCaps* GrVkPipelineStateBuilder::caps() const { return fGpu->caps(); } void GrVkPipelineStateBuilder::finalizeFragmentOutputColor(GrShaderVar& outputColor) { outputColor.addLayoutQualifier("location = 0, index = 0"); } void GrVkPipelineStateBuilder::finalizeFragmentSecondaryColor(GrShaderVar& outputColor) { outputColor.addLayoutQualifier("location = 0, index = 1"); } bool GrVkPipelineStateBuilder::createVkShaderModule(VkShaderStageFlagBits stage, const GrGLSLShaderBuilder& builder, VkShaderModule* shaderModule, VkPipelineShaderStageCreateInfo* stageInfo, const SkSL::Program::Settings& settings, Desc* desc, SkSL::String* outSPIRV, SkSL::Program::Inputs* outInputs) { SkString shaderString; for (int i = 0; i < builder.fCompilerStrings.count(); ++i) { if (builder.fCompilerStrings[i]) { shaderString.append(builder.fCompilerStrings[i]); shaderString.append("\n"); } } if (!GrCompileVkShaderModule(fGpu, shaderString.c_str(), stage, shaderModule, stageInfo, settings, outSPIRV, outInputs)) { return false; } if (outInputs->fRTHeight) { this->addRTHeightUniform(SKSL_RTHEIGHT_NAME); } if (outInputs->fFlipY) { desc->setSurfaceOriginKey(GrGLSLFragmentShaderBuilder::KeyForSurfaceOrigin( this->origin())); } return true; } bool GrVkPipelineStateBuilder::installVkShaderModule(VkShaderStageFlagBits stage, const GrGLSLShaderBuilder& builder, VkShaderModule* shaderModule, VkPipelineShaderStageCreateInfo* stageInfo, SkSL::String spirv, SkSL::Program::Inputs inputs) { if (!GrInstallVkShaderModule(fGpu, spirv, stage, shaderModule, stageInfo)) { return false; } if (inputs.fRTHeight) { this->addRTHeightUniform(SKSL_RTHEIGHT_NAME); } return true; } int GrVkPipelineStateBuilder::loadShadersFromCache(const SkData& cached, VkShaderModule* outVertShaderModule, VkShaderModule* outFragShaderModule, VkShaderModule* outGeomShaderModule, VkPipelineShaderStageCreateInfo* outStageInfo) { // format for shader cache entries is: // shader_size vertSize; // char[vertSize] vert; // SkSL::Program::Inputs vertInputs; // shader_size fragSize; // char[fragSize] frag; // SkSL::Program::Inputs fragInputs; // shader_size geomSize; // char[geomSize] geom; // SkSL::Program::Inputs geomInputs; size_t offset = 0; // vertex shader shader_size vertSize = *((shader_size*) ((char*) cached.data() + offset)); offset += sizeof(shader_size); SkSL::String vert((char*) cached.data() + offset, vertSize); offset += vertSize; SkSL::Program::Inputs vertInputs; memcpy(&vertInputs, (char*) cached.data() + offset, sizeof(vertInputs)); offset += sizeof(vertInputs); // fragment shader shader_size fragSize = *((shader_size*) ((char*) cached.data() + offset)); offset += sizeof(shader_size); SkSL::String frag((char*) cached.data() + offset, fragSize); offset += fragSize; SkSL::Program::Inputs fragInputs; memcpy(&fragInputs, (char*) cached.data() + offset, sizeof(fragInputs)); offset += sizeof(fragInputs); // geometry shader shader_size geomSize = *((shader_size*) ((char*) cached.data() + offset)); offset += sizeof(shader_size); SkSL::String geom((char*) cached.data() + offset, geomSize); offset += geomSize; SkSL::Program::Inputs geomInputs; memcpy(&geomInputs, (char*) cached.data() + offset, sizeof(geomInputs)); offset += sizeof(geomInputs); SkASSERT(offset == cached.size()); SkAssertResult(this->installVkShaderModule(VK_SHADER_STAGE_VERTEX_BIT, fVS, outVertShaderModule, &outStageInfo[0], vert, vertInputs)); SkAssertResult(this->installVkShaderModule(VK_SHADER_STAGE_FRAGMENT_BIT, fFS, outFragShaderModule, &outStageInfo[1], frag, fragInputs)); if (geomSize) { SkAssertResult(this->installVkShaderModule(VK_SHADER_STAGE_GEOMETRY_BIT, fGS, outGeomShaderModule, &outStageInfo[2], geom, geomInputs)); return 3; } else { return 2; } } void GrVkPipelineStateBuilder::storeShadersInCache(const SkSL::String& vert, const SkSL::Program::Inputs& vertInputs, const SkSL::String& frag, const SkSL::Program::Inputs& fragInputs, const SkSL::String& geom, const SkSL::Program::Inputs& geomInputs) { Desc* desc = static_cast(this->desc()); // see loadShadersFromCache for the layout of cache entries sk_sp key = SkData::MakeWithoutCopy(desc->asKey(), desc->shaderKeyLength()); size_t dataLength = (sizeof(shader_size) + sizeof(SkSL::Program::Inputs)) * 3 + vert.length() + frag.length() + geom.length(); std::unique_ptr data(new uint8_t[dataLength]); size_t offset = 0; // vertex shader *((shader_size*) (data.get() + offset)) = (shader_size) vert.length(); offset += sizeof(shader_size); memcpy(data.get() + offset, vert.data(), vert.length()); offset += vert.length(); memcpy(data.get() + offset, &vertInputs, sizeof(vertInputs)); offset += sizeof(vertInputs); // fragment shader *((shader_size*) (data.get() + offset)) = (shader_size) frag.length(); offset += sizeof(shader_size); memcpy(data.get() + offset, frag.data(), frag.length()); offset += frag.length(); memcpy(data.get() + offset, &fragInputs, sizeof(fragInputs)); offset += sizeof(fragInputs); // geometry shader *((shader_size*) (data.get() + offset)) = (shader_size) geom.length(); offset += sizeof(shader_size); memcpy(data.get() + offset, geom.data(), geom.length()); offset += geom.length(); memcpy(data.get() + offset, &geomInputs, sizeof(geomInputs)); offset += sizeof(geomInputs); SkASSERT(offset == dataLength); this->gpu()->getContext()->contextPriv().getPersistentCache()->store( *key, *SkData::MakeWithoutCopy(data.get(), dataLength)); } GrVkPipelineState* GrVkPipelineStateBuilder::finalize(const GrStencilSettings& stencil, GrPrimitiveType primitiveType, VkRenderPass compatibleRenderPass, Desc* desc) { VkDescriptorSetLayout dsLayout[2]; VkPipelineLayout pipelineLayout; VkShaderModule vertShaderModule = VK_NULL_HANDLE; VkShaderModule geomShaderModule = VK_NULL_HANDLE; VkShaderModule fragShaderModule = VK_NULL_HANDLE; GrVkResourceProvider& resourceProvider = fGpu->resourceProvider(); // These layouts are not owned by the PipelineStateBuilder and thus should not be destroyed dsLayout[GrVkUniformHandler::kUniformBufferDescSet] = resourceProvider.getUniformDSLayout(); GrVkDescriptorSetManager::Handle samplerDSHandle; resourceProvider.getSamplerDescriptorSetHandle(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, fUniformHandler, &samplerDSHandle); dsLayout[GrVkUniformHandler::kSamplerDescSet] = resourceProvider.getSamplerDSLayout(samplerDSHandle); // Create the VkPipelineLayout VkPipelineLayoutCreateInfo layoutCreateInfo; memset(&layoutCreateInfo, 0, sizeof(VkPipelineLayoutCreateFlags)); layoutCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; layoutCreateInfo.pNext = 0; layoutCreateInfo.flags = 0; layoutCreateInfo.setLayoutCount = 2; layoutCreateInfo.pSetLayouts = dsLayout; layoutCreateInfo.pushConstantRangeCount = 0; layoutCreateInfo.pPushConstantRanges = nullptr; GR_VK_CALL_ERRCHECK(fGpu->vkInterface(), CreatePipelineLayout(fGpu->device(), &layoutCreateInfo, nullptr, &pipelineLayout)); // We need to enable the following extensions so that the compiler can correctly make spir-v // from our glsl shaders. fVS.extensions().appendf("#extension GL_ARB_separate_shader_objects : enable\n"); fFS.extensions().appendf("#extension GL_ARB_separate_shader_objects : enable\n"); fVS.extensions().appendf("#extension GL_ARB_shading_language_420pack : enable\n"); fFS.extensions().appendf("#extension GL_ARB_shading_language_420pack : enable\n"); this->finalizeShaders(); VkPipelineShaderStageCreateInfo shaderStageInfo[3]; SkSL::Program::Settings settings; settings.fCaps = this->caps()->shaderCaps(); settings.fFlipY = this->origin() != kTopLeft_GrSurfaceOrigin; settings.fSharpenTextures = this->gpu()->getContext()->contextPriv().sharpenMipmappedTextures(); SkASSERT(!this->fragColorIsInOut()); sk_sp cached; auto persistentCache = fGpu->getContext()->contextPriv().getPersistentCache(); if (persistentCache) { sk_sp key = SkData::MakeWithoutCopy(desc->asKey(), desc->shaderKeyLength()); cached = persistentCache->load(*key); } int numShaderStages; if (cached) { numShaderStages = this->loadShadersFromCache(*cached, &vertShaderModule, &fragShaderModule, &geomShaderModule, shaderStageInfo); } else { numShaderStages = 2; // We always have at least vertex and fragment stages. SkSL::String vert; SkSL::Program::Inputs vertInputs; SkSL::String frag; SkSL::Program::Inputs fragInputs; SkSL::String geom; SkSL::Program::Inputs geomInputs; SkAssertResult(this->createVkShaderModule(VK_SHADER_STAGE_VERTEX_BIT, fVS, &vertShaderModule, &shaderStageInfo[0], settings, desc, &vert, &vertInputs)); SkAssertResult(this->createVkShaderModule(VK_SHADER_STAGE_FRAGMENT_BIT, fFS, &fragShaderModule, &shaderStageInfo[1], settings, desc, &frag, &fragInputs)); if (this->primitiveProcessor().willUseGeoShader()) { SkAssertResult(this->createVkShaderModule(VK_SHADER_STAGE_GEOMETRY_BIT, fGS, &geomShaderModule, &shaderStageInfo[2], settings, desc, &geom, &geomInputs)); ++numShaderStages; } if (persistentCache) { this->storeShadersInCache(vert, vertInputs, frag, fragInputs, geom, geomInputs); } } GrVkPipeline* pipeline = resourceProvider.createPipeline(this->numColorSamples(), fPrimProc, fPipeline, stencil, shaderStageInfo, numShaderStages, primitiveType, compatibleRenderPass, pipelineLayout); GR_VK_CALL(fGpu->vkInterface(), DestroyShaderModule(fGpu->device(), vertShaderModule, nullptr)); GR_VK_CALL(fGpu->vkInterface(), DestroyShaderModule(fGpu->device(), fragShaderModule, nullptr)); // This if check should not be needed since calling destroy on a VK_NULL_HANDLE is allowed. // However this is causing a crash in certain drivers (e.g. NVidia). if (this->primitiveProcessor().willUseGeoShader()) { GR_VK_CALL(fGpu->vkInterface(), DestroyShaderModule(fGpu->device(), geomShaderModule, nullptr)); } if (!pipeline) { GR_VK_CALL(fGpu->vkInterface(), DestroyPipelineLayout(fGpu->device(), pipelineLayout, nullptr)); return nullptr; } return new GrVkPipelineState(fGpu, pipeline, pipelineLayout, samplerDSHandle, fUniformHandles, fUniformHandler.fUniforms, fUniformHandler.fCurrentGeometryUBOOffset, fUniformHandler.fCurrentFragmentUBOOffset, fUniformHandler.fSamplers, std::move(fGeometryProcessor), std::move(fXferProcessor), std::move(fFragmentProcessors), fFragmentProcessorCnt); } ////////////////////////////////////////////////////////////////////////////// uint32_t get_blend_info_key(const GrPipeline& pipeline) { GrXferProcessor::BlendInfo blendInfo; pipeline.getXferProcessor().getBlendInfo(&blendInfo); static const uint32_t kBlendWriteShift = 1; static const uint32_t kBlendCoeffShift = 5; GR_STATIC_ASSERT(kLast_GrBlendCoeff < (1 << kBlendCoeffShift)); GR_STATIC_ASSERT(kFirstAdvancedGrBlendEquation - 1 < 4); uint32_t key = blendInfo.fWriteColor; key |= (blendInfo.fSrcBlend << kBlendWriteShift); key |= (blendInfo.fDstBlend << (kBlendWriteShift + kBlendCoeffShift)); key |= (blendInfo.fEquation << (kBlendWriteShift + 2 * kBlendCoeffShift)); return key; } bool GrVkPipelineStateBuilder::Desc::Build(Desc* desc, GrRenderTarget* renderTarget, const GrPrimitiveProcessor& primProc, const GrPipeline& pipeline, const GrStencilSettings& stencil, GrPrimitiveType primitiveType, GrVkGpu* gpu) { if (!INHERITED::Build(desc, renderTarget->config(), primProc, primitiveType == GrPrimitiveType::kPoints, pipeline, gpu)) { return false; } GrProcessorKeyBuilder b(&desc->key()); b.add32(GrVkGpu::kShader_PersistentCacheKeyType); int keyLength = desc->key().count(); SkASSERT(0 == (keyLength % 4)); desc->fShaderKeyLength = SkToU32(keyLength); GrVkRenderTarget* vkRT = (GrVkRenderTarget*)renderTarget; vkRT->simpleRenderPass()->genKey(&b); stencil.genKey(&b); b.add32(get_blend_info_key(pipeline)); b.add32((uint32_t)primitiveType); return true; }