Skip to content

Commit ef7143e

Browse files
authored
[Vulkan] Added debug saving of Vulkan shaders, environment variable documentation. (#8333)
Frequently, looking at the shaders generated by the Vulkan codegen is useful for debugging. While this can be done by checking the `mod.imported_modules[0].get_source()`, that requires the shader to first pass validation. Co-authored-by: Eric Lunderberg <elunderberg@octoml.ai>
1 parent 33277c3 commit ef7143e

5 files changed

Lines changed: 108 additions & 20 deletions

File tree

docs/dev/runtimes/vulkan.rst

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -205,3 +205,55 @@ not enabled in the ``Target``, an exception will be raised.
205205
* - ``supports_int64``
206206
-
207207
- Int64
208+
209+
210+
Vulkan-Specific Environment Variables
211+
-------------------------------------
212+
213+
Both the SPIR-V code generation and the Vulkan runtime have
214+
environment variables that can modify some of the runtime behavior.
215+
These are intended for debugging purposes, both to more easily test
216+
specific code paths, and to output more information as needed. All
217+
boolean flags are true if the environment variable is set to a
218+
non-zero integer. An unset variable, the integer zero, or an empty
219+
string are all false boolean flags.
220+
221+
.. _VK_KHR_push_descriptor: https://khronos.org/registry/vulkan/specs/1.2-extensions/man/html/VK_KHR_push_descriptor.html
222+
223+
.. _VK_KHR_descriptor_update_template: https://www.khronos.org/registry/vulkan/specs/1.2-extensions/man/html/VK_KHR_descriptor_update_template.html
224+
225+
.. _VK_KHR_dedicated_allocation: https://www.khronos.org/registry/vulkan/specs/1.2-extensions/man/html/VK_KHR_dedicated_allocation.html
226+
227+
.. _VkMemoryDedicatedRequirements: https://www.khronos.org/registry/vulkan/specs/1.2-extensions/man/html/VkMemoryDedicatedRequirements.html
228+
229+
.. _Vulkan validation layers: https://github.com/KhronosGroup/Vulkan-LoaderAndValidationLayers/blob/master/layers/README.md
230+
231+
.. _spvValidate: https://github.com/KhronosGroup/SPIRV-Tools#validator
232+
233+
234+
* ``TVM_VULKAN_DISABLE_PUSH_DESCRIPTOR`` - A boolean flag. If true,
235+
TVM will explicitly allocate descriptors, and will not use the
236+
`VK_KHR_push_descriptor`_ or `VK_KHR_descriptor_update_template`_
237+
extensions. If false, TVM will decide whether to use these
238+
extensions based on their availability.
239+
240+
* ``TVM_VULKAN_DISABLE_DEDICATED_ALLOCATION`` - A boolean flag. If
241+
true, TVM will not mark memory allocations as being dedicated
242+
allocations, and will not use the `VK_KHR_dedicated_allocation`_
243+
extension. If false, TVM will decide whether memory allocations
244+
should be marked as dedicated based on the
245+
`VkMemoryDedicatedRequirements`_ for that buffer.
246+
247+
* ``TVM_VULKAN_ENABLE_VALIDATION_LAYERS`` - A boolean flag. If true,
248+
TVM will enable `Vulkan validation layers`_ that the device
249+
supports. If false, no validation layers are enabled.
250+
251+
* ``TVM_VULKAN_DISABLE_SHADER_VALIDATION`` - A boolean flag. If true,
252+
the SPIR-V shader validation done with `spvValidate`_ is skipped.
253+
If false (default), all SPIR-V shaders generated by TVM are
254+
validated with `spvValidate`_.
255+
256+
* ``TVM_VULKAN_DEBUG_SHADER_SAVEPATH`` - A path to a directory. If
257+
set to a non-empty string, the Vulkan codegen will save tir, binary
258+
SPIR-V, and disassembled SPIR-V shaders to this directory, to be
259+
used for debugging purposes.

src/runtime/vulkan/vulkan_device.cc

Lines changed: 7 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
#include <unordered_map>
2525
#include <utility>
2626

27+
#include "../../support/utils.h"
2728
#include "vulkan_common.h"
2829
#include "vulkan_device.h"
2930
#include "vulkan_device_api.h"
@@ -121,24 +122,15 @@ VulkanDeviceProperties::VulkanDeviceProperties(const VulkanInstance& instance,
121122
// Support is available based on these extensions, but allow it to
122123
// be disabled based on an environment variable.
123124
supports_push_descriptor = device.HasExtension("VK_KHR_push_descriptor") &&
124-
device.HasExtension("VK_KHR_descriptor_update_template");
125-
{
126-
const char* disable = std::getenv("TVM_VULKAN_DISABLE_PUSH_DESCRIPTOR");
127-
if (disable && *disable) {
128-
supports_push_descriptor = false;
129-
}
130-
}
125+
device.HasExtension("VK_KHR_descriptor_update_template") &&
126+
!support::BoolEnvironmentVar("TVM_VULKAN_DISABLE_PUSH_DESCRIPTOR");
131127

132128
// Support is available based on these extensions, but allow it to
133129
// be disabled based on an environment variable.
134-
supports_dedicated_allocation = device.HasExtension("VK_KHR_get_memory_requirements2") &&
135-
device.HasExtension("VK_KHR_dedicated_allocation");
136-
{
137-
const char* disable = std::getenv("TVM_VULKAN_DISABLE_DEDICATED_ALLOCATION");
138-
if (disable && *disable) {
139-
supports_dedicated_allocation = false;
140-
}
141-
}
130+
supports_dedicated_allocation =
131+
device.HasExtension("VK_KHR_get_memory_requirements2") &&
132+
device.HasExtension("VK_KHR_dedicated_allocation") &&
133+
!support::BoolEnvironmentVar("TVM_VULKAN_DISABLE_DEDICATED_ALLOCATION");
142134

143135
// The check of VK_SHADER_STAGE_COMPUTE_BIT isn't technically
144136
// needed, since it will be set so long at least one queue has

src/runtime/vulkan/vulkan_instance.cc

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
#include <cstdlib>
2323
#include <utility>
2424

25+
#include "../../support/utils.h"
2526
#include "vulkan_common.h"
2627

2728
namespace tvm {
@@ -32,10 +33,7 @@ VulkanInstance::VulkanInstance() {
3233
const auto layers = []() {
3334
std::vector<const char*> layers;
3435

35-
const char* validation_enabled_env = std::getenv("TVM_VULKAN_ENABLE_VALIDATION_LAYERS");
36-
bool validation_enabled = validation_enabled_env && *validation_enabled_env;
37-
38-
if (validation_enabled) {
36+
if (support::BoolEnvironmentVar("TVM_VULKAN_ENABLE_VALIDATION_LAYERS")) {
3937
uint32_t inst_layer_prop_count;
4038
VULKAN_CALL(vkEnumerateInstanceLayerProperties(&inst_layer_prop_count, nullptr));
4139
std::vector<VkLayerProperties> inst_layer_prop(inst_layer_prop_count);

src/support/utils.h

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@
3737
#include <algorithm>
3838
#include <array>
3939
#include <cctype>
40+
#include <cstdlib>
4041
#include <memory>
4142
#include <sstream>
4243
#include <string>
@@ -199,6 +200,31 @@ inline uint64_t HashCombine(uint64_t key, const T& value) {
199200
return key ^ (uint64_t(value) + 0x9e3779b9 + (key << 6) + (key >> 2));
200201
}
201202

203+
/*!
204+
* \brief Return whether a boolean flag is set as an environment variable.
205+
*
206+
* Returns true if the environment variable is set to a non-zero
207+
* integer, or to a non-empty string that is not an integer.
208+
*
209+
* Returns false if the environment variable is unset, if the
210+
* environment variable is set to the integer zero, or if the
211+
* environment variable is an empty string.
212+
*/
213+
inline bool BoolEnvironmentVar(const char* varname) {
214+
const char* var = std::getenv(varname);
215+
if (!var) {
216+
return false;
217+
}
218+
219+
int x = 0;
220+
std::istringstream is(var);
221+
if (is >> x) {
222+
return x;
223+
}
224+
225+
return *var;
226+
}
227+
202228
} // namespace support
203229
} // namespace tvm
204230
#endif // TVM_SUPPORT_UTILS_H_

src/target/spirv/build_vulkan.cc

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,12 @@
2626
#include <libspirv.h>
2727
#include <tvm/tir/transform.h>
2828

29+
#include <fstream>
30+
#include <sstream>
31+
2932
#include "../../runtime/vulkan/vulkan_module.h"
3033
#include "../../runtime/vulkan/vulkan_shader.h"
34+
#include "../../support/utils.h"
3135
#include "../build_common.h"
3236
#include "codegen_spirv.h"
3337

@@ -121,7 +125,23 @@ runtime::Module BuildSPIRV(IRModule mod, Target target, bool webgpu_restriction)
121125

122126
VulkanShader shader = cg.BuildFunction(f, entry);
123127

124-
spirv_tools.ValidateShader(shader.data);
128+
if (auto path = std::getenv("TVM_VULKAN_DEBUG_SHADER_SAVEPATH")) {
129+
if (*path) {
130+
std::stringstream ss;
131+
ss << path << "/" << f_name << "_";
132+
std::string prefix = ss.str();
133+
134+
std::ofstream(prefix + "tir.txt") << f;
135+
std::ofstream(prefix + "spv.txt") << spirv_tools.BinaryToText(shader.data);
136+
std::ofstream(prefix + "spv.spv", std::ios::binary)
137+
.write(reinterpret_cast<const char*>(shader.data.data()),
138+
sizeof(shader.data[0]) * shader.data.size());
139+
}
140+
}
141+
142+
if (!support::BoolEnvironmentVar("TVM_VULKAN_DISABLE_SHADER_VALIDATION")) {
143+
spirv_tools.ValidateShader(shader.data);
144+
}
125145

126146
if (webgpu_restriction) {
127147
for (auto param : f->params) {

0 commit comments

Comments
 (0)