blob: 09a9da6be9844a79ef1837d4496d0ce1cb79dfb3 [file] [log] [blame]
/*
* 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();
}