620 lines · 29.3 KB · C++
RawDownload
//  Created by dr Karol Suprynowicz on 2024/12/27.//  Based on work by by Bradley Austin Davis//  Copyright 2018 High Fidelity, Inc.//  Copyright 2023-2025 Overte e.V.////  Distributed under the Apache License, Version 2.0.//  See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html//  SPDX-License-Identifier: Apache-2.0// #include "VKPipelineCache.h" #include <iomanip> #include "VKShared.h"#include <vk/Pipelines.h>#include "VKTexture.h" using namespace gpu;using namespace gpu::vk; void Cache::Pipeline::setPipeline(const gpu::PipelinePointer& pipeline) {    if (!gpu::compare(this->pipeline, pipeline)) {        gpu::assign(this->pipeline, pipeline);        if (pipeline) {            program = pipeline->getProgram();        }        clearStrides(); // VKTODO: this doesn't fix the issue with strides for basic shapes being corrupted, sometimes strides are still cleared wile they shouldn't be so more investigation is needed    }} void Cache::Pipeline::setVertexFormat(const gpu::Stream::FormatPointer& format) {    if (!gpu::compare(this->format, format)) {        gpu::assign(this->format, format);        clearStrides();    }} void Cache::Pipeline::setFramebuffer(const gpu::FramebufferPointer& framebuffer) {    if (!gpu::compare(this->framebuffer, framebuffer)) {        gpu::assign(this->framebuffer, framebuffer);    }} void Cache::Pipeline::updateBindingMap(BindingMap& bindingMap,                             const LocationMap& locationMap,                             VkShaderStageFlagBits shaderStage) {    for (const auto& entry : locationMap) {        bindingMap[entry.second] |= shaderStage;    }} void Cache::Pipeline::setBindingMap(BindingMap& bindingMap, const LocationMap& vertexMap, const LocationMap& fragmentMap) {    bindingMap.clear();    updateBindingMap(bindingMap, vertexMap, VK_SHADER_STAGE_VERTEX_BIT);    updateBindingMap(bindingMap, fragmentMap, VK_SHADER_STAGE_FRAGMENT_BIT);} Cache::BindingMap Cache::Pipeline::getBindingMap(const LocationMap& vertexMap, const LocationMap& fragmentMap) {    BindingMap result;    setBindingMap(result, vertexMap, fragmentMap);    return result;} // VKTODO: This needs to be used instead of getPipeline// Returns structure containing pipeline layout and descriptor set layoutsCache::PipelineLayout Cache::Pipeline::getPipelineAndDescriptorLayout(const vks::Context& context) {     std::vector<VkDescriptorSetLayoutBinding> uniLayout;#define SEP_DESC 1#if SEP_DESC    std::vector<VkDescriptorSetLayoutBinding> texLayout;    std::vector<VkDescriptorSetLayoutBinding> stoLayout;#else    auto& texLayout = uniLayout;    auto& stoLayout = uniLayout;#endif    PipelineLayout layout{};     layout.vertexReflection = program->getShaders()[0]->getReflection();    layout.fragmentReflection = program->getShaders()[1]->getReflection();     layout.uniformBindingMap = getBindingMap(layout.vertexReflection.uniformBuffers, layout.fragmentReflection.uniformBuffers);    layout.textureBindingMap = getBindingMap(layout.vertexReflection.textures, layout.fragmentReflection.textures);    layout.storageBindingMap = getBindingMap(layout.vertexReflection.resourceBuffers, layout.fragmentReflection.resourceBuffers); #ifdef OVERTE_VK_PIPELINE_DEBUG    layout.vertexShader = program->getShaders()[0]->getSource().name;    layout.fragmentShader = program->getShaders()[1]->getSource().name;#endif     for (const auto& entry : layout.uniformBindingMap) {        VkDescriptorSetLayoutBinding binding =            vks::initializers::descriptorSetLayoutBinding(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, entry.second,                                                          entry.first, 1);        uniLayout.push_back(binding);    }    /*if (fragmentRefelection.textures.count("webTexture")){        printf("webTexture");    }*/    for (const auto& entry : layout.textureBindingMap) {        VkDescriptorSetLayoutBinding binding =            vks::initializers::descriptorSetLayoutBinding(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, entry.second,                                                          entry.first, 1);        texLayout.push_back(binding);    }    for (const auto& entry : layout.storageBindingMap) {        VkDescriptorSetLayoutBinding binding =            vks::initializers::descriptorSetLayoutBinding(VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, entry.second,                                                          entry.first, 1);        stoLayout.push_back(binding);    }     // Create the descriptor set layouts    std::vector<VkDescriptorSetLayout> layouts;    if (!uniLayout.empty() || !texLayout.empty() || !stoLayout.empty()) {        VkDescriptorSetLayoutCreateInfo descriptorSetLayoutCI =            vks::initializers::descriptorSetLayoutCreateInfo(uniLayout.data(), uniLayout.size());        VkDescriptorSetLayout descriptorSetLayout;        VK_CHECK_RESULT(vkCreateDescriptorSetLayout(context.device->logicalDevice, &descriptorSetLayoutCI, nullptr,                                                    &descriptorSetLayout));        layouts.push_back(descriptorSetLayout);        layout.uniformLayout = descriptorSetLayout;    }#if SEP_DESC    // Descriptor set needs to be created even if it's empty if later descriptor sets are not empty.    if (!texLayout.empty() || !stoLayout.empty()) {        VkDescriptorSetLayoutCreateInfo descriptorSetLayoutCI =            vks::initializers::descriptorSetLayoutCreateInfo(texLayout.data(), texLayout.size());        VkDescriptorSetLayout descriptorSetLayout;        VK_CHECK_RESULT(vkCreateDescriptorSetLayout(context.device->logicalDevice, &descriptorSetLayoutCI, nullptr,                                                    &descriptorSetLayout));        layouts.push_back(descriptorSetLayout);        layout.textureLayout = descriptorSetLayout;    } else {        printf("empty");    }    if (!stoLayout.empty()) {        VkDescriptorSetLayoutCreateInfo descriptorSetLayoutCI =            vks::initializers::descriptorSetLayoutCreateInfo(stoLayout.data(), stoLayout.size());        VkDescriptorSetLayout descriptorSetLayout;        VK_CHECK_RESULT(vkCreateDescriptorSetLayout(context.device->logicalDevice, &descriptorSetLayoutCI, nullptr,                                                    &descriptorSetLayout));        layouts.push_back(descriptorSetLayout);        layout.storageLayout = descriptorSetLayout;    }#endif    VkPipelineLayoutCreateInfo pipelineLayoutCI =        vks::initializers::pipelineLayoutCreateInfo(layouts.data(), (uint32_t)layouts.size());    VkPipelineLayout pipelineLayout;    VK_CHECK_RESULT(        vkCreatePipelineLayout(context.device->logicalDevice, &pipelineLayoutCI, nullptr, &pipelineLayout));     layout.pipelineLayout = pipelineLayout;     return layout;} Cache::Pipeline::RenderpassKey Cache::Pipeline::getRenderPassKey(gpu::Framebuffer* framebuffer, const vks::Context &context) const {    RenderpassKey result;    if (!framebuffer) {        result.emplace_back(VK_FORMAT_R8G8B8A8_SRGB, VK_IMAGE_LAYOUT_UNDEFINED); // VKTODO: this is definitely wrong, why is it that way?        Q_ASSERT(false);    } else {        for (const auto& attachment : framebuffer->getRenderBuffers()) {            if (attachment.isValid()) {                // VKTODO: why _element often has different format than texture's pixel format, and seemingly wrong one?                //result.push_back(evalTexelFormatInternal(attachment._element));                VkImageLayout layout = VK_IMAGE_LAYOUT_UNDEFINED;                auto *gpuTexture = Backend::getGPUObject<VKTexture>(*attachment._texture);                if (gpuTexture) {                    auto attachmentTexture = dynamic_cast<VKAttachmentTexture*>(gpuTexture);                    if (attachmentTexture) {                        layout = attachmentTexture->getVkImageLayout();                    }                }                result.emplace_back(evalTexelFormatInternal(attachment._texture->getTexelFormat(), context), layout);            }        }        if (framebuffer->hasDepthStencil()) {            VkImageLayout layout = VK_IMAGE_LAYOUT_UNDEFINED;            if (framebuffer->getDepthStencilBuffer()) {                auto* gpuTexture = Backend::getGPUObject<VKTexture>(*framebuffer->getDepthStencilBuffer());                if (gpuTexture) {                    auto attachmentTexture = dynamic_cast<VKAttachmentTexture*>(gpuTexture);                    if (attachmentTexture) {                        layout = attachmentTexture->getVkImageLayout();                    }                }            }            result.emplace_back(evalTexelFormatInternal(framebuffer->getDepthStencilBufferFormat(), context), layout);        }    }    return result;} VkRenderPass Cache::Pipeline::getRenderPass(const vks::Context& context) {    const auto framebuffer = gpu::acquire(this->framebuffer);     RenderpassKey key = getRenderPassKey(framebuffer, context);    auto itr = _renderPassMap.find(key);    if (itr == _renderPassMap.end()) {        auto &renderBuffers = framebuffer->getRenderBuffers();        std::vector<VkAttachmentDescription> attachments;        attachments.reserve(key.size());        std::vector<VkAttachmentReference> colorAttachmentReferences;        VkAttachmentReference depthReference{};        for (size_t i = 0; i < key.size(); i++) {            Q_ASSERT(i < renderBuffers.size());            std::shared_ptr<gpu::Texture> texture = renderBuffers[i]._texture;            if (!texture) {                // Last texture of the key can be depth stencil attachment                Q_ASSERT(i + 1 == key.size());                texture = framebuffer->getDepthStencilBuffer();            } else {                texture = renderBuffers[i]._texture;            }            VKAttachmentTexture *attachmentTexture = nullptr;            if (texture) {                auto gpuObject = Backend::getGPUObject<VKTexture>(*texture);                if (gpuObject) {                    attachmentTexture = dynamic_cast<VKAttachmentTexture*>(gpuObject);                }            }            VkAttachmentDescription attachment{};            attachment.format = key[i].first;            // Framebuffers are always cleared with a separate command in the renderer/            if (!attachmentTexture || attachmentTexture->getVkImageLayout() == VK_IMAGE_LAYOUT_UNDEFINED) {                attachment.loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;                attachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_CLEAR;            } else {                attachment.loadOp = VK_ATTACHMENT_LOAD_OP_LOAD;                attachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_LOAD;            }            attachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE;            attachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_STORE;            //attachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;            attachment.samples = VK_SAMPLE_COUNT_1_BIT;            if (texture->isDepthStencilRenderTarget()) {                if (!attachmentTexture || attachmentTexture->getVkImageLayout() == VK_IMAGE_LAYOUT_UNDEFINED) {                    attachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;                    attachment.finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;                    depthReference.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;                } else {                    if (attachmentTexture->getVkImageLayout() == VK_IMAGE_LAYOUT_GENERAL) {                        attachment.initialLayout = VK_IMAGE_LAYOUT_GENERAL;                        attachment.finalLayout = VK_IMAGE_LAYOUT_GENERAL;                        depthReference.layout = VK_IMAGE_LAYOUT_GENERAL;                    } else {                        Q_ASSERT(attachmentTexture->getVkImageLayout() == VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL);                        attachment.initialLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;                        attachment.finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;                        depthReference.layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL;                    }                }                depthReference.attachment = (uint32_t)(attachments.size());            } else {                if (!attachmentTexture || attachmentTexture->getVkImageLayout() == VK_IMAGE_LAYOUT_UNDEFINED) {                    attachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;                } else {                    Q_ASSERT(attachmentTexture->getVkImageLayout() == VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL);                    attachment.initialLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;                }                attachment.finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;                VkAttachmentReference reference;                reference.attachment = (uint32_t)(attachments.size());                reference.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;                colorAttachmentReferences.push_back(reference);            }            attachments.push_back(attachment);        }         std::vector<VkSubpassDescription> subpasses;        {            VkSubpassDescription subpass{};            subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;            //if (depthReference.layout != VK_IMAGE_LAYOUT_UNDEFINED) {            if (framebuffer->getDepthStencilBuffer()) {                Q_ASSERT(depthReference.layout != VK_IMAGE_LAYOUT_UNDEFINED);                subpass.pDepthStencilAttachment = &depthReference;            }            subpass.colorAttachmentCount = (uint32_t)colorAttachmentReferences.size();            subpass.pColorAttachments = colorAttachmentReferences.data();            subpasses.push_back(subpass);        }         VkRenderPassCreateInfo renderPassInfo{};        renderPassInfo.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;        renderPassInfo.attachmentCount = (uint32_t)attachments.size();        renderPassInfo.pAttachments = attachments.data();        renderPassInfo.subpassCount = (uint32_t)subpasses.size();        renderPassInfo.pSubpasses = subpasses.data();        VkRenderPass renderPass;        VK_CHECK_RESULT(vkCreateRenderPass(context.device->logicalDevice, &renderPassInfo, nullptr, &renderPass));        _renderPassMap[key] = renderPass;        return renderPass;    }    return itr->second;} // VKTODO:std::string Cache::Pipeline::getRenderpassKeyString(const RenderpassKey& renderpassKey) {    std::vector<char> key_buffer;    key_buffer.resize((sizeof(VkFormat) + sizeof(VkImageLayout) + 1) * renderpassKey.size() * 2 + 2, 0);    int key_pos = 0;    key_buffer[key_pos++] = 's';    //std::string key;    for (const auto& e : renderpassKey) {        union VkFormatBytes {            VkFormat format;            char bytes[sizeof(VkFormat)];        } formatBytes;        formatBytes.format = e.first;        union VkImageLayoutBytes {            VkImageLayout format;            char bytes[sizeof(VkFormat)];        } imageLayoutBytes;        imageLayoutBytes.format = e.second;        key_buffer[key_pos++] = '_';        for (const char &byte: formatBytes.bytes) {            key_buffer[key_pos++] = (byte >> 4) + 'A';            key_buffer[key_pos++] = (byte & 0x0f) + 'A';        }        for (const char &byte: imageLayoutBytes.bytes) {            key_buffer[key_pos++] = (byte >> 4) + 'A';            key_buffer[key_pos++] = (byte & 0x0f) + 'A';        }    }    key_buffer[key_pos] = 0;    return { key_buffer.data() }; } std::string Cache::Pipeline::getStridesKey() const {    char key_buffer[(sizeof(Offset) + 1) * MAX_NUM_INPUT_BUFFERS * 2 + 2];    int key_pos = 0;    key_buffer[key_pos++] = 's';    for (int i = 0; i < MAX_NUM_INPUT_BUFFERS; i++) {        if (_bufferStrideSet[i]) {            union OffsetBytes {                Offset offset;                char bytes[sizeof(Offset)];            } offsetBytes;            offsetBytes.offset = _bufferStrides[i];            key_buffer[key_pos++] = '_';            for (char byte : offsetBytes.bytes) {                key_buffer[key_pos++] = (byte >> 4) + 'A';                key_buffer[key_pos++] = (byte & 0x0f) + 'A';            }            //key += "_" + hex(i) + "s" + hex(_bufferStrides[i]);        }    }    key_buffer[key_pos] = 0;    return { key_buffer };} // VKTODO: use binary key if performance with text key is not good enoughstd::string Cache::Pipeline::getKey(const vks::Context& context) const {    const auto framebuffer = gpu::acquire(this->framebuffer);    RenderpassKey renderpassKey = getRenderPassKey(framebuffer, context);    const gpu::Pipeline& pipeline = *gpu::acquire(this->pipeline);    const gpu::State& state = *pipeline.getState();    const auto& vertexShader = pipeline.getProgram()->getShaders()[0]->getSource();    const auto& fragmentShader = pipeline.getProgram()->getShaders()[1]->getSource();    // VKTODO account for customized shaders (preferably by forcing shaders to have a new unique ID at runtime when they're using replacement strings)    std::string key = bytesToAscii(shader::makeProgramId(vertexShader.id, fragmentShader.id)) +          + "_" + getRenderpassKeyString(renderpassKey)          + "_" + state.getKey()          + "_" + format->getKey()          + "_" + bytesToAscii(primitiveTopology)          + "_" + getStridesKey();    return key;} VkStencilOpState Cache::getStencilOp(const gpu::State::StencilTest& stencil) {    VkStencilOpState result;    result.compareOp = (VkCompareOp)stencil.getFunction();    result.passOp = (VkStencilOp)stencil.getPassOp();    result.failOp = (VkStencilOp)stencil.getFailOp();    result.depthFailOp = (VkStencilOp)stencil.getDepthFailOp();    result.reference = stencil.getReference();    result.compareMask = stencil.getReadMask();    result.writeMask = 0xFF;    return result;} VkShaderModule Cache::getShaderModule(const vks::Context& context, const shader::Source& source) {    auto itr = moduleMap.find(source.id);    if (moduleMap.end() == itr) {        const auto& dialectSource = source.dialectSources.find(shader::Dialect::glsl450)->second;        const auto& variantSource = dialectSource.variantSources.find(shader::Variant::Mono)->second;        const auto& spirv = variantSource.spirv;        VkShaderModule result;        VkShaderModuleCreateInfo shaderModuleCreateInfo{};        shaderModuleCreateInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;        shaderModuleCreateInfo.codeSize = spirv.size();        shaderModuleCreateInfo.pCode = (const uint32_t*)spirv.data();        VK_CHECK_RESULT(vkCreateShaderModule(context.device->logicalDevice, &shaderModuleCreateInfo, nullptr, &result));        moduleMap[source.id] = result;        return result;    }    return itr->second;} const Cache::PipelineLayout& Cache::getPipeline(const vks::Context& context) {    auto key = pipelineState.getKey(context);    auto pipelineIterator = pipelineMap.find(key);    if (pipelineIterator != pipelineMap.end()) {        return pipelineIterator->second;    }     auto renderpass = pipelineState.getRenderPass(context);    auto pipelineLayout = pipelineState.getPipelineAndDescriptorLayout(context);    const gpu::Pipeline& pipeline = *gpu::acquire(pipelineState.pipeline);    const gpu::State& state = *pipeline.getState();     const auto& vertexShader = pipeline.getProgram()->getShaders()[0]->getSource();    const auto& fragmentShader = pipeline.getProgram()->getShaders()[1]->getSource();    // FIXME     const gpu::State::Data& stateData = state.getValues();    vks::pipelines::GraphicsPipelineBuilder builder{ context.device->logicalDevice, pipelineLayout.pipelineLayout,                                                     renderpass };     // Input assembly    {        auto& inputAssembly = builder.inputAssemblyState;        inputAssembly.topology = PRIMITIVE_TO_VK[pipelineState.primitiveTopology];        // VKTODO: this looks unfinished        // ia.primitiveRestartEnable = ???        // ia.topology = vk::PrimitiveTopology::eTriangleList; ???    }     // Shader modules    {        builder.shaderStages.resize(2, {});        {            auto& shaderStage = builder.shaderStages[0];            shaderStage.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;            shaderStage.stage = VK_SHADER_STAGE_VERTEX_BIT;            shaderStage.pName = "main";            shaderStage.module = getShaderModule(context, vertexShader);        }        {            auto& shaderStage = builder.shaderStages[1];            shaderStage.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;            shaderStage.stage = VK_SHADER_STAGE_FRAGMENT_BIT;            shaderStage.pName = "main";            shaderStage.module = getShaderModule(context, fragmentShader);        }    }     // Rasterization state    {        auto& rasterizationState = builder.rasterizationState;        rasterizationState.cullMode = (VkCullModeFlagBits)stateData.cullMode;        rasterizationState.depthBiasEnable = (stateData.depthBias != 0.0f && stateData.depthBiasSlopeScale != 0.0f);        // VKTODO: How come the depth bias values behave differently on OpenGL vs Vulkan?        // Facing a Text entity face-on that's directly touching another wall will sometimes        // clip into the wall, though in most cases it works perfectly fine. Weirdly,        // this doesn't seem to affect Image entities in the same situation.        rasterizationState.depthBiasConstantFactor = stateData.depthBias;        rasterizationState.depthBiasSlopeFactor = stateData.depthBiasSlopeScale;        rasterizationState.depthBiasClamp = 0.0f;        rasterizationState.depthClampEnable = stateData.flags.depthClampEnable ? VK_TRUE : VK_FALSE;  // VKTODO        rasterizationState.frontFace =            stateData.flags.frontFaceClockwise ? VK_FRONT_FACE_CLOCKWISE : VK_FRONT_FACE_COUNTER_CLOCKWISE;        // ra.lineWidth        rasterizationState.polygonMode = (VkPolygonMode)(2 - stateData.fillMode);    }     // Color blending    {        auto& colorBlendState = builder.colorBlendState;        auto& attachmentStates = colorBlendState.blendAttachmentStates;        Q_ASSERT(pipelineState.framebuffer);        auto& blendFunction = stateData.blendFunction;        auto& attachmentState = colorBlendState.blendAttachmentStates[0];        attachmentState.blendEnable = stateData.blendFunction.isEnabled();        attachmentState.srcColorBlendFactor = BLEND_ARGS_TO_VK[blendFunction.getSourceColor()];        attachmentState.dstColorBlendFactor = BLEND_ARGS_TO_VK[blendFunction.getDestinationColor()];        attachmentState.colorBlendOp = BLEND_OPS_TO_VK[blendFunction.getOperationColor()];        attachmentState.srcAlphaBlendFactor = BLEND_ARGS_TO_VK[blendFunction.getSourceAlpha()];        attachmentState.dstAlphaBlendFactor = BLEND_ARGS_TO_VK[blendFunction.getDestinationAlpha()];        attachmentState.alphaBlendOp = BLEND_OPS_TO_VK[blendFunction.getOperationAlpha()];        attachmentState.colorWriteMask = colorMaskToVk(stateData.colorWriteMask);        auto& rbs = pipelineState.framebuffer->getRenderBuffers();        uint32_t rbCount = 0;        for (const auto& rb : rbs) {            if (rb.isValid()) {                ++rbCount;            }        }         for (uint32_t i = 1; i < rbCount; ++i) {            attachmentStates.push_back(attachmentStates.back());        }    }     // Depth/Stencil

Lines 1–500 of 620.