Repository: google/filament
File: filament/backend/src/vulkan/VulkanDriver.cpp (C++)

Code before:

    // If async prewarming is enabled, let's find the proper layout and build the pipeline.
    std::array<resource_ptr<VulkanDescriptorSetLayout>, MAX_DESCRIPTOR_SET_COUNT> layouts {};
    VulkanDescriptorSetLayout::DescriptorSetLayoutArray vkLayouts {};
    bool hasExternalSamplers = false;
    for (const auto& layoutBinding : program.getDescriptorSetLayouts()) {
        DescriptorSetLayout layoutDescription = layoutBinding.layout;
        auto layoutHandle = mResourceManager.allocHandle<VulkanDescriptorSetLayout>();
        auto layout = mDescriptorSetLayoutCache.createLayout(layoutHandle, std::move(layoutDescription));
        layouts[layoutBinding.set] = layout;
        vkLayouts[layoutBinding.set] = layout->getVkLayout();
        if (layout->bitmask.externalSampler.count() > 0) {
            hasExternalSamplers = true;
        }
    }

    StereoscopicType stereoscopicType = mStereoscopicType;
    if (stereoscopicType == StereoscopicType::MULTIVIEW && !program.isMultiview()) {
        stereoscopicType = StereoscopicType::NONE;
    }

    // If there are no external samplers, that's it! We have enough to compile the
    // fake pipeline at this point. If there are, though, this is unnecessary - there
    // MUST be some external samplers defined. In that case, we'll go on to start
    // instantiating the YCbCr conversions for them.
    if (!hasExternalSamplers) {
        mPipelineCache.asyncPrewarmCache(
            *vprogram.get(),
            mPipelineLayoutCache.getLayout(vkLayouts, vprogram),
            stereoscopicType,
            mStereoscopicEyeCount,
            program.getPriorityQueue());
        return;
    }

    // If we have external samplers, let's do this again with the external samplers
    // specified.
    for (const auto& format : mPlatform->getCachePrewarmExternalFormats()) {
        // The values that seem to matter in terms of cache hits here are the model conversion,
        // and model range. We need some value for externalFormat that is known to support that
        // pair of values, but we need not find every possible externalFormat. As we test on
        // more devices, this may change.
        VulkanYcbcrConversionCache::Params ycbcrConversion {
            .conversion = {
                .ycbcrModel = fvkutils::getYcbcrModelConversionFilament(format.ycbcrModelConversion),
                .r = fvkutils::getSwizzleFilament(VK_COMPONENT_SWIZZLE_R, 0),
                .g = fvkutils::getSwizzleFilament(VK_COMPONENT_SWIZZLE_G, 1),
                .b = fvkutils::getSwizzleFilament(VK_COMPONENT_SWIZZLE_B, 2),
                .a = fvkutils::getSwizzleFilament(VK_COMPONENT_SWIZZLE_A, 3),
                .ycbcrRange = fvkutils::getYcbcrRangeFilament(format.ycbcrRange),
                .xChromaOffset = fvkutils::getChromaLocationFilament(VK_CHROMA_LOCATION_MIDPOINT),

Review comment (suggestion):
So we do need to build both version when an external sampler is present.

Is possible to have a material that has only 1 external sampler, which can be used with non-YUV images and in this case when building the pipeline it will hitch once.

Code after:
}

void VulkanDriver::destroyTexture(Handle<HwTexture> th) {
    if (!th) {
        return;
    }
    auto texture = resource_ptr<VulkanTexture>::cast(&mResourceManager, th);
    texture.dec();

    mExternalImageManager.removeExternallySampledTexture(texture);
}

void VulkanDriver::createProgramR(Handle<HwProgram> ph, Program&& program, utils::ImmutableCString&& tag) {
    FVK_SYSTRACE_SCOPE();
    auto vprogram = resource_ptr<VulkanProgram>::make(&mResourceManager, ph, mPlatform->getDevice(),
            program);
    vprogram.inc();
    mResourceManager.associateHandle(ph.getId(), std::move(tag));

    if (!mContext.shouldUsePipelineCachePrewarming()) {
        return;
    }

    // If async prewarming is enabled, let's find the proper layout and build the pipeline.
    std::array<resource_ptr<VulkanDescriptorSetLayout>, MAX_DESCRIPTOR_SET_COUNT> layouts {};
    VulkanDescriptorSetLayout::DescriptorSetLayoutArray vkLayouts {};
    bool hasExternalSamplers = false;
    for (const auto& layoutBinding : program.getDescriptorSetLayouts()) {
        DescriptorSetLayout layoutDescription = layoutBinding.layout;
        auto layoutHandle = mResourceManager.allocHandle<VulkanDescriptorSetLayout>();
        auto layout = mDescriptorSetLayoutCache.createLayout(layoutHandle, std::move(layoutDescription));
        layouts[layoutBinding.set] = layout;
        vkLayouts[layoutBinding.set] = layout->getVkLayout();
        if (layout->bitmask.externalSampler.count() > 0) {
            hasExternalSamplers = true;
        }
    }

    StereoscopicType stereoscopicType = mStereoscopicType;
    if (stereoscopicType == StereoscopicType::MULTIVIEW && !program.isMultiview()) {
        stereoscopicType = StereoscopicType::NONE;
    }

    // Base case - build the pipeline without any external samplers.
    mPipelineCache.asyncPrewarmCache(
        *vprogram.get(),
        mPipelineLayoutCache.getLayout(vkLayouts, vprogram),
        stereoscopicType,
        mStereoscopicEyeCount,
        program.getPriorityQueue());