| /* |
| * Copyright (c) 2015-2022 The Khronos Group Inc. |
| * Copyright (c) 2015-2022 Valve Corporation |
| * Copyright (c) 2015-2022 LunarG, Inc. |
| * Copyright (c) 2015-2022 Google, Inc. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Author: Nathaniel Cesario <nathaniel@lunarg.com> |
| */ |
| |
| #include "../layer_validation_tests.h" |
| #include "vk_extension_helper.h" |
| |
| #include <algorithm> |
| #include <array> |
| #include <chrono> |
| #include <memory> |
| #include <mutex> |
| #include <thread> |
| |
| #include "cast_utils.h" |
| |
| class VkPositiveGraphicsLibraryLayerTest : public VkLayerTest {}; |
| |
| TEST_F(VkPositiveGraphicsLibraryLayerTest, VertexInputGraphicsPipelineLibrary) { |
| TEST_DESCRIPTION("Create a vertex input graphics library"); |
| |
| SetTargetApiVersion(VK_API_VERSION_1_2); |
| AddRequiredExtensions(VK_EXT_GRAPHICS_PIPELINE_LIBRARY_EXTENSION_NAME); |
| ASSERT_NO_FATAL_FAILURE(InitFramework()); |
| if (DeviceValidationVersion() < VK_API_VERSION_1_2) { |
| GTEST_SKIP() << "At least Vulkan version 1.2 is required"; |
| } |
| |
| if (!AreRequiredExtensionsEnabled()) { |
| GTEST_SKIP() << RequiredExtensionsNotSupported() << " not supported"; |
| } |
| |
| auto gpl_features = LvlInitStruct<VkPhysicalDeviceGraphicsPipelineLibraryFeaturesEXT>(); |
| auto features2 = GetPhysicalDeviceFeatures2(gpl_features); |
| if (!gpl_features.graphicsPipelineLibrary) { |
| printf("%s VkPhysicalDeviceGraphicsPipelineLibraryFeaturesEXT::graphicsPipelineLibrary not supported", kSkipPrefix); |
| return; |
| } |
| |
| ASSERT_NO_FATAL_FAILURE(InitState(nullptr, &features2)); |
| |
| CreatePipelineHelper pipe(*this); |
| pipe.InitVertexInputLibInfo(); |
| pipe.InitState(); |
| ASSERT_VK_SUCCESS(pipe.CreateGraphicsPipeline(true, false)); |
| } |
| |
| TEST_F(VkPositiveGraphicsLibraryLayerTest, PreRasterGraphicsPipelineLibrary) { |
| TEST_DESCRIPTION("Create a pre-raster graphics library"); |
| |
| SetTargetApiVersion(VK_API_VERSION_1_2); |
| AddRequiredExtensions(VK_EXT_GRAPHICS_PIPELINE_LIBRARY_EXTENSION_NAME); |
| ASSERT_NO_FATAL_FAILURE(InitFramework()); |
| if (DeviceValidationVersion() < VK_API_VERSION_1_2) { |
| GTEST_SKIP() << "At least Vulkan version 1.2 is required"; |
| } |
| |
| if (!AreRequiredExtensionsEnabled()) { |
| GTEST_SKIP() << RequiredExtensionsNotSupported() << " not supported"; |
| } |
| |
| auto gpl_features = LvlInitStruct<VkPhysicalDeviceGraphicsPipelineLibraryFeaturesEXT>(); |
| auto features2 = GetPhysicalDeviceFeatures2(gpl_features); |
| if (!gpl_features.graphicsPipelineLibrary) { |
| printf("%s VkPhysicalDeviceGraphicsPipelineLibraryFeaturesEXT::graphicsPipelineLibrary not supported", kSkipPrefix); |
| return; |
| } |
| |
| ASSERT_NO_FATAL_FAILURE(InitState(nullptr, &features2)); |
| |
| ASSERT_NO_FATAL_FAILURE(InitRenderTarget()); |
| |
| const auto vs_spv = GLSLToSPV(VK_SHADER_STAGE_VERTEX_BIT, bindStateVertShaderText); |
| auto vs_ci = LvlInitStruct<VkShaderModuleCreateInfo>(); |
| vs_ci.codeSize = vs_spv.size() * sizeof(decltype(vs_spv)::value_type); |
| vs_ci.pCode = vs_spv.data(); |
| |
| auto stage_ci = LvlInitStruct<VkPipelineShaderStageCreateInfo>(&vs_ci); |
| stage_ci.stage = VK_SHADER_STAGE_VERTEX_BIT; |
| stage_ci.module = VK_NULL_HANDLE; |
| stage_ci.pName = "main"; |
| |
| CreatePipelineHelper pipe(*this); |
| pipe.InitPreRasterLibInfo(1, &stage_ci); |
| pipe.InitState(); |
| pipe.CreateGraphicsPipeline(); |
| } |
| |
| TEST_F(VkPositiveGraphicsLibraryLayerTest, FragmentShaderGraphicsPipelineLibrary) { |
| TEST_DESCRIPTION("Create a fragment shader graphics library"); |
| |
| SetTargetApiVersion(VK_API_VERSION_1_2); |
| AddRequiredExtensions(VK_EXT_GRAPHICS_PIPELINE_LIBRARY_EXTENSION_NAME); |
| ASSERT_NO_FATAL_FAILURE(InitFramework()); |
| if (DeviceValidationVersion() < VK_API_VERSION_1_2) { |
| GTEST_SKIP() << "At least Vulkan version 1.2 is required"; |
| } |
| |
| if (!AreRequiredExtensionsEnabled()) { |
| GTEST_SKIP() << RequiredExtensionsNotSupported() << " not supported"; |
| } |
| |
| auto gpl_features = LvlInitStruct<VkPhysicalDeviceGraphicsPipelineLibraryFeaturesEXT>(); |
| auto features2 = GetPhysicalDeviceFeatures2(gpl_features); |
| if (!gpl_features.graphicsPipelineLibrary) { |
| GTEST_SKIP() << "VkPhysicalDeviceGraphicsPipelineLibraryFeaturesEXT::graphicsPipelineLibrary not supported"; |
| } |
| |
| ASSERT_NO_FATAL_FAILURE(InitState(nullptr, &features2)); |
| |
| ASSERT_NO_FATAL_FAILURE(InitRenderTarget()); |
| |
| const auto fs_spv = GLSLToSPV(VK_SHADER_STAGE_FRAGMENT_BIT, bindStateFragShaderText); |
| auto fs_ci = LvlInitStruct<VkShaderModuleCreateInfo>(); |
| fs_ci.codeSize = fs_spv.size() * sizeof(decltype(fs_spv)::value_type); |
| fs_ci.pCode = fs_spv.data(); |
| |
| auto stage_ci = LvlInitStruct<VkPipelineShaderStageCreateInfo>(&fs_ci); |
| stage_ci.stage = VK_SHADER_STAGE_FRAGMENT_BIT; |
| stage_ci.module = VK_NULL_HANDLE; |
| stage_ci.pName = "main"; |
| |
| CreatePipelineHelper pipe(*this); |
| pipe.InitFragmentLibInfo(1, &stage_ci); |
| pipe.InitState(); |
| pipe.CreateGraphicsPipeline(); |
| } |
| |
| TEST_F(VkPositiveGraphicsLibraryLayerTest, FragmentOutputGraphicsPipelineLibrary) { |
| TEST_DESCRIPTION("Create a fragment output graphics library"); |
| |
| SetTargetApiVersion(VK_API_VERSION_1_2); |
| AddRequiredExtensions(VK_EXT_GRAPHICS_PIPELINE_LIBRARY_EXTENSION_NAME); |
| ASSERT_NO_FATAL_FAILURE(InitFramework()); |
| if (DeviceValidationVersion() < VK_API_VERSION_1_2) { |
| GTEST_SKIP() << "At least Vulkan version 1.2 is required"; |
| } |
| |
| if (!AreRequiredExtensionsEnabled()) { |
| GTEST_SKIP() << RequiredExtensionsNotSupported() << " not supported"; |
| } |
| |
| auto gpl_features = LvlInitStruct<VkPhysicalDeviceGraphicsPipelineLibraryFeaturesEXT>(); |
| auto features2 = GetPhysicalDeviceFeatures2(gpl_features); |
| if (!gpl_features.graphicsPipelineLibrary) { |
| printf("%s VkPhysicalDeviceGraphicsPipelineLibraryFeaturesEXT::graphicsPipelineLibrary not supported", kSkipPrefix); |
| return; |
| } |
| |
| ASSERT_NO_FATAL_FAILURE(InitState(nullptr, &features2)); |
| ASSERT_NO_FATAL_FAILURE(InitRenderTarget()); |
| |
| CreatePipelineHelper pipe(*this); |
| pipe.InitFragmentOutputLibInfo(); |
| pipe.InitState(); |
| ASSERT_VK_SUCCESS(pipe.CreateGraphicsPipeline(true, false)); |
| } |
| |
| TEST_F(VkPositiveGraphicsLibraryLayerTest, ExeLibrary) { |
| TEST_DESCRIPTION("Create an executable library by linking one or more graphics libraries"); |
| |
| SetTargetApiVersion(VK_API_VERSION_1_2); |
| AddRequiredExtensions(VK_EXT_GRAPHICS_PIPELINE_LIBRARY_EXTENSION_NAME); |
| ASSERT_NO_FATAL_FAILURE(InitFramework()); |
| if (DeviceValidationVersion() < VK_API_VERSION_1_2) { |
| GTEST_SKIP() << "At least Vulkan version 1.2 is required"; |
| } |
| |
| if (!AreRequiredExtensionsEnabled()) { |
| GTEST_SKIP() << RequiredExtensionsNotSupported() << " not supported"; |
| } |
| |
| auto gpl_features = LvlInitStruct<VkPhysicalDeviceGraphicsPipelineLibraryFeaturesEXT>(); |
| auto features2 = GetPhysicalDeviceFeatures2(gpl_features); |
| if (!gpl_features.graphicsPipelineLibrary) { |
| printf("%s VkPhysicalDeviceGraphicsPipelineLibraryFeaturesEXT::graphicsPipelineLibrary not supported", kSkipPrefix); |
| return; |
| } |
| |
| ASSERT_NO_FATAL_FAILURE(InitState(nullptr, &features2)); |
| ASSERT_NO_FATAL_FAILURE(InitRenderTarget()); |
| |
| CreatePipelineHelper vertex_input_lib(*this); |
| vertex_input_lib.InitVertexInputLibInfo(); |
| vertex_input_lib.InitState(); |
| ASSERT_VK_SUCCESS(vertex_input_lib.CreateGraphicsPipeline(true, false)); |
| |
| // Layout, renderPass, and subpass all need to be shared across libraries in the same executable pipeline |
| VkPipelineLayout layout = VK_NULL_HANDLE; |
| VkRenderPass render_pass = VK_NULL_HANDLE; |
| uint32_t subpass = 0; |
| |
| CreatePipelineHelper pre_raster_lib(*this); |
| { |
| const auto vs_spv = GLSLToSPV(VK_SHADER_STAGE_VERTEX_BIT, bindStateVertShaderText); |
| auto vs_ci = LvlInitStruct<VkShaderModuleCreateInfo>(); |
| vs_ci.codeSize = vs_spv.size() * sizeof(decltype(vs_spv)::value_type); |
| vs_ci.pCode = vs_spv.data(); |
| |
| auto stage_ci = LvlInitStruct<VkPipelineShaderStageCreateInfo>(&vs_ci); |
| stage_ci.stage = VK_SHADER_STAGE_VERTEX_BIT; |
| stage_ci.module = VK_NULL_HANDLE; |
| stage_ci.pName = "main"; |
| |
| pre_raster_lib.InitPreRasterLibInfo(1, &stage_ci); |
| pre_raster_lib.InitState(); |
| ASSERT_VK_SUCCESS(pre_raster_lib.CreateGraphicsPipeline()); |
| } |
| |
| layout = pre_raster_lib.gp_ci_.layout; |
| render_pass = pre_raster_lib.gp_ci_.renderPass; |
| subpass = pre_raster_lib.gp_ci_.subpass; |
| |
| CreatePipelineHelper frag_shader_lib(*this); |
| { |
| const auto fs_spv = GLSLToSPV(VK_SHADER_STAGE_FRAGMENT_BIT, bindStateFragShaderText); |
| auto fs_ci = LvlInitStruct<VkShaderModuleCreateInfo>(); |
| fs_ci.codeSize = fs_spv.size() * sizeof(decltype(fs_spv)::value_type); |
| fs_ci.pCode = fs_spv.data(); |
| |
| auto stage_ci = LvlInitStruct<VkPipelineShaderStageCreateInfo>(&fs_ci); |
| stage_ci.stage = VK_SHADER_STAGE_FRAGMENT_BIT; |
| stage_ci.module = VK_NULL_HANDLE; |
| stage_ci.pName = "main"; |
| |
| frag_shader_lib.InitFragmentLibInfo(1, &stage_ci); |
| // frag_shader_lib.InitState(); |
| // // Layout, renderPass, and subpass all need to be shared across libraries in the same executable pipeline |
| frag_shader_lib.gp_ci_.layout = layout; |
| frag_shader_lib.gp_ci_.renderPass = render_pass; |
| frag_shader_lib.gp_ci_.subpass = subpass; |
| ASSERT_VK_SUCCESS(frag_shader_lib.CreateGraphicsPipeline(true, false)); |
| } |
| |
| CreatePipelineHelper frag_out_lib(*this); |
| frag_out_lib.InitFragmentOutputLibInfo(); |
| // frag_out_lib.InitState(); |
| // Layout, renderPass, and subpass all need to be shared across libraries in the same executable pipeline |
| frag_out_lib.gp_ci_.renderPass = render_pass; |
| frag_out_lib.gp_ci_.subpass = subpass; |
| ASSERT_VK_SUCCESS(frag_out_lib.CreateGraphicsPipeline(true, false)); |
| |
| VkPipeline libraries[4] = { |
| vertex_input_lib.pipeline_, |
| pre_raster_lib.pipeline_, |
| frag_shader_lib.pipeline_, |
| frag_out_lib.pipeline_, |
| }; |
| auto link_info = LvlInitStruct<VkPipelineLibraryCreateInfoKHR>(); |
| link_info.libraryCount = size(libraries); |
| link_info.pLibraries = libraries; |
| |
| auto exe_pipe_ci = LvlInitStruct<VkGraphicsPipelineCreateInfo>(&link_info); |
| vk_testing::Pipeline exe_pipe(*m_device, exe_pipe_ci); |
| ASSERT_TRUE(exe_pipe.initialized()); |
| } |
| |
| TEST_F(VkPositiveGraphicsLibraryLayerTest, DrawWithNullDSLs) { |
| TEST_DESCRIPTION("Make a draw with a pipeline layout derived from null DSLs"); |
| |
| AddRequiredExtensions(VK_EXT_GRAPHICS_PIPELINE_LIBRARY_EXTENSION_NAME); |
| |
| ASSERT_NO_FATAL_FAILURE(InitFramework()); |
| |
| if (!AreRequiredExtensionsEnabled()) { |
| GTEST_SKIP() << RequiredExtensionsNotSupported() << " not supported"; |
| } |
| |
| auto gpl_features = LvlInitStruct<VkPhysicalDeviceGraphicsPipelineLibraryFeaturesEXT>(); |
| auto features2 = GetPhysicalDeviceFeatures2(gpl_features); |
| if (!gpl_features.graphicsPipelineLibrary) { |
| GTEST_SKIP() << "VkPhysicalDeviceGraphicsPipelineLibraryFeaturesEXT::graphicsPipelineLibrary not supported"; |
| } |
| |
| ASSERT_NO_FATAL_FAILURE(InitState(nullptr, &features2)); |
| ASSERT_NO_FATAL_FAILURE(InitRenderTarget()); |
| |
| // Prepare descriptors |
| OneOffDescriptorSet ds(m_device, { |
| {0, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1, VK_SHADER_STAGE_VERTEX_BIT, nullptr}, |
| }); |
| OneOffDescriptorSet ds2(m_device, { |
| {0, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1, VK_SHADER_STAGE_FRAGMENT_BIT, nullptr}, |
| }); |
| OneOffDescriptorSet ds_empty(m_device, {}); // empty set |
| |
| // We _vs and _fs layouts are identical, but we want them to be separate handles handles for the sake layout merging |
| VkPipelineLayoutObj pipeline_layout_vs(m_device, {&ds.layout_, nullptr, &ds2.layout_}, {}, |
| VK_PIPELINE_LAYOUT_CREATE_INDEPENDENT_SETS_BIT_EXT); |
| VkPipelineLayoutObj pipeline_layout_fs(m_device, {&ds.layout_, nullptr, &ds2.layout_}, {}, |
| VK_PIPELINE_LAYOUT_CREATE_INDEPENDENT_SETS_BIT_EXT); |
| VkPipelineLayoutObj pipeline_layout_empty(m_device, {&ds.layout_, &ds_empty.layout_, &ds2.layout_}, {}, |
| VK_PIPELINE_LAYOUT_CREATE_INDEPENDENT_SETS_BIT_EXT); |
| VkPipelineLayoutObj pipeline_layout_null(m_device, {&ds.layout_, nullptr, &ds2.layout_}, {}, |
| VK_PIPELINE_LAYOUT_CREATE_INDEPENDENT_SETS_BIT_EXT); |
| |
| const std::array<VkDescriptorSet, 3> desc_sets = {ds.set_, ds_empty.set_, ds2.set_}; |
| |
| auto ub_ci = LvlInitStruct<VkBufferCreateInfo>(); |
| ub_ci.usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT; |
| ub_ci.size = 1024; |
| VkBufferObj uniform_buffer(*m_device, ub_ci); |
| ds.WriteDescriptorBufferInfo(0, uniform_buffer.handle(), 0, 1024); |
| ds.UpdateDescriptorSets(); |
| ds2.WriteDescriptorBufferInfo(0, uniform_buffer.handle(), 0, 1024); |
| ds2.UpdateDescriptorSets(); |
| |
| // Layout, renderPass, and subpass all need to be shared across libraries in the same executable pipeline |
| VkRenderPass render_pass = VK_NULL_HANDLE; |
| uint32_t subpass = 0; |
| |
| CreatePipelineHelper vertex_input_lib(*this); |
| vertex_input_lib.InitVertexInputLibInfo(); |
| vertex_input_lib.InitState(); |
| ASSERT_VK_SUCCESS(vertex_input_lib.CreateGraphicsPipeline(true, false)); |
| |
| CreatePipelineHelper pre_raster_lib(*this); |
| { |
| const char vs_src[] = R"glsl( |
| #version 450 |
| layout(set=2, binding=0) uniform foo { float x; } bar; |
| void main() { |
| gl_Position = vec4(bar.x); |
| } |
| )glsl"; |
| const auto vs_spv = GLSLToSPV(VK_SHADER_STAGE_VERTEX_BIT, vs_src); |
| auto vs_ci = LvlInitStruct<VkShaderModuleCreateInfo>(); |
| vs_ci.codeSize = vs_spv.size() * sizeof(decltype(vs_spv)::value_type); |
| vs_ci.pCode = vs_spv.data(); |
| |
| auto stage_ci = LvlInitStruct<VkPipelineShaderStageCreateInfo>(&vs_ci); |
| stage_ci.stage = VK_SHADER_STAGE_VERTEX_BIT; |
| stage_ci.module = VK_NULL_HANDLE; |
| stage_ci.pName = "main"; |
| |
| pre_raster_lib.InitPreRasterLibInfo(1, &stage_ci); |
| pre_raster_lib.InitState(); |
| pre_raster_lib.gp_ci_.layout = pipeline_layout_vs.handle(); |
| ASSERT_VK_SUCCESS(pre_raster_lib.CreateGraphicsPipeline(true, false)); |
| } |
| |
| render_pass = pre_raster_lib.gp_ci_.renderPass; |
| subpass = pre_raster_lib.gp_ci_.subpass; |
| |
| CreatePipelineHelper frag_shader_lib(*this); |
| { |
| const auto fs_spv = GLSLToSPV(VK_SHADER_STAGE_FRAGMENT_BIT, bindStateFragShaderText); |
| auto fs_ci = LvlInitStruct<VkShaderModuleCreateInfo>(); |
| fs_ci.codeSize = fs_spv.size() * sizeof(decltype(fs_spv)::value_type); |
| fs_ci.pCode = fs_spv.data(); |
| |
| auto stage_ci = LvlInitStruct<VkPipelineShaderStageCreateInfo>(&fs_ci); |
| stage_ci.stage = VK_SHADER_STAGE_FRAGMENT_BIT; |
| stage_ci.module = VK_NULL_HANDLE; |
| stage_ci.pName = "main"; |
| |
| frag_shader_lib.InitFragmentLibInfo(1, &stage_ci); |
| frag_shader_lib.InitState(); |
| |
| frag_shader_lib.gp_ci_.renderPass = render_pass; |
| frag_shader_lib.gp_ci_.subpass = subpass; |
| frag_shader_lib.gp_ci_.layout = pipeline_layout_fs.handle(); |
| frag_shader_lib.CreateGraphicsPipeline(true, false); |
| } |
| |
| CreatePipelineHelper frag_out_lib(*this); |
| frag_out_lib.InitFragmentOutputLibInfo(); |
| // frag_out_lib.InitState(); |
| // Layout, renderPass, and subpass all need to be shared across libraries in the same executable pipeline |
| frag_out_lib.gp_ci_.renderPass = render_pass; |
| frag_out_lib.gp_ci_.subpass = subpass; |
| ASSERT_VK_SUCCESS(frag_out_lib.CreateGraphicsPipeline(true, false)); |
| |
| VkPipeline libraries[4] = { |
| vertex_input_lib.pipeline_, |
| pre_raster_lib.pipeline_, |
| frag_shader_lib.pipeline_, |
| frag_out_lib.pipeline_, |
| }; |
| auto link_info = LvlInitStruct<VkPipelineLibraryCreateInfoKHR>(); |
| link_info.libraryCount = size(libraries); |
| link_info.pLibraries = libraries; |
| |
| auto exe_pipe_ci = LvlInitStruct<VkGraphicsPipelineCreateInfo>(&link_info); |
| exe_pipe_ci.layout = pipeline_layout_empty.handle(); |
| vk_testing::Pipeline exe_pipe_empty(*m_device, exe_pipe_ci); |
| ASSERT_TRUE(exe_pipe_empty.initialized()); |
| |
| exe_pipe_ci.layout = pipeline_layout_null.handle(); |
| vk_testing::Pipeline exe_pipe_null(*m_device, exe_pipe_ci); |
| ASSERT_TRUE(exe_pipe_null.initialized()); |
| |
| m_commandBuffer->begin(); |
| m_commandBuffer->BeginRenderPass(m_renderPassBeginInfo); |
| |
| // Draw with pipeline created with empty set |
| vk::CmdBindPipeline(m_commandBuffer->handle(), VK_PIPELINE_BIND_POINT_GRAPHICS, exe_pipe_empty.handle()); |
| vk::CmdBindDescriptorSets(m_commandBuffer->handle(), VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline_layout_empty.handle(), 0, |
| static_cast<uint32_t>(desc_sets.size()), desc_sets.data(), 0, nullptr); |
| vk::CmdDraw(m_commandBuffer->handle(), 3, 1, 0, 0); |
| |
| // Draw with pipeline created with null set |
| vk::CmdBindPipeline(m_commandBuffer->handle(), VK_PIPELINE_BIND_POINT_GRAPHICS, exe_pipe_null.handle()); |
| vk::CmdBindDescriptorSets(m_commandBuffer->handle(), VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline_layout_null.handle(), 0, |
| static_cast<uint32_t>(desc_sets.size()), desc_sets.data(), 0, nullptr); |
| vk::CmdDraw(m_commandBuffer->handle(), 3, 1, 0, 0); |
| |
| m_commandBuffer->EndRenderPass(); |
| m_commandBuffer->end(); |
| } |