@@ -201,23 +201,77 @@ commands or one that only allows memory transfer related commands.
201201We need to check which queue families are supported by the device and which one
202202of these supports the commands that we want to use. For that purpose we'll add a
203203new function `findQueueFamilies` that looks for all the queue families we need.
204- Right now we'll only look for a queue that supports graphics commands, but we
205- may extend this function to look for more at a later point in time.
206204
207- This function will return the indices of the queue families that satisfy certain
208- desired properties. The best way to do that is using a structure where we use `std::optional` to track if an index was found:
205+ Right now we are only going to look for a queue that supports graphics commands,
206+ so the function could look like this:
207+
208+ ```c++
209+ uint32_t findQueueFamilies(VkPhysicalDevice device) {
210+ // Logic to find graphics queue family
211+ }
212+ ```
213+
214+ However, in one of the next chapters we're already going to look for yet another
215+ queue, so it's better to prepare for that and bundle the indices into a struct:
209216
210217``` c++
211218struct QueueFamilyIndices {
212- std::optional<uint32_t> graphicsFamily;
219+ uint32_t graphicsFamily;
220+ };
213221
214- bool isComplete() {
215- return graphicsFamily.has_value();
216- }
222+ QueueFamilyIndices findQueueFamilies(VkPhysicalDevice device) {
223+ QueueFamilyIndices indices;
224+ // Logic to find queue family indices to populate struct with
225+ return indices;
226+ }
227+ ```
228+
229+ But what if a queue family is not available? We could throw an exception in
230+ `findQueueFamilies`, but this function is not really the right place to make
231+ decisions about device suitability. For example, we may *prefer* devices with a
232+ dedicated transfer queue family, but not require it. Therefore we need some way
233+ of indicating whether a particular queue family was found.
234+
235+ It's not really possible to use a magic value to indicate the nonexistence of a
236+ queue family, since any value of `uint32_t` could in theory be a valid queue
237+ family index including `0`. Luckily C++17 introduced a data structure to
238+ distinguish between the case of a value existing or not:
239+
240+ ```c++
241+ #include <optional>
242+
243+ ...
244+
245+ std::optional<uint32_t> graphicsFamily;
246+
247+ std::cout << std::boolalpha << graphicsFamily.has_value() << std::endl; // false
248+
249+ graphicsFamily = 0;
250+
251+ std::cout << std::boolalpha << graphicsFamily.has_value() << std::endl; // true
252+ ```
253+
254+ ` std::optional ` is a wrapper that contains no value until you assign something
255+ to it. At any point you can query if it contains a value or not by calling its
256+ ` has_value() ` member function. That means that we can change the logic to:
257+
258+ ``` c++
259+ #include < optional>
260+
261+ ...
262+
263+ struct QueueFamilyIndices {
264+ std::optional<uint32_t> graphicsFamily;
217265};
266+
267+ QueueFamilyIndices findQueueFamilies(VkPhysicalDevice device) {
268+ QueueFamilyIndices indices;
269+ // Assign index to queue families that could be found
270+ return indices;
271+ }
218272```
219273
220- Note that this also requires including ` <optional> ` . We can now begin implementing ` findQueueFamilies ` :
274+ We can now begin to actually implement `findQueueFamilies`:
221275
222276```c++
223277QueueFamilyIndices findQueueFamilies(VkPhysicalDevice device) {
@@ -252,10 +306,6 @@ for (const auto& queueFamily : queueFamilies) {
252306 indices.graphicsFamily = i;
253307 }
254308
255- if (indices.isComplete()) {
256- break;
257- }
258-
259309 i++;
260310}
261311```
@@ -265,13 +315,48 @@ check in the `isDeviceSuitable` function to ensure that the device can process
265315the commands we want to use:
266316
267317``` c++
318+ bool isDeviceSuitable (VkPhysicalDevice device) {
319+ QueueFamilyIndices indices = findQueueFamilies(device);
320+
321+ return indices.graphicsFamily.has_value();
322+ }
323+ ```
324+
325+ To make this a little bit more convenient, we'll also add a generic check to the
326+ struct itself:
327+
328+ ```c++
329+ struct QueueFamilyIndices {
330+ std::optional<uint32_t> graphicsFamily;
331+
332+ bool isComplete() {
333+ return graphicsFamily.has_value();
334+ }
335+ };
336+
337+ ...
338+
268339bool isDeviceSuitable(VkPhysicalDevice device) {
269340 QueueFamilyIndices indices = findQueueFamilies(device);
270341
271342 return indices.isComplete();
272343}
273344```
274345
346+ We can now also use this for an early exit from ` findQueueFamilies ` :
347+
348+ ``` c++
349+ for (const auto & queueFamily : queueFamilies) {
350+ ...
351+
352+ if (indices.isComplete()) {
353+ break;
354+ }
355+
356+ i++;
357+ }
358+ ```
359+
275360Great, that's all we need for now to find the right physical device! The next
276361step is to [ create a logical device] ( !en/Drawing_a_triangle/Setup/Logical_device_and_queues )
277362to interface with it.
0 commit comments