Debugging VPP programs
VPP offers some features helpful for debugging. You can find a short overview of them in this section.
Debug probes
It is still harder to debug shader code than regular C++ code, as we have no source level debugger available. One of particular simple methods to use in such cases, known even to beginner programmers, is to temporarily add logging to the program in the form of printf
statements. For graphics applications however this is impractical, as we are working on millions of pixels in parallel. Better way would be to have a "printf"
which generates images. That's exactly what debug probes in VPP do.
Images generated by debug probes are not displayed anywhere, but there is one way that they are intended to be viewed: use rendering debugger like RenderDoc. These debug probe images will appear as additional images in the pipeline state and you can conveniently examine them.
Debug probes are extremely useful when you do not get result from your shader that you expected and suspect a computation error somewhere. They can help e.g. to visualize intermediate results.
More information on debug probes can be found on the documentation page for vpp::Shader and vpp::FragmentShader classes, which implement probe functionality as methods.
Vulkan validation support
VPP provides support for Vulkan validation layer. In order to turn on the validation, append .validation ( true )
call when creating the vpp::Instance object by vpp::createInstance().
This however does not suffice, as we did not specify where the validation warnings should go. vpp::StreamDebugReporter class allows simple redirection of these messages into std::ostream
. To gather the messages, create a std::ostream
(with lifetime as long as your vpp::Instance object) and vpp::StreamDebugReporter object. Supply references to the stream and instance objects in its constructor. This is sufficient to have validation messages routed to specified stream.
An example:
class MyRenderingEngine
{
private:
#ifdef _DEBUG
static const bool VALIDATION = true;
#else
static const bool VALIDATION = false;
#endif
std::ostringstream m_validationLog;
public:
MyRenderingEngine :: MyRenderingEngine() :
m_instance ( createInstance().validation ( VALIDATION ) ),
m_debugReporter ( m_validationLog, m_instance ),
{
}
};
It can be also a file stream or any other kind of C++ output stream.
In case if more advanced error processing is needed, you ca create your own subclass of vpp::DebugReporter class and use it instead of vpp::StreamDebugReporter.
{
public:
VkDebugReportFlagsEXT flags,
VkDebugReportObjectTypeEXT objectType,
uint64_t object,
size_t location,
int32_t messageCode,
const char* pLayerPrefix,
const char* pMessage );
const std::string& shaderCode,
const char* pShaderType );
};
You can override two methods here (both are optional): debugReport
and shaderCompilationLog
. Mosty likely you will be interested in the first one. The second is explained below.
When overriding debugReport
, return VK_FALSE
value, as Vulkan specification recommends. Returning VK_TRUE
will abort your program.
Shader compilation log
Although VPP tranparently handles SPIR-V generation, sometimes it is useful to look at the disassembled version of generated SPIR-V code. Use this feature e.g. if you suspect a bug in VPP. In order to obtain the code dump, you need to do two things:
- On the global level, supply
SHADERS
flag to the constructor of vpp::DebugReporter or vpp::StreamDebugReporter. This globally enables shader dumps - but will not cause any dumps to be generated yet.
- In particular shader you want to dump, use special function named
DebugCodeDump()
, like in the example below.
{
const Mat4 m2w = inFramePar [ & GFramePar::m_model2world ];
const Mat4 w2v = inFramePar [ & GFramePar::m_world2view ];
const Mat4 v2p = inFramePar [ & GFramePar::m_view2projected ];
const Vec4 inPos = m_vertices [ & GVertexAttr::m_position ];
const Vec4 inColor = m_vertices [ & GVertexAttr::m_color ];
const Vec4 result = v2p * w2v * m2w * inPos;
outColor = inColor;
}
The vertex shader shown above will emit SPIR-V code like this:
-----------------------------------------------------
Shader compilation (vertex)
-------------------- Code start ---------------------
Capability Shader
1: ExtInstImport "GLSL.std.450"
MemoryModel Logical GLSL450
EntryPoint Vertex 4 "main"
Source GLSL 450
Name 4 "main"
Name 9 "struct vppui::TFramePar<2>"
Name 31 "gl_PerVertex"
MemberName 31(gl_PerVertex) 0 "gl_Position"
MemberName 31(gl_PerVertex) 1 "gl_PointSize"
MemberDecorate 9(struct vppui::TFramePar<2>) 0 Offset 0
MemberDecorate 9(struct vppui::TFramePar<2>) 0 ColMajor
MemberDecorate 9(struct vppui::TFramePar<2>) 0 MatrixStride 16
MemberDecorate 9(struct vppui::TFramePar<2>) 1 Offset 64
MemberDecorate 9(struct vppui::TFramePar<2>) 1 ColMajor
MemberDecorate 9(struct vppui::TFramePar<2>) 1 MatrixStride 16
MemberDecorate 9(struct vppui::TFramePar<2>) 2 Offset 128
MemberDecorate 9(struct vppui::TFramePar<2>) 2 ColMajor
MemberDecorate 9(struct vppui::TFramePar<2>) 2 MatrixStride 16
Decorate 9(struct vppui::TFramePar<2>) Block
Decorate 11 DescriptorSet 0
Decorate 11 Binding 0
Decorate 24 Binding 0
Decorate 24 Location 0
Decorate 26 Binding 0
Decorate 26 Location 1
Decorate 31(gl_PerVertex) Block
MemberDecorate 31(gl_PerVertex) 0 BuiltIn Position
MemberDecorate 31(gl_PerVertex) 1 BuiltIn PointSize
Decorate 38 Location 0
2: TypeVoid
3: TypeFunction 2
6: TypeFloat 32
7: TypeVector 6(float) 4
8: TypeMatrix 7(fvec4) 4
9(struct vppui::TFramePar<2>): TypeStruct 8 8 8
10: TypePointer Uniform 9(struct vppui::TFramePar<2>)
11: 10(ptr) Variable Uniform
12: TypeInt 32 0
13: 12(int) Constant 0
14: TypePointer Uniform 8
17: 12(int) Constant 1
20: 12(int) Constant 2
23: TypePointer Input 7(fvec4)
24: 23(ptr) Variable Input
26: 23(ptr) Variable Input
31(gl_PerVertex): TypeStruct 7(fvec4) 6(float)
32: TypePointer Output 31(gl_PerVertex)
33: 32(ptr) Variable Output
34: TypeInt 32 1
35: 34(int) Constant 0
36: TypePointer Output 7(fvec4)
38: 36(ptr) Variable Output
4(main): 2 Function None 3
5: Label
15: 14(ptr) AccessChain 11 13
16: 8 Load 15
18: 14(ptr) AccessChain 11 17
19: 8 Load 18
21: 14(ptr) AccessChain 11 20
22: 8 Load 21
25: 7(fvec4) Load 24
27: 7(fvec4) Load 26
28: 8 MatrixTimesMatrix 22 19
29: 8 MatrixTimesMatrix 28 16
30: 7(fvec4) MatrixTimesVector 29 25
37: 36(ptr) AccessChain 33 35
Store 37 30
Store 38 27
FunctionEnd
--------------------- Code end ----------------------