Skip to content

Commit

Permalink
printf: Move implementation inside VVL
Browse files Browse the repository at this point in the history
  • Loading branch information
spencer-lunarg committed Jul 19, 2024
1 parent aa1480d commit 64b42f6
Show file tree
Hide file tree
Showing 19 changed files with 709 additions and 100 deletions.
2 changes: 2 additions & 0 deletions BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,8 @@ vvl_sources = [
"layers/gpu/spirv/pass.h",
"layers/gpu/spirv/ray_query_pass.cpp",
"layers/gpu/spirv/ray_query_pass.h",
"layers/gpu/spirv/debug_printf_pass.cpp",
"layers/gpu/spirv/debug_printf_pass.h",
"layers/gpu/spirv/type_manager.cpp",
"layers/gpu/spirv/type_manager.h",
"layers/layer_options.cpp",
Expand Down
4 changes: 2 additions & 2 deletions docs/debug_printf.md
Original file line number Diff line number Diff line change
Expand Up @@ -98,8 +98,7 @@ where:
* `N2`, `N3`, ... are result ids of scalar and vector values to be printed
* `NN` is the result id of the debug printf operation. This value is undefined.

Note that the `VK_KHR_shader_non_semantic_info` device extension must also be enabled in
the Vulkan application using this shader.
> `OpExtInstImport` of any `NonSemantic*` is properly supported with the `VK_KHR_shader_non_semantic_info` device extension. Some older compiler stacks might not handle these unknown instructions well, some will ignore it as desired.
## Debug Printf Output
The strings resulting from a Debug Printf will, by default, be sent to the debug callback
Expand Down Expand Up @@ -197,6 +196,7 @@ buffer size.
* VkPhysicalDevice features: `fragmentStoresAndAtomics` and `vertexPipelineStoresAndAtomics`
are required
* The `VK_KHR_shader_non_semantic_info` extension must be supported and enabled
* If using the Validation Layers, we attempt to strip it out to allow wider range of users to still use Debug Printf
* RenderDoc release 1.14 or later
* When using Debug Printf with a debug callback, it is recommended to disable validation,
as the debug level of INFO or DEBUG causes the validation layers to produce many messages
Expand Down
105 changes: 63 additions & 42 deletions layers/gpu/debug_printf/debug_printf.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,14 @@
*/

#include "gpu/debug_printf/debug_printf.h"
#include "spirv-tools/instrument.hpp"
#include <iostream>
#include "generated/layer_chassis_dispatch.h"
#include "state_tracker/shader_stage_state.h"
#include "chassis/chassis_modification_state.h"
#include "gpu/spirv/module.h"
#include "gpu/shaders/gpu_error_header.h"

#include <iostream>
#include <fstream>

namespace debug_printf {

Expand All @@ -47,8 +50,8 @@ void Validator::PostCreateDevice(const VkDeviceCreateInfo *pCreateInfo, const Lo
use_stdout = true;
}

const uint32_t kDebugOutputPrintfStream = 3; // from instrument.hpp
VkDescriptorSetLayoutBinding binding = {kDebugOutputPrintfStream, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1,
binding_slot_ = (uint32_t)instrumentation_bindings_.size(); // get next free binding
VkDescriptorSetLayoutBinding binding = {binding_slot_, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1,
kShaderStageAllGraphics | VK_SHADER_STAGE_COMPUTE_BIT | kShaderStageAllRayTracing,
nullptr};
instrumentation_bindings_.push_back(binding);
Expand Down Expand Up @@ -80,48 +83,65 @@ void Validator::DestroyBuffer(BufferInfo &buffer_info) {
}
}

static bool GpuValidateShader(const std::vector<uint32_t> &input, bool SetRelaxBlockLayout, bool SetScalerBlockLayout,
spv_target_env target_env, std::string &error) {
// Use SPIRV-Tools validator to try and catch any issues with the module
spv_context ctx = spvContextCreate(target_env);
spv_const_binary_t binary{input.data(), input.size()};
spv_diagnostic diag = nullptr;
spv_validator_options options = spvValidatorOptionsCreate();
spvValidatorOptionsSetRelaxBlockLayout(options, SetRelaxBlockLayout);
spvValidatorOptionsSetScalarBlockLayout(options, SetScalerBlockLayout);
spv_result_t result = spvValidateWithOptions(ctx, options, &binary, &diag);
if (result != SPV_SUCCESS && diag) error = diag->error;
return (result == SPV_SUCCESS);
}

// Call the SPIR-V Optimizer to run the instrumentation pass on the shader.
bool Validator::InstrumentShader(const vvl::span<const uint32_t> &input, uint32_t unique_shader_id, const Location &loc,
std::vector<uint32_t> &out_instrumented_spirv) {
if (input[0] != spv::MagicNumber) return false;

// Load original shader SPIR-V
out_instrumented_spirv.clear();
out_instrumented_spirv.reserve(input.size());
out_instrumented_spirv.insert(out_instrumented_spirv.end(), &input.front(), &input.back() + 1);
std::vector<uint32_t> binary;
binary.reserve(input.size());
binary.insert(binary.end(), &input.front(), &input.back() + 1);

// Call the optimizer to instrument the shader.
// Use the unique_shader_module_id as a shader ID so we can look up its handle later in the shader_map.
// If descriptor indexing is enabled, enable length checks and updated descriptor checks
using namespace spvtools;
spv_target_env target_env = PickSpirvEnv(api_version, IsExtEnabled(device_extensions.vk_khr_spirv_1_4));
spvtools::ValidatorOptions val_options;
AdjustValidatorOptions(device_extensions, enabled_features, val_options, nullptr);
spvtools::OptimizerOptions opt_options;
opt_options.set_run_validator(true);
opt_options.set_validator_options(val_options);
Optimizer optimizer(target_env);
const spvtools::MessageConsumer debug_printf_console_message_consumer =
[this, loc](spv_message_level_t level, const char *, const spv_position_t &position, const char *message) -> void {
switch (level) {
case SPV_MSG_FATAL:
case SPV_MSG_INTERNAL_ERROR:
case SPV_MSG_ERROR:
this->LogError("UNASSIGNED-Debug-Printf", this->device, loc,
"Error during shader instrumentation in spirv-opt: line %zu: %s", position.index, message);
break;
default:
break;
if (gpuav_settings.debug_dump_instrumented_shaders) {
std::string file_name = "dump_" + std::to_string(unique_shader_id) + "_before.spv";
std::ofstream debug_file(file_name, std::ios::out | std::ios::binary);
debug_file.write(reinterpret_cast<char *>(binary.data()), static_cast<std::streamsize>(binary.size() * sizeof(uint32_t)));
}

gpuav::spirv::Module module(binary, unique_shader_id, desc_set_bind_index_);

bool modified = module.RunPassDebugPrintf(binding_slot_);
if (!modified) return false;

module.PostProcess();
module.ToBinary(out_instrumented_spirv);

if (gpuav_settings.debug_dump_instrumented_shaders) {
std::string file_name = "dump_" + std::to_string(unique_shader_id) + "_after.spv";
std::ofstream debug_file(file_name, std::ios::out | std::ios::binary);
debug_file.write(reinterpret_cast<char *>(out_instrumented_spirv.data()),
static_cast<std::streamsize>(out_instrumented_spirv.size() * sizeof(uint32_t)));
}

// (Maybe) validate the instrumented shader
if (gpuav_settings.debug_validate_instrumented_shaders) {
std::string instrumented_error;
if (!GpuValidateShader(out_instrumented_spirv, device_extensions.vk_khr_relaxed_block_layout,
device_extensions.vk_ext_scalar_block_layout, target_env, instrumented_error)) {
std::ostringstream strm;
strm << "Instrumented shader (id " << unique_shader_id << ") is invalid, spirv-val error:\n"
<< instrumented_error << " Proceeding with non instrumented shader.";
InternalError(device, loc, strm.str().c_str());
return false;
}
};
optimizer.SetMessageConsumer(debug_printf_console_message_consumer);
optimizer.RegisterPass(CreateInstDebugPrintfPass(desc_set_bind_index_, unique_shader_id));
const bool pass =
optimizer.Run(out_instrumented_spirv.data(), out_instrumented_spirv.size(), &out_instrumented_spirv, opt_options);
if (!pass) {
InternalError(device, loc, "Failure to instrument shader in spirv-opt. Proceeding with non-instrumented shader.");
}
return pass;

return true;
}
// Create the instrumented shader data to provide to the driver.
void Validator::PreCallRecordCreateShaderModule(VkDevice device, const VkShaderModuleCreateInfo *pCreateInfo,
Expand Down Expand Up @@ -292,10 +312,10 @@ void Validator::AnalyzeAndGenerateMessage(VkCommandBuffer command_buffer, VkQueu
// 4 Printf Format String Id
// 5 Printf Values Word 0 (optional)
// 6 Printf Values Word 1 (optional)
uint32_t expect = debug_output_buffer[1];
uint32_t expect = debug_output_buffer[gpuav::kDebugPrintfOutputBufferSize];
if (!expect) return;

uint32_t index = spvtools::kDebugOutputDataOffset;
uint32_t index = gpuav::kDebugPrintfOutputBufferData;
while (debug_output_buffer[index]) {
std::stringstream shader_message;

Expand Down Expand Up @@ -405,13 +425,14 @@ void Validator::AnalyzeAndGenerateMessage(VkCommandBuffer command_buffer, VkQueu
}
index += debug_record->size;
}
if ((index - spvtools::kDebugOutputDataOffset) != expect) {
if ((index - gpuav::kDebugPrintfOutputBufferData) != expect) {
std::stringstream message;
message << "Debug Printf message was truncated due to a buffer size (" << printf_settings.buffer_size
<< ") being too small for the messages. (This can be adjusted with VK_LAYER_PRINTF_BUFFER_SIZE or vkconfig)";
InternalWarning(queue, loc, message.str().c_str());
}
memset(debug_output_buffer, 0, 4 * (debug_output_buffer[spvtools::kDebugOutputSizeOffset] + spvtools::kDebugOutputDataOffset));
memset(debug_output_buffer, 0,
4 * (debug_output_buffer[gpuav::kDebugPrintfOutputBufferSize] + gpuav::kDebugPrintfOutputBufferData));
}

// For the given command buffer, map its debug data buffers and read their contents for analysis.
Expand Down Expand Up @@ -691,7 +712,7 @@ void Validator::AllocateDebugPrintfResources(const VkCommandBuffer cmd_buffer, c
desc_writes.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
desc_writes.pBufferInfo = &output_desc_buffer_info;
desc_writes.dstSet = desc_sets[0];
desc_writes.dstBinding = 3;
desc_writes.dstBinding = binding_slot_;
DispatchUpdateDescriptorSets(device, desc_count, &desc_writes, 0, nullptr);

const auto pipeline_layout =
Expand Down
1 change: 1 addition & 0 deletions layers/gpu/debug_printf/debug_printf.h
Original file line number Diff line number Diff line change
Expand Up @@ -190,5 +190,6 @@ class Validator : public gpu::GpuShaderInstrumentor {
private:
bool verbose = false;
bool use_stdout = false;
uint32_t binding_slot_ = 0;
};
} // namespace debug_printf
3 changes: 2 additions & 1 deletion layers/gpu/instrumentation/gpuav_instrumentation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -99,10 +99,11 @@ bool Validator::InstrumentShader(const vvl::span<const uint32_t> &input, uint32_
return false;
}

for (const auto info : module.link_info_) {
for (const auto &info : module.link_info_) {
module.LinkFunction(info);
}

module.PostProcess();
module.ToBinary(out_instrumented_spirv);

if (gpuav_settings.debug_dump_instrumented_shaders) {
Expand Down
8 changes: 8 additions & 0 deletions layers/gpu/shaders/gpu_error_header.h
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,14 @@ const int kErrorBufferByteSize = 4 * kErrorRecordSize * kErrorRecordCounts + 2 *

#ifdef __cplusplus
} // namespace glsl
#endif

// DebugPrintf
// ---
const int kDebugPrintfOutputBufferSize = 0;
const int kDebugPrintfOutputBufferData = 1;

#ifdef __cplusplus
} // namespace gpuav
#endif
#endif
2 changes: 2 additions & 0 deletions layers/gpu/spirv/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ target_sources(gpu_av_spirv PRIVATE
buffer_device_address_pass.cpp
ray_query_pass.h
ray_query_pass.cpp
debug_printf_pass.h
debug_printf_pass.cpp

# Framework
instruction.h
Expand Down
Loading

0 comments on commit 64b42f6

Please sign in to comment.