You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: en/03_Drawing_a_triangle/00_Setup/03_Physical_devices_and_queue_families.adoc
+81-17Lines changed: 81 additions & 17 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -199,6 +199,83 @@ void pickPhysicalDevice() {
199
199
throw std::runtime_error("failed to find a suitable GPU!");
200
200
}
201
201
}
202
+
203
+
=== Understanding the nested lambda functions
204
+
205
+
The `pickPhysicalDevice()` function above uses several nested lambda functions, which might look complex if you're not familiar with this C++ feature. Let's break down what's happening:
206
+
207
+
==== What are lambda functions?
208
+
209
+
Lambda functions (or lambda expressions) are a C++ feature that allows you to define anonymous functions inline. They're especially useful for short operations that you don't need to define as separate named functions.
210
+
211
+
The basic syntax of a lambda is:
212
+
```cpp
213
+
[capture-list](parameters) { body }
214
+
```
215
+
216
+
- The `capture-list` specifies which variables from the surrounding scope are accessible inside the lambda
217
+
- `parameters` are the input parameters, just like in regular functions
218
+
- `body` contains the code that will be executed
219
+
220
+
==== The main device selection lambda
221
+
222
+
In our `pickPhysicalDevice()` function, we use `std::ranges::find_if` with a lambda to find the first suitable device:
223
+
224
+
```cpp
225
+
const auto devIter = std::ranges::find_if(devices,
226
+
[&](auto const & device) {
227
+
// Lambda body that checks if the device is suitable
228
+
// ...
229
+
return isSuitable;
230
+
});
231
+
```
232
+
233
+
The `[&]` capture list means this lambda can access all variables from the
234
+
surrounding scope by reference. This is necessary because we need to access
235
+
the `deviceExtensions` variable. NB: this can be replaced by the explicit
236
+
capture of deviceExtnesions variable, however, the & (capture of everything) is
237
+
convenience andclarity as goals in mind.
238
+
239
+
==== Nested lambda for finding graphics queue family
240
+
241
+
Inside the main lambda, we have another lambda that checks if a queue family supports graphics operations:
242
+
243
+
```cpp
244
+
const auto qfpIter = std::ranges::find_if(queueFamilies,
assert(graphicsQueueFamilyProperty != queueFamilyProperties.end() && "No graphics queue family found!");
57
+
58
+
// Calculate the index of the graphics queue family
59
+
auto graphicsIndex = static_cast<uint32_t>( std::distance( queueFamilyProperties.begin(), graphicsQueueFamilyProperty ) );
60
+
----
61
+
62
+
Let's break down this code to understand what it's doing:
63
+
64
+
1. First, we get all queue family properties from the physical device using `getQueueFamilyProperties()`.
65
+
66
+
2. Then, we use `std::ranges::find_if` to find the first queue family that supports graphics operations:
67
+
- `std::ranges::find_if` takes a collection and a predicate function (the lambda), and returns an iterator to the first element that satisfies the predicate.
68
+
- The lambda function `[]( auto const & qfp ) { ... }` checks if a queue family has graphics capabilities.
69
+
- Inside the lambda, we use a bitwise AND operation `&` to check if the `queueFlags` include the `eGraphics` flag.
70
+
- The comparison `!= static_cast<vk::QueueFlags>(0)` ensures the result is non-zero, meaning the graphics flag is set.
71
+
72
+
3. We use `assert` to verify that we found a queue family with graphics support.
73
+
74
+
4. Finally, we calculate the index of the graphics queue family using `std::distance`, which gives us the position of the iterator in the collection.
75
+
76
+
Now that we have the graphics queue family index, we can use it to create our queue:
0 commit comments