diff --git a/CMakeLists.txt b/CMakeLists.txt index a7b8a89f3c..d34547002f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -208,6 +208,9 @@ find_library(SPIRV_REMAPPER_LIB NAMES SPVRemapper find_library(SPIRV_TOOLS_LIB NAMES SPIRV-Tools HINTS ${SPIRV_TOOLS_SEARCH_PATH} ) +find_library(SPIRV_TOOLS_OPT_LIB NAMES SPIRV-Tools-opt + HINTS ${SPIRV_TOOLS_SEARCH_PATH}/opt ) + if (WIN32) add_library(glslang STATIC IMPORTED) add_library(OGLCompiler STATIC IMPORTED) @@ -217,6 +220,7 @@ if (WIN32) add_library(SPVRemapper STATIC IMPORTED) add_library(Loader STATIC IMPORTED) add_library(SPIRV-Tools STATIC IMPORTED) + add_library(SPIRV-Tools-opt STATIC IMPORTED) find_library(GLSLANG_DLIB NAMES glslangd HINTS ${GLSLANG_DEBUG_SEARCH_PATH} ) @@ -232,6 +236,8 @@ if (WIN32) HINTS ${GLSLANG_DEBUG_SEARCH_PATH} ) find_library(SPIRV_TOOLS_DLIB NAMES SPIRV-Tools HINTS ${SPIRV_TOOLS_DEBUG_SEARCH_PATH} ) + find_library(SPIRV_TOOLS_OPT_DLIB NAMES SPIRV-Tools-opt + HINTS ${SPIRV_TOOLS_DEBUG_SEARCH_PATH}/opt ) set_target_properties(glslang PROPERTIES IMPORTED_LOCATION "${GLSLANG_LIB}" @@ -254,12 +260,15 @@ if (WIN32) set_target_properties(SPIRV-Tools PROPERTIES IMPORTED_LOCATION "${SPIRV_TOOLS_LIB}" IMPORTED_LOCATION_DEBUG "${SPIRV_TOOLS_DLIB}") + set_target_properties(SPIRV-Tools-opt PROPERTIES + IMPORTED_LOCATION "${SPIRV_TOOLS_OPT_LIB}" + IMPORTED_LOCATION_DEBUG "${SPIRV_TOOLS_OPT_DLIB}") set (GLSLANG_LIBRARIES glslang OGLCompiler OSDependent HLSL SPIRV SPVRemapper) - set (SPIRV_TOOLS_LIBRARIES SPIRV-Tools) + set (SPIRV_TOOLS_LIBRARIES SPIRV-Tools-opt SPIRV-Tools) else () set (GLSLANG_LIBRARIES ${GLSLANG_LIB} ${OGLCompiler_LIB} ${OSDependent_LIB} ${HLSL_LIB} ${SPIRV_LIB} ${SPIRV_REMAPPER_LIB}) - set (SPIRV_TOOLS_LIBRARIES ${SPIRV_TOOLS_LIB}) + set (SPIRV_TOOLS_LIBRARIES ${SPIRV_TOOLS_OPT_LIB} ${SPIRV_TOOLS_LIB}) endif() set (PYTHON_CMD ${PYTHON_EXECUTABLE}) diff --git a/layers/core_validation.cpp b/layers/core_validation.cpp index ded7b803c6..4b24b0f076 100644 --- a/layers/core_validation.cpp +++ b/layers/core_validation.cpp @@ -66,7 +66,8 @@ #include "vk_layer_data.h" #include "vk_layer_extension_utils.h" #include "vk_layer_utils.h" -#include "spirv-tools/libspirv.h" +#include "spirv-tools/libspirv.hpp" +#include "spirv-tools/optimizer.hpp" #if defined __ANDROID__ #include @@ -406,6 +407,14 @@ SURFACE_STATE *GetSurfaceState(instance_layer_data *instance_data, VkSurfaceKHR return &it->second; } +struct shader_module *GetShaderState(layer_data *dev_data, VkShaderModule shader_module) { + auto it = dev_data->shaderModuleMap.find(shader_module); + if (it == dev_data->shaderModuleMap.end()) { + return nullptr; + } + return it->second.get(); +} + // Return ptr to memory binding for given handle of specified type static BINDABLE *GetObjectMemBinding(layer_data *dev_data, uint64_t handle, VkDebugReportObjectTypeEXT type) { switch (type) { @@ -1241,8 +1250,6 @@ static unsigned get_locations_consumed_by_type(shader_module const *src, unsigne default: // Everything else is just 1. return 1; - - // TODO: extend to handle 64bit scalar types, whose vectors may need multiple locations. } } @@ -2610,12 +2617,39 @@ static bool validate_pipeline_shader_stage( layer_data *dev_data, VkPipelineShaderStageCreateInfo const *pStage, PIPELINE_STATE *pipeline, shader_module **out_module, spirv_inst_iter *out_entrypoint) { bool pass = true; - auto module_it = dev_data->shaderModuleMap.find(pStage->module); - auto module = *out_module = module_it->second.get(); + auto module = *out_module = GetShaderState(dev_data, pStage->module); auto report_data = dev_data->report_data; + pass &= validate_specialization_offsets(report_data, pStage); + if (!module->has_valid_spirv) return pass; + spvtools::Optimizer opt(SPV_ENV_VULKAN_1_0); + + if (pStage->pSpecializationInfo) { + // massage the specialization data blob into the form spirv-tools wants + std::unordered_map specializations; + for (auto i = 0u; i < pStage->pSpecializationInfo->mapEntryCount; i++) { + auto const & mapEntry = pStage->pSpecializationInfo->pMapEntries[i]; + specializations[mapEntry.constantID] = std::string( + reinterpret_cast(pStage->pSpecializationInfo->pData) + mapEntry.offset, + mapEntry.size); + } + opt.RegisterPass(spvtools::CreateSetSpecConstantDefaultValuePass(specializations)); + } + + opt.RegisterPass(spvtools::CreateFreezeSpecConstantValuePass()); + opt.RegisterPass(spvtools::CreateUnifyConstantPass()); + + std::vector final_spirv; + if (!opt.Run(module->words.data(), module->words.size(), &final_spirv)) { + // TODO: route any spirv-tools optimizer diagnostics here + if (log_msg(dev_data->report_data, VK_DEBUG_REPORT_ERROR_BIT_EXT, VkDebugReportObjectTypeEXT(0), 0, __LINE__, SHADER_CHECKER_INCONSISTENT_SPIRV, + "SC", "Internal error preprocessing shader module")) { + return false; + } + } + // Find the entrypoint auto entrypoint = *out_entrypoint = find_entrypoint(module, pStage->pName, pStage->stage); if (entrypoint == module->end()) { @@ -2637,7 +2671,6 @@ static bool validate_pipeline_shader_stage( auto pipelineLayout = pipeline->pipeline_layout; - pass &= validate_specialization_offsets(report_data, pStage); pass &= validate_push_constant_usage(report_data, &pipelineLayout.push_constant_ranges, module, accessible_ids, pStage->stage); // Validate descriptor use diff --git a/tests/layer_validation_tests.cpp b/tests/layer_validation_tests.cpp index 9573e9d01f..48b269aeef 100644 --- a/tests/layer_validation_tests.cpp +++ b/tests/layer_validation_tests.cpp @@ -14697,6 +14697,107 @@ TEST_F(VkLayerTest, CreatePipelineMissingEntrypoint) { m_errorMonitor->VerifyFound(); } +TEST_F(VkLayerTest, CreatePipelineVsFsArraySizeDefaultSpecialization) { + m_errorMonitor->SetDesiredFailureMsg(VK_DEBUG_REPORT_ERROR_BIT_EXT, "Type mismatch"); + m_errorMonitor->SetUnexpectedError("consumes input location 1.0 which is not written"); + + // Trigger a type mismatch when default specialization constant values are + // not used. At time of writing, SC doesn't even look at the default values, and + // so instead treats everything as `1`. + + ASSERT_NO_FATAL_FAILURE(Init()); + ASSERT_NO_FATAL_FAILURE(InitRenderTarget()); + + char const *vsSource = + "#version 450\n" + "out gl_PerVertex {\n" + " vec4 gl_Position;\n" + "};\n" + "layout(constant_id=0) const uint size = 2;\n" + "layout(location=0) out float xs[size];\n" + "void main(){\n" + " gl_Position = vec4(0);\n" + " xs[0] = 42.0;\n" + "}\n"; + char const *fsSource = + "#version 450\n" + "\n" + "layout(location=0) in float xs[1];\n" + "layout(location=0) out vec4 color;\n" + "void main(){\n" + " color = vec4(xs[0]);\n" + "}\n"; + + VkShaderObj vs(m_device, vsSource, VK_SHADER_STAGE_VERTEX_BIT, this); + VkShaderObj fs(m_device, fsSource, VK_SHADER_STAGE_FRAGMENT_BIT, this); + + VkPipelineObj pipe(m_device); + pipe.AddColorAttachment(); + pipe.AddShader(&vs); + pipe.AddShader(&fs); + + VkDescriptorSetObj descriptorSet(m_device); + descriptorSet.AppendDummy(); + descriptorSet.CreateVKDescriptorSet(m_commandBuffer); + + pipe.CreateVKPipeline(descriptorSet.GetPipelineLayout(), renderPass()); + + m_errorMonitor->VerifyFound(); +} + +TEST_F(VkLayerTest, CreatePipelineVsFsArraySizeSpecialization) { + m_errorMonitor->SetDesiredFailureMsg(VK_DEBUG_REPORT_ERROR_BIT_EXT, "Type mismatch"); + + // Trigger a type mismatch when the VS output array size is specialized. If + // specializations are not applied, then these interfaces match fine. + + ASSERT_NO_FATAL_FAILURE(Init()); + ASSERT_NO_FATAL_FAILURE(InitRenderTarget()); + + char const *vsSource = + "#version 450\n" + "out gl_PerVertex {\n" + " vec4 gl_Position;\n" + "};\n" + "layout(constant_id=0) const uint size = 1;\n" + "layout(location=0) out float xs[size];\n" + "void main(){\n" + " gl_Position = vec4(0);\n" + " for (int i = 0; i < size; i++) { xs[i] = 42.0; }\n" + "}\n"; + char const *fsSource = + "#version 450\n" + "\n" + "layout(location=0) in float xs[1];\n" + "layout(location=0) out vec4 color;\n" + "void main(){\n" + " color = vec4(xs[0]);\n" + "}\n"; + + VkShaderObj vs(m_device, vsSource, VK_SHADER_STAGE_VERTEX_BIT, this); + VkShaderObj fs(m_device, fsSource, VK_SHADER_STAGE_FRAGMENT_BIT, this); + + // Specialize the VS + uint32_t actual_size = 2; + VkSpecializationMapEntry spec_entry = { 0, 0, sizeof(actual_size) }; + VkSpecializationInfo spec = { 1, &spec_entry, sizeof(actual_size), &actual_size }; + auto vs_stage = vs.GetStageCreateInfo(); + vs_stage.pSpecializationInfo = &spec; + + VkPipelineObj pipe(m_device); + pipe.AddColorAttachment(); + pipe.AddShader(vs_stage); + pipe.AddShader(&fs); + + VkDescriptorSetObj descriptorSet(m_device); + descriptorSet.AppendDummy(); + descriptorSet.CreateVKDescriptorSet(m_commandBuffer); + + pipe.CreateVKPipeline(descriptorSet.GetPipelineLayout(), renderPass()); + + m_errorMonitor->VerifyFound(); +} + TEST_F(VkLayerTest, CreatePipelineDepthStencilRequired) { m_errorMonitor->SetDesiredFailureMsg(VK_DEBUG_REPORT_ERROR_BIT_EXT, "pDepthStencilState is NULL when rasterization is enabled and subpass "