Skip to content

Commit b30d794

Browse files
layers: Rework creating a spirv::Module object
1 parent fe567f2 commit b30d794

17 files changed

+378
-49
lines changed

layers/VkLayer_khronos_validation.json.in

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -313,7 +313,7 @@
313313
{
314314
"key": "check_shaders",
315315
"label": "Shader",
316-
"description": "This will validate the contents of the SPIR-V which can be CPU intensive during application start up. This does internal checks as well as calling spirv-val.",
316+
"description": "This will validate the contents of the SPIR-V which can be CPU intensive during application start up. This does internal checks as well as calling spirv-val. (Same effect using VK_VALIDATION_FEATURE_DISABLE_SHADERS_EXT)",
317317
"type": "BOOL",
318318
"default": true,
319319
"expanded": true,

layers/core_checks/cc_shader_object.cpp

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -287,9 +287,9 @@ bool CoreChecks::PreCallValidateCreateShadersEXT(VkDevice device, uint32_t creat
287287
VkShaderEXT* pShaders, const ErrorObject& error_obj) const {
288288
bool skip = false;
289289

290-
// the spec clarifies that VK_VALIDATION_FEATURE_DISABLE_SHADERS_EXT works on VK_EXT_shader_object as well
290+
// If user has VK_VALIDATION_FEATURE_DISABLE_SHADERS_EXT, just skip all things related to creating the shader object
291291
if (disabled[shader_validation]) {
292-
return skip; // VK_VALIDATION_FEATURE_DISABLE_SHADERS_EXT
292+
return skip;
293293
}
294294

295295
if (enabled_features.shaderObject == VK_FALSE) {
@@ -323,7 +323,8 @@ bool CoreChecks::PreCallValidateCreateShadersEXT(VkDevice device, uint32_t creat
323323
spv_const_binary_t binary{static_cast<const uint32_t*>(create_info.pCode), create_info.codeSize / sizeof(uint32_t)};
324324
skip |= RunSpirvValidation(binary, create_info_loc, cache);
325325

326-
const auto spirv = std::make_shared<spirv::Module>(create_info.codeSize, static_cast<const uint32_t*>(create_info.pCode));
326+
const auto spirv =
327+
vvl::CreateSpirvModuleState(create_info.codeSize, static_cast<const uint32_t*>(create_info.pCode), global_settings);
327328
vku::safe_VkShaderCreateInfoEXT safe_create_info = vku::safe_VkShaderCreateInfoEXT(&pCreateInfos[i]);
328329
const ShaderStageState stage_state(nullptr, &safe_create_info, nullptr, spirv);
329330
skip |= ValidateShaderStage(stage_state, nullptr, create_info_loc);

layers/core_checks/cc_spirv.cpp

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1774,12 +1774,21 @@ bool CoreChecks::ValidateShaderStage(const ShaderStageState &stage_state, const
17741774
}
17751775
}
17761776

1777+
// Skip if VK_VALIDATION_FEATURE_DISABLE_SHADERS_EXT is set
1778+
// Both the validation and running spirv-opt on the spec constants really makes this function slow
1779+
// See https://github.com/KhronosGroup/Vulkan-ValidationLayers/pull/10566 for more info
1780+
if (disabled[shader_validation]) {
1781+
return skip;
1782+
}
1783+
17771784
if ((pipeline && pipeline->uses_shader_module_id) || !stage_state.spirv_state) {
17781785
return skip; // these edge cases should be validated already
17791786
}
17801787

17811788
const spirv::Module &module_state = *stage_state.spirv_state.get();
1782-
if (!module_state.valid_spirv) return skip; // checked elsewhere
1789+
if (!module_state.valid_spirv) {
1790+
return skip; // checked elsewhere
1791+
}
17831792

17841793
if (!stage_state.entrypoint) {
17851794
const char *vuid = pipeline ? "VUID-VkPipelineShaderStageCreateInfo-pName-00707" : "VUID-VkShaderCreateInfoEXT-pName-08440";
@@ -1814,8 +1823,7 @@ bool CoreChecks::ValidateShaderStage(const ShaderStageState &stage_state, const
18141823
uint32_t total_task_payload_memory = 0;
18151824

18161825
// If specialization-constant instructions are present in the shader, the specializations should be applied.
1817-
// Skip if VK_VALIDATION_FEATURE_DISABLE_SHADERS_EXT is set.
1818-
if (!disabled[shader_validation] && module_state.static_data_.has_specialization_constants) {
1826+
if (module_state.static_data_.has_specialization_constants && global_settings.spirv_const_fold) {
18191827
// setup the call back if the optimizer fails
18201828
spv_target_env spirv_environment = PickSpirvEnv(api_version, IsExtEnabled(extensions.vk_khr_spirv_1_4));
18211829
spvtools::Optimizer optimizer(spirv_environment);
@@ -2133,10 +2141,8 @@ void CoreChecks::PreCallRecordCreateShaderModule(VkDevice device, const VkShader
21332141
const RecordObject &record_obj, chassis::CreateShaderModule &chassis_state) {
21342142
// Normally would validate in PreCallValidate, but need a non-const function to update chassis_state
21352143
// This is on the stack, we don't have to worry about threading hazards and this could be moved and used const_cast
2136-
if (chassis_state.module_state) {
2137-
chassis_state.skip |=
2138-
stateless_spirv_validator.Validate(*chassis_state.module_state, chassis_state.stateless_data, record_obj.location);
2139-
}
2144+
chassis_state.skip |=
2145+
stateless_spirv_validator.Validate(*chassis_state.module_state, chassis_state.stateless_data, record_obj.location);
21402146
}
21412147

21422148
void CoreChecks::PreCallRecordCreateShadersEXT(VkDevice device, uint32_t createInfoCount, const VkShaderCreateInfoEXT *pCreateInfos,

layers/core_checks/core_validation.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -222,7 +222,7 @@ class CoreChecks : public vvl::DeviceProxy {
222222

223223
CoreChecks(vvl::dispatch::Device* dev, core::Instance* instance_vo)
224224
: BaseClass(dev, instance_vo, LayerObjectTypeCoreValidation),
225-
stateless_spirv_validator(dev->debug_report, dev->stateless_device_data) {}
225+
stateless_spirv_validator(dev->debug_report, dev->stateless_device_data, dev->settings.disabled[shader_validation]) {}
226226

227227
ReadLockGuard ReadLock() const override;
228228
WriteLockGuard WriteLock() override;

layers/layer_options.cpp

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1119,6 +1119,28 @@ void ProcessConfigAndEnvSettings(ConfigAndEnvSettings *settings_data) {
11191119
"errors in Core Check are solved, please disable, then only use GPU-AV for best performance.");
11201120
}
11211121

1122+
// Set at the end once we decide what settings are actually on
1123+
if (settings_data->disables[shader_validation] || settings_data->disables[core_checks]) {
1124+
// only is used for core validation checks
1125+
global_settings.spirv_const_fold = false;
1126+
1127+
// sync val relies on the information from spirv::Module being parsed and stored
1128+
if (!settings_data->enables[sync_validation]) {
1129+
// GPU-AV/DebugPrintf relies on spirv::Module to hold the original SPIR-V
1130+
if (!settings_data->enables[gpu_validation] && !settings_data->enables[debug_printf_validation]) {
1131+
global_settings.spirv_store = false;
1132+
} else if (settings_data->disables[shader_validation]) {
1133+
setting_warnings.emplace_back(
1134+
"Shader Validation was explicitly turned off, but the SPIR-V still needs to be stored, but not validated, in "
1135+
"order for GPU-AV/DebugPrintf to get information from the original SPIR-V.");
1136+
}
1137+
} else if (settings_data->disables[shader_validation]) {
1138+
setting_warnings.emplace_back(
1139+
"Shader Validation was explicitly turned off, but the SPIR-V still needs to be parsed/stored, but not validated, "
1140+
"in order for Sync Validation to get read/write information out the SPIR-V.");
1141+
}
1142+
}
1143+
11221144
// Last as previous settings are needed so we can make sure they line up with the DebugReport settings
11231145
ProcessDebugReportSettings(settings_data, layer_setting_set, setting_warnings);
11241146

layers/layer_options.h

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,20 @@ struct GlobalSettings {
8484
bool fine_grained_locking = true;
8585

8686
bool debug_disable_spirv_val = false;
87+
88+
// We have the spirv::Module state object designed to hold the SPIR-V and parse it so we can do validation on it, the
89+
// issue/tricky part is gpuav/debugPrint use this to get the original SPIR-V (which we need to provide any useful error
90+
// messages). These settings are here to make it more obvious what we want at runtime
91+
//
92+
// Stores a copy, needed to provide error messages and how DebugPrintf works
93+
// (performance is fast, but memory overhead is higher)
94+
bool spirv_store = true;
95+
// Parsing of the SPIR-V to provide information for validation (core check, sync val, etc)
96+
// (small performance hit for large shader)
97+
bool spirv_parse = true;
98+
// If we want to have spirv-opt potentially do constant folding on the spec constants
99+
// (by far the largest performance bottle neck for large shaders using spec cosntants)
100+
bool spirv_const_fold = true;
87101
};
88102

89103
class DebugReport;

layers/state_tracker/pipeline_library_state.cpp

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,8 @@ PreRasterState::PreRasterState(const vvl::Pipeline &p, const vvl::DeviceState &s
9999
// don't need to worry about GroupDecoration in GPL
100100
spirv::StatelessData *stateless_data_stage =
101101
(stateless_data && i < kCommonMaxGraphicsShaderStages) ? &stateless_data[i] : nullptr;
102-
auto spirv_module = std::make_shared<spirv::Module>(shader_ci->codeSize, shader_ci->pCode, stateless_data_stage);
102+
auto spirv_module = vvl::CreateSpirvModuleState(shader_ci->codeSize, shader_ci->pCode, state_data.global_settings,
103+
stateless_data_stage);
103104
module_state = std::make_shared<vvl::ShaderModule>(VK_NULL_HANDLE, spirv_module);
104105
if (stateless_data_stage) {
105106
stateless_data_stage->pipeline_pnext_module = spirv_module;
@@ -219,8 +220,8 @@ void SetFragmentShaderInfoPrivate(const vvl::Pipeline &pipeline_state, FragmentS
219220
// don't need to worry about GroupDecoration in GPL
220221
spirv::StatelessData *stateless_data_stage =
221222
(stateless_data && i < kCommonMaxGraphicsShaderStages) ? &stateless_data[i] : nullptr;
222-
auto spirv_module =
223-
std::make_shared<spirv::Module>(shader_ci->codeSize, shader_ci->pCode, stateless_data_stage);
223+
auto spirv_module = vvl::CreateSpirvModuleState(shader_ci->codeSize, shader_ci->pCode,
224+
state_data.global_settings, stateless_data_stage);
224225
module_state = std::make_shared<vvl::ShaderModule>(VK_NULL_HANDLE, spirv_module);
225226
if (stateless_data_stage) {
226227
stateless_data_stage->pipeline_pnext_module = spirv_module;

layers/state_tracker/pipeline_state.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,8 @@ std::vector<ShaderStageState> Pipeline::GetStageStates(const DeviceState &state_
9191
// This support was also added in VK_KHR_maintenance5
9292
if (const auto shader_ci = vku::FindStructInPNextChain<VkShaderModuleCreateInfo>(stage_ci.pNext)) {
9393
// don't need to worry about GroupDecoration in GPL
94-
auto spirv_module = std::make_shared<spirv::Module>(shader_ci->codeSize, shader_ci->pCode, stateless_data);
94+
auto spirv_module =
95+
CreateSpirvModuleState(shader_ci->codeSize, shader_ci->pCode, state_data.global_settings, stateless_data);
9596
module_state = std::make_shared<vvl::ShaderModule>(VK_NULL_HANDLE, spirv_module);
9697
if (stateless_data) {
9798
stateless_data->pipeline_pnext_module = spirv_module;

layers/state_tracker/shader_module.cpp

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
#include <queue>
2222

2323
#include <vulkan/utility/vk_format_utils.h>
24+
#include "layer_options.h"
2425
#include "utils/assert_utils.h"
2526
#include "utils/hash_util.h"
2627
#include "generated/spirv_grammar_helper.h"
@@ -944,8 +945,11 @@ EntryPoint::EntryPoint(const Module& module_state, const Instruction& entrypoint
944945
}
945946
}
946947

947-
Module::StaticData::StaticData(const Module& module_state, StatelessData* stateless_data) {
948-
if (!module_state.valid_spirv) return;
948+
Module::StaticData::StaticData(const Module& module_state, bool parse, StatelessData* stateless_data) {
949+
// If parse is set off, save time because StaticData is never accessed
950+
if (!module_state.valid_spirv || !parse) {
951+
return;
952+
}
949953

950954
// Parse the words first so we have instruction class objects to use
951955
{
@@ -2601,3 +2605,15 @@ bool Module::UsesStorageCapabilityStorageClass(const Instruction& insn) const {
26012605
}
26022606

26032607
} // namespace spirv
2608+
2609+
namespace vvl {
2610+
// Need to allow a way to not waste time copying over to spirv::Module::words_ when we don't want to store the SPIR-V
2611+
std::shared_ptr<spirv::Module> CreateSpirvModuleState(size_t codeSize, const uint32_t* pCode, const GlobalSettings& global_settings,
2612+
spirv::StatelessData* stateless_data) {
2613+
const bool is_valid_spirv = (pCode && pCode[0] == spv::MagicNumber && ((codeSize % 4) == 0));
2614+
if (!global_settings.spirv_store) {
2615+
return std::make_shared<spirv::Module>(is_valid_spirv);
2616+
}
2617+
return std::make_shared<spirv::Module>(codeSize, pCode, is_valid_spirv, global_settings.spirv_parse, stateless_data);
2618+
}
2619+
} // namespace vvl

layers/state_tracker/shader_module.h

Lines changed: 20 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -632,7 +632,7 @@ struct Module {
632632
// The goal of this struct is to move everything that is ready only into here
633633
struct StaticData {
634634
StaticData() = default;
635-
StaticData(const Module &module_state, StatelessData *stateless_data = nullptr);
635+
StaticData(const Module &module_state, bool parse, StatelessData *stateless_data);
636636
StaticData &operator=(StaticData &&) = default;
637637
StaticData(StaticData &&) = default;
638638

@@ -713,14 +713,20 @@ struct Module {
713713
VulkanTypedHandle handle_; // Will be updated once its known its valid SPIR-V
714714
VulkanTypedHandle handle() const { return handle_; } // matches normal convention to get handle
715715

716-
// Used for when modifying the SPIR-V (spirv-opt, GPU-AV instrumentation, etc) and need reparse it for VVL validation
717-
Module(vvl::span<const uint32_t> code) : valid_spirv(true), words_(code.begin(), code.end()), static_data_(*this) {}
716+
// Only currently used for when modifying the SPIR-V after spirv-opt and we need reparse it for VVL validation
717+
explicit Module(vvl::span<const uint32_t> code)
718+
: valid_spirv(true), words_(code.begin(), code.end()), static_data_(*this, true, nullptr) {}
718719

720+
// Used when we want to create a spirv::Module object (to make it easier to have a handle everywhere) but don't actually want to
721+
// store/parse the SPIR-V itself (because it is turned off via settings)
722+
explicit Module(bool is_valid_spirv) : valid_spirv(is_valid_spirv) {}
723+
724+
// "Normal" case
719725
// StatelessData is a pointer as we have cases were we don't need it and simpler to just null check the few cases that use it
720-
Module(size_t codeSize, const uint32_t *pCode, StatelessData *stateless_data = nullptr)
721-
: valid_spirv(pCode && pCode[0] == spv::MagicNumber && ((codeSize % 4) == 0)),
726+
Module(size_t codeSize, const uint32_t *pCode, bool is_valid_spirv, bool parse, StatelessData *stateless_data)
727+
: valid_spirv(is_valid_spirv),
722728
words_(pCode, pCode + codeSize / sizeof(uint32_t)),
723-
static_data_(*this, stateless_data) {}
729+
static_data_(*this, parse, stateless_data) {}
724730

725731
const Instruction *FindDef(uint32_t id) const {
726732
auto it = static_data_.definitions.find(id);
@@ -789,14 +795,19 @@ struct Module {
789795

790796
} // namespace spirv
791797

798+
struct GlobalSettings;
799+
792800
// Represents a VkShaderModule handle
793801
namespace vvl {
802+
803+
// Need to allow a way to not waste time copying over to spirv::Module::words_ when we don't want to store the SPIR-V
804+
std::shared_ptr<spirv::Module> CreateSpirvModuleState(size_t codeSize, const uint32_t *pCode, const GlobalSettings &global_settings,
805+
spirv::StatelessData *stateless_data = nullptr);
806+
794807
struct ShaderModule : public StateObject {
795808
ShaderModule(VkShaderModule handle, std::shared_ptr<spirv::Module> &spirv_module)
796809
: StateObject(handle, kVulkanObjectTypeShaderModule), spirv(spirv_module) {
797-
if (spirv) {
798-
spirv->handle_ = handle_;
799-
}
810+
spirv->handle_ = handle_;
800811
}
801812

802813
// For when we need to create a module with no SPIR-V backing it

0 commit comments

Comments
 (0)