Skip to content
This repository has been archived by the owner on Jul 19, 2018. It is now read-only.

WIP -- specialization constant support #1646

Open
wants to merge 10 commits into
base: master
Choose a base branch
from
13 changes: 11 additions & 2 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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} )
Expand All @@ -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}"
Expand All @@ -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})
Expand Down
45 changes: 39 additions & 6 deletions layers/core_validation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 <android/log.h>
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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.
}
}

Expand Down Expand Up @@ -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<uint32_t, std::string> specializations;
for (auto i = 0u; i < pStage->pSpecializationInfo->mapEntryCount; i++) {
auto const & mapEntry = pStage->pSpecializationInfo->pMapEntries[i];
specializations[mapEntry.constantID] = std::string(
reinterpret_cast<char const *>(pStage->pSpecializationInfo->pData) + mapEntry.offset,
mapEntry.size);
}
opt.RegisterPass(spvtools::CreateSetSpecConstantDefaultValuePass(specializations));
}

opt.RegisterPass(spvtools::CreateFreezeSpecConstantValuePass());
opt.RegisterPass(spvtools::CreateUnifyConstantPass());

std::vector<uint32_t> 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()) {
Expand All @@ -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
Expand Down
101 changes: 101 additions & 0 deletions tests/layer_validation_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 "
Expand Down