Skip to content

Commit b8723bd

Browse files
committed
Fix #6 Migrate VK_EXT_debug_report to VK_EXT_debug_utils
1 parent a4990f0 commit b8723bd

File tree

12 files changed

+629
-372
lines changed

12 files changed

+629
-372
lines changed

bookcontents/chapter-02/chapter-02.md

Lines changed: 68 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -217,7 +217,6 @@ public class Instance {
217217
```
218218

219219
Let's get back to the constructor. Now we have a list of the names of the supported layers (array of Strings) we need to transform it to a pointer of a list of null terminated Strings:
220-
221220
```java
222221
public class Instance {
223222
...
@@ -239,7 +238,6 @@ public class Instance {
239238
```
240239

241240
Now that we've setup all the validation layers, we move on to extensions. Because Vulkan is a cross-platform API, links to windowing systems are handled through extensions. In our case we will be using GLFW, which has extensions for Vulkan, so we need to include this as an extension with the following code:
242-
243241
```java
244242
public class Instance {
245243
...
@@ -256,82 +254,86 @@ public class Instance {
256254
}
257255
```
258256

259-
Depending if we have enabled validation or not we will need to add another extension to support debugging:
260-
257+
Depending if we have enabled validation or not we will need to add the extension used for debugging. We will use the `VK_EXT_debug_utils` extension (which should be used instead of older debug extension such as `VK_EXT_debug_report` and `VK_EXT_debug_marker`).
261258
```java
262259
public class Instance {
263260
...
264261
public Instance(boolean validate) {
265262
...
266263
PointerBuffer requiredExtensions;
267264
if (supportsValidation) {
268-
// Debug extension
269-
ByteBuffer vkDebugReportExtension = stack.UTF8(EXTDebugReport.VK_EXT_DEBUG_REPORT_EXTENSION_NAME);
265+
ByteBuffer vkDebugUtilsExtension = stack.UTF8(EXTDebugUtils.VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
270266
requiredExtensions = stack.mallocPointer(glfwExtensions.remaining() + 1);
271-
requiredExtensions.put(glfwExtensions).put(vkDebugReportExtension);
267+
requiredExtensions.put(glfwExtensions).put(vkDebugUtilsExtension);
272268
} else {
273269
requiredExtensions = stack.mallocPointer(glfwExtensions.remaining());
274270
requiredExtensions.put(glfwExtensions);
275271
}
276-
requiredExtensions.flip(); ...
272+
requiredExtensions.flip();
273+
...
277274
}
278275
...
279276
}
280277
```
281278

282-
Additionally, if we have enabled the debug extension, we will be interested in setting a callback, so we can, for example, log the information reported. We have already enabled the debugging extension, but we need to configure it. This is done through an extension structure used while creating the Vulkan instance. As mentioned at the beginning, most of Vulkan creation structures reserve an extension parameter which can be used to configure extension-specific data. Therefore, if validation is enabled, we will execute the following code:
283-
279+
Additionally, if we have enabled the debug extension, we will be interested in setting a callback, so we can, for example, log the information reported. We have already enabled the debugging extension, but we need to create it. We also need to pass this extension while creating the instance to properly log errors while creating and destroying the instance:
284280
```java
285281
public class Instance {
286282
...
287283
public Instance(boolean validate) {
288284
...
289285
long extension = MemoryUtil.NULL;
290286
if (supportsValidation) {
291-
VkDebugReportCallbackCreateInfoEXT dbgCreateInfo = VkDebugReportCallbackCreateInfoEXT.callocStack(stack)
292-
.sType(EXTDebugReport.VK_STRUCTURE_TYPE_DEBUG_REPORT_CALLBACK_CREATE_INFO_EXT)
293-
.flags(EXTDebugReport.VK_DEBUG_REPORT_ERROR_BIT_EXT | EXTDebugReport.VK_DEBUG_REPORT_WARNING_BIT_EXT)
294-
.pfnCallback(DBG_FUNC)
295-
.pUserData(MemoryUtil.NULL);
296-
extension = dbgCreateInfo.address();
287+
debugUtils = createDebugCallBack();
288+
extension = debugUtils.address();
297289
}
298290
...
299291
}
300292
...
301293
}
302294
```
303-
304-
We create an instance of the class `VkDebugReportCallbackCreateInfoEXT`, in which, through the flags attribute we restrict the callbacks to error or warning messages. The callback is set using the `pfnCallback` method. In this case, we have created a function which has been defined like this:
305-
295+
This is done in a new method named `createDebugCallBack`:
306296
```java
307297
public class Instance {
308298
...
309-
private static final VkDebugReportCallbackEXT DBG_FUNC = VkDebugReportCallbackEXT.create(
310-
(flags, objectType, object, location, messageCode, pLayerPrefix, pMessage, pUserData) -> {
311-
String msg = VkDebugReportCallbackEXT.getString(pMessage);
312-
Level logLevel = Level.DEBUG;
313-
if ((flags & EXTDebugReport.VK_DEBUG_REPORT_INFORMATION_BIT_EXT) != 0) {
314-
logLevel = Level.INFO;
315-
} else if ((flags & EXTDebugReport.VK_DEBUG_REPORT_WARNING_BIT_EXT) != 0) {
316-
logLevel = Level.WARN;
317-
} else if ((flags & EXTDebugReport.VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT) != 0) {
318-
logLevel = Level.WARN;
319-
} else if ((flags & EXTDebugReport.VK_DEBUG_REPORT_ERROR_BIT_EXT) != 0) {
320-
logLevel = Level.ERROR;
321-
}
322-
323-
LOGGER.log(logLevel, "VkDebugReportCallbackEXT, messageCode: [{}], message: [{}]", messageCode, msg);
324-
325-
return VK_FALSE;
326-
}
327-
);
299+
public static final int MESSAGE_SEVERITY_BITMASK = VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT |
300+
VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT;
301+
public static final int MESSAGE_TYPE_BITMASK = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT |
302+
VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT |
303+
VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT;
304+
...
305+
private static VkDebugUtilsMessengerCreateInfoEXT createDebugCallBack() {
306+
VkDebugUtilsMessengerCreateInfoEXT result = VkDebugUtilsMessengerCreateInfoEXT
307+
.calloc()
308+
.sType(VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT)
309+
.messageSeverity(MESSAGE_SEVERITY_BITMASK)
310+
.messageType(MESSAGE_TYPE_BITMASK)
311+
.pfnUserCallback((messageSeverity, messageTypes, pCallbackData, pUserData) -> {
312+
VkDebugUtilsMessengerCallbackDataEXT callbackData = VkDebugUtilsMessengerCallbackDataEXT.create(pCallbackData);
313+
Level logLevel = Level.DEBUG;
314+
if ((messageSeverity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT) != 0) {
315+
logLevel = Level.INFO;
316+
} else if ((messageSeverity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT) != 0) {
317+
logLevel = Level.WARN;
318+
} else if ((messageSeverity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT) != 0) {
319+
logLevel = Level.ERROR;
320+
}
321+
322+
LOGGER.log(logLevel, "VkDebugUtilsCallback, {}", callbackData.pMessageString());
323+
return VK_FALSE;
324+
});
325+
return result;
326+
}
328327
...
329328
}
330329
```
330+
In this method, we instantiate a `VkDebugUtilsMessengerCreateInfoEXT` which is defined by the following attributes:
331+
- `sType`: The type of the structure: `VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT`.
332+
- `messageSeverity`: This will hold a bit mask with the levels of the messages that we are interested in receiving. In our case, we will receive error and warning messages.
333+
- `messageType`: This will hold a bit mask with the types of messages that we are interested in receiving. In our case, we will receive validation and performance messages.
334+
- `pfnUserCallback`: The function that will be invoked when a message matches the criteria established by the `messageSeverity` and `messageType` fields. In our case, we just log the message with the proper logging level according to the `messageSeverity` parameter.
331335

332-
This method just logs the message reported using an appropriate severity level.
333-
334-
Finally, we have everything we need in order to create the Vulkan instance. In order to do so, we need to setup yet another structure: `VkInstanceCreateInfo`, which is defined as follows:
336+
Finally, going back to the constructor, we have everything we need in order to create the Vulkan instance. In order to do so, we need to setup yet another structure: `VkInstanceCreateInfo`, which is defined as follows:
335337

336338
- Structure type: `VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO`.
337339
- Next extension: In our case, the debug extension configuration structure or `NULL` if no validation is requested or supported.
@@ -357,6 +359,25 @@ public class Instance {
357359
PointerBuffer pInstance = stack.mallocPointer(1);
358360
vkCheck(vkCreateInstance(instanceInfo, null, pInstance), "Error creating instance");
359361
vkInstance = new VkInstance(pInstance.get(0), instanceInfo);
362+
...
363+
}
364+
}
365+
...
366+
}
367+
```
368+
369+
The last step is to instantiate the debug extension. We have passed its configuration while creating the Vulkan instance, but this will only be used in the instance creation and destruction phases. For the rest of the code, we will need to instantiate by calling the `vkCreateDebugUtilsMessengerEXT` Vulkan function.
370+
```java
371+
public class Instance {
372+
...
373+
public Instance(boolean validate) {
374+
...
375+
vkDebugHandle = VK_NULL_HANDLE;
376+
if (supportsValidation) {
377+
LongBuffer longBuff = stack.mallocLong(1);
378+
vkCheck(vkCreateDebugUtilsMessengerEXT(vkInstance, debugUtils, null, longBuff), "Error creating debug utils");
379+
vkDebugHandle = longBuff.get(0);
380+
}
360381
}
361382
}
362383
...
@@ -391,6 +412,12 @@ public class Instance {
391412
...
392413
public void cleanup() {
393414
LOGGER.debug("Destroying Vulkan instance");
415+
if (vkDebugHandle != VK_NULL_HANDLE) {
416+
vkDestroyDebugUtilsMessengerEXT(vkInstance, vkDebugHandle, null);
417+
}
418+
if (debugUtils != null) {
419+
debugUtils.free();
420+
}
394421
vkDestroyInstance(vkInstance, null);
395422
}
396423
...
@@ -401,7 +428,6 @@ public class Instance {
401428
```
402429

403430
Finally, we can use the Instance class in our `Render` class, in the `init` and `cleanup` methods.
404-
405431
```java
406432
public class Render {
407433
...

booksamples/chapter-02/src/main/java/org/vulkanb/eng/graph/vk/Instance.java

Lines changed: 51 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -6,36 +6,26 @@
66
import org.lwjgl.system.*;
77
import org.lwjgl.vulkan.*;
88

9-
import java.nio.ByteBuffer;
9+
import java.nio.*;
1010
import java.util.*;
1111

12+
import static org.lwjgl.vulkan.EXTDebugUtils.*;
1213
import static org.lwjgl.vulkan.VK11.*;
1314
import static org.vulkanb.eng.graph.vk.VulkanUtils.vkCheck;
1415

1516
public class Instance {
1617

17-
private static final Logger LOGGER = LogManager.getLogger();
18-
private static final VkDebugReportCallbackEXT DBG_FUNC = VkDebugReportCallbackEXT.create(
19-
(flags, objectType, object, location, messageCode, pLayerPrefix, pMessage, pUserData) -> {
20-
String msg = VkDebugReportCallbackEXT.getString(pMessage);
21-
Level logLevel = Level.DEBUG;
22-
if ((flags & EXTDebugReport.VK_DEBUG_REPORT_INFORMATION_BIT_EXT) != 0) {
23-
logLevel = Level.INFO;
24-
} else if ((flags & EXTDebugReport.VK_DEBUG_REPORT_WARNING_BIT_EXT) != 0) {
25-
logLevel = Level.WARN;
26-
} else if ((flags & EXTDebugReport.VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT) != 0) {
27-
logLevel = Level.WARN;
28-
} else if ((flags & EXTDebugReport.VK_DEBUG_REPORT_ERROR_BIT_EXT) != 0) {
29-
logLevel = Level.ERROR;
30-
}
31-
32-
LOGGER.log(logLevel, "VkDebugReportCallbackEXT, messageCode: [{}], message: [{}]", messageCode, msg);
18+
public static final int MESSAGE_SEVERITY_BITMASK = VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT |
19+
VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT;
20+
public static final int MESSAGE_TYPE_BITMASK = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT |
21+
VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT |
22+
VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT;
3323

34-
return VK_FALSE;
35-
}
36-
);
24+
private static final Logger LOGGER = LogManager.getLogger();
3725

3826
private final VkInstance vkInstance;
27+
private VkDebugUtilsMessengerCreateInfoEXT debugUtils;
28+
private long vkDebugHandle;
3929

4030
public Instance(boolean validate) {
4131
LOGGER.debug("Creating Vulkan instance");
@@ -78,10 +68,9 @@ public Instance(boolean validate) {
7868

7969
PointerBuffer requiredExtensions;
8070
if (supportsValidation) {
81-
// Debug extension
82-
ByteBuffer vkDebugReportExtension = stack.UTF8(EXTDebugReport.VK_EXT_DEBUG_REPORT_EXTENSION_NAME);
71+
ByteBuffer vkDebugUtilsExtension = stack.UTF8(EXTDebugUtils.VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
8372
requiredExtensions = stack.mallocPointer(glfwExtensions.remaining() + 1);
84-
requiredExtensions.put(glfwExtensions).put(vkDebugReportExtension);
73+
requiredExtensions.put(glfwExtensions).put(vkDebugUtilsExtension);
8574
} else {
8675
requiredExtensions = stack.mallocPointer(glfwExtensions.remaining());
8776
requiredExtensions.put(glfwExtensions);
@@ -90,12 +79,8 @@ public Instance(boolean validate) {
9079

9180
long extension = MemoryUtil.NULL;
9281
if (supportsValidation) {
93-
VkDebugReportCallbackCreateInfoEXT dbgCreateInfo = VkDebugReportCallbackCreateInfoEXT.callocStack(stack)
94-
.sType(EXTDebugReport.VK_STRUCTURE_TYPE_DEBUG_REPORT_CALLBACK_CREATE_INFO_EXT)
95-
.flags(EXTDebugReport.VK_DEBUG_REPORT_ERROR_BIT_EXT | EXTDebugReport.VK_DEBUG_REPORT_WARNING_BIT_EXT)
96-
.pfnCallback(DBG_FUNC)
97-
.pUserData(MemoryUtil.NULL);
98-
extension = dbgCreateInfo.address();
82+
debugUtils = createDebugCallBack();
83+
extension = debugUtils.address();
9984
}
10085

10186
// Create instance info
@@ -109,11 +94,47 @@ public Instance(boolean validate) {
10994
PointerBuffer pInstance = stack.mallocPointer(1);
11095
vkCheck(vkCreateInstance(instanceInfo, null, pInstance), "Error creating instance");
11196
vkInstance = new VkInstance(pInstance.get(0), instanceInfo);
97+
98+
vkDebugHandle = VK_NULL_HANDLE;
99+
if (supportsValidation) {
100+
LongBuffer longBuff = stack.mallocLong(1);
101+
vkCheck(vkCreateDebugUtilsMessengerEXT(vkInstance, debugUtils, null, longBuff), "Error creating debug utils");
102+
vkDebugHandle = longBuff.get(0);
103+
}
112104
}
113105
}
114106

107+
private static VkDebugUtilsMessengerCreateInfoEXT createDebugCallBack() {
108+
VkDebugUtilsMessengerCreateInfoEXT result = VkDebugUtilsMessengerCreateInfoEXT
109+
.calloc()
110+
.sType(VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT)
111+
.messageSeverity(MESSAGE_SEVERITY_BITMASK)
112+
.messageType(MESSAGE_TYPE_BITMASK)
113+
.pfnUserCallback((messageSeverity, messageTypes, pCallbackData, pUserData) -> {
114+
VkDebugUtilsMessengerCallbackDataEXT callbackData = VkDebugUtilsMessengerCallbackDataEXT.create(pCallbackData);
115+
Level logLevel = Level.DEBUG;
116+
if ((messageSeverity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT) != 0) {
117+
logLevel = Level.INFO;
118+
} else if ((messageSeverity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT) != 0) {
119+
logLevel = Level.WARN;
120+
} else if ((messageSeverity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT) != 0) {
121+
logLevel = Level.ERROR;
122+
}
123+
124+
LOGGER.log(logLevel, "VkDebugUtilsCallback, {}", callbackData.pMessageString());
125+
return VK_FALSE;
126+
});
127+
return result;
128+
}
129+
115130
public void cleanup() {
116131
LOGGER.debug("Destroying Vulkan instance");
132+
if (vkDebugHandle != VK_NULL_HANDLE) {
133+
vkDestroyDebugUtilsMessengerEXT(vkInstance, vkDebugHandle, null);
134+
}
135+
if (debugUtils != null) {
136+
debugUtils.free();
137+
}
117138
vkDestroyInstance(vkInstance, null);
118139
}
119140

@@ -168,4 +189,4 @@ private String[] getSupportedValidationLayers(MemoryStack stack) {
168189
public VkInstance getVkInstance() {
169190
return vkInstance;
170191
}
171-
}
192+
}

0 commit comments

Comments
 (0)