c++ GLFW/Vulkan透明帧缓冲区不支持交换链源图像Alpha通道?

u1ehiz5o  于 2023-06-07  发布在  其他
关注(0)|答案(1)|浏览(189)

我有一个启用透明的GLFW窗口,Vulkan Swapchain使用VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR,图形管道使用Normal Blend (Src * One) + (Dst * One_Minus_Src)
这分为三个部分,窗口/交换链/图形spipeline创建。另请参阅项目的Github以获取更多信息:https://github.com/Yaazarai/TinyVulkan-Dynamic
问题是这样的:透明帧缓冲区可以工作,但它不考虑Alpha通道,因为唯一支持的交换链合成Alpha模式是VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR,这使得帧缓冲区在合成多个窗口曲面时始终具有Alpha分量1。
我已经检查了每个复合模式,当不使用VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR时,Vulkan总是出错。改变混合模式也不起作用,因为帧缓冲区的alpha分量总是1。

Validation Layer: Validation Error: [ VUID-VkSwapchainCreateInfoKHR-compositeAlpha-01280 ] Object 0: handle = 0x2655f873250, type = VK_OBJECT_TYPE_DEVICE; | MessageID = 0xb0051a12 | vkCreateSwapchainKHR() called with a non-supported pCreateInfo->compositeAlpha (i.e. VK_COMPOSITE_ALPHA_PRE_MULTIPLIED_BIT_KHR).  Supported values are:
    VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR
. The Vulkan spec states: compositeAlpha must be one of the bits present in the supportedCompositeAlpha member of the VkSurfaceCapabilitiesKHR structure returned by vkGetPhysicalDeviceSurfaceCapabilitiesKHR for the surface
// Creates a Vulkan Swapchain with the supported Surface Capabilities based
// on the GLFW Vulkan Window surface passed to the VkPhysicalDevice.
void CreateSwapChainImages(uint32_t width = 0, uint32_t height = 0) {
    TinyVkSwapChainSupporter swapChainSupport = QuerySwapChainSupport(renderDevice.physicalDevice);
    VkSurfaceFormatKHR surfaceFormat = QuerySwapSurfaceFormat(swapChainSupport.formats);
    VkPresentModeKHR presentMode = QuerySwapPresentMode(swapChainSupport.presentModes);
    VkExtent2D extent = QuerySwapExtent(swapChainSupport.capabilities);
    uint32_t imageCount = std::min(swapChainSupport.capabilities.maxImageCount, std::max(swapChainSupport.capabilities.minImageCount, static_cast<uint32_t>(bufferingMode)));

    if (width != 0 && height != 0) {
        extent = {
            std::min(std::max((uint32_t)width, swapChainSupport.capabilities.minImageExtent.width), swapChainSupport.capabilities.maxImageExtent.width),
            std::min(std::max((uint32_t)height, swapChainSupport.capabilities.minImageExtent.height), swapChainSupport.capabilities.maxImageExtent.height)
        };
    } else {
        extent = QuerySwapExtent(swapChainSupport.capabilities);
    }

    if (swapChainSupport.capabilities.maxImageCount > 0 && imageCount > swapChainSupport.capabilities.maxImageCount)
        imageCount = swapChainSupport.capabilities.maxImageCount;

    VkSwapchainCreateInfoKHR createInfo{};
    createInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
    createInfo.surface = renderDevice.presentationSurface;
    createInfo.minImageCount = imageCount;
    createInfo.imageFormat = surfaceFormat.format;
    createInfo.imageColorSpace = surfaceFormat.colorSpace;
    createInfo.imageExtent = extent;
    createInfo.imageArrayLayers = 1; // Change when developing VR or other 3D stereoscopic applications
    createInfo.imageUsage = imageUsage;

    TinyVkQueueFamily indices = TinyVkQueueFamily::FindQueueFamilies(renderDevice.physicalDevice, renderDevice.presentationSurface);
    uint32_t queueFamilyIndices[] = { indices.graphicsFamily.value(), indices.presentFamily.value() };

    if (indices.graphicsFamily != indices.presentFamily) {
        createInfo.imageSharingMode = VK_SHARING_MODE_CONCURRENT;
        createInfo.queueFamilyIndexCount = 2;
        createInfo.pQueueFamilyIndices = queueFamilyIndices;
    } else {
        createInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
        createInfo.queueFamilyIndexCount = 0; // Optional
        createInfo.pQueueFamilyIndices = nullptr; // Optional
    }

    createInfo.preTransform = swapChainSupport.capabilities.currentTransform;
    createInfo.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
    createInfo.presentMode = presentMode;
    createInfo.clipped = VK_TRUE;
    createInfo.oldSwapchain = swapChain;

    if (vkCreateSwapchainKHR(renderDevice.logicalDevice, &createInfo, nullptr, &swapChain) != VK_SUCCESS)
        throw std::runtime_error("TinyVulkan: Failed to create swap chain!");

    vkGetSwapchainImagesKHR(renderDevice.logicalDevice, swapChain, &imageCount, nullptr);
    images.resize(imageCount);
    vkGetSwapchainImagesKHR(renderDevice.logicalDevice, swapChain, &imageCount, images.data());

    imageFormat = surfaceFormat.format;
    imageExtent = extent;
}

// Creates the graphics pipeline with the appropriate Normal Blend mod
void CreateGraphicsPipeline() {
    ///////////////////////////////////////////////////////////////////////////////////////////////////////
    /////////// This section specifies that MvkVertex provides the vertex layout description ///////////
    const VkVertexInputBindingDescription bindingDescription = vertexDescription.binding;
    const std::vector<VkVertexInputAttributeDescription>& attributeDescriptions = vertexDescription.attributes;

    VkPipelineVertexInputStateCreateInfo vertexInputInfo{};
    vertexInputInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
    vertexInputInfo.vertexBindingDescriptionCount = 1;
    vertexInputInfo.pVertexBindingDescriptions = &bindingDescription;
    vertexInputInfo.vertexAttributeDescriptionCount = static_cast<uint32_t>(attributeDescriptions.size());
    vertexInputInfo.pVertexAttributeDescriptions = attributeDescriptions.data();
    ///////////////////////////////////////////////////////////////////////////////////////////////////////
    ///////////////////////////////////////////////////////////////////////////////////////////////////////
    VkPipelineLayoutCreateInfo pipelineLayoutInfo{};
    pipelineLayoutInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
                
    pipelineLayoutInfo.pushConstantRangeCount = 0;
    uint32_t pushConstantRangeCount = static_cast<uint32_t>(pushConstantRanges.size());
    if (pushConstantRangeCount > 0) {
        pipelineLayoutInfo.pushConstantRangeCount = pushConstantRangeCount;
        pipelineLayoutInfo.pPushConstantRanges = pushConstantRanges.data();
    }

    if (descriptorBindings.size() > 0) {
        VkDescriptorSetLayoutCreateInfo descriptorCreateInfo{};
        descriptorCreateInfo.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
        descriptorCreateInfo.flags = VK_DESCRIPTOR_SET_LAYOUT_CREATE_PUSH_DESCRIPTOR_BIT_KHR;
        descriptorCreateInfo.bindingCount = static_cast<uint32_t>(descriptorBindings.size());
        descriptorCreateInfo.pBindings = descriptorBindings.data();

        if (vkCreateDescriptorSetLayout(renderDevice.logicalDevice, &descriptorCreateInfo, nullptr, &descriptorLayout) != VK_SUCCESS)
            throw std::runtime_error("TinyVulkan: Failed to create push descriptor bindings! ");

        pipelineLayoutInfo.setLayoutCount = 1;
        pipelineLayoutInfo.pSetLayouts = &descriptorLayout;
    }

    if (vkCreatePipelineLayout(renderDevice.logicalDevice, &pipelineLayoutInfo, nullptr, &pipelineLayout) != VK_SUCCESS)
        throw std::runtime_error("TinyVulkan: Failed to create graphics pipeline layout!");
    ///////////////////////////////////////////////////////////////////////////////////////////////////////
    ///////////////////////////////////////////////////////////////////////////////////////////////////////
    VkPipelineInputAssemblyStateCreateInfo inputAssembly{};
    inputAssembly.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
    inputAssembly.topology = vertexTopology;
    inputAssembly.primitiveRestartEnable = VK_FALSE;

    VkPipelineViewportStateCreateInfo viewportState{};
    viewportState.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
    viewportState.viewportCount = 1;
    viewportState.scissorCount = 1;
    viewportState.flags = 0;

    VkPipelineRasterizationStateCreateInfo rasterizer{};
    rasterizer.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
    rasterizer.depthClampEnable = VK_FALSE;
    rasterizer.rasterizerDiscardEnable = VK_FALSE;
    rasterizer.polygonMode = polgyonTopology;
    rasterizer.lineWidth = 1.0f;
    rasterizer.cullMode = VK_CULL_MODE_BACK_BIT;
    rasterizer.frontFace = VK_FRONT_FACE_CLOCKWISE;
    rasterizer.depthBiasEnable = VK_FALSE;

    VkPipelineMultisampleStateCreateInfo multisampling{};
    multisampling.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
    multisampling.sampleShadingEnable = VK_FALSE;
    multisampling.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;

    VkPipelineColorBlendStateCreateInfo colorBlending{};
    colorBlending.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
    colorBlending.logicOpEnable = VK_FALSE;
    colorBlending.logicOp = VK_LOGIC_OP_COPY;
    colorBlending.attachmentCount = 1;

    VkPipelineColorBlendAttachmentState blendDescription = colorBlendState;
    colorBlending.pAttachments = &blendDescription;
    colorBlending.blendConstants[0] = 0.0f;
    colorBlending.blendConstants[1] = 0.0f;
    colorBlending.blendConstants[2] = 0.0f;
    colorBlending.blendConstants[3] = 0.0f;

    const std::array<VkDynamicState, 2> dynamicStateEnables = { VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR };
    dynamicState.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO;
    dynamicState.flags = 0;
    dynamicState.pDynamicStates = dynamicStateEnables.data();
    dynamicState.dynamicStateCount = static_cast<uint32_t>(dynamicStateEnables.size());
    dynamicState.pNext = nullptr;

    VkPipelineRenderingCreateInfoKHR renderingCreateInfo{};
    renderingCreateInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO_KHR;
    renderingCreateInfo.colorAttachmentCount = 1;
    renderingCreateInfo.pColorAttachmentFormats = &imageFormat;
    renderingCreateInfo.depthAttachmentFormat = QueryDepthFormat();

    VkPipelineDepthStencilStateCreateInfo depthStencilInfo{};
    depthStencilInfo.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO;
    depthStencilInfo.depthTestEnable = enableDepthTesting;
    depthStencilInfo.depthWriteEnable = enableDepthTesting;
    depthStencilInfo.depthCompareOp = VK_COMPARE_OP_LESS;
    depthStencilInfo.depthBoundsTestEnable = VK_FALSE;
    depthStencilInfo.minDepthBounds = 0.0f; // Optional
    depthStencilInfo.maxDepthBounds = 1.0f; // Optional
    depthStencilInfo.stencilTestEnable = VK_FALSE;
    depthStencilInfo.front = {}; // Optional
    depthStencilInfo.back = {}; // Optional

    ///////////////////////////////////////////////////////////////////////////////////////////////////////
    ///////////////////////////////////////////////////////////////////////////////////////////////////////
    VkGraphicsPipelineCreateInfo pipelineInfo{};
    pipelineInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
    pipelineInfo.stageCount = static_cast<uint32_t>(shaderStages.shaderCreateInfo.size());
    pipelineInfo.pStages = shaderStages.shaderCreateInfo.data();
    pipelineInfo.pVertexInputState = &vertexInputInfo;
    pipelineInfo.pInputAssemblyState = &inputAssembly;
    pipelineInfo.pViewportState = &viewportState;
    pipelineInfo.pRasterizationState = &rasterizer;
    pipelineInfo.pMultisampleState = &multisampling;
    pipelineInfo.pColorBlendState = &colorBlending;
    pipelineInfo.pDepthStencilState = &depthStencilInfo;

    pipelineInfo.pDynamicState = &dynamicState;
    pipelineInfo.pNext = &renderingCreateInfo;

    pipelineInfo.layout = pipelineLayout;
    pipelineInfo.renderPass = nullptr;
    pipelineInfo.subpass = 0;
    pipelineInfo.basePipelineHandle = VK_NULL_HANDLE; // Optional
    pipelineInfo.basePipelineIndex = -1; // Optional

    if (vkCreateGraphicsPipelines(renderDevice.logicalDevice, VK_NULL_HANDLE, 1, &pipelineInfo, nullptr, &graphicsPipeline) != VK_SUCCESS)
        throw std::runtime_error("TinyVulkan: Failed to create graphics pipeline!");
}

// Creates the GLFW transparent framebuffer enabled window.
TinyVkWindow(std::string title, int width, int height, bool resizable, bool transparentFramebuffer = false, bool hasMinSize = false, int minWidth = 200, int minHeight = 200) {
    onDispose.hook(callback<bool>([this](bool forceDispose){this->Disposable(forceDispose); }));
    onWindowResized.hook(callback<GLFWwindow*, int, int>([this](GLFWwindow* hwnd, int width, int height) { if (hwnd != hwndWindow) return; hwndWidth = width; hwndHeight = height; }));
    onWindowPositionMoved.hook(callback<GLFWwindow*, int, int>([this](GLFWwindow* hwnd, int xpos, int ypos) { if (hwnd != hwndWindow) return; hwndXpos = xpos; hwndYpos = ypos; }));

    hwndWindow = InitiateWindow(title, width, height, resizable, transparentFramebuffer);
    glfwSetWindowUserPointer(hwndWindow, this);
    glfwSetFramebufferSizeCallback(hwndWindow, TinyVkWindow::OnFrameBufferNotifyReSizeCallback);
    glfwSetWindowPosCallback(hwndWindow, TinyVkWindow::OnWindowPositionCallback);

    if (hasMinSize) glfwSetWindowSizeLimits(hwndWindow, minWidth, minHeight, GLFW_DONT_CARE, GLFW_DONT_CARE);

    InitGLFWInput();
}

这里有一个奇怪的地方,透明的帧缓冲区可以工作,帧缓冲区的VkClearColor也可以清除窗口表面,但正如所述,alpha分量被忽略了。这意味着曲面合成期间的Alpha分量由透明颜色的RGB分量的值确定,如下所示,给定暗紫色(RGBA)0.1, 0.0, 0.1, 0.0的透明颜色。正确的结果应该是透明的,黑色的,没有由于0.0阿尔法不尊重,所以我们得到紫色代替。

mrphzbgm

mrphzbgm1#

好吧,我不完全理解预乘alpha颜色的数学,但一切实际上都按预期工作(感谢Discord图形编程)。问题是,使用预乘alpha时,零alpha分量不是有效的颜色,因此当使用SwapChain渲染时,颜色合成器的行为是意外的-正如前面所说的,这是因为我不能在SwapChain上使用VK_COMPOSITE_PREMULITPLIED_ALPHA。然后,解决方案是使用渲染到纹理模型进行渲染,然后将其输出到SwapChain以获得所需的结果。
然而,如果你想要显式透明的窗口(GIF录制应用程序或其他东西的Idk),那么预乘alpha实际上是可取的,因为现在你的背景清晰的颜色(如下面的图2所示)与合成清晰的背景预乘。使用render-to-texture然后render-to-swapchain时情况并非如此(下图1)-您会注意到输出图像更暗,因为它是用图像渲染器预乘以黑色的。
这个答案是由图形编程/Vulkan Discord社区提供的。
参考:https://ciechanow.ski/alpha-compositing/

相关问题