c++visual-studiocompiler-optimizationvulkanrelease-builds

Compiler "Optimizes" Out Object Initialization Function


I have an object that I need to populate before using called pipelineInfo. To populate the object I use a function called createPipelineInfo. This works perfectly well when I use visual studios to compile a debug build but when I try to compile a release build the compiler "optimizes" out the entire createPipelineInfo function.

Here is the call to initialize the object and its use:

VkGraphicsPipelineCreateInfo pipelineInfo = createPipelineInfo(shaderStages, vertexInputInfo, inputAssembly, viewportState, rasterizer,
    multisampling, colorBlending, pipelineLayout, renderPass);

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

The following is the createPipelineInfo function:

inline static VkGraphicsPipelineCreateInfo createPipelineInfo(
    const std::array<VkPipelineShaderStageCreateInfo, 2> shaderStages,
    const VkPipelineVertexInputStateCreateInfo& vertexInputInfo,
    const VkPipelineInputAssemblyStateCreateInfo& inputAssembly,
    const VkPipelineViewportStateCreateInfo& viewportState,
    const VkPipelineRasterizationStateCreateInfo& rasterizer,
    const VkPipelineMultisampleStateCreateInfo& multisampling,
    const VkPipelineColorBlendStateCreateInfo& colorBlending,
    const VkPipelineLayout& pipelineLayout,
    const VkRenderPass& renderPass) {

    VkGraphicsPipelineCreateInfo pipelineInfo{};

    //Shader Stage
    pipelineInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
    pipelineInfo.stageCount = 2;
    pipelineInfo.pStages = shaderStages.data();

    //Fixed Pipeline Stage
    pipelineInfo.pVertexInputState = &vertexInputInfo;
    pipelineInfo.pInputAssemblyState = &inputAssembly;
    pipelineInfo.pViewportState = &viewportState;
    pipelineInfo.pRasterizationState = &rasterizer;
    pipelineInfo.pMultisampleState = &multisampling;
    //pipelineInfo.pDepthStencilState = &depthStencil;
    pipelineInfo.pColorBlendState = &colorBlending;
    pipelineInfo.pDynamicState = nullptr; // Optional

    //Pipeline Layout
    pipelineInfo.layout = pipelineLayout;
    pipelineInfo.renderPass = renderPass;
    pipelineInfo.subpass = 0;
    pipelineInfo.basePipelineHandle = VK_NULL_HANDLE;

    return pipelineInfo;
}

On the other hand if I copy the body of the function and dump it in place of the function call everything works perfectly fine.

VkGraphicsPipelineCreateInfo pipelineInfo{};

//Shader Stage
pipelineInfo.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
pipelineInfo.stageCount = 2;
pipelineInfo.pStages = shaderStages.data();

//Fixed Pipeline Stage
pipelineInfo.pVertexInputState = &vertexInputInfo;
pipelineInfo.pInputAssemblyState = &inputAssembly;
pipelineInfo.pViewportState = &viewportState;
pipelineInfo.pRasterizationState = &rasterizer;
pipelineInfo.pMultisampleState = &multisampling;
//pipelineInfo.pDepthStencilState = &depthStencil;
pipelineInfo.pColorBlendState = &colorBlending;
pipelineInfo.pDynamicState = nullptr; // Optional

//Pipeline Layout
pipelineInfo.layout = pipelineLayout;
pipelineInfo.renderPass = renderPass;
pipelineInfo.subpass = 0;
pipelineInfo.basePipelineHandle = VK_NULL_HANDLE;


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

I'm trying to figure out why the compiler is optimizing out the function call and failing that trying to develop a work around that doesn't involve dumping the body of the function in place of every call to the function.


Solution

  • wild guess: this parameter is passed by copy

    const std::array<VkPipelineShaderStageCreateInfo, 2> shaderStages
    

    so when taking the address of its contents here with data method call:

    pipelineInfo.pStages = shaderStages.data();
    

    you invoke undefined behaviour. The compiler isn't smart enough to 1) warn you about taking a reference to temporary because of the complexity of the calls, and 2) it doesn't automatically perform copy elision on parameter passing.

    Fix: pass it by reference (note that all other parameters use a by reference mode for a reason)

    const std::array<VkPipelineShaderStageCreateInfo, 2> &shaderStages