init
This commit is contained in:
@@ -0,0 +1,34 @@
|
||||
# Copyright (c) 2024, Sascha Willems
|
||||
#
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 the "License";
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#
|
||||
|
||||
get_filename_component(FOLDER_NAME ${CMAKE_CURRENT_LIST_DIR} NAME)
|
||||
get_filename_component(PARENT_DIR ${CMAKE_CURRENT_LIST_DIR} PATH)
|
||||
get_filename_component(CATEGORY_NAME ${PARENT_DIR} NAME)
|
||||
|
||||
add_sample(
|
||||
ID ${FOLDER_NAME}
|
||||
CATEGORY ${CATEGORY_NAME}
|
||||
AUTHOR "Sascha Willems"
|
||||
NAME "Shader debugPrintf"
|
||||
DESCRIPTION "Demonstrates the use of the debugprintf command to display debugging information from shaders to the host"
|
||||
SHADER_FILES_GLSL
|
||||
"shader_debugprintf/glsl/scene.vert"
|
||||
"shader_debugprintf/glsl/scene.frag"
|
||||
SHADER_FILES_HLSL
|
||||
"shader_debugprintf/hlsl/scene.vert.hlsl"
|
||||
"shader_debugprintf/hlsl/scene.frag.hlsl"
|
||||
DXC_ADDITIONAL_ARGUMENTS "-fspv-extension=SPV_KHR_non_semantic_info")
|
||||
@@ -0,0 +1,238 @@
|
||||
////
|
||||
- Copyright (c) 2024, Sascha Willems
|
||||
-
|
||||
- SPDX-License-Identifier: Apache-2.0
|
||||
-
|
||||
- Licensed under the Apache License, Version 2.0 the "License";
|
||||
- you may not use this file except in compliance with the License.
|
||||
- You may obtain a copy of the License at
|
||||
-
|
||||
- http://www.apache.org/licenses/LICENSE-2.0
|
||||
-
|
||||
- Unless required by applicable law or agreed to in writing, software
|
||||
- distributed under the License is distributed on an "AS IS" BASIS,
|
||||
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
- See the License for the specific language governing permissions and
|
||||
- limitations under the License.
|
||||
-
|
||||
////
|
||||
= Shader debug Printf
|
||||
|
||||
ifdef::site-gen-antora[]
|
||||
TIP: The source for this sample can be found in the https://github.com/KhronosGroup/Vulkan-Samples/tree/main/samples/extensions/shader_debugprintf[Khronos Vulkan samples github repository].
|
||||
endif::[]
|
||||
|
||||
:pp: {plus}{plus}
|
||||
|
||||
image::./images/sample.png[Sample]
|
||||
|
||||
== Overview
|
||||
|
||||
This sample demonstrates the usage of the https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VK_KHR_shader_non_semantic_info.html[VK_KHR_shader_non_semantic_info] for adding means to output debug messages from SPIR-V shaders using a print function at runtime.
|
||||
|
||||
It allows you to output invocation related debug messages from a given shader stage and can help to find problems or bugs together with a graphics debugger like https://www.renderdoc.org[RenderDoc]. But it's also possible to display that data in your application (e.g. a debug window or the command line), although this has some limitations like a fixed buffer size that all messages need to fit in.
|
||||
|
||||
== (Debug) Printf for shaders
|
||||
|
||||
With this extension properly setup you can use https://en.wikipedia.org/wiki/Printf[Printf] statements (which exist in some form in most CPU side programming languages) in a shader to output messages from the current invocation of the shader (e.g. the current vertex processed in the vertex shader or the current fragment produced in the fragment shader). This lets you output values specific to that single invocation, adding a way of e.g. finding bugs or problems that only occur in certain invocations of execution parts of a shader.
|
||||
|
||||
For example outputting the transformed vertex positions in a vertex shader could look like this in glsl:
|
||||
|
||||
[,glsl]
|
||||
----
|
||||
void main() {
|
||||
vec4 outPosition = ubo.view * ubo.model * inPosition;
|
||||
debugPrintfEXT("Transformed position = %v4f", outPosition);
|
||||
}
|
||||
----
|
||||
|
||||
And like this in HLSL:
|
||||
[,hlsl]
|
||||
----
|
||||
VSOutput main(VSInput input) {
|
||||
float4 output.Position = mul(ubo.view, mul(ubo.model, input.Position));
|
||||
printf("Transformed position = %v4f", output.Position);
|
||||
}
|
||||
----
|
||||
|
||||
== Setup
|
||||
|
||||
Shader printf is a feature of the validation layers. So the first step is to enable the `VK_LAYER_KHRONOS_validation` layer at instance creation. In addition to the layer(s) to be enabled we also need to provide `VkValidationFeaturesEXT` with the `VK_VALIDATION_FEATURE_ENABLE_DEBUG_PRINTF_EXT` feature enabled:
|
||||
|
||||
[,cpp]
|
||||
----
|
||||
void ShaderDebugPrintf::create_instance()
|
||||
{
|
||||
...
|
||||
|
||||
// Shader printf is a feature of the validation layers that needs to be enabled
|
||||
std::vector<VkValidationFeatureEnableEXT> validation_feature_enables = {VK_VALIDATION_FEATURE_ENABLE_DEBUG_PRINTF_EXT};
|
||||
|
||||
VkValidationFeaturesEXT validation_features{VK_STRUCTURE_TYPE_VALIDATION_FEATURES_EXT};
|
||||
validation_features.enabledValidationFeatureCount = 1;
|
||||
validation_features.pEnabledValidationFeatures = validation_feature_enables.data();
|
||||
----
|
||||
|
||||
This then needs to be chained into the instance creation:
|
||||
|
||||
[,cpp]
|
||||
----
|
||||
std::vector<const char *> validation_layers = {"VK_LAYER_KHRONOS_validation"};
|
||||
|
||||
VkInstanceCreateInfo instance_create_info{VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO};
|
||||
...
|
||||
instance_create_info.pNext = &validation_features;
|
||||
----
|
||||
|
||||
*UPDATE:* This sample now leverages an alternative mechanism provided by the Vulkan Samples framework to simplify enablement of the `VK_LAYER_KHRONOS_validation` layer and the `VK_VALIDATION_FEATURE_ENABLE_DEBUG_PRINTF_EXT` feature:
|
||||
|
||||
[,cpp]
|
||||
----
|
||||
const std::vector<const char *> ShaderDebugPrintf::get_validation_layers()
|
||||
{
|
||||
// Initialize validation layer override list to default (empty)
|
||||
std::vector<const char *> validation_layers = ApiVulkanSample::get_validation_layers();
|
||||
|
||||
// Force activation of validation layer for access to debugPrintfEXT feature
|
||||
validation_layers.push_back("VK_LAYER_KHRONOS_validation");
|
||||
|
||||
return validation_layers;
|
||||
}
|
||||
----
|
||||
|
||||
The `VK_VALIDATION_FEATURE_ENABLE_DEBUG_PRINTF_EXT` feature is enabled by the new `VK_EXT_layer_settings` extension which is supported by the Vulkan Samples framework and used during instance creation. If the layer settings extension is available at runtime, the Vulkan Samples framework will enable the requested `debugPrintfEXT` feature using the `VkLayerSettingEXT` structure shown below. If `VK_EXT_layer_settings` is not available at runtime, the sample falls back to the custom `create_instance()` logic described above.
|
||||
|
||||
[,cpp]
|
||||
----
|
||||
ShaderDebugPrintf::ShaderDebugPrintf()
|
||||
{
|
||||
...
|
||||
|
||||
// If layer settings available, use it to configure validation layer for debugPrintfEXT
|
||||
add_instance_extension(VK_EXT_LAYER_SETTINGS_EXTENSION_NAME, /*optional*/ true);
|
||||
|
||||
VkLayerSettingEXT layerSetting;
|
||||
layerSetting.pLayerName = "VK_LAYER_KHRONOS_validation";
|
||||
layerSetting.pSettingName = "enables";
|
||||
layerSetting.type = VK_LAYER_SETTING_TYPE_STRING_EXT;
|
||||
layerSetting.valueCount = 1;
|
||||
|
||||
// Make this static so layer setting reference remains valid after leaving constructor scope
|
||||
static const char *layerEnables = "VK_VALIDATION_FEATURE_ENABLE_DEBUG_PRINTF_EXT";
|
||||
layerSetting.pValues = &layerEnables;
|
||||
|
||||
add_layer_setting(layerSetting);
|
||||
}
|
||||
----
|
||||
|
||||
== Displaying debug printf output in your application
|
||||
|
||||
If all you want to do is read the shader printf messages in a debugger like RenderDoc there's no additional work involved. Simply run your application with above instance setup and a printf in your shader and you can read those messages. Or use VkConfig from the LunarG SDK to redirect the messages to a specific output.
|
||||
|
||||
But if you want to display or process those messages in your own application you need to do that via a debug messenger callback. This is pretty much the same as setting up such a callback for validation:
|
||||
|
||||
[,cpp]
|
||||
----
|
||||
VkDebugUtilsMessengerCreateInfoEXT debug_utils_messenger_create_info{VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT};
|
||||
debug_utils_messenger_create_info.messageSeverity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT;
|
||||
debug_utils_messenger_create_info.messageType = VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT;
|
||||
debug_utils_messenger_create_info.pfnUserCallback = debug_utils_message_callback;
|
||||
|
||||
VK_CHECK(vkCreateDebugUtilsMessengerEXT(vulkan_instance, &debug_utils_messenger_create_info, nullptr, &debug_utils_messenger));
|
||||
----
|
||||
|
||||
[NOTE]
|
||||
====
|
||||
Shader printf messages are of `info` type, so in order for them to be passed to the callback you need to enable the `VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT` `messageSeverity` flag.
|
||||
====
|
||||
|
||||
With the validation layers and feature enabled and a debug callback wired up, we can process or output the shader printf messages. The most basic option could look like this:
|
||||
|
||||
[,cpp]
|
||||
----
|
||||
VKAPI_ATTR VkBool32 VKAPI_CALL ShaderDebugPrintf::debug_utils_message_callback(
|
||||
VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity,
|
||||
VkDebugUtilsMessageTypeFlagsEXT messageType,
|
||||
const VkDebugUtilsMessengerCallbackDataEXT *pCallbackData,
|
||||
void *pUserData)
|
||||
{
|
||||
std::cout << pCallbackData->pMessage << "\n";
|
||||
return VK_FALSE;
|
||||
}
|
||||
----
|
||||
|
||||
How you process those messages is up to you, in our sample we limit the output to the first invocation of a given draw call:
|
||||
|
||||
[,glsl]
|
||||
----
|
||||
// Output the vertex position using debug printf
|
||||
if (gl_VertexIndex == 0) {
|
||||
debugPrintfEXT("Position = %v4f", outPos);
|
||||
}
|
||||
----
|
||||
|
||||
And then gather those so they can be displayed in the graphical user interface. The sample also checks if it's actually a printf message (`pMessageIdName == WARNING-DEBUG-PRINTF`), which is something one could do to distinguish between actual validation messages and printf outputs:
|
||||
|
||||
[,cpp]
|
||||
----
|
||||
VKAPI_ATTR VkBool32 VKAPI_CALL ShaderDebugPrintf::debug_utils_message_callback(
|
||||
VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity,
|
||||
VkDebugUtilsMessageTypeFlagsEXT messageType,
|
||||
const VkDebugUtilsMessengerCallbackDataEXT *pCallbackData,
|
||||
void *pUserData)
|
||||
{
|
||||
if (strcmp(pCallbackData->pMessageIdName, "WARNING-DEBUG-PRINTF") == 0)
|
||||
{
|
||||
// Validation messages are a bit verbose, but we only want the text from the shader, so we cut off everything before the first word from the shader message
|
||||
// See scene.vert: debugPrintfEXT("Position = %v4f", outPos);
|
||||
std::string shader_message{pCallbackData->pMessage};
|
||||
shader_message = shader_message.substr(shader_message.find("Position"));
|
||||
debug_output.append(shader_message + "\n");
|
||||
}
|
||||
return VK_FALSE;
|
||||
}
|
||||
----
|
||||
|
||||
== Printf output buffer size
|
||||
|
||||
The default buffer size for debug outputs is limited. If you output a lot of messages, e.g. by doing a printf per fragment shader invocation, this may not suffice and you'll get a validation warning telling you about the buffer being to small. This buffer size can be increased via the VkConfig utility from the LunarG SDK. See https://www.lunarg.com/wp-content/uploads/2021/08/Using-Debug-Printf-02August2021.pdf[this whitepaper] for details on that.
|
||||
|
||||
[NOTE]
|
||||
====
|
||||
This **does not** apply to outputting messages in graphics debuggers.
|
||||
====
|
||||
|
||||
== Memory considerations
|
||||
|
||||
Using debug printf will consume a descriptor set, so if you use every last descriptor set it may not work and you may need to increase the set count at pool allocation. Thus in the sample we allocate an additional set from the pool. It also consumes device memory based on the number and/or size fo the debug messages.
|
||||
|
||||
== Printf in a graphics debugger
|
||||
|
||||
While displaying the printf messages directly in the application may be useful to a certain extent (low number of invocations or low frequency of information) the most interesting use case is to use it in combination with a graphics debugger like RenderDoc.
|
||||
|
||||
If we remove the sample's limitation to output only for the first invocation by changing `scene.vert` from:
|
||||
|
||||
[,glsl]
|
||||
----
|
||||
// Output the vertex position using debug printf
|
||||
if (gl_VertexIndex == 0) {
|
||||
debugPrintfEXT("Position = %v4f", outPos);
|
||||
}
|
||||
----
|
||||
|
||||
to this:
|
||||
|
||||
[,glsl]
|
||||
----
|
||||
debugPrintfEXT("Position = %v4f", outPos);
|
||||
----
|
||||
|
||||
And then run the sample in RenderDoc we now get a list of all shader debug printf messages for all invocations:
|
||||
|
||||
image::./images/renderdoc_shader_messages.png[RenderDoc]
|
||||
|
||||
Selecting a draw call with at least one debug message on the Event browser will show a label with the no. of messages for that draw call (e.g. 1250 msg(s)). Clicking on such a label will open the list of messages for that draw call. From here you can navigate to e.g. the mesh viewer for a given vertex (in the case of a vertex shader) or the texture view (in the case of a fragment shader).
|
||||
|
||||
== Conclusion
|
||||
|
||||
While not meant as a full debugging solution, using printf in a shader can help find problems that are otherwise hard to find or visualize. Used on conjunction with a proper graphics debugger like RenderDoc, shader printf is a great tool for debugging shaders.
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 146 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 340 KiB |
@@ -0,0 +1,595 @@
|
||||
/* Copyright (c) 2024-2025, Sascha Willems
|
||||
* Copyright (c) 2024-2025, Arm Limited and Contributors
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 the "License";
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#include "shader_debugprintf.h"
|
||||
|
||||
#include "scene_graph/components/sub_mesh.h"
|
||||
|
||||
#define validation_layer_name "VK_LAYER_KHRONOS_validation"
|
||||
|
||||
std::string ShaderDebugPrintf::debug_output{};
|
||||
|
||||
VKAPI_ATTR VkBool32 VKAPI_CALL ShaderDebugPrintf::debug_utils_message_callback(
|
||||
VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity,
|
||||
VkDebugUtilsMessageTypeFlagsEXT messageType,
|
||||
const VkDebugUtilsMessengerCallbackDataEXT *pCallbackData,
|
||||
void *pUserData)
|
||||
{
|
||||
// Look for Validation Layer message id names: VVL-DEBUG-PRINTF or WARNING-DEBUG-PRINTF or UNASSIGNED-DEBUG-PRINTF (have observed WARNING and UNASSIGNED with older Vulkan SDKs)
|
||||
if (strcmp(pCallbackData->pMessageIdName, "VVL-DEBUG-PRINTF") == 0 || strcmp(pCallbackData->pMessageIdName, "WARNING-DEBUG-PRINTF") == 0 || strcmp(pCallbackData->pMessageIdName, "UNASSIGNED-DEBUG-PRINTF") == 0)
|
||||
{
|
||||
// Validation messages are a bit verbose, but we only want the text from the shader, so we cut off everything before the first word from the shader message
|
||||
// See scene.vert: debugPrintfEXT("Position = %v3f", outPos);
|
||||
std::string shader_message{pCallbackData->pMessage};
|
||||
shader_message = shader_message.substr(shader_message.find("Position"));
|
||||
debug_output.append(shader_message + "\n");
|
||||
}
|
||||
return VK_FALSE;
|
||||
}
|
||||
|
||||
ShaderDebugPrintf::ShaderDebugPrintf()
|
||||
{
|
||||
title = "Shader debugprintf";
|
||||
|
||||
add_device_extension(VK_KHR_SHADER_NON_SEMANTIC_INFO_EXTENSION_NAME);
|
||||
|
||||
add_instance_layer(validation_layer_name);
|
||||
}
|
||||
|
||||
ShaderDebugPrintf::~ShaderDebugPrintf()
|
||||
{
|
||||
if (has_device())
|
||||
{
|
||||
vkDestroyPipeline(get_device().get_handle(), pipelines.skysphere, nullptr);
|
||||
vkDestroyPipeline(get_device().get_handle(), pipelines.sphere, nullptr);
|
||||
|
||||
vkDestroyPipelineLayout(get_device().get_handle(), pipeline_layout, nullptr);
|
||||
|
||||
vkDestroyDescriptorSetLayout(get_device().get_handle(), descriptor_set_layout, nullptr);
|
||||
|
||||
vkDestroySampler(get_device().get_handle(), textures.skysphere.sampler, nullptr);
|
||||
}
|
||||
|
||||
if (has_instance())
|
||||
{
|
||||
vkDestroyDebugUtilsMessengerEXT(get_instance().get_handle(), debug_utils_messenger, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
void ShaderDebugPrintf::request_gpu_features(vkb::PhysicalDevice &gpu)
|
||||
{
|
||||
auto const &supportedFeatures = gpu.get_features();
|
||||
auto &requestedFeatures = gpu.get_mutable_requested_features();
|
||||
|
||||
// debugPrintfEXT requires fragmentStoresAndAtomics and vertexPipelineStoresAndAtomics
|
||||
if (supportedFeatures.fragmentStoresAndAtomics && supportedFeatures.vertexPipelineStoresAndAtomics)
|
||||
{
|
||||
requestedFeatures.fragmentStoresAndAtomics = VK_TRUE;
|
||||
requestedFeatures.vertexPipelineStoresAndAtomics = VK_TRUE;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw vkb::VulkanException(VK_ERROR_FEATURE_NOT_PRESENT, "Selected GPU does not support features fragmentStoresAndAtomics and/or vertexPipelineStoresAndAtomics");
|
||||
}
|
||||
|
||||
// Enable anisotropic filtering if supported
|
||||
if (supportedFeatures.samplerAnisotropy)
|
||||
{
|
||||
requestedFeatures.samplerAnisotropy = VK_TRUE;
|
||||
}
|
||||
}
|
||||
|
||||
void ShaderDebugPrintf::build_command_buffers()
|
||||
{
|
||||
VkCommandBufferBeginInfo command_buffer_begin_info = vkb::initializers::command_buffer_begin_info();
|
||||
|
||||
VkClearValue clear_values[2];
|
||||
clear_values[0].color = {{0.0f, 0.0f, 0.0f, 0.0f}};
|
||||
clear_values[1].depthStencil = {0.0f, 0};
|
||||
|
||||
VkRenderPassBeginInfo render_pass_begin_info = vkb::initializers::render_pass_begin_info();
|
||||
render_pass_begin_info.renderPass = render_pass;
|
||||
render_pass_begin_info.renderArea.offset.x = 0;
|
||||
render_pass_begin_info.renderArea.offset.y = 0;
|
||||
render_pass_begin_info.clearValueCount = 2;
|
||||
render_pass_begin_info.pClearValues = clear_values;
|
||||
|
||||
for (int32_t i = 0; i < draw_cmd_buffers.size(); ++i)
|
||||
{
|
||||
VK_CHECK(vkBeginCommandBuffer(draw_cmd_buffers[i], &command_buffer_begin_info));
|
||||
|
||||
VkClearValue clear_values[2];
|
||||
clear_values[0].color = {{0.0f, 0.0f, 0.0f, 0.0f}};
|
||||
clear_values[1].depthStencil = {0.0f, 0};
|
||||
|
||||
// Final composition
|
||||
VkRenderPassBeginInfo render_pass_begin_info = vkb::initializers::render_pass_begin_info();
|
||||
render_pass_begin_info.framebuffer = framebuffers[i];
|
||||
render_pass_begin_info.renderPass = render_pass;
|
||||
render_pass_begin_info.clearValueCount = 2;
|
||||
render_pass_begin_info.renderArea.extent.width = width;
|
||||
render_pass_begin_info.renderArea.extent.height = height;
|
||||
render_pass_begin_info.pClearValues = clear_values;
|
||||
|
||||
vkCmdBeginRenderPass(draw_cmd_buffers[i], &render_pass_begin_info, VK_SUBPASS_CONTENTS_INLINE);
|
||||
|
||||
VkViewport viewport = vkb::initializers::viewport(static_cast<float>(width), static_cast<float>(height), 0.0f, 1.0f);
|
||||
vkCmdSetViewport(draw_cmd_buffers[i], 0, 1, &viewport);
|
||||
|
||||
VkRect2D scissor = vkb::initializers::rect2D(width, height, 0, 0);
|
||||
vkCmdSetScissor(draw_cmd_buffers[i], 0, 1, &scissor);
|
||||
|
||||
if (display_skysphere)
|
||||
{
|
||||
vkCmdBindPipeline(draw_cmd_buffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelines.skysphere);
|
||||
push_const_block.object_type = 0;
|
||||
vkCmdPushConstants(draw_cmd_buffers[i], pipeline_layout, VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, 0, sizeof(push_const_block), &push_const_block);
|
||||
vkCmdBindDescriptorSets(draw_cmd_buffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline_layout, 0, 1, &descriptor_sets.skysphere, 0, nullptr);
|
||||
|
||||
draw_model(models.skysphere, draw_cmd_buffers[i]);
|
||||
}
|
||||
|
||||
// Spheres
|
||||
vkCmdBindPipeline(draw_cmd_buffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipelines.sphere);
|
||||
vkCmdBindDescriptorSets(draw_cmd_buffers[i], VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline_layout, 0, 1, &descriptor_sets.sphere, 0, nullptr);
|
||||
std::vector<glm::vec3> mesh_colors = {
|
||||
glm::vec3(1.0f, 0.0f, 0.0f),
|
||||
glm::vec3(0.0f, 1.0f, 0.0f),
|
||||
glm::vec3(0.0f, 0.0f, 1.0f),
|
||||
};
|
||||
std::vector<glm::vec3> mesh_offsets = {
|
||||
glm::vec3(-2.5f, 0.0f, 0.0f),
|
||||
glm::vec3(0.0f, 0.0f, 0.0f),
|
||||
glm::vec3(2.5f, 0.0f, 0.0f),
|
||||
};
|
||||
for (uint32_t j = 0; j < 3; j++)
|
||||
{
|
||||
push_const_block.object_type = 1;
|
||||
push_const_block.offset = glm::vec4(mesh_offsets[j], 0.0f);
|
||||
push_const_block.color = glm::vec4(mesh_colors[j], 0.0f);
|
||||
vkCmdPushConstants(draw_cmd_buffers[i], pipeline_layout, VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, 0, sizeof(push_const_block), &push_const_block);
|
||||
draw_model(models.scene, draw_cmd_buffers[i]);
|
||||
}
|
||||
|
||||
draw_ui(draw_cmd_buffers[i]);
|
||||
|
||||
vkCmdEndRenderPass(draw_cmd_buffers[i]);
|
||||
|
||||
VK_CHECK(vkEndCommandBuffer(draw_cmd_buffers[i]));
|
||||
}
|
||||
}
|
||||
|
||||
void ShaderDebugPrintf::load_assets()
|
||||
{
|
||||
models.skysphere = load_model("scenes/geosphere.gltf");
|
||||
textures.skysphere = load_texture("textures/skysphere_rgba.ktx", vkb::sg::Image::Color);
|
||||
models.scene = load_model("scenes/geosphere.gltf");
|
||||
}
|
||||
|
||||
void ShaderDebugPrintf::setup_descriptor_pool()
|
||||
{
|
||||
// Note: Using debugprintf in a shader consumes a descriptor set, so we need to allocate one additional descriptor set
|
||||
std::vector<VkDescriptorPoolSize> pool_sizes = {
|
||||
vkb::initializers::descriptor_pool_size(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 2),
|
||||
vkb::initializers::descriptor_pool_size(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 2)};
|
||||
uint32_t num_descriptor_sets = 2;
|
||||
VkDescriptorPoolCreateInfo descriptor_pool_create_info =
|
||||
vkb::initializers::descriptor_pool_create_info(static_cast<uint32_t>(pool_sizes.size()), pool_sizes.data(), num_descriptor_sets);
|
||||
VK_CHECK(vkCreateDescriptorPool(get_device().get_handle(), &descriptor_pool_create_info, nullptr, &descriptor_pool));
|
||||
}
|
||||
|
||||
void ShaderDebugPrintf::setup_descriptor_set_layout()
|
||||
{
|
||||
// Object rendering (into offscreen buffer)
|
||||
std::vector<VkDescriptorSetLayoutBinding> set_layout_bindings = {
|
||||
vkb::initializers::descriptor_set_layout_binding(VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, VK_SHADER_STAGE_VERTEX_BIT, 0),
|
||||
vkb::initializers::descriptor_set_layout_binding(VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, VK_SHADER_STAGE_FRAGMENT_BIT, 1),
|
||||
};
|
||||
|
||||
VkDescriptorSetLayoutCreateInfo descriptor_layout_create_info =
|
||||
vkb::initializers::descriptor_set_layout_create_info(set_layout_bindings.data(), static_cast<uint32_t>(set_layout_bindings.size()));
|
||||
|
||||
VK_CHECK(vkCreateDescriptorSetLayout(get_device().get_handle(), &descriptor_layout_create_info, nullptr, &descriptor_set_layout));
|
||||
VkPipelineLayoutCreateInfo pipeline_layout_create_info =
|
||||
vkb::initializers::pipeline_layout_create_info(
|
||||
&descriptor_set_layout,
|
||||
1);
|
||||
|
||||
// Pass object offset and color via push constant
|
||||
VkPushConstantRange push_constant_range = vkb::initializers::push_constant_range(VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, sizeof(push_const_block), 0);
|
||||
pipeline_layout_create_info.pushConstantRangeCount = 1;
|
||||
pipeline_layout_create_info.pPushConstantRanges = &push_constant_range;
|
||||
|
||||
VK_CHECK(vkCreatePipelineLayout(get_device().get_handle(), &pipeline_layout_create_info, nullptr, &pipeline_layout));
|
||||
}
|
||||
|
||||
void ShaderDebugPrintf::setup_descriptor_sets()
|
||||
{
|
||||
VkDescriptorSetAllocateInfo alloc_info =
|
||||
vkb::initializers::descriptor_set_allocate_info(
|
||||
descriptor_pool,
|
||||
&descriptor_set_layout,
|
||||
1);
|
||||
|
||||
// Sphere model object descriptor set
|
||||
VK_CHECK(vkAllocateDescriptorSets(get_device().get_handle(), &alloc_info, &descriptor_sets.sphere));
|
||||
|
||||
VkDescriptorBufferInfo matrix_buffer_descriptor = create_descriptor(*uniform_buffers.matrices);
|
||||
VkDescriptorImageInfo environment_image_descriptor = create_descriptor(textures.skysphere);
|
||||
std::vector<VkWriteDescriptorSet> write_descriptor_sets = {
|
||||
vkb::initializers::write_descriptor_set(descriptor_sets.sphere, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 0, &matrix_buffer_descriptor),
|
||||
vkb::initializers::write_descriptor_set(descriptor_sets.sphere, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, &environment_image_descriptor),
|
||||
};
|
||||
vkUpdateDescriptorSets(get_device().get_handle(), static_cast<uint32_t>(write_descriptor_sets.size()), write_descriptor_sets.data(), 0, nullptr);
|
||||
|
||||
// Sky sphere descriptor set
|
||||
VK_CHECK(vkAllocateDescriptorSets(get_device().get_handle(), &alloc_info, &descriptor_sets.skysphere));
|
||||
|
||||
matrix_buffer_descriptor = create_descriptor(*uniform_buffers.matrices);
|
||||
environment_image_descriptor = create_descriptor(textures.skysphere);
|
||||
write_descriptor_sets = {
|
||||
vkb::initializers::write_descriptor_set(descriptor_sets.skysphere, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 0, &matrix_buffer_descriptor),
|
||||
vkb::initializers::write_descriptor_set(descriptor_sets.skysphere, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, &environment_image_descriptor),
|
||||
};
|
||||
vkUpdateDescriptorSets(get_device().get_handle(), static_cast<uint32_t>(write_descriptor_sets.size()), write_descriptor_sets.data(), 0, nullptr);
|
||||
}
|
||||
|
||||
void ShaderDebugPrintf::prepare_pipelines()
|
||||
{
|
||||
VkPipelineInputAssemblyStateCreateInfo input_assembly_state =
|
||||
vkb::initializers::pipeline_input_assembly_state_create_info(
|
||||
VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST,
|
||||
0,
|
||||
VK_FALSE);
|
||||
|
||||
VkPipelineRasterizationStateCreateInfo rasterization_state =
|
||||
vkb::initializers::pipeline_rasterization_state_create_info(
|
||||
VK_POLYGON_MODE_FILL,
|
||||
VK_CULL_MODE_BACK_BIT,
|
||||
VK_FRONT_FACE_COUNTER_CLOCKWISE,
|
||||
0);
|
||||
|
||||
VkPipelineColorBlendAttachmentState blend_attachment_state =
|
||||
vkb::initializers::pipeline_color_blend_attachment_state(
|
||||
0xf,
|
||||
VK_FALSE);
|
||||
|
||||
VkPipelineColorBlendStateCreateInfo color_blend_state =
|
||||
vkb::initializers::pipeline_color_blend_state_create_info(
|
||||
1,
|
||||
&blend_attachment_state);
|
||||
|
||||
// Note: Using reversed depth-buffer for increased precision, so Greater depth values are kept
|
||||
VkPipelineDepthStencilStateCreateInfo depth_stencil_state =
|
||||
vkb::initializers::pipeline_depth_stencil_state_create_info(
|
||||
VK_FALSE,
|
||||
VK_FALSE,
|
||||
VK_COMPARE_OP_GREATER);
|
||||
|
||||
VkPipelineViewportStateCreateInfo viewport_state =
|
||||
vkb::initializers::pipeline_viewport_state_create_info(1, 1, 0);
|
||||
|
||||
VkPipelineMultisampleStateCreateInfo multisample_state =
|
||||
vkb::initializers::pipeline_multisample_state_create_info(
|
||||
VK_SAMPLE_COUNT_1_BIT,
|
||||
0);
|
||||
|
||||
std::vector<VkDynamicState> dynamic_state_enables = {
|
||||
VK_DYNAMIC_STATE_VIEWPORT,
|
||||
VK_DYNAMIC_STATE_SCISSOR};
|
||||
VkPipelineDynamicStateCreateInfo dynamic_state =
|
||||
vkb::initializers::pipeline_dynamic_state_create_info(
|
||||
dynamic_state_enables.data(),
|
||||
static_cast<uint32_t>(dynamic_state_enables.size()),
|
||||
0);
|
||||
|
||||
VkGraphicsPipelineCreateInfo pipeline_create_info =
|
||||
vkb::initializers::pipeline_create_info(
|
||||
pipeline_layout,
|
||||
render_pass,
|
||||
0);
|
||||
|
||||
std::vector<VkPipelineColorBlendAttachmentState> blend_attachment_states = {
|
||||
vkb::initializers::pipeline_color_blend_attachment_state(0xf, VK_FALSE),
|
||||
vkb::initializers::pipeline_color_blend_attachment_state(0xf, VK_FALSE),
|
||||
};
|
||||
|
||||
// Vertex bindings an attributes for model rendering
|
||||
// Binding description
|
||||
std::vector<VkVertexInputBindingDescription> vertex_input_bindings = {
|
||||
vkb::initializers::vertex_input_binding_description(0, sizeof(Vertex), VK_VERTEX_INPUT_RATE_VERTEX),
|
||||
};
|
||||
|
||||
std::array<VkPipelineShaderStageCreateInfo, 2> shader_stages;
|
||||
|
||||
// Attribute descriptions
|
||||
std::vector<VkVertexInputAttributeDescription> vertex_input_attributes = {
|
||||
vkb::initializers::vertex_input_attribute_description(0, 0, VK_FORMAT_R32G32B32_SFLOAT, 0), // Position
|
||||
vkb::initializers::vertex_input_attribute_description(0, 1, VK_FORMAT_R32G32B32_SFLOAT, sizeof(float) * 3), // Normal
|
||||
vkb::initializers::vertex_input_attribute_description(0, 2, VK_FORMAT_R32G32_SFLOAT, sizeof(float) * 6), // UV
|
||||
};
|
||||
|
||||
VkPipelineVertexInputStateCreateInfo vertex_input_state = vkb::initializers::pipeline_vertex_input_state_create_info();
|
||||
vertex_input_state.vertexBindingDescriptionCount = static_cast<uint32_t>(vertex_input_bindings.size());
|
||||
vertex_input_state.pVertexBindingDescriptions = vertex_input_bindings.data();
|
||||
vertex_input_state.vertexAttributeDescriptionCount = static_cast<uint32_t>(vertex_input_attributes.size());
|
||||
vertex_input_state.pVertexAttributeDescriptions = vertex_input_attributes.data();
|
||||
|
||||
pipeline_create_info.layout = pipeline_layout;
|
||||
pipeline_create_info.renderPass = render_pass;
|
||||
pipeline_create_info.pInputAssemblyState = &input_assembly_state;
|
||||
pipeline_create_info.pRasterizationState = &rasterization_state;
|
||||
pipeline_create_info.pColorBlendState = &color_blend_state;
|
||||
pipeline_create_info.pMultisampleState = &multisample_state;
|
||||
pipeline_create_info.pViewportState = &viewport_state;
|
||||
pipeline_create_info.pDepthStencilState = &depth_stencil_state;
|
||||
pipeline_create_info.pDynamicState = &dynamic_state;
|
||||
pipeline_create_info.pVertexInputState = &vertex_input_state;
|
||||
pipeline_create_info.stageCount = static_cast<uint32_t>(shader_stages.size());
|
||||
pipeline_create_info.pStages = shader_stages.data();
|
||||
|
||||
shader_stages[0] = load_shader("shader_debugprintf", "scene.vert.spv", VK_SHADER_STAGE_VERTEX_BIT);
|
||||
shader_stages[1] = load_shader("shader_debugprintf", "scene.frag.spv", VK_SHADER_STAGE_FRAGMENT_BIT);
|
||||
|
||||
// skysphere pipeline (background cube)
|
||||
rasterization_state.cullMode = VK_CULL_MODE_BACK_BIT;
|
||||
|
||||
VK_CHECK(vkCreateGraphicsPipelines(get_device().get_handle(), pipeline_cache, 1, &pipeline_create_info, nullptr, &pipelines.skysphere));
|
||||
|
||||
// sphere model pipeline
|
||||
depth_stencil_state.depthWriteEnable = VK_TRUE;
|
||||
depth_stencil_state.depthTestEnable = VK_TRUE;
|
||||
// Flip cull mode
|
||||
rasterization_state.cullMode = VK_CULL_MODE_FRONT_BIT;
|
||||
VK_CHECK(vkCreateGraphicsPipelines(get_device().get_handle(), pipeline_cache, 1, &pipeline_create_info, nullptr, &pipelines.sphere));
|
||||
}
|
||||
|
||||
// Prepare and initialize uniform buffer containing shader uniforms
|
||||
void ShaderDebugPrintf::prepare_uniform_buffers()
|
||||
{
|
||||
// Matrices vertex shader uniform buffer
|
||||
uniform_buffers.matrices = std::make_unique<vkb::core::BufferC>(get_device(),
|
||||
sizeof(ubo_vs),
|
||||
VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT,
|
||||
VMA_MEMORY_USAGE_CPU_TO_GPU);
|
||||
|
||||
update_uniform_buffers();
|
||||
}
|
||||
|
||||
void ShaderDebugPrintf::update_uniform_buffers()
|
||||
{
|
||||
ubo_vs.projection = camera.matrices.perspective;
|
||||
ubo_vs.modelview = camera.matrices.view * glm::mat4(1.0f);
|
||||
ubo_vs.skysphere_modelview = camera.matrices.view;
|
||||
uniform_buffers.matrices->convert_and_update(ubo_vs);
|
||||
}
|
||||
|
||||
void ShaderDebugPrintf::draw()
|
||||
{
|
||||
ApiVulkanSample::prepare_frame();
|
||||
submit_info.commandBufferCount = 1;
|
||||
submit_info.pCommandBuffers = &draw_cmd_buffers[current_buffer];
|
||||
VK_CHECK(vkQueueSubmit(queue, 1, &submit_info, VK_NULL_HANDLE));
|
||||
ApiVulkanSample::submit_frame();
|
||||
}
|
||||
|
||||
bool ShaderDebugPrintf::prepare(const vkb::ApplicationOptions &options)
|
||||
{
|
||||
if (!ApiVulkanSample::prepare(options))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Register debug utils callback here vs in ShaderDebugPrintf::create_instance() so it works with both override and layer settings
|
||||
VkDebugUtilsMessengerCreateInfoEXT debug_utils_messenger_create_info{VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT};
|
||||
debug_utils_messenger_create_info.messageSeverity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT;
|
||||
debug_utils_messenger_create_info.messageType = VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT;
|
||||
debug_utils_messenger_create_info.pfnUserCallback = debug_utils_message_callback;
|
||||
VK_CHECK(vkCreateDebugUtilsMessengerEXT(get_instance().get_handle(), &debug_utils_messenger_create_info, nullptr, &debug_utils_messenger));
|
||||
|
||||
camera.type = vkb::CameraType::LookAt;
|
||||
camera.set_position(glm::vec3(0.0f, 0.0f, -6.0f));
|
||||
camera.set_rotation(glm::vec3(0.0f, 180.0f, 0.0f));
|
||||
|
||||
// Note: Using reversed depth-buffer for increased precision, so Znear and Zfar are flipped
|
||||
camera.set_perspective(60.0f, static_cast<float>(width) / static_cast<float>(height), 256.0f, 0.1f);
|
||||
|
||||
load_assets();
|
||||
prepare_uniform_buffers();
|
||||
setup_descriptor_set_layout();
|
||||
prepare_pipelines();
|
||||
setup_descriptor_pool();
|
||||
setup_descriptor_sets();
|
||||
build_command_buffers();
|
||||
prepared = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
// This sample overrides the instance creation part of the framework to chain in additional structures
|
||||
std::unique_ptr<vkb::core::InstanceC> ShaderDebugPrintf::create_instance()
|
||||
{
|
||||
auto debugprintf_api_version = VK_API_VERSION_1_1;
|
||||
|
||||
// Enumerate all instance layer properties so we can find and use the validation layer (VVL) version in subsequent steps
|
||||
// The VVL version is needed to work around validation layer performance issues when running with Vulkan SDKs <= 1.3.290
|
||||
uint32_t layer_property_count;
|
||||
VK_CHECK(vkEnumerateInstanceLayerProperties(&layer_property_count, nullptr));
|
||||
std::vector<VkLayerProperties> layer_properties(layer_property_count);
|
||||
VK_CHECK(vkEnumerateInstanceLayerProperties(&layer_property_count, layer_properties.data()));
|
||||
|
||||
const auto vvl_properties = std::ranges::find_if(layer_properties,
|
||||
[](VkLayerProperties const &properties) { return strcmp(properties.layerName, validation_layer_name) == 0; });
|
||||
|
||||
// Make sure we have found the validation layer before checking the VVL version and enumerating VVL instance extensions for VK_EXT_layer_settings
|
||||
if (vvl_properties != layer_properties.end())
|
||||
{
|
||||
// debugPrintfEXT layer feature requires Vulkan API 1.1, but override with API 1.2 for Vulkan SDKs <= 1.3.290 to work around VVL performance defect
|
||||
// See VVL issue https://github.com/KhronosGroup/Vulkan-ValidationLayers/issues/7562 for defect and fix information (fix available in SDK 1.3.296)
|
||||
// Note: An additional, unrelated VVL performance issue affecting nVidia GPUs was found in SDK 1.3.296 following release - for nVidia GPUs please
|
||||
// use SDK 1.3.290 until a fix is made available in a later SDK (see https://github.com/KhronosGroup/Vulkan-ValidationLayers/pull/8766).
|
||||
if (vvl_properties->specVersion <= VK_MAKE_API_VERSION(0, 1, 3, 290))
|
||||
{
|
||||
debugprintf_api_version = VK_API_VERSION_1_2;
|
||||
}
|
||||
|
||||
// Enumerate all instance extensions for the validation layer to determine if VK_EXT_layer_settings is supported by the layer
|
||||
uint32_t vvl_extension_count;
|
||||
VK_CHECK(vkEnumerateInstanceExtensionProperties(validation_layer_name, &vvl_extension_count, nullptr));
|
||||
std::vector<VkExtensionProperties> vvl_instance_extensions(vvl_extension_count);
|
||||
VK_CHECK(vkEnumerateInstanceExtensionProperties(validation_layer_name, &vvl_extension_count, vvl_instance_extensions.data()));
|
||||
|
||||
// When VK_EXT_layer_settings is available at runtime, the debugPrintfEXT layer feature is enabled using the standard framework
|
||||
// For this case set Vulkan API version and return via base class, otherwise the remainder of this custom override is required
|
||||
if (std::ranges::any_of(vvl_instance_extensions,
|
||||
[](VkExtensionProperties const &extension) { return strcmp(extension.extensionName, VK_EXT_LAYER_SETTINGS_EXTENSION_NAME) == 0; }))
|
||||
{
|
||||
set_api_version(debugprintf_api_version);
|
||||
|
||||
// Since layer settings extension is available, use it to configure validation layer for debugPrintfEXT
|
||||
VkLayerSettingEXT layerSetting;
|
||||
layerSetting.pLayerName = validation_layer_name;
|
||||
layerSetting.pSettingName = "enables";
|
||||
layerSetting.type = VK_LAYER_SETTING_TYPE_STRING_EXT;
|
||||
layerSetting.valueCount = 1;
|
||||
|
||||
// Make this static so layer setting reference remains valid after leaving the current scope
|
||||
static const char *layerEnables = "VK_VALIDATION_FEATURE_ENABLE_DEBUG_PRINTF_EXT";
|
||||
layerSetting.pValues = &layerEnables;
|
||||
|
||||
add_layer_setting(layerSetting);
|
||||
|
||||
// Run standard create_instance() from framework with set_api_version() and add_layer_setting() support
|
||||
return VulkanSample::create_instance();
|
||||
}
|
||||
}
|
||||
|
||||
// As a fallack, run remainder of this custom create_instance() override (without layer settings support) and return
|
||||
std::vector<const char *> enabled_extensions;
|
||||
enabled_extensions.push_back(VK_KHR_SURFACE_EXTENSION_NAME);
|
||||
|
||||
for (const char *extension_name : window->get_required_surface_extensions())
|
||||
{
|
||||
enabled_extensions.push_back(extension_name);
|
||||
}
|
||||
|
||||
enabled_extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
|
||||
enabled_extensions.push_back(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME);
|
||||
|
||||
#if (defined(VKB_ENABLE_PORTABILITY))
|
||||
// Enumerate all instance extensions for the loader + driver to determine if VK_KHR_portability_enumeration is available
|
||||
uint32_t available_extension_count;
|
||||
VK_CHECK(vkEnumerateInstanceExtensionProperties(nullptr, &available_extension_count, nullptr));
|
||||
std::vector<VkExtensionProperties> available_instance_extensions(available_extension_count);
|
||||
VK_CHECK(vkEnumerateInstanceExtensionProperties(nullptr, &available_extension_count, available_instance_extensions.data()));
|
||||
|
||||
// If VK_KHR_portability_enumeration is available in the portability implementation, then we must enable the extension
|
||||
bool portability_enumeration_available = false;
|
||||
if (std::ranges::any_of(available_instance_extensions,
|
||||
[](VkExtensionProperties const &extension) { return strcmp(extension.extensionName, VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME) == 0; }))
|
||||
{
|
||||
enabled_extensions.push_back(VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME);
|
||||
portability_enumeration_available = true;
|
||||
}
|
||||
#endif
|
||||
|
||||
VkApplicationInfo app_info{VK_STRUCTURE_TYPE_APPLICATION_INFO};
|
||||
app_info.pApplicationName = "Shader debugprintf";
|
||||
app_info.pEngineName = "Vulkan Samples";
|
||||
app_info.apiVersion = debugprintf_api_version;
|
||||
|
||||
// Enable VK_EXT_validation_features extension for configuring validation layer features using VkValidationFeaturesEXT
|
||||
enabled_extensions.push_back(VK_EXT_VALIDATION_FEATURES_EXTENSION_NAME);
|
||||
|
||||
// Shader printf is a feature of the validation layers that needs to be enabled
|
||||
std::vector<VkValidationFeatureEnableEXT> validation_feature_enables = {VK_VALIDATION_FEATURE_ENABLE_DEBUG_PRINTF_EXT};
|
||||
|
||||
VkValidationFeaturesEXT validation_features{VK_STRUCTURE_TYPE_VALIDATION_FEATURES_EXT};
|
||||
validation_features.enabledValidationFeatureCount = static_cast<uint32_t>(validation_feature_enables.size());
|
||||
validation_features.pEnabledValidationFeatures = validation_feature_enables.data();
|
||||
|
||||
std::vector<const char *> validation_layers = {validation_layer_name};
|
||||
|
||||
VkInstanceCreateInfo instance_create_info{VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO};
|
||||
instance_create_info.ppEnabledExtensionNames = enabled_extensions.data();
|
||||
instance_create_info.enabledExtensionCount = static_cast<uint32_t>(enabled_extensions.size());
|
||||
instance_create_info.pApplicationInfo = &app_info;
|
||||
instance_create_info.ppEnabledLayerNames = validation_layers.data();
|
||||
instance_create_info.enabledLayerCount = static_cast<uint32_t>(validation_layers.size());
|
||||
#if (defined(VKB_ENABLE_PORTABILITY))
|
||||
if (portability_enumeration_available)
|
||||
{
|
||||
instance_create_info.flags |= VK_INSTANCE_CREATE_ENUMERATE_PORTABILITY_BIT_KHR;
|
||||
}
|
||||
#endif
|
||||
instance_create_info.pNext = &validation_features;
|
||||
|
||||
VkInstance vulkan_instance;
|
||||
VkResult result = vkCreateInstance(&instance_create_info, nullptr, &vulkan_instance);
|
||||
|
||||
if (result != VK_SUCCESS)
|
||||
{
|
||||
throw vkb::VulkanException{result, "Could not create instance"};
|
||||
}
|
||||
|
||||
volkLoadInstance(vulkan_instance);
|
||||
|
||||
return std::make_unique<vkb::core::InstanceC>(vulkan_instance, enabled_extensions);
|
||||
}
|
||||
|
||||
void ShaderDebugPrintf::render(float delta_time)
|
||||
{
|
||||
if (!prepared)
|
||||
{
|
||||
return;
|
||||
}
|
||||
draw();
|
||||
if (camera.updated)
|
||||
{
|
||||
update_uniform_buffers();
|
||||
}
|
||||
}
|
||||
|
||||
void ShaderDebugPrintf::on_update_ui_overlay(vkb::Drawer &drawer)
|
||||
{
|
||||
if (drawer.header("Settings"))
|
||||
{
|
||||
if (drawer.checkbox("skysphere", &display_skysphere))
|
||||
{
|
||||
rebuild_command_buffers();
|
||||
}
|
||||
}
|
||||
if (drawer.header("Debug output"))
|
||||
{
|
||||
drawer.text(debug_output.c_str());
|
||||
}
|
||||
|
||||
// Clear saved debug output, so we only get output for the last frame
|
||||
debug_output.clear();
|
||||
}
|
||||
|
||||
bool ShaderDebugPrintf::resize(const uint32_t width, const uint32_t height)
|
||||
{
|
||||
ApiVulkanSample::resize(width, height);
|
||||
update_uniform_buffers();
|
||||
return true;
|
||||
}
|
||||
|
||||
std::unique_ptr<vkb::Application> create_shader_debugprintf()
|
||||
{
|
||||
return std::make_unique<ShaderDebugPrintf>();
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
/* Copyright (c) 2024-2025, Sascha Willems
|
||||
* Copyright (c) 2024-2025, Arm Limited and Contributors
|
||||
*
|
||||
* SPDX-License-Identifier: Apache-2.0
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 the "License";
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "api_vulkan_sample.h"
|
||||
|
||||
class ShaderDebugPrintf : public ApiVulkanSample
|
||||
{
|
||||
public:
|
||||
bool display_skysphere = true;
|
||||
|
||||
struct
|
||||
{
|
||||
Texture skysphere;
|
||||
} textures;
|
||||
|
||||
struct
|
||||
{
|
||||
std::unique_ptr<vkb::sg::SubMesh> skysphere;
|
||||
std::unique_ptr<vkb::sg::SubMesh> scene;
|
||||
} models;
|
||||
|
||||
struct
|
||||
{
|
||||
std::unique_ptr<vkb::core::BufferC> matrices;
|
||||
} uniform_buffers;
|
||||
|
||||
struct UBOVS
|
||||
{
|
||||
glm::mat4 projection;
|
||||
glm::mat4 modelview;
|
||||
glm::mat4 skysphere_modelview;
|
||||
float modelscale = 0.05f;
|
||||
} ubo_vs;
|
||||
|
||||
struct
|
||||
{
|
||||
VkPipeline skysphere{VK_NULL_HANDLE};
|
||||
VkPipeline sphere{VK_NULL_HANDLE};
|
||||
} pipelines;
|
||||
VkPipelineLayout pipeline_layout{VK_NULL_HANDLE};
|
||||
|
||||
struct
|
||||
{
|
||||
VkDescriptorSet skysphere{VK_NULL_HANDLE};
|
||||
VkDescriptorSet sphere{VK_NULL_HANDLE};
|
||||
} descriptor_sets;
|
||||
VkDescriptorSetLayout descriptor_set_layout{VK_NULL_HANDLE};
|
||||
|
||||
struct
|
||||
{
|
||||
glm::vec4 offset;
|
||||
glm::vec4 color;
|
||||
uint32_t object_type{0};
|
||||
} push_const_block;
|
||||
|
||||
VkDebugUtilsMessengerEXT debug_utils_messenger{VK_NULL_HANDLE};
|
||||
|
||||
static std::string debug_output;
|
||||
|
||||
VKAPI_ATTR static VkBool32 VKAPI_CALL debug_utils_message_callback(
|
||||
VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity,
|
||||
VkDebugUtilsMessageTypeFlagsEXT messageType,
|
||||
const VkDebugUtilsMessengerCallbackDataEXT *pCallbackData,
|
||||
void *pUserData);
|
||||
|
||||
ShaderDebugPrintf();
|
||||
~ShaderDebugPrintf();
|
||||
virtual void request_gpu_features(vkb::PhysicalDevice &gpu) override;
|
||||
void build_command_buffers() override;
|
||||
void load_assets();
|
||||
void setup_descriptor_pool();
|
||||
void setup_descriptor_set_layout();
|
||||
void setup_descriptor_sets();
|
||||
void prepare_pipelines();
|
||||
void prepare_uniform_buffers();
|
||||
void update_uniform_buffers();
|
||||
void draw();
|
||||
bool prepare(const vkb::ApplicationOptions &options) override;
|
||||
std::unique_ptr<vkb::core::InstanceC> create_instance() override;
|
||||
virtual void render(float delta_time) override;
|
||||
virtual void on_update_ui_overlay(vkb::Drawer &drawer) override;
|
||||
virtual bool resize(const uint32_t width, const uint32_t height) override;
|
||||
};
|
||||
|
||||
std::unique_ptr<vkb::Application> create_shader_debugprintf();
|
||||
Reference in New Issue
Block a user