1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
/*
This example details some common debugging techniques you might find helpful
when something goes wrong.
I hope you enjoy this choose-your-own-debugger adventure!
First you will want to read this:
https://github.com/attackgoat/screen-13/blob/master/examples/getting-started.md
Enter your "name" to begin:
cargo run --example debugger
You see an error message up ahead:
thread 'main' panicked at 'run with `RUST_LOG=trace` environment variable ...
Note:
In your own program you should init a compatible logger of some kind! Also note
that in a release build a bunch of debug_assert! checks won't run and also the GPU
is very likely to accept anything you give it and only just maybe crash or catch fire.
All programs must be tested with Vulkan validation layers enabled and look for
validity and synchronization errors.
To continue, uncomment line 30.
*/
fn main() -> Result<(), screen_13_window::WindowError> {
use {log::debug, screen_13::prelude::*, screen_13_window::Window, std::sync::Arc};
// 👋, 🌎!
//pretty_env_logger::init();
/*
The code ahead is filled with a dangerous `panic` if you did not install the Vulkan SDK!
You must now choose:
- If you did not install the SDK, you must goto line 8, above.
- If you have a recent SDK installed, you may advance the function pointer.
*/
Window::builder().debug(true).build()?.run(|frame| {
/*
You have now entered the per-frame callback. Everything is happening *so* fast. We just
executed line two of our program!
Note:
This callback runs each time the operating system requests a new window image and it
expects you to render something to `frame.swapchain_image` using
`frame.render_graph`. Note that this scope is infalliable. You may create additional
images and graphs if you choose. You can resolve multiple render graphs per frame -
but you only need to do that if you have a hot-section that is part of a VERY large
graph.
When something goes wrong, it is probably *not* during this frame closure. The
reason is that during this scope nearly everything is deferred until frame
resolution where we try to schedule the work and get it displayed on the screen.
Typically, as here, we let Screen 13 handle all graph resolution (no code or
concerns here) - but it is valid to control the process manually, see the available
functions in the API docs.
There are a few cases that will cause issues, and a few of interest are shown below.
You see a breakpoint approaching in the distance.
For this next bit, you'll want to install a debugger such as gdb or:
https://marketplace.visualstudio.com/items?itemName=vadimcn.vscode-lldb
See: https://askubuntu.com/questions/41629/after-upgrade-gdb-wont-attach-to-process
Instructions for VS Code - your adventure continues!:
- Setup your tasks.json file:
{
"version": "0.2.0",
"configurations": [
{
"type": "lldb",
"request": "attach",
"name": "Attach",
"pid": "${command:pickMyProcess}"
}
]
}
- Run `cargo run --example debugger`
- You should see the PID in the console output
- Enter the VS Code Debugger; click `[>] Attach (screen-13)`
- Enter the PID
- In the call stack pane, select the first thread; pause it
- You are now parked on a syscall
- Walk up about 12 stack frames by scrolling down and selecting:
`{closure#0} debugger.rs 110:21`
- It's 🕓 to de-🐛!
*/
/*
Case #1:
This will cause a validation error now (because this image is created here).
You have followed the above directions and now have an active debug session looking at
line 115. You try to step forward in vain. Comment out the second `image` binding to
continue.
It is left as an excerise to the reader to determine *what* might have gone wrong here.
*/
#[allow(unused_variables)]
let image = frame.render_graph.bind_node(
Image::create(
frame.device,
ImageInfo::image_2d(
1024,
1024,
vk::Format::R8G8B8A8_UNORM,
vk::ImageUsageFlags::STORAGE | vk::ImageUsageFlags::TRANSFER_SRC,
),
)
.unwrap(),
);
let image = frame.render_graph.bind_node(
Image::create(
frame.device,
ImageInfo::image_2d(
u32::MAX,
u32::MAX,
vk::Format::UNDEFINED,
vk::ImageUsageFlags::default(),
),
)
.unwrap(),
);
// Note: This is just for example
let compute_pipeline = Arc::new(
ComputePipeline::create(
frame.device,
ComputePipelineInfo::default(),
Shader::new_compute(
inline_spirv::inline_spirv!(
r#"
#version 460 core
layout(local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
layout(set = 0, binding = 42, rgba8) restrict readonly uniform image2D an_image;
void main() {/* TODO: 📈...💰! */}
"#,
comp
)
.as_slice(),
),
)
.unwrap(),
);
/*
Case #2:
We are about to record a compute pass which causes Screen 13 to panic
Note: You'll see a panic here:
thread 'main' panicked at 'uninitialized swapchain image ...'
This will cause a static assertion after this closure completes, but before it is
called again. It happens in display.rs.
Because this error does not cause validation layer messages, it does not hit the debug
"breakpoint" we setup on line 39. You have two choices:
- Read how pass_ref.rs lays out data which resolver.rs submits; debug it (you die)
- Goto line 180 and fix the bug
This is a valid thing to panic over because we expect that all frames will render
something to the swapchain image. The operating system is nicely asking that we repaint
the window, and so failing to do that means we're not a well-behaved program and so the
display code decides to panic. We *should* render an error message at least.
The simple fix is to write something - anything - to the swapchain. You may blit,
compute, copy, render, store, transfer or any number of other things and that will
signal it's OK to proceed without panicking.
Here is a fixed line 180:
.write_descriptor(42, frame.swapchain_image)
*/
frame
.render_graph
.begin_pass("This doesn't look good...")
.bind_pipeline(&compute_pipeline)
.write_descriptor(42, image)
.record_compute(|compute, _| {
compute.dispatch(1024, 1024, 1);
});
// Growing tired of your advenutes, you signal that it is time to close the window and exit
*frame.will_exit = true;
})?;
debug!("GAME OVER");
Ok(())
/*
The game is over - OR IS IT?!
We never actually wrote to the swapchain image! AH!
It turns out the image binding in the shader was set to read-only AND we didn't provide any
implementation in the main function either. In this case the image is noise, and *nothing*
complained.
Where to next? Fire up RenderDoc, capture a frame and have fun! But beware - RenderDoc does
a replay of the capture it created; and it resubmits things ever so slightly differently at
times - you most likely will NOT see any synchronization issues in RenderDoc if you DO see
them in Screen 13.
If you ever get stuck, switch between `vkconfig` settings of API dump and synchronization;
those usually say exactly what is going wrong, and usually you need to use multiple layers
together or not, depending on the order they "crash" in which may obscure the root cause.
THANK YOU FOR PLAYING!
*/
}