init
This commit is contained in:
@@ -0,0 +1,529 @@
|
||||
# Copyright (c) 2019-2025, Arm Limited and Contributors
|
||||
# Copyright (c) 2025, NVIDIA CORPORATION. All rights reserved.
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
|
||||
cmake_minimum_required(VERSION 3.16)
|
||||
|
||||
project(framework LANGUAGES C CXX)
|
||||
|
||||
set(FRAMEWORK_FILES
|
||||
# Header Files
|
||||
gui.h
|
||||
drawer.h
|
||||
spirv_reflection.h
|
||||
gltf_loader.h
|
||||
buffer_pool.h
|
||||
debug_info.h
|
||||
fence_pool.h
|
||||
heightmap.h
|
||||
semaphore_pool.h
|
||||
resource_binding_state.h
|
||||
resource_cache.h
|
||||
resource_record.h
|
||||
resource_replay.h
|
||||
vulkan_sample.h
|
||||
api_vulkan_sample.h
|
||||
timer.h
|
||||
camera.h
|
||||
builder_base.h
|
||||
vulkan_type_mapping.h
|
||||
hpp_api_vulkan_sample.h
|
||||
hpp_fence_pool.h
|
||||
hpp_gltf_loader.h
|
||||
hpp_resource_binding_state.h
|
||||
hpp_resource_cache.h
|
||||
hpp_resource_record.h
|
||||
hpp_resource_replay.h
|
||||
hpp_semaphore_pool.h
|
||||
# Source Files
|
||||
drawer.cpp
|
||||
spirv_reflection.cpp
|
||||
gltf_loader.cpp
|
||||
debug_info.cpp
|
||||
fence_pool.cpp
|
||||
heightmap.cpp
|
||||
semaphore_pool.cpp
|
||||
resource_binding_state.cpp
|
||||
resource_cache.cpp
|
||||
resource_record.cpp
|
||||
resource_replay.cpp
|
||||
api_vulkan_sample.cpp
|
||||
timer.cpp
|
||||
camera_core.cpp
|
||||
hpp_api_vulkan_sample.cpp
|
||||
hpp_resource_cache.cpp)
|
||||
|
||||
set(COMMON_FILES
|
||||
# Header Files
|
||||
common/ktx_common.h
|
||||
common/vk_common.h
|
||||
common/vk_initializers.h
|
||||
common/glm_common.h
|
||||
common/resource_caching.h
|
||||
common/helpers.h
|
||||
common/error.h
|
||||
common/utils.h
|
||||
common/strings.h
|
||||
common/tags.h
|
||||
common/hpp_error.h
|
||||
common/hpp_resource_caching.h
|
||||
common/hpp_strings.h
|
||||
common/hpp_utils.h
|
||||
common/hpp_vk_common.h
|
||||
# Source Files
|
||||
common/error.cpp
|
||||
common/ktx_common.cpp
|
||||
common/vk_common.cpp
|
||||
common/utils.cpp
|
||||
common/strings.cpp)
|
||||
|
||||
set(GEOMETRY_FILES
|
||||
# Header Files
|
||||
geometry/frustum.h
|
||||
# Source Files
|
||||
geometry/frustum.cpp)
|
||||
|
||||
set(RENDERING_FILES
|
||||
# Header files
|
||||
rendering/pipeline_state.h
|
||||
rendering/postprocessing_pipeline.h
|
||||
rendering/postprocessing_pass.h
|
||||
rendering/postprocessing_renderpass.h
|
||||
rendering/postprocessing_computepass.h
|
||||
rendering/render_context.h
|
||||
rendering/render_frame.h
|
||||
rendering/render_pipeline.h
|
||||
rendering/render_target.h
|
||||
rendering/subpass.h
|
||||
rendering/hpp_pipeline_state.h
|
||||
rendering/hpp_render_context.h
|
||||
rendering/hpp_render_pipeline.h
|
||||
rendering/hpp_render_target.h
|
||||
# Source files
|
||||
rendering/pipeline_state.cpp
|
||||
rendering/postprocessing_pipeline.cpp
|
||||
rendering/postprocessing_pass.cpp
|
||||
rendering/postprocessing_renderpass.cpp
|
||||
rendering/postprocessing_computepass.cpp
|
||||
rendering/render_context.cpp
|
||||
rendering/render_pipeline.cpp
|
||||
rendering/render_target.cpp
|
||||
rendering/hpp_render_context.cpp
|
||||
rendering/hpp_render_target.cpp)
|
||||
|
||||
set(RENDERING_SUBPASSES_FILES
|
||||
# Header files
|
||||
rendering/subpasses/forward_subpass.h
|
||||
rendering/subpasses/lighting_subpass.h
|
||||
rendering/subpasses/geometry_subpass.h
|
||||
rendering/subpasses/hpp_forward_subpass.h
|
||||
# Source files
|
||||
rendering/subpasses/forward_subpass.cpp
|
||||
rendering/subpasses/lighting_subpass.cpp
|
||||
rendering/subpasses/geometry_subpass.cpp)
|
||||
|
||||
set(SCENE_GRAPH_FILES
|
||||
# Header Files
|
||||
scene_graph/component.h
|
||||
scene_graph/node.h
|
||||
scene_graph/scene.h
|
||||
scene_graph/script.h
|
||||
scene_graph/hpp_scene.h
|
||||
# Source Files
|
||||
scene_graph/component.cpp
|
||||
scene_graph/node.cpp
|
||||
scene_graph/scene.cpp
|
||||
scene_graph/script.cpp)
|
||||
|
||||
set(SCENE_GRAPH_COMPONENT_FILES
|
||||
# Header Files
|
||||
scene_graph/components/aabb.h
|
||||
scene_graph/components/camera.h
|
||||
scene_graph/components/perspective_camera.h
|
||||
scene_graph/components/orthographic_camera.h
|
||||
scene_graph/components/image.h
|
||||
scene_graph/components/light.h
|
||||
scene_graph/components/material.h
|
||||
scene_graph/components/mesh.h
|
||||
scene_graph/components/pbr_material.h
|
||||
scene_graph/components/sampler.h
|
||||
scene_graph/components/sub_mesh.h
|
||||
scene_graph/components/texture.h
|
||||
scene_graph/components/transform.h
|
||||
scene_graph/components/image/astc.h
|
||||
scene_graph/components/image/ktx.h
|
||||
scene_graph/components/image/stb.h
|
||||
scene_graph/components/hpp_image.h
|
||||
scene_graph/components/hpp_material.h
|
||||
scene_graph/components/hpp_mesh.h
|
||||
scene_graph/components/hpp_sub_mesh.h
|
||||
scene_graph/components/hpp_texture.h
|
||||
# Source Files
|
||||
scene_graph/components/aabb.cpp
|
||||
scene_graph/components/camera.cpp
|
||||
scene_graph/components/perspective_camera.cpp
|
||||
scene_graph/components/orthographic_camera.cpp
|
||||
scene_graph/components/image.cpp
|
||||
scene_graph/components/light.cpp
|
||||
scene_graph/components/material.cpp
|
||||
scene_graph/components/mesh.cpp
|
||||
scene_graph/components/pbr_material.cpp
|
||||
scene_graph/components/sampler.cpp
|
||||
scene_graph/components/sub_mesh.cpp
|
||||
scene_graph/components/texture.cpp
|
||||
scene_graph/components/transform.cpp
|
||||
scene_graph/components/image/astc.cpp
|
||||
scene_graph/components/image/ktx.cpp
|
||||
scene_graph/components/image/stb.cpp
|
||||
scene_graph/components/hpp_image.cpp)
|
||||
|
||||
set(SCENE_GRAPH_SCRIPTS_FILES
|
||||
# Header Files
|
||||
scene_graph/scripts/free_camera.h
|
||||
scene_graph/scripts/node_animation.h
|
||||
scene_graph/scripts/animation.h
|
||||
# Source Files
|
||||
scene_graph/scripts/free_camera.cpp
|
||||
scene_graph/scripts/node_animation.cpp
|
||||
scene_graph/scripts/animation.cpp)
|
||||
|
||||
set(STATS_FILES
|
||||
# Header Files
|
||||
stats/stats.h
|
||||
stats/stats_common.h
|
||||
stats/stats_provider.h
|
||||
stats/frame_time_stats_provider.h
|
||||
stats/vulkan_stats_provider.h
|
||||
stats/hpp_stats.h
|
||||
|
||||
# Source Files
|
||||
stats/stats.cpp
|
||||
stats/stats_provider.cpp
|
||||
stats/frame_time_stats_provider.cpp
|
||||
stats/vulkan_stats_provider.cpp)
|
||||
|
||||
set(CORE_FILES
|
||||
# Header Files
|
||||
core/instance.h
|
||||
core/physical_device.h
|
||||
core/device.h
|
||||
core/debug.h
|
||||
core/shader_module.h
|
||||
core/pipeline_layout.h
|
||||
core/pipeline.h
|
||||
core/descriptor_set_layout.h
|
||||
core/descriptor_pool.h
|
||||
core/descriptor_set.h
|
||||
core/queue.h
|
||||
core/command_pool.h
|
||||
core/command_pool_base.h
|
||||
core/swapchain.h
|
||||
core/command_buffer.h
|
||||
core/allocated.h
|
||||
core/buffer.h
|
||||
core/image.h
|
||||
core/image_view.h
|
||||
core/sampled_image.h
|
||||
core/sampler.h
|
||||
core/framebuffer.h
|
||||
core/render_pass.h
|
||||
core/query_pool.h
|
||||
core/acceleration_structure.h
|
||||
core/hpp_debug.h
|
||||
core/hpp_descriptor_pool.h
|
||||
core/hpp_descriptor_set.h
|
||||
core/hpp_descriptor_set_layout.h
|
||||
core/hpp_framebuffer.h
|
||||
core/hpp_image.h
|
||||
core/hpp_image_view.h
|
||||
core/hpp_physical_device.h
|
||||
core/hpp_pipeline.h
|
||||
core/hpp_pipeline_layout.h
|
||||
core/hpp_query_pool.h
|
||||
core/hpp_queue.h
|
||||
core/hpp_render_pass.h
|
||||
core/hpp_sampler.h
|
||||
core/hpp_shader_module.h
|
||||
core/hpp_swapchain.h
|
||||
core/vulkan_resource.h
|
||||
# Source Files
|
||||
core/command_pool_base.cpp
|
||||
core/physical_device.cpp
|
||||
core/debug.cpp
|
||||
core/image_core.cpp
|
||||
core/shader_module.cpp
|
||||
core/pipeline_layout.cpp
|
||||
core/pipeline.cpp
|
||||
core/descriptor_set_layout.cpp
|
||||
core/descriptor_pool.cpp
|
||||
core/descriptor_set.cpp
|
||||
core/queue.cpp
|
||||
core/swapchain.cpp
|
||||
core/allocated.cpp
|
||||
core/image_core.cpp
|
||||
core/image_view.cpp
|
||||
core/sampled_image.cpp
|
||||
core/sampler_core.cpp
|
||||
core/framebuffer.cpp
|
||||
core/render_pass.cpp
|
||||
core/query_pool.cpp
|
||||
core/acceleration_structure.cpp
|
||||
core/hpp_debug.cpp
|
||||
core/hpp_image_core.cpp
|
||||
core/hpp_image_view.cpp
|
||||
core/hpp_physical_device.cpp
|
||||
core/hpp_pipeline_layout.cpp
|
||||
core/hpp_queue.cpp
|
||||
core/hpp_sampler.cpp
|
||||
core/hpp_swapchain.cpp
|
||||
)
|
||||
|
||||
set(PLATFORM_FILES
|
||||
# Header Files
|
||||
platform/application.h
|
||||
platform/platform.h
|
||||
platform/window.h
|
||||
platform/input_events.h
|
||||
platform/configuration.h
|
||||
platform/headless_window.h
|
||||
platform/plugins/plugin.h
|
||||
platform/plugins/plugin_base.h
|
||||
|
||||
# Source Files
|
||||
platform/headless_window.cpp
|
||||
platform/application.cpp
|
||||
platform/platform.cpp
|
||||
platform/window.cpp
|
||||
platform/input_events.cpp
|
||||
platform/configuration.cpp
|
||||
platform/plugins/plugin.cpp)
|
||||
|
||||
set(GLFW_FILES
|
||||
# Header Files
|
||||
platform/glfw_window.h
|
||||
# Source Files
|
||||
platform/glfw_window.cpp
|
||||
)
|
||||
|
||||
set(ANDROID_FILES
|
||||
# Header Files
|
||||
platform/android/android_platform.h
|
||||
platform/android/android_window.h
|
||||
stats/hwcpipe_stats_provider.h
|
||||
# Source Files
|
||||
platform/android/android_platform.cpp
|
||||
platform/android/android_window.cpp
|
||||
stats/hwcpipe_stats_provider.cpp)
|
||||
|
||||
set(IOS_FILES
|
||||
# Header Files
|
||||
platform/ios/ios_platform.h
|
||||
platform/ios/ios_window.h
|
||||
# Source Files
|
||||
platform/ios/ios_platform.mm
|
||||
platform/ios/ios_window.mm)
|
||||
|
||||
set(WINDOWS_FILES
|
||||
# Header Files
|
||||
platform/windows/windows_platform.h
|
||||
# Source Files
|
||||
platform/windows/windows_platform.cpp)
|
||||
|
||||
set(UNIX_FILES
|
||||
# Header Files
|
||||
platform/unix/unix_platform.h
|
||||
# Source Files
|
||||
platform/unix/unix_platform.cpp)
|
||||
|
||||
set(LINUX_D2D_FILES
|
||||
# Header Files
|
||||
platform/unix/unix_d2d_platform.h
|
||||
platform/unix/direct_window.h
|
||||
# Source Files
|
||||
platform/unix/unix_d2d_platform.cpp
|
||||
platform/unix/direct_window.cpp)
|
||||
|
||||
source_group("\\" FILES ${FRAMEWORK_FILES})
|
||||
source_group("common\\" FILES ${COMMON_FILES})
|
||||
source_group("platform\\" FILES ${PLATFORM_FILES})
|
||||
source_group("platform\\" FILES ${GLFW_FILES})
|
||||
source_group("platform\\windows" FILES ${WINDOWS_FILES})
|
||||
source_group("platform\\android" FILES ${ANDROID_FILES})
|
||||
source_group("platform\\ios" FILES ${IOS_FILES})
|
||||
source_group("platform\\unix" FILES ${UNIX_FILES})
|
||||
source_group("platform\\unix" FILES ${LINUX_D2D_FILES})
|
||||
source_group("core\\" FILES ${CORE_FILES})
|
||||
source_group("geometry\\" FILES ${GEOMETRY_FILES})
|
||||
source_group("rendering\\" FILES ${RENDERING_FILES})
|
||||
source_group("rendering\\subpasses" FILES ${RENDERING_SUBPASSES_FILES})
|
||||
source_group("scene_graph\\" FILES ${SCENE_GRAPH_FILES})
|
||||
source_group("scene_graph\\components\\" FILES ${SCENE_GRAPH_COMPONENT_FILES})
|
||||
source_group("scene_graph\\scripts\\" FILES ${SCENE_GRAPH_SCRIPTS_FILES})
|
||||
source_group("stats\\" FILES ${STATS_FILES})
|
||||
|
||||
set(PROJECT_FILES
|
||||
${PLATFORM_FILES}
|
||||
${COMMON_FILES}
|
||||
${FRAMEWORK_FILES}
|
||||
${CORE_FILES}
|
||||
${GEOMETRY_FILES}
|
||||
${RENDERING_FILES}
|
||||
${RENDERING_SUBPASSES_FILES}
|
||||
${SCENE_GRAPH_FILES}
|
||||
${SCENE_GRAPH_COMPONENT_FILES}
|
||||
${SCENE_GRAPH_SCRIPTS_FILES}
|
||||
${STATS_FILES})
|
||||
|
||||
# No need for explicit casts from vk::HandleType to VkHandleType on ANDROID
|
||||
if(ANDROID)
|
||||
add_definitions(-DVULKAN_HPP_TYPESAFE_CONVERSION=1)
|
||||
endif()
|
||||
|
||||
# Add files based on platform
|
||||
if(ANDROID)
|
||||
list(APPEND PROJECT_FILES ${ANDROID_FILES})
|
||||
message(STATUS "Android platform detected")
|
||||
else()
|
||||
if(DIRECT_TO_DISPLAY)
|
||||
list(APPEND PROJECT_FILES ${LINUX_D2D_FILES})
|
||||
message(STATUS "Unix d2d platform detected")
|
||||
else()
|
||||
if(IOS)
|
||||
enable_language(OBJCXX)
|
||||
list(APPEND PROJECT_FILES ${IOS_FILES})
|
||||
message(STATUS "iOS platform detected")
|
||||
else()
|
||||
list(APPEND PROJECT_FILES ${GLFW_FILES})
|
||||
if(WIN32)
|
||||
list(APPEND PROJECT_FILES ${WINDOWS_FILES})
|
||||
message(STATUS "Windows platform detected")
|
||||
else()
|
||||
list(APPEND PROJECT_FILES ${UNIX_FILES})
|
||||
message(STATUS "Unix platform detected")
|
||||
endif()
|
||||
endif ()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# mask out the min/max macros from minwindef.h
|
||||
if(MSVC)
|
||||
add_definitions(-DNOMINMAX)
|
||||
endif()
|
||||
|
||||
#NB: switch this to shared library and things stop working. (there is likely two copies of volk somewhere.
|
||||
add_library(${PROJECT_NAME} OBJECT ${PROJECT_FILES})
|
||||
set_target_properties(${PROJECT_NAME} PROPERTIES POSITION_INDEPENDENT_CODE ON)
|
||||
|
||||
# compiler flags based on compiler type
|
||||
if(NOT MSVC)
|
||||
target_compile_options(${PROJECT_NAME} PUBLIC -fexceptions)
|
||||
endif()
|
||||
|
||||
if(MSVC)
|
||||
target_compile_options(${PROJECT_NAME} PUBLIC /MP)
|
||||
endif()
|
||||
|
||||
if(${VKB_VALIDATION_LAYERS})
|
||||
target_compile_definitions(${PROJECT_NAME} PUBLIC VKB_VALIDATION_LAYERS)
|
||||
endif()
|
||||
|
||||
# GPU assisted validation layers are not available on macOS.
|
||||
if(${VKB_VALIDATION_LAYERS_GPU_ASSISTED})
|
||||
if (APPLE)
|
||||
message(WARNING "GPU assisted validation layers are not currently available on macOS.")
|
||||
else()
|
||||
target_compile_definitions(${PROJECT_NAME} PUBLIC VKB_VALIDATION_LAYERS)
|
||||
target_compile_definitions(${PROJECT_NAME} PUBLIC VKB_VALIDATION_LAYERS_GPU_ASSISTED)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(${VKB_VALIDATION_LAYERS_BEST_PRACTICES})
|
||||
target_compile_definitions(${PROJECT_NAME} PUBLIC VKB_VALIDATION_LAYERS)
|
||||
target_compile_definitions(${PROJECT_NAME} PUBLIC VKB_VALIDATION_LAYERS_BEST_PRACTICES)
|
||||
endif()
|
||||
|
||||
if(${VKB_VALIDATION_LAYERS_SYNCHRONIZATION})
|
||||
target_compile_definitions(${PROJECT_NAME} PUBLIC VKB_VALIDATION_LAYERS)
|
||||
target_compile_definitions(${PROJECT_NAME} PUBLIC VKB_VALIDATION_LAYERS_SYNCHRONIZATION)
|
||||
endif()
|
||||
|
||||
if(${VKB_VULKAN_DEBUG})
|
||||
target_compile_definitions(${PROJECT_NAME} PUBLIC VKB_VULKAN_DEBUG)
|
||||
endif()
|
||||
|
||||
if(${VKB_ENABLE_PORTABILITY})
|
||||
message(STATUS "Vulkan Portability Enumeration and Portability Subset extensions are enabled")
|
||||
target_compile_definitions(${PROJECT_NAME} PUBLIC VKB_ENABLE_PORTABILITY)
|
||||
endif()
|
||||
|
||||
if(${VKB_WARNINGS_AS_ERRORS})
|
||||
message(STATUS "Warnings as Errors Enabled")
|
||||
if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang" OR "${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
|
||||
target_compile_options(${PROJECT_NAME} PRIVATE -Werror)
|
||||
elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "MSVC")
|
||||
target_compile_options(${PROJECT_NAME} PRIVATE /W3 /WX)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
target_include_directories(${PROJECT_NAME} PUBLIC $<TARGET_PROPERTY:plugins,INCLUDE_DIRECTORIES> ${CMAKE_CURRENT_SOURCE_DIR})
|
||||
|
||||
# Link third party libraries
|
||||
target_link_libraries(${PROJECT_NAME} PUBLIC
|
||||
vkb__core
|
||||
vkb__filesystem
|
||||
volk
|
||||
ktx
|
||||
stb
|
||||
${ASTC_TARGET}
|
||||
imgui
|
||||
tinygltf
|
||||
glm
|
||||
vma
|
||||
spirv-cross-glsl
|
||||
spdlog
|
||||
plugins)
|
||||
|
||||
if(${NEED_LINK_ATOMIC})
|
||||
target_link_libraries(${PROJECT_NAME} PUBLIC atomic)
|
||||
endif()
|
||||
|
||||
# Link platform specific libraries
|
||||
if(ANDROID)
|
||||
# Import game-activity static lib inside the game-activity_static prefab module.
|
||||
find_package(game-activity REQUIRED CONFIG)
|
||||
target_link_libraries(${PROJECT_NAME} PUBLIC hwcpipe log android game-activity::game-activity_static)
|
||||
else()
|
||||
if (DIRECT_TO_DISPLAY)
|
||||
target_link_libraries(${PROJECT_NAME} PRIVATE dl)
|
||||
else()
|
||||
if (NOT IOS)
|
||||
target_link_libraries(${PROJECT_NAME} PRIVATE glfw)
|
||||
endif ()
|
||||
endif()
|
||||
endif()
|
||||
|
||||
if(VKB_DO_CLANG_TIDY)
|
||||
set_target_properties(framework PROPERTIES CXX_CLANG_TIDY "${VKB_DO_CLANG_TIDY}")
|
||||
endif()
|
||||
|
||||
if (VKB_PROFILING)
|
||||
## Enable profling
|
||||
target_compile_definitions(${PROJECT_NAME} PUBLIC VKB_PROFILING=1)
|
||||
else()
|
||||
## Disable profiling
|
||||
target_compile_definitions(${PROJECT_NAME} PUBLIC VKB_PROFILING=0)
|
||||
endif()
|
||||
@@ -0,0 +1,231 @@
|
||||
////
|
||||
- Copyright (c) 2023-2025, 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.
|
||||
-
|
||||
////
|
||||
= Framework
|
||||
// omit in toc
|
||||
:pp: {plus}{plus}
|
||||
|
||||
This folder contains the base framework used by the samples.
|
||||
It offers sample base classes, encapsulates common functionality for e.g.
|
||||
loading assets (images, models, shaders), wraps common Vulkan objects and implements frequently used concepts like a cache and a scene graph.
|
||||
The framework also implements platform support for Windows, Linux, MacOS and Android.
|
||||
|
||||
It can be used as a guideline for writing advanced Vulkan applications.
|
||||
|
||||
Before trying to implement common functions, consider checking if the framework doesn't already provides what you are looking for.
|
||||
|
||||
== Sample base classes
|
||||
|
||||
The framework provides two different sample base classes.
|
||||
When xref:../scripts/README.adoc[creating new samples], you can choose between one of them:
|
||||
|
||||
=== High level base sample class
|
||||
|
||||
This base class abstracts away most of the Vulkan API calls and as such makes heavy use of the Vulkan object wrapper classes of the framework.
|
||||
Writing samples with the base class is less verbose.
|
||||
|
||||
See link:./vulkan_sample.h[vulkan_sample.h] and link:./vulkan_sample.cpp[vulkan_sample.cpp].
|
||||
|
||||
=== API sample base class
|
||||
|
||||
This base class uses less abstraction, letting you work more explicitly with the api.
|
||||
|
||||
See link:./api_vulkan_sample.h[api_vulkan_sample.h] and link:./api_vulkan_sample.cpp[api_vulkan_sample.cpp].
|
||||
|
||||
=== Support for Vulkan-Hpp
|
||||
|
||||
While the framework itself primarily uses the C-Interface for Vulkan, both the high level and the API sample base class also come with https://github.com/KhronosGroup/Vulkan-Hpp[Vulkan-Hpp] variants, letting you write samples using the C{pp} Vulkan language bindings instead.
|
||||
|
||||
See link:./hpp_vulkan_sample.h[hpp_vulkan_sample.h] / link:./hpp_vulkan_sample.cpp[hpp_vulkan_sample.cpp] and link:./hpp_api_vulkan_sample.h[hpp_api_vulkan_sample.h] / link:./hpp_api_vulkan_sample.cpp[hpp_api_vulkan_sample.cpp].
|
||||
|
||||
== Commonly used framework concepts
|
||||
|
||||
=== Enabling extensions
|
||||
|
||||
Vulkan is an extensible api.
|
||||
New features are usually exposed through either instance or device extensions.
|
||||
Extensions can be enabled in the constructor of both the high level and api base sample class:
|
||||
|
||||
[,cpp]
|
||||
----
|
||||
MySample::MySample()
|
||||
{
|
||||
add_instance_extension(VK_SOME_INSTANCE_EXTENSION_NAME);
|
||||
add_device_extension(VK_SOME_DEVICE_EXTENSION_NAME);
|
||||
}
|
||||
----
|
||||
|
||||
NOTE: The framework uses the https://github.com/zeux/volk[Volk] meta-loader, which will automatically load extension function pointers for all enabled extensions.
|
||||
There is no need to manually get extension pointer functions.
|
||||
|
||||
=== Changing the Vulkan api version
|
||||
|
||||
By default all samples create a Vulkan 1.1 instance.
|
||||
Higher versions can be requested in the constructor of a sample:
|
||||
|
||||
[,cpp]
|
||||
----
|
||||
MySample::MySample()
|
||||
{
|
||||
set_api_version(VK_API_VERSION_1_2);
|
||||
}
|
||||
----
|
||||
|
||||
=== Requesting GPU features
|
||||
|
||||
Most extensions also require enabling corresponding feature flags.
|
||||
This can be done by overriding the `request_gpu_features` function of the base class:
|
||||
|
||||
[,cpp]
|
||||
----
|
||||
void MySample::request_gpu_features(vkb::PhysicalDevice &gpu)
|
||||
{
|
||||
// Get a reference to the feature structure required for an extension
|
||||
auto &requested_extension_feature = gpu
|
||||
request_extension_features<VkPhysicalDeviceSomeExtensionFeaturesKHR>(VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SOME_EXTENSION_FEATURES_KHR);
|
||||
|
||||
// Enable a selected feature
|
||||
requested_extension_feature.featureName = VK_TRUE;
|
||||
}
|
||||
----
|
||||
|
||||
=== Extending the graphical user interface
|
||||
|
||||
The framework includes a graphical user interface based on https://github.com/ocornut/imgui[Dear ImGui].
|
||||
This can be used by samples to display values and add controls like buttons, dropdowns, etc.
|
||||
|
||||
To add additional elements to the UI of a sample, you override the respective function from the base class:
|
||||
|
||||
Samples based on the high level base class need to override the `draw_gui` function:
|
||||
|
||||
[,cpp]
|
||||
----
|
||||
void MySample::draw_gui()
|
||||
{
|
||||
if (ImGui::Checkbox("Enable Option", &option_enabled))
|
||||
{
|
||||
...
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
Samples based on the api base class need to override the `on_update_ui_overlay` function:
|
||||
|
||||
[,cpp]
|
||||
----
|
||||
void MyApiSample::on_update_ui_overlay(vkb::Drawer &drawer)
|
||||
{
|
||||
if (drawer.checkbox("Enable option", &option_enabled))
|
||||
{
|
||||
...
|
||||
}
|
||||
}
|
||||
----
|
||||
|
||||
=== Loading models
|
||||
|
||||
The framework supports https://www.khronos.org/gltf/[glTF models] and includes a loader for this format.
|
||||
|
||||
The high level base class works with a single glTF scene loaded at startup.
|
||||
The scene is part of the base class, and there is no need to explicitly draw it:
|
||||
|
||||
[,cpp]
|
||||
----
|
||||
bool MySample::prepare(const vkb::ApplicationOptions &options)
|
||||
{
|
||||
scene = load_scene("filename.gltf");
|
||||
}
|
||||
----
|
||||
|
||||
With the api base class are explicitly declared, loaded and rendered:
|
||||
|
||||
[,cpp]
|
||||
----
|
||||
// my_sample.h
|
||||
class MyApiSample : public ApiVulkanSample
|
||||
{
|
||||
std::unique_ptr<vkb::sg::SubMesh> modelA;
|
||||
std::unique_ptr<vkb::sg::SubMesh> modelB;
|
||||
...
|
||||
}
|
||||
|
||||
// my_sample.cpp
|
||||
bool MyApiSample::prepare(const vkb::ApplicationOptions &options)
|
||||
{
|
||||
modelA = load_model("filenameA.gltf");
|
||||
modelB = load_model("filenameB.gltf");
|
||||
}
|
||||
|
||||
void MyApiSample::build_command_buffers()
|
||||
{
|
||||
vkBeginCommandBuffer(...);
|
||||
...
|
||||
draw_model(modelA, draw_cmd_buffers[i]);
|
||||
...
|
||||
draw_model(modelB, draw_cmd_buffers[i]);
|
||||
...
|
||||
vkEndCommandBufer(...);
|
||||
}
|
||||
----
|
||||
|
||||
=== Loading images
|
||||
|
||||
The framework supports the https://www.khronos.org/ktx/[KTX] GPU container format and includes a loader for this format.
|
||||
As a container format, KTX supports different image formats ranging from basic RGBA images to compressed formats.
|
||||
|
||||
[,cpp]
|
||||
----
|
||||
texture = load_texture("rgba_texture.ktx", vkb::sg::Image::Color);
|
||||
----
|
||||
|
||||
Images (textures) loaded like this can then be used as descriptors later on:
|
||||
|
||||
[,cpp]
|
||||
----
|
||||
VkDescriptorImageInfo image_descriptor = create_descriptor(texture);
|
||||
----
|
||||
|
||||
=== Loading shaders
|
||||
|
||||
The framework supports loading textual GLSL shaders.
|
||||
These shaders are then compiled to https://registry.khronos.org/SPIR-V/specs/unified1/SPIRV.html[SPIR-V] at runtime so Vulkan can ingest them.
|
||||
|
||||
With the high level base class, shaders are attached to the scene graphics' render pipeline:
|
||||
|
||||
[,cpp]
|
||||
----
|
||||
vkb::ShaderSource vert_shader("vs.vert");
|
||||
vkb::ShaderSource frag_shader("fs.frag");
|
||||
auto scene_subpass = std::make_unique<vkb::ForwardSubpass>(get_render_context(), std::move(vert_shader), std::move(frag_shader), *scene, *camera);
|
||||
|
||||
auto render_pipeline = vkb::RenderPipeline();
|
||||
render_pipeline.add_subpass(std::move(scene_subpass));
|
||||
|
||||
set_render_pipeline(std::move(render_pipeline));
|
||||
----
|
||||
|
||||
While in the api base class, this is again more explicitly by creating shader modules used at pipeline creation time:
|
||||
|
||||
[,cpp]
|
||||
----
|
||||
std::array<VkPipelineShaderStageCreateInfo, 2> shader_stages;
|
||||
...
|
||||
shader_stages[0] = load_shader("vs.vert", VK_SHADER_STAGE_VERTEX_BIT);
|
||||
shader_stages[1] = load_shader("fs.frag", VK_SHADER_STAGE_FRAGMENT_BIT);
|
||||
VK_CHECK(vkCreateGraphicsPipelines(get_device().get_handle(), pipeline_cache, 1, &pipeline_create_info, nullptr, &pipeline));
|
||||
----
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,466 @@
|
||||
/* Copyright (c) 2019-2025, 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef _WIN32
|
||||
# pragma comment(linker, "/subsystem:windows")
|
||||
# include <ShellScalingAPI.h>
|
||||
# include <fcntl.h>
|
||||
# include <io.h>
|
||||
# include <windows.h>
|
||||
#endif
|
||||
|
||||
#include <chrono>
|
||||
#include <iostream>
|
||||
#include <random>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#include "camera.h"
|
||||
#include "common/vk_common.h"
|
||||
#include "common/vk_initializers.h"
|
||||
#include "core/buffer.h"
|
||||
#include "core/swapchain.h"
|
||||
#include "gui.h"
|
||||
#include "platform/platform.h"
|
||||
#include "rendering/render_context.h"
|
||||
#include "scene_graph/components/image.h"
|
||||
#include "scene_graph/components/sampler.h"
|
||||
#include "scene_graph/components/texture.h"
|
||||
#include "vulkan_sample.h"
|
||||
|
||||
/**
|
||||
* @brief A swapchain buffer
|
||||
*/
|
||||
struct SwapchainBuffer
|
||||
{
|
||||
VkImage image;
|
||||
VkImageView view;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A texture wrapper that owns its image data and links it with a sampler
|
||||
*/
|
||||
struct Texture
|
||||
{
|
||||
std::unique_ptr<vkb::sg::Image> image;
|
||||
VkSampler sampler;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief The structure of a vertex
|
||||
*/
|
||||
struct Vertex
|
||||
{
|
||||
glm::vec3 pos;
|
||||
glm::vec3 normal;
|
||||
glm::vec2 uv;
|
||||
glm::vec4 joint0;
|
||||
glm::vec4 weight0;
|
||||
glm::vec3 color;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief The structure of a vertex for storage buffer
|
||||
* Simplified to position and normal for easier alignment
|
||||
*/
|
||||
struct AlignedVertex
|
||||
{
|
||||
glm::vec4 pos;
|
||||
glm::vec4 normal;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief The structure of a meshlet for mesh shader
|
||||
*/
|
||||
struct Meshlet
|
||||
{
|
||||
uint32_t vertices[64];
|
||||
uint32_t indices[126];
|
||||
uint32_t vertex_count;
|
||||
uint32_t index_count;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Sascha Willems base class for use in his ported samples into the framework
|
||||
*
|
||||
* See vkb::VulkanSample for documentation
|
||||
*/
|
||||
class ApiVulkanSample : public vkb::VulkanSampleC
|
||||
{
|
||||
public:
|
||||
ApiVulkanSample() = default;
|
||||
|
||||
virtual ~ApiVulkanSample();
|
||||
|
||||
virtual bool prepare(const vkb::ApplicationOptions &options) override;
|
||||
|
||||
virtual void input_event(const vkb::InputEvent &input_event) override;
|
||||
|
||||
virtual void update(float delta_time) override;
|
||||
|
||||
virtual bool resize(const uint32_t width, const uint32_t height) override;
|
||||
|
||||
virtual void render(float delta_time) = 0;
|
||||
|
||||
enum RenderPassCreateFlags
|
||||
{
|
||||
ColorAttachmentLoad = 0x00000001
|
||||
};
|
||||
|
||||
protected:
|
||||
/// Stores the swapchain image buffers
|
||||
std::vector<SwapchainBuffer> swapchain_buffers;
|
||||
|
||||
virtual void create_render_context() override;
|
||||
|
||||
// Handle to the device graphics queue that command buffers are submitted to
|
||||
VkQueue queue;
|
||||
|
||||
// Depth buffer format (selected during Vulkan initialization)
|
||||
VkFormat depth_format;
|
||||
|
||||
// Command buffer pool
|
||||
VkCommandPool cmd_pool;
|
||||
|
||||
/** @brief Pipeline stages used to wait at for graphics queue submissions */
|
||||
VkPipelineStageFlags submit_pipeline_stages = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
|
||||
|
||||
// Contains command buffers and semaphores to be presented to the queue
|
||||
VkSubmitInfo submit_info;
|
||||
|
||||
// Command buffers used for rendering
|
||||
std::vector<VkCommandBuffer> draw_cmd_buffers;
|
||||
|
||||
// Global render pass for frame buffer writes
|
||||
VkRenderPass render_pass;
|
||||
|
||||
// List of available frame buffers (same as number of swap chain images)
|
||||
std::vector<VkFramebuffer> framebuffers;
|
||||
|
||||
// Active frame buffer index
|
||||
uint32_t current_buffer = 0;
|
||||
|
||||
// Descriptor set pool
|
||||
VkDescriptorPool descriptor_pool = VK_NULL_HANDLE;
|
||||
|
||||
// List of shader modules created (stored for cleanup)
|
||||
std::vector<VkShaderModule> shader_modules;
|
||||
|
||||
// Pipeline cache object
|
||||
VkPipelineCache pipeline_cache;
|
||||
|
||||
// Synchronization semaphores
|
||||
struct
|
||||
{
|
||||
// Swap chain image presentation
|
||||
VkSemaphore acquired_image_ready;
|
||||
|
||||
// Command buffer submission and execution
|
||||
VkSemaphore render_complete;
|
||||
} semaphores;
|
||||
|
||||
// Synchronization fences
|
||||
std::vector<VkFence> wait_fences;
|
||||
|
||||
/**
|
||||
* @brief Populates the swapchain_buffers vector with the image and imageviews
|
||||
*/
|
||||
void create_swapchain_buffers();
|
||||
|
||||
/**
|
||||
* @brief Updates the swapchains image usage, if a swapchain exists and recreates all resources based on swapchain images
|
||||
* @param image_usage_flags The usage flags the new swapchain images will have
|
||||
*/
|
||||
void update_swapchain_image_usage_flags(std::set<VkImageUsageFlagBits> image_usage_flags);
|
||||
|
||||
/**
|
||||
* @brief Handles changes to the surface, e.g. on resize
|
||||
*/
|
||||
void handle_surface_changes();
|
||||
|
||||
/**
|
||||
* @brief Creates a buffer descriptor
|
||||
* @param buffer The buffer from which to create the descriptor from
|
||||
* @param size The size of the descriptor (default: VK_WHOLE_SIZE)
|
||||
* @param offset The offset of the descriptor (default: 0)
|
||||
*/
|
||||
VkDescriptorBufferInfo create_descriptor(vkb::core::BufferC &buffer, VkDeviceSize size = VK_WHOLE_SIZE, VkDeviceSize offset = 0);
|
||||
|
||||
/**
|
||||
* @brief Creates an image descriptor
|
||||
* @param texture The texture from which to create the descriptor from
|
||||
* @param descriptor_type The type of image descriptor (default: VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER)
|
||||
*/
|
||||
VkDescriptorImageInfo create_descriptor(Texture &texture, VkDescriptorType descriptor_type = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER);
|
||||
|
||||
/**
|
||||
* @brief Loads in a ktx 2D texture
|
||||
* @param file The filename of the texture to load
|
||||
* @param content_type The type of content in the image file
|
||||
*/
|
||||
Texture load_texture(const std::string &file, vkb::sg::Image::ContentType content_type);
|
||||
|
||||
/**
|
||||
* @brief Loads in a ktx 2D texture array
|
||||
* @param file The filename of the texture to load
|
||||
* @param content_type The type of content in the image file
|
||||
*/
|
||||
Texture load_texture_array(const std::string &file, vkb::sg::Image::ContentType content_type);
|
||||
|
||||
/**
|
||||
* @brief Loads in a ktx 2D texture cubemap
|
||||
* @param file The filename of the texture to load
|
||||
* @param content_type The type of content in the image file
|
||||
*/
|
||||
Texture load_texture_cubemap(const std::string &file, vkb::sg::Image::ContentType content_type);
|
||||
|
||||
/**
|
||||
* @brief Loads in a single model from a GLTF file
|
||||
* @param file The filename of the model to load
|
||||
* @param index The index of the model to load from the GLTF file (default: 0)
|
||||
* @param storage_buffer Set true to store model in SSBO
|
||||
* @param additional_buffer_usage_flags Additional buffer usage flags to be applied to vertex and index buffers
|
||||
*/
|
||||
std::unique_ptr<vkb::sg::SubMesh> load_model(const std::string &file, uint32_t index = 0, bool storage_buffer = false, VkBufferUsageFlags additional_buffer_usage_flags = 0);
|
||||
|
||||
/**
|
||||
* @brief Records the necessary drawing commands to a command buffer
|
||||
* @param model The model to draw
|
||||
* @param command_buffer The command buffer to record to
|
||||
* @param instance_count The number of instances (default: 1)
|
||||
*/
|
||||
void draw_model(std::unique_ptr<vkb::sg::SubMesh> &model, VkCommandBuffer command_buffer, uint32_t instance_count = 1);
|
||||
|
||||
/**
|
||||
* @brief Synchronously execute a block code within a command buffer, then submit the command buffer and wait for completion.
|
||||
* @param f a block of code which is passed a command buffer which is already in the begin state.
|
||||
* @param signalSemaphore An optional semaphore to signal when the commands have completed execution.
|
||||
*/
|
||||
void with_command_buffer(const std::function<void(VkCommandBuffer command_buffer)> &f, VkSemaphore signalSemaphore = VK_NULL_HANDLE);
|
||||
|
||||
/**
|
||||
* @brief Synchronously execute a block code within a command buffer vkb wrapper, then submit the command buffer and wait for completion.
|
||||
* @param f a block of code which is passed a command buffer which is already in the begin state.
|
||||
*/
|
||||
void with_vkb_command_buffer(const std::function<void(vkb::core::CommandBufferC &command_buffer)> &f);
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief Called when a view change occurs, can be overriden in derived samples to handle updating uniforms
|
||||
*/
|
||||
virtual void view_changed();
|
||||
|
||||
/**
|
||||
* @brief Called after the mouse cursor is moved and before internal events (like camera rotation) is handled
|
||||
* @param x The width from the origin
|
||||
* @param y The height from the origin
|
||||
* @param handled Whether the event was handled
|
||||
*/
|
||||
virtual void mouse_moved(double x, double y, bool &handled);
|
||||
|
||||
/**
|
||||
* @brief To be overridden by the derived class. Records the relevant commands to the rendering command buffers
|
||||
* Called when the framebuffers need to be rebuilt
|
||||
*/
|
||||
virtual void build_command_buffers() = 0;
|
||||
|
||||
/**
|
||||
* @brief Rebuild the command buffers by first resetting the corresponding command pool and then building the command buffers.
|
||||
*/
|
||||
void rebuild_command_buffers();
|
||||
|
||||
/**
|
||||
* @brief Creates the fences for rendering
|
||||
*/
|
||||
void create_synchronization_primitives();
|
||||
|
||||
/**
|
||||
* @brief Creates a new (graphics) command pool object storing command buffers
|
||||
*/
|
||||
void create_command_pool();
|
||||
|
||||
/**
|
||||
* @brief Setup default depth and stencil views
|
||||
*/
|
||||
virtual void setup_depth_stencil();
|
||||
|
||||
/**
|
||||
* @brief Create framebuffers for all requested swap chain images
|
||||
* Can be overriden in derived class to setup a custom framebuffer (e.g. for MSAA)
|
||||
*/
|
||||
virtual void setup_framebuffer();
|
||||
|
||||
/**
|
||||
* @brief Setup a default render pass
|
||||
* Can be overriden in derived class to setup a custom render pass (e.g. for MSAA)
|
||||
*/
|
||||
virtual void setup_render_pass();
|
||||
|
||||
/**
|
||||
* @brief Update flags for the default render pass and recreate it
|
||||
* @param flags Optional flags for render pass creation
|
||||
*/
|
||||
void update_render_pass_flags(uint32_t flags = 0);
|
||||
|
||||
/**
|
||||
* @brief Check if command buffers are valid (!= VK_NULL_HANDLE)
|
||||
*/
|
||||
bool check_command_buffers();
|
||||
|
||||
/**
|
||||
* @brief Create command buffers for drawing commands
|
||||
*/
|
||||
void create_command_buffers();
|
||||
|
||||
/**
|
||||
* @brief Destroy all command buffers, may be necessary during runtime if options are toggled
|
||||
*/
|
||||
void destroy_command_buffers();
|
||||
|
||||
/**
|
||||
* @brief Recreate the current command buffer draw_cmd_buffer[current_buffer]
|
||||
*/
|
||||
void recreate_current_command_buffer();
|
||||
|
||||
/**
|
||||
* @brief Create a cache pool for rendering pipelines
|
||||
*/
|
||||
void create_pipeline_cache();
|
||||
|
||||
/**
|
||||
* @brief Load a SPIR-V shader
|
||||
* @param file The file location of the shader relative to the shaders folder
|
||||
* @param stage The shader stage
|
||||
*/
|
||||
VkPipelineShaderStageCreateInfo load_shader(const std::string &file, VkShaderStageFlagBits stage);
|
||||
|
||||
/**
|
||||
* @brief Load a SPIR-V shader based on current shader language selection
|
||||
* @param sample_folder_name Base folder where the shaders are located (without GLSL/HLSL sub folder)
|
||||
* @param shader_filename Base name of the shader file
|
||||
* @param stage The shader stage
|
||||
*/
|
||||
VkPipelineShaderStageCreateInfo load_shader(const std::string &sample_folder_name, const std::string &shader_filename, VkShaderStageFlagBits stage);
|
||||
|
||||
/**
|
||||
* @brief Updates the overlay
|
||||
* @param delta_time The time taken since the last frame
|
||||
* @param additional_ui Function that implements an additional Gui
|
||||
*/
|
||||
void update_overlay(float delta_time, const std::function<void()> &additional_ui) override;
|
||||
|
||||
/**
|
||||
* @brief If the gui is enabled, then record the drawing commands to a command buffer
|
||||
* @param command_buffer A valid command buffer that is ready to be recorded to
|
||||
*/
|
||||
void draw_ui(const VkCommandBuffer command_buffer);
|
||||
|
||||
/**
|
||||
* @brief Prepare the frame for workload submission, acquires the next image from the swap chain and
|
||||
* sets the default wait and signal semaphores
|
||||
*/
|
||||
void prepare_frame();
|
||||
|
||||
/**
|
||||
* @brief Submit the frames' workload
|
||||
*/
|
||||
void submit_frame();
|
||||
|
||||
/**
|
||||
* @brief Called when the UI overlay is updating, can be used to add custom elements to the overlay
|
||||
* @param drawer The drawer from the gui to draw certain elements
|
||||
*/
|
||||
virtual void on_update_ui_overlay(vkb::Drawer &drawer);
|
||||
|
||||
/**
|
||||
* @brief Initializes the UI. Can be overridden to customize the way it is displayed.
|
||||
*/
|
||||
virtual void prepare_gui();
|
||||
|
||||
private:
|
||||
/** brief Indicates that the view (position, rotation) has changed and buffers containing camera matrices need to be updated */
|
||||
bool view_updated = false;
|
||||
// Destination dimensions for resizing the window
|
||||
uint32_t dest_width;
|
||||
uint32_t dest_height;
|
||||
bool resizing = false;
|
||||
|
||||
void handle_mouse_move(int32_t x, int32_t y);
|
||||
|
||||
#if defined(VKB_DEBUG) || defined(VKB_VALIDATION_LAYERS)
|
||||
/// The debug report callback
|
||||
VkDebugReportCallbackEXT debug_report_callback{VK_NULL_HANDLE};
|
||||
#endif
|
||||
|
||||
public:
|
||||
bool prepared = false;
|
||||
uint32_t width = 1280;
|
||||
uint32_t height = 720;
|
||||
|
||||
VkClearColorValue default_clear_color = {{0.002f, 0.002f, 0.002f, 1.0f}};
|
||||
|
||||
float zoom = 0;
|
||||
|
||||
// Defines a frame rate independent timer value clamped from -1.0...1.0
|
||||
// For use in animations, rotations, etc.
|
||||
float timer = 0.0f;
|
||||
// Multiplier for speeding up (or slowing down) the global timer
|
||||
float timer_speed = 0.0025f;
|
||||
|
||||
bool paused = false;
|
||||
|
||||
// Use to adjust mouse rotation speed
|
||||
float rotation_speed = 1.0f;
|
||||
// Use to adjust mouse zoom speed
|
||||
float zoom_speed = 1.0f;
|
||||
|
||||
vkb::Camera camera;
|
||||
|
||||
glm::vec3 rotation = glm::vec3();
|
||||
glm::vec3 camera_pos = glm::vec3();
|
||||
glm::vec2 mouse_pos;
|
||||
|
||||
std::string title = "Vulkan Example";
|
||||
std::string name = "vulkanExample";
|
||||
|
||||
struct ImageData
|
||||
{
|
||||
VkImage image{VK_NULL_HANDLE};
|
||||
VkDeviceMemory mem{VK_NULL_HANDLE};
|
||||
VkImageView view{VK_NULL_HANDLE};
|
||||
} depth_stencil;
|
||||
|
||||
struct
|
||||
{
|
||||
bool left = false;
|
||||
bool right = false;
|
||||
bool middle = false;
|
||||
} mouse_buttons;
|
||||
|
||||
struct TouchPos
|
||||
{
|
||||
int32_t x;
|
||||
int32_t y;
|
||||
} touch_pos;
|
||||
bool touch_down = false;
|
||||
double touch_timer = 0.0;
|
||||
|
||||
uint32_t frame_count = 0;
|
||||
float accumulated_time = 0.0f;
|
||||
uint32_t fps = 1; // to prevent division by zero on first frame
|
||||
};
|
||||
@@ -0,0 +1,377 @@
|
||||
/* Copyright (c) 2019-2025, Arm Limited and Contributors
|
||||
* Copyright (c) 2024-2025, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* 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 "common/helpers.h"
|
||||
#include "core/buffer.h"
|
||||
#include "core/device.h"
|
||||
|
||||
namespace vkb
|
||||
{
|
||||
/**
|
||||
* @brief An allocation of vulkan memory; different buffer allocations,
|
||||
* with different offset and size, may come from the same Vulkan buffer
|
||||
*/
|
||||
template <vkb::BindingType bindingType>
|
||||
class BufferAllocation
|
||||
{
|
||||
public:
|
||||
using DeviceSizeType = typename std::conditional<bindingType == vkb::BindingType::Cpp, vk::DeviceSize, VkDeviceSize>::type;
|
||||
|
||||
public:
|
||||
BufferAllocation() = default;
|
||||
BufferAllocation(const BufferAllocation &) = default;
|
||||
BufferAllocation(BufferAllocation &&) = default;
|
||||
BufferAllocation &operator=(const BufferAllocation &) = default;
|
||||
BufferAllocation &operator=(BufferAllocation &&) = default;
|
||||
|
||||
BufferAllocation(vkb::core::Buffer<bindingType> &buffer, DeviceSizeType size, DeviceSizeType offset);
|
||||
|
||||
bool empty() const;
|
||||
vkb::core::Buffer<bindingType> &get_buffer();
|
||||
DeviceSizeType get_offset() const;
|
||||
DeviceSizeType get_size() const;
|
||||
void update(const std::vector<uint8_t> &data, uint32_t offset = 0);
|
||||
template <typename T>
|
||||
void update(const T &value, uint32_t offset = 0);
|
||||
|
||||
private:
|
||||
vkb::core::BufferCpp *buffer = nullptr;
|
||||
vk::DeviceSize offset = 0;
|
||||
vk::DeviceSize size = 0;
|
||||
};
|
||||
|
||||
using BufferAllocationC = BufferAllocation<vkb::BindingType::C>;
|
||||
using BufferAllocationCpp = BufferAllocation<vkb::BindingType::Cpp>;
|
||||
|
||||
template <>
|
||||
inline BufferAllocation<vkb::BindingType::Cpp>::BufferAllocation(vkb::core::BufferCpp &buffer, vk::DeviceSize size, vk::DeviceSize offset) :
|
||||
buffer(&buffer),
|
||||
offset(offset),
|
||||
size(size)
|
||||
{}
|
||||
|
||||
template <>
|
||||
inline BufferAllocation<vkb::BindingType::C>::BufferAllocation(vkb::core::BufferC &buffer, VkDeviceSize size, VkDeviceSize offset) :
|
||||
buffer(reinterpret_cast<vkb::core::BufferCpp *>(&buffer)),
|
||||
offset(static_cast<vk::DeviceSize>(offset)),
|
||||
size(static_cast<vk::DeviceSize>(size))
|
||||
{}
|
||||
|
||||
template <vkb::BindingType bindingType>
|
||||
bool BufferAllocation<bindingType>::empty() const
|
||||
{
|
||||
return size == 0 || buffer == nullptr;
|
||||
}
|
||||
|
||||
template <vkb::BindingType bindingType>
|
||||
typename vkb::core::Buffer<bindingType> &BufferAllocation<bindingType>::get_buffer()
|
||||
{
|
||||
assert(buffer && "Invalid buffer pointer");
|
||||
if constexpr (bindingType == vkb::BindingType::Cpp)
|
||||
{
|
||||
return *buffer;
|
||||
}
|
||||
else
|
||||
{
|
||||
return reinterpret_cast<vkb::core::BufferC &>(*buffer);
|
||||
}
|
||||
}
|
||||
|
||||
template <vkb::BindingType bindingType>
|
||||
typename BufferAllocation<bindingType>::DeviceSizeType BufferAllocation<bindingType>::get_offset() const
|
||||
{
|
||||
if constexpr (bindingType == vkb::BindingType::Cpp)
|
||||
{
|
||||
return offset;
|
||||
}
|
||||
else
|
||||
{
|
||||
return static_cast<VkDeviceSize>(offset);
|
||||
}
|
||||
}
|
||||
|
||||
template <vkb::BindingType bindingType>
|
||||
typename BufferAllocation<bindingType>::DeviceSizeType BufferAllocation<bindingType>::get_size() const
|
||||
{
|
||||
if constexpr (bindingType == vkb::BindingType::Cpp)
|
||||
{
|
||||
return size;
|
||||
}
|
||||
else
|
||||
{
|
||||
return static_cast<VkDeviceSize>(size);
|
||||
}
|
||||
}
|
||||
|
||||
template <vkb::BindingType bindingType>
|
||||
void BufferAllocation<bindingType>::update(const std::vector<uint8_t> &data, uint32_t offset)
|
||||
{
|
||||
assert(buffer && "Invalid buffer pointer");
|
||||
|
||||
if (offset + data.size() <= size)
|
||||
{
|
||||
buffer->update(data, to_u32(this->offset) + offset);
|
||||
}
|
||||
else
|
||||
{
|
||||
LOGE("Ignore buffer allocation update");
|
||||
}
|
||||
}
|
||||
|
||||
template <vkb::BindingType bindingType>
|
||||
template <typename T>
|
||||
void BufferAllocation<bindingType>::update(const T &value, uint32_t offset)
|
||||
{
|
||||
update(to_bytes(value), offset);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Helper class which handles multiple allocation from the same underlying Vulkan buffer.
|
||||
*/
|
||||
template <vkb::BindingType bindingType>
|
||||
class BufferBlock
|
||||
{
|
||||
public:
|
||||
using BufferUsageFlagsType = typename std::conditional<bindingType == vkb::BindingType::Cpp, vk::BufferUsageFlags, VkBufferUsageFlags>::type;
|
||||
using DeviceSizeType = typename std::conditional<bindingType == vkb::BindingType::Cpp, vk::DeviceSize, VkDeviceSize>::type;
|
||||
|
||||
public:
|
||||
BufferBlock() = delete;
|
||||
BufferBlock(BufferBlock const &rhs) = delete;
|
||||
BufferBlock(BufferBlock &&rhs) = default;
|
||||
BufferBlock &operator=(BufferBlock const &rhs) = delete;
|
||||
BufferBlock &operator=(BufferBlock &&rhs) = default;
|
||||
|
||||
BufferBlock(vkb::core::Device<bindingType> &device, DeviceSizeType size, BufferUsageFlagsType usage, VmaMemoryUsage memory_usage);
|
||||
|
||||
/**
|
||||
* @return An usable view on a portion of the underlying buffer
|
||||
*/
|
||||
BufferAllocation<bindingType> allocate(DeviceSizeType size);
|
||||
|
||||
/**
|
||||
* @brief check if this BufferBlock can allocate a given amount of memory
|
||||
* @param size the number of bytes to check
|
||||
* @return \c true if \a size bytes can be allocated from this \c BufferBlock, otherwise \c false.
|
||||
*/
|
||||
bool can_allocate(DeviceSizeType size) const;
|
||||
|
||||
DeviceSizeType get_size() const;
|
||||
void reset();
|
||||
|
||||
private:
|
||||
/**
|
||||
* @ brief Determine the current aligned offset.
|
||||
* @return The current aligned offset.
|
||||
*/
|
||||
vk::DeviceSize aligned_offset() const;
|
||||
vk::DeviceSize determine_alignment(vk::BufferUsageFlags usage, vk::PhysicalDeviceLimits const &limits) const;
|
||||
|
||||
private:
|
||||
vkb::core::BufferCpp buffer;
|
||||
vk::DeviceSize alignment = 0; // Memory alignment, it may change according to the usage
|
||||
vk::DeviceSize offset = 0; // Current offset, it increases on every allocation
|
||||
};
|
||||
|
||||
using BufferBlockC = BufferBlock<vkb::BindingType::C>;
|
||||
using BufferBlockCpp = BufferBlock<vkb::BindingType::Cpp>;
|
||||
|
||||
template <vkb::BindingType bindingType>
|
||||
BufferBlock<bindingType>::BufferBlock(vkb::core::Device<bindingType> &device, DeviceSizeType size, BufferUsageFlagsType usage, VmaMemoryUsage memory_usage) :
|
||||
buffer{device, size, usage, memory_usage}
|
||||
{
|
||||
if constexpr (bindingType == BindingType::Cpp)
|
||||
{
|
||||
alignment = determine_alignment(usage, device.get_gpu().get_properties().limits);
|
||||
}
|
||||
else
|
||||
{
|
||||
alignment =
|
||||
determine_alignment(static_cast<vk::BufferUsageFlags>(usage), static_cast<vk::PhysicalDeviceLimits>(device.get_gpu().get_properties().limits));
|
||||
}
|
||||
}
|
||||
|
||||
template <vkb::BindingType bindingType>
|
||||
BufferAllocation<bindingType> BufferBlock<bindingType>::allocate(DeviceSizeType size)
|
||||
{
|
||||
if (can_allocate(size))
|
||||
{
|
||||
// Move the current offset and return an allocation
|
||||
auto aligned = aligned_offset();
|
||||
offset = aligned + size;
|
||||
if constexpr (bindingType == vkb::BindingType::Cpp)
|
||||
{
|
||||
return BufferAllocationCpp{buffer, size, aligned};
|
||||
}
|
||||
else
|
||||
{
|
||||
return BufferAllocationC{reinterpret_cast<vkb::core::BufferC &>(buffer), static_cast<VkDeviceSize>(size), static_cast<VkDeviceSize>(aligned)};
|
||||
}
|
||||
}
|
||||
|
||||
// No more space available from the underlying buffer, return empty allocation
|
||||
return BufferAllocation<bindingType>{};
|
||||
}
|
||||
|
||||
template <vkb::BindingType bindingType>
|
||||
bool BufferBlock<bindingType>::can_allocate(DeviceSizeType size) const
|
||||
{
|
||||
assert(size > 0 && "Allocation size must be greater than zero");
|
||||
return (aligned_offset() + size <= buffer.get_size());
|
||||
}
|
||||
|
||||
template <vkb::BindingType bindingType>
|
||||
typename BufferBlock<bindingType>::DeviceSizeType BufferBlock<bindingType>::get_size() const
|
||||
{
|
||||
return buffer.get_size();
|
||||
}
|
||||
|
||||
template <vkb::BindingType bindingType>
|
||||
void BufferBlock<bindingType>::reset()
|
||||
{
|
||||
offset = 0;
|
||||
}
|
||||
|
||||
template <vkb::BindingType bindingType>
|
||||
vk::DeviceSize BufferBlock<bindingType>::aligned_offset() const
|
||||
{
|
||||
return (offset + alignment - 1) & ~(alignment - 1);
|
||||
}
|
||||
|
||||
template <vkb::BindingType bindingType>
|
||||
vk::DeviceSize BufferBlock<bindingType>::determine_alignment(vk::BufferUsageFlags usage, vk::PhysicalDeviceLimits const &limits) const
|
||||
{
|
||||
if (usage == vk::BufferUsageFlagBits::eUniformBuffer)
|
||||
{
|
||||
return limits.minUniformBufferOffsetAlignment;
|
||||
}
|
||||
else if (usage == vk::BufferUsageFlagBits::eStorageBuffer)
|
||||
{
|
||||
return limits.minStorageBufferOffsetAlignment;
|
||||
}
|
||||
else if (usage == vk::BufferUsageFlagBits::eUniformTexelBuffer)
|
||||
{
|
||||
return limits.minTexelBufferOffsetAlignment;
|
||||
}
|
||||
else if (usage == vk::BufferUsageFlagBits::eIndexBuffer || usage == vk::BufferUsageFlagBits::eVertexBuffer ||
|
||||
usage == vk::BufferUsageFlagBits::eIndirectBuffer)
|
||||
{
|
||||
// Used to calculate the offset, required when allocating memory (its value should be power of 2)
|
||||
return 16;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw std::runtime_error("Usage not recognised");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief A pool of buffer blocks for a specific usage.
|
||||
* It may contain inactive blocks that can be recycled.
|
||||
*
|
||||
* BufferPool is a linear allocator for buffer chunks, it gives you a view of the size you want.
|
||||
* A BufferBlock is the corresponding VkBuffer and you can get smaller offsets inside it.
|
||||
* Since a shader cannot specify dynamic UBOs, it has to be done from the code
|
||||
* (set_resource_dynamic).
|
||||
*
|
||||
* When a new frame starts, buffer blocks are returned: the offset is reset and contents are
|
||||
* overwritten. The minimum allocation size is 256 kb, if you ask for more you get a dedicated
|
||||
* buffer allocation.
|
||||
*
|
||||
* We re-use descriptor sets: we only need one for the corresponding buffer infos (and we only
|
||||
* have one VkBuffer per BufferBlock), then it is bound and we use dynamic offsets.
|
||||
*/
|
||||
template <vkb::BindingType bindingType>
|
||||
class BufferPool
|
||||
{
|
||||
public:
|
||||
using BufferUsageFlagsType = typename std::conditional<bindingType == vkb::BindingType::Cpp, vk::BufferUsageFlags, VkBufferUsageFlags>::type;
|
||||
using DeviceSizeType = typename std::conditional<bindingType == vkb::BindingType::Cpp, vk::DeviceSize, VkDeviceSize>::type;
|
||||
|
||||
public:
|
||||
BufferPool(
|
||||
vkb::core::Device<bindingType> &device, DeviceSizeType block_size, BufferUsageFlagsType usage, VmaMemoryUsage memory_usage = VMA_MEMORY_USAGE_CPU_TO_GPU);
|
||||
|
||||
BufferBlock<bindingType> &request_buffer_block(DeviceSizeType minimum_size, bool minimal = false);
|
||||
|
||||
void reset();
|
||||
|
||||
private:
|
||||
vkb::core::DeviceCpp &device;
|
||||
std::vector<std::unique_ptr<BufferBlockCpp>> buffer_blocks; /// List of blocks requested (need to be pointers in order to keep their address constant on vector resizing)
|
||||
vk::DeviceSize block_size = 0; /// Minimum size of the blocks
|
||||
vk::BufferUsageFlags usage;
|
||||
VmaMemoryUsage memory_usage{};
|
||||
};
|
||||
|
||||
using BufferPoolC = BufferPool<vkb::BindingType::C>;
|
||||
using BufferPoolCpp = BufferPool<vkb::BindingType::Cpp>;
|
||||
|
||||
template <vkb::BindingType bindingType>
|
||||
BufferPool<bindingType>::BufferPool(vkb::core::Device<bindingType> &device,
|
||||
DeviceSizeType block_size,
|
||||
BufferUsageFlagsType usage,
|
||||
VmaMemoryUsage memory_usage) :
|
||||
device{reinterpret_cast<vkb::core::DeviceCpp &>(device)}, block_size{block_size}, usage{usage}, memory_usage{memory_usage}
|
||||
{
|
||||
}
|
||||
|
||||
template <vkb::BindingType bindingType>
|
||||
BufferBlock<bindingType> &BufferPool<bindingType>::request_buffer_block(DeviceSizeType minimum_size, bool minimal)
|
||||
{
|
||||
// Find a block in the range of the blocks which can fit the minimum size
|
||||
auto it = minimal ? std::ranges::find_if(buffer_blocks,
|
||||
[&minimum_size](auto const &buffer_block) { return (buffer_block->get_size() == minimum_size) && buffer_block->can_allocate(minimum_size); }) :
|
||||
std::ranges::find_if(buffer_blocks,
|
||||
[&minimum_size](auto const &buffer_block) { return buffer_block->can_allocate(minimum_size); });
|
||||
|
||||
if (it == buffer_blocks.end())
|
||||
{
|
||||
LOGD("Building #{} buffer block ({})", buffer_blocks.size(), vk::to_string(usage));
|
||||
|
||||
vk::DeviceSize new_block_size = minimal ? minimum_size : std::max(block_size, minimum_size);
|
||||
|
||||
// Create a new block and get the iterator on it
|
||||
it = buffer_blocks.emplace(buffer_blocks.end(), std::make_unique<BufferBlockCpp>(device, new_block_size, usage, memory_usage));
|
||||
}
|
||||
|
||||
if constexpr (bindingType == vkb::BindingType::Cpp)
|
||||
{
|
||||
return *it->get();
|
||||
}
|
||||
else
|
||||
{
|
||||
return reinterpret_cast<BufferBlockC &>(*it->get());
|
||||
}
|
||||
}
|
||||
|
||||
template <vkb::BindingType bindingType>
|
||||
void BufferPool<bindingType>::reset()
|
||||
{
|
||||
// Attention: Resetting the BufferPool is not supposed to clear the BufferBlocks, but just reset them!
|
||||
// The actual VkBuffers are used to hash the DescriptorSet in RenderFrame::request_descriptor_set.
|
||||
// Don't know (for now) how that works with resetted buffers!
|
||||
for (auto &buffer_block : buffer_blocks)
|
||||
{
|
||||
buffer_block->reset();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace vkb
|
||||
@@ -0,0 +1,263 @@
|
||||
/* Copyright (c) 2021-2025, NVIDIA CORPORATION. All rights reserved.
|
||||
* Copyright (c) 2025, Bradley Austin Davis. All rights reserved.
|
||||
*
|
||||
* 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 "common/vk_common.h"
|
||||
#include "vulkan_type_mapping.h"
|
||||
#include <vulkan/vulkan.hpp>
|
||||
|
||||
namespace vkb
|
||||
{
|
||||
|
||||
namespace allocated
|
||||
{
|
||||
|
||||
/**
|
||||
* @brief Many Vulkan resource types (most notably Images and to a lesser extent Buffers)
|
||||
* and and their corresponding memory allocations have many parameters that need to be setup
|
||||
* when creating them. Although many of these have reasonable defaults, constructors with
|
||||
* numerous arguments, some or all of which may have default arguments, aren't well suited
|
||||
* to partial customization. This is a common failing of languages that don't support named
|
||||
* arguments and has led to the common use of the [builder pattern](https://en.wikipedia.org/wiki/Builder_pattern),
|
||||
* where a helper class is used to store all the options that can be tweaked for an object
|
||||
* when it's created. A builder class will have reasonable defaults where appropriate and only
|
||||
* require arguments for the builder constructor when a value is always required for creation to occur
|
||||
* (for example, the size of a buffer or the extent of an image). Remaining parameters can be set
|
||||
* with methods on the builder class, which return a reference to the builder object, allowing
|
||||
* chaining of the method calls.
|
||||
*
|
||||
* This builder class serves as a base containing options that are common to all
|
||||
* [VMA](https://gpuopen.com/vulkan-memory-allocator/) allocated and managed resources.
|
||||
* For instance, the VMA create and usage flags are set here, but the image or buffer
|
||||
* usage flags are handled in the derived builder classes specific to those types.
|
||||
*
|
||||
* The following is an example of how the builder pattern is used in the codebase:
|
||||
```cpp
|
||||
vkb::core::ImageBuilder(VkExtent3D{grid_width, grid_height, 1})
|
||||
.with_format(VK_FORMAT_R8G8B8A8_UNORM)
|
||||
.with_usage(VK_IMAGE_USAGE_STORAGE_BIT | VK_IMAGE_USAGE_SAMPLED_BIT)
|
||||
.with_vma_usage(VMA_MEMORY_USAGE_GPU_ONLY)
|
||||
.with_sample_count(VK_SAMPLE_COUNT_1_BIT)
|
||||
.with_mip_levels(1)
|
||||
.with_array_layers(1)
|
||||
.with_tiling(VK_IMAGE_TILING_OPTIMAL)
|
||||
.with_queue_families(static_cast<uint32_t>(queue_families.size()), queue_families.data())
|
||||
.with_sharing_mode(sharing_mode));
|
||||
```
|
||||
* The actual image can be created with `build()` which returns a `vkb::core::Image` or `buildPtr` which returns a `std::unique_ptr<vkb::core::Image>`.
|
||||
* Alternatively, the builder can be used as an argument to the `Image` constructor, which will build the image for you in place.
|
||||
* @note The builder pattern is intended to displace the currently used `vkb::core::Image` and `vkb::core::Buffer` constructors with numerous
|
||||
* arguments, but this is a work in progress and not currently in wide use in the codebase.
|
||||
*
|
||||
* @tparam BuilderType Allow the same builder base class to be used
|
||||
* with a variety of subclasses while using casting to return the corect dervied type
|
||||
* from the modifier methods.
|
||||
* @tparam bindingType A flag indicating whether this is being used with the C or C++ API
|
||||
* @tparam CreateInfoType The type of the Vulkan create info structure. Either a `VkSomethingCreateInfo`
|
||||
* or `vk::SomethingCreateInfo` for the C or C++ API respectively.
|
||||
*/
|
||||
template <vkb::BindingType bindingType, typename BuilderType, typename CreateInfoType>
|
||||
class BuilderBase
|
||||
{
|
||||
public:
|
||||
using MemoryPropertyFlagsType = typename std::conditional<bindingType == vkb::BindingType::Cpp, vk::MemoryPropertyFlags, VkMemoryPropertyFlags>::type;
|
||||
using SharingModeType = typename std::conditional<bindingType == vkb::BindingType::Cpp, vk::SharingMode, VkSharingMode>::type;
|
||||
|
||||
public:
|
||||
VmaAllocationCreateInfo const &get_allocation_create_info() const;
|
||||
CreateInfoType const &get_create_info() const;
|
||||
std::string const &get_debug_name() const;
|
||||
BuilderType &with_debug_name(const std::string &name);
|
||||
BuilderType &with_implicit_sharing_mode();
|
||||
BuilderType &with_memory_type_bits(uint32_t type_bits);
|
||||
BuilderType &with_queue_families(uint32_t count, const uint32_t *family_indices);
|
||||
BuilderType &with_queue_families(std::vector<uint32_t> const &queue_families);
|
||||
BuilderType &with_sharing_mode(SharingModeType sharing_mode);
|
||||
BuilderType &with_vma_flags(VmaAllocationCreateFlags flags);
|
||||
BuilderType &with_vma_pool(VmaPool pool);
|
||||
BuilderType &with_vma_preferred_flags(MemoryPropertyFlagsType flags);
|
||||
BuilderType &with_vma_required_flags(MemoryPropertyFlagsType flags);
|
||||
BuilderType &with_vma_usage(VmaMemoryUsage usage);
|
||||
|
||||
protected:
|
||||
BuilderBase(const BuilderBase &other) = delete;
|
||||
BuilderBase(const CreateInfoType &create_info);
|
||||
|
||||
CreateInfoType &get_create_info();
|
||||
|
||||
private:
|
||||
// we always want to store a vk::CreateInfo, so we have to figure out that type, depending on the BindingType!
|
||||
using HPPCreateInfoType = typename vkb::VulkanTypeMapping<bindingType, CreateInfoType>::Type;
|
||||
|
||||
protected:
|
||||
VmaAllocationCreateInfo alloc_create_info = {};
|
||||
HPPCreateInfoType create_info = {};
|
||||
std::string debug_name = {};
|
||||
};
|
||||
|
||||
template <typename BuilderType, typename CreateInfoType>
|
||||
using BuilderBaseC = BuilderBase<vkb::BindingType::C, BuilderType, CreateInfoType>;
|
||||
template <typename BuilderType, typename CreateInfoType>
|
||||
using BuilderBaseCpp = BuilderBase<vkb::BindingType::Cpp, BuilderType, CreateInfoType>;
|
||||
|
||||
template <vkb::BindingType bindingType, typename BuilderType, typename CreateInfoType>
|
||||
inline BuilderBase<bindingType, BuilderType, CreateInfoType>::BuilderBase(const CreateInfoType &create_info_) :
|
||||
create_info{reinterpret_cast<HPPCreateInfoType const &>(create_info_)}
|
||||
{
|
||||
alloc_create_info.usage = VMA_MEMORY_USAGE_AUTO;
|
||||
};
|
||||
|
||||
template <vkb::BindingType bindingType, typename BuilderType, typename CreateInfoType>
|
||||
inline VmaAllocationCreateInfo const &BuilderBase<bindingType, BuilderType, CreateInfoType>::get_allocation_create_info() const
|
||||
{
|
||||
return alloc_create_info;
|
||||
}
|
||||
|
||||
template <vkb::BindingType bindingType, typename BuilderType, typename CreateInfoType>
|
||||
inline CreateInfoType const &BuilderBase<bindingType, BuilderType, CreateInfoType>::get_create_info() const
|
||||
{
|
||||
if constexpr (bindingType == vkb::BindingType::Cpp)
|
||||
{
|
||||
return create_info;
|
||||
}
|
||||
else
|
||||
{
|
||||
return *reinterpret_cast<typename HPPCreateInfoType::NativeType const *>(&create_info);
|
||||
}
|
||||
}
|
||||
|
||||
template <vkb::BindingType bindingType, typename BuilderType, typename CreateInfoType>
|
||||
inline CreateInfoType &BuilderBase<bindingType, BuilderType, CreateInfoType>::get_create_info()
|
||||
{
|
||||
if constexpr (bindingType == vkb::BindingType::Cpp)
|
||||
{
|
||||
return create_info;
|
||||
}
|
||||
else
|
||||
{
|
||||
return *reinterpret_cast<typename HPPCreateInfoType::NativeType *>(&create_info);
|
||||
}
|
||||
}
|
||||
|
||||
template <vkb::BindingType bindingType, typename BuilderType, typename CreateInfoType>
|
||||
inline std::string const &BuilderBase<bindingType, BuilderType, CreateInfoType>::get_debug_name() const
|
||||
{
|
||||
return debug_name;
|
||||
}
|
||||
|
||||
template <vkb::BindingType bindingType, typename BuilderType, typename CreateInfoType>
|
||||
inline BuilderType &BuilderBase<bindingType, BuilderType, CreateInfoType>::with_debug_name(const std::string &name)
|
||||
{
|
||||
debug_name = name;
|
||||
return *static_cast<BuilderType *>(this);
|
||||
}
|
||||
|
||||
template <vkb::BindingType bindingType, typename BuilderType, typename CreateInfoType>
|
||||
inline BuilderType &BuilderBase<bindingType, BuilderType, CreateInfoType>::with_implicit_sharing_mode()
|
||||
{
|
||||
create_info.sharingMode = (1 < create_info.queueFamilyIndexCount) ? vk::SharingMode::eConcurrent : vk::SharingMode::eExclusive;
|
||||
return *static_cast<BuilderType *>(this);
|
||||
}
|
||||
|
||||
template <vkb::BindingType bindingType, typename BuilderType, typename CreateInfoType>
|
||||
inline BuilderType &BuilderBase<bindingType, BuilderType, CreateInfoType>::with_memory_type_bits(uint32_t type_bits)
|
||||
{
|
||||
alloc_create_info.memoryTypeBits = type_bits;
|
||||
return *static_cast<BuilderType *>(this);
|
||||
}
|
||||
|
||||
template <vkb::BindingType bindingType, typename BuilderType, typename CreateInfoType>
|
||||
inline BuilderType &BuilderBase<bindingType, BuilderType, CreateInfoType>::with_queue_families(uint32_t count, const uint32_t *family_indices)
|
||||
{
|
||||
create_info.queueFamilyIndexCount = count;
|
||||
create_info.pQueueFamilyIndices = family_indices;
|
||||
return *static_cast<BuilderType *>(this);
|
||||
}
|
||||
|
||||
template <vkb::BindingType bindingType, typename BuilderType, typename CreateInfoType>
|
||||
inline BuilderType &BuilderBase<bindingType, BuilderType, CreateInfoType>::with_queue_families(std::vector<uint32_t> const &queue_families)
|
||||
{
|
||||
return with_queue_families(static_cast<uint32_t>(queue_families.size()), queue_families.data());
|
||||
}
|
||||
|
||||
template <vkb::BindingType bindingType, typename BuilderType, typename CreateInfoType>
|
||||
inline BuilderType &BuilderBase<bindingType, BuilderType, CreateInfoType>::with_sharing_mode(SharingModeType sharing_mode)
|
||||
{
|
||||
if constexpr (bindingType == vkb::BindingType::Cpp)
|
||||
{
|
||||
create_info.sharingMode = sharing_mode;
|
||||
}
|
||||
else
|
||||
{
|
||||
create_info.sharingMode = static_cast<vk::SharingMode>(sharing_mode);
|
||||
}
|
||||
return *static_cast<BuilderType *>(this);
|
||||
}
|
||||
|
||||
template <vkb::BindingType bindingType, typename BuilderType, typename CreateInfoType>
|
||||
inline BuilderType &BuilderBase<bindingType, BuilderType, CreateInfoType>::with_vma_flags(VmaAllocationCreateFlags flags)
|
||||
{
|
||||
alloc_create_info.flags = flags;
|
||||
return *static_cast<BuilderType *>(this);
|
||||
}
|
||||
|
||||
template <vkb::BindingType bindingType, typename BuilderType, typename CreateInfoType>
|
||||
inline BuilderType &BuilderBase<bindingType, BuilderType, CreateInfoType>::with_vma_pool(VmaPool pool)
|
||||
{
|
||||
alloc_create_info.pool = pool;
|
||||
return *static_cast<BuilderType *>(this);
|
||||
}
|
||||
|
||||
template <vkb::BindingType bindingType, typename BuilderType, typename CreateInfoType>
|
||||
inline BuilderType &BuilderBase<bindingType, BuilderType, CreateInfoType>::with_vma_preferred_flags(MemoryPropertyFlagsType flags)
|
||||
{
|
||||
if constexpr (bindingType == vkb::BindingType::Cpp)
|
||||
{
|
||||
alloc_create_info.preferredFlags = static_cast<VkMemoryPropertyFlags>(flags);
|
||||
}
|
||||
else
|
||||
{
|
||||
alloc_create_info.preferredFlags = flags;
|
||||
}
|
||||
return *static_cast<BuilderType *>(this);
|
||||
}
|
||||
|
||||
template <vkb::BindingType bindingType, typename BuilderType, typename CreateInfoType>
|
||||
inline BuilderType &BuilderBase<bindingType, BuilderType, CreateInfoType>::with_vma_required_flags(MemoryPropertyFlagsType flags)
|
||||
{
|
||||
if constexpr (bindingType == vkb::BindingType::Cpp)
|
||||
{
|
||||
alloc_create_info.requiredFlags = static_cast<VkMemoryPropertyFlags>(flags);
|
||||
}
|
||||
else
|
||||
{
|
||||
alloc_create_info.requiredFlags = flags;
|
||||
}
|
||||
return *static_cast<BuilderType *>(this);
|
||||
}
|
||||
|
||||
template <vkb::BindingType bindingType, typename BuilderType, typename CreateInfoType>
|
||||
inline BuilderType &BuilderBase<bindingType, BuilderType, CreateInfoType>::with_vma_usage(VmaMemoryUsage usage)
|
||||
{
|
||||
alloc_create_info.usage = usage;
|
||||
return *static_cast<BuilderType *>(this);
|
||||
}
|
||||
|
||||
} // namespace allocated
|
||||
} // namespace vkb
|
||||
@@ -0,0 +1,93 @@
|
||||
/* Copyright (c) 2019-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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/error.h"
|
||||
|
||||
#include "common/glm_common.h"
|
||||
#include <glm/gtc/matrix_transform.hpp>
|
||||
#include <glm/gtc/quaternion.hpp>
|
||||
|
||||
namespace vkb
|
||||
{
|
||||
enum CameraType
|
||||
{
|
||||
LookAt,
|
||||
FirstPerson
|
||||
};
|
||||
|
||||
class Camera
|
||||
{
|
||||
public:
|
||||
void update(float deltaTime);
|
||||
|
||||
// Update camera passing separate axis data (gamepad)
|
||||
// Returns true if view or position has been changed
|
||||
bool update_gamepad(glm::vec2 axis_left, glm::vec2 axis_right, float delta_time);
|
||||
|
||||
CameraType type = CameraType::LookAt;
|
||||
|
||||
glm::vec3 rotation = glm::vec3();
|
||||
glm::vec3 position = glm::vec3();
|
||||
|
||||
float rotation_speed = 1.0f;
|
||||
float translation_speed = 1.0f;
|
||||
|
||||
bool updated = false;
|
||||
|
||||
struct
|
||||
{
|
||||
glm::mat4 perspective;
|
||||
glm::mat4 view;
|
||||
} matrices;
|
||||
|
||||
struct
|
||||
{
|
||||
bool left = false;
|
||||
bool right = false;
|
||||
bool up = false;
|
||||
bool down = false;
|
||||
} keys;
|
||||
|
||||
bool moving();
|
||||
|
||||
float get_near_clip();
|
||||
|
||||
float get_far_clip();
|
||||
|
||||
void set_perspective(float fov, float aspect, float znear, float zfar);
|
||||
|
||||
void update_aspect_ratio(float aspect);
|
||||
|
||||
void set_position(const glm::vec3 &position);
|
||||
|
||||
void set_rotation(const glm::vec3 &rotation);
|
||||
|
||||
void rotate(const glm::vec3 &delta);
|
||||
|
||||
void set_translation(const glm::vec3 &translation);
|
||||
|
||||
void translate(const glm::vec3 &delta);
|
||||
|
||||
private:
|
||||
float fov;
|
||||
float znear, zfar;
|
||||
|
||||
void update_view_matrix();
|
||||
};
|
||||
} // namespace vkb
|
||||
@@ -0,0 +1,203 @@
|
||||
/* Copyright (c) 2019-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.
|
||||
*/
|
||||
|
||||
#include "camera.h"
|
||||
|
||||
namespace vkb
|
||||
{
|
||||
void Camera::update_view_matrix()
|
||||
{
|
||||
glm::mat4 rotation_matrix = glm::mat4(1.0f);
|
||||
glm::mat4 transformation_matrix;
|
||||
|
||||
rotation_matrix = glm::rotate(rotation_matrix, glm::radians(rotation.x), glm::vec3(1.0f, 0.0f, 0.0f));
|
||||
rotation_matrix = glm::rotate(rotation_matrix, glm::radians(rotation.y), glm::vec3(0.0f, 1.0f, 0.0f));
|
||||
rotation_matrix = glm::rotate(rotation_matrix, glm::radians(rotation.z), glm::vec3(0.0f, 0.0f, 1.0f));
|
||||
|
||||
transformation_matrix = glm::translate(glm::mat4(1.0f), position);
|
||||
|
||||
if (type == CameraType::FirstPerson)
|
||||
{
|
||||
matrices.view = rotation_matrix * transformation_matrix;
|
||||
}
|
||||
else
|
||||
{
|
||||
matrices.view = transformation_matrix * rotation_matrix;
|
||||
}
|
||||
|
||||
updated = true;
|
||||
}
|
||||
|
||||
bool Camera::moving()
|
||||
{
|
||||
return keys.left || keys.right || keys.up || keys.down;
|
||||
}
|
||||
|
||||
float Camera::get_near_clip()
|
||||
{
|
||||
return znear;
|
||||
}
|
||||
|
||||
float Camera::get_far_clip()
|
||||
{
|
||||
return zfar;
|
||||
}
|
||||
|
||||
void Camera::set_perspective(float fov, float aspect, float znear, float zfar)
|
||||
{
|
||||
this->fov = fov;
|
||||
this->znear = znear;
|
||||
this->zfar = zfar;
|
||||
matrices.perspective = glm::perspective(glm::radians(fov), aspect, znear, zfar);
|
||||
updated = true;
|
||||
}
|
||||
|
||||
void Camera::update_aspect_ratio(float aspect)
|
||||
{
|
||||
matrices.perspective = glm::perspective(glm::radians(fov), aspect, znear, zfar);
|
||||
updated = true;
|
||||
}
|
||||
|
||||
void Camera::set_position(const glm::vec3 &position)
|
||||
{
|
||||
this->position = position;
|
||||
update_view_matrix();
|
||||
}
|
||||
|
||||
void Camera::set_rotation(const glm::vec3 &rotation)
|
||||
{
|
||||
this->rotation = rotation;
|
||||
update_view_matrix();
|
||||
}
|
||||
|
||||
void Camera::rotate(const glm::vec3 &delta)
|
||||
{
|
||||
this->rotation += delta;
|
||||
update_view_matrix();
|
||||
}
|
||||
|
||||
void Camera::set_translation(const glm::vec3 &translation)
|
||||
{
|
||||
this->position = translation;
|
||||
update_view_matrix();
|
||||
}
|
||||
|
||||
void Camera::translate(const glm::vec3 &delta)
|
||||
{
|
||||
this->position += delta;
|
||||
update_view_matrix();
|
||||
}
|
||||
|
||||
void Camera::update(float deltaTime)
|
||||
{
|
||||
updated = false;
|
||||
if (type == CameraType::FirstPerson)
|
||||
{
|
||||
if (moving())
|
||||
{
|
||||
glm::vec3 front;
|
||||
front.x = -cos(glm::radians(rotation.x)) * sin(glm::radians(rotation.y));
|
||||
front.y = sin(glm::radians(rotation.x));
|
||||
front.z = cos(glm::radians(rotation.x)) * cos(glm::radians(rotation.y));
|
||||
front = glm::normalize(front);
|
||||
|
||||
float move_speed = deltaTime * translation_speed;
|
||||
|
||||
if (keys.up)
|
||||
{
|
||||
position += front * move_speed;
|
||||
}
|
||||
if (keys.down)
|
||||
{
|
||||
position -= front * move_speed;
|
||||
}
|
||||
if (keys.left)
|
||||
{
|
||||
position -= glm::normalize(glm::cross(front, glm::vec3(0.0f, 1.0f, 0.0f))) * move_speed;
|
||||
}
|
||||
if (keys.right)
|
||||
{
|
||||
position += glm::normalize(glm::cross(front, glm::vec3(0.0f, 1.0f, 0.0f))) * move_speed;
|
||||
}
|
||||
|
||||
update_view_matrix();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool Camera::update_gamepad(glm::vec2 axis_left, glm::vec2 axis_right, float delta_time)
|
||||
{
|
||||
bool changed = false;
|
||||
|
||||
if (type == CameraType::FirstPerson)
|
||||
{
|
||||
// Use the common console thumbstick layout
|
||||
// Left = view, right = move
|
||||
|
||||
const float dead_zone = 0.0015f;
|
||||
const float range = 1.0f - dead_zone;
|
||||
|
||||
glm::vec3 front;
|
||||
front.x = -cos(glm::radians(rotation.x)) * sin(glm::radians(rotation.y));
|
||||
front.y = sin(glm::radians(rotation.x));
|
||||
front.z = cos(glm::radians(rotation.x)) * cos(glm::radians(rotation.y));
|
||||
front = glm::normalize(front);
|
||||
|
||||
float move_speed = delta_time * translation_speed * 2.0f;
|
||||
float new_rotation_speed = delta_time * rotation_speed * 50.0f;
|
||||
|
||||
// Move
|
||||
if (fabsf(axis_left.y) > dead_zone)
|
||||
{
|
||||
float pos = (fabsf(axis_left.y) - dead_zone) / range;
|
||||
position -= front * pos * ((axis_left.y < 0.0f) ? -1.0f : 1.0f) * move_speed;
|
||||
changed = true;
|
||||
}
|
||||
if (fabsf(axis_left.x) > dead_zone)
|
||||
{
|
||||
float pos = (fabsf(axis_left.x) - dead_zone) / range;
|
||||
position += glm::normalize(glm::cross(front, glm::vec3(0.0f, 1.0f, 0.0f))) * pos * ((axis_left.x < 0.0f) ? -1.0f : 1.0f) * move_speed;
|
||||
changed = true;
|
||||
}
|
||||
|
||||
// Rotate
|
||||
if (fabsf(axis_right.x) > dead_zone)
|
||||
{
|
||||
float pos = (fabsf(axis_right.x) - dead_zone) / range;
|
||||
rotation.y += pos * ((axis_right.x < 0.0f) ? -1.0f : 1.0f) * new_rotation_speed;
|
||||
changed = true;
|
||||
}
|
||||
if (fabsf(axis_right.y) > dead_zone)
|
||||
{
|
||||
float pos = (fabsf(axis_right.y) - dead_zone) / range;
|
||||
rotation.x -= pos * ((axis_right.y < 0.0f) ? -1.0f : 1.0f) * new_rotation_speed;
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// todo: move code from example base class for look-at
|
||||
}
|
||||
|
||||
if (changed)
|
||||
{
|
||||
update_view_matrix();
|
||||
}
|
||||
|
||||
return changed;
|
||||
}
|
||||
} // namespace vkb
|
||||
@@ -0,0 +1,35 @@
|
||||
/* Copyright (c) 2018-2020, 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 "error.h"
|
||||
|
||||
#include "helpers.h"
|
||||
|
||||
namespace vkb
|
||||
{
|
||||
VulkanException::VulkanException(const VkResult result, const std::string &msg) :
|
||||
result{result},
|
||||
std::runtime_error{msg}
|
||||
{
|
||||
error_message = std::string(std::runtime_error::what()) + std::string{" : "} + to_string(result);
|
||||
}
|
||||
|
||||
const char *VulkanException::what() const noexcept
|
||||
{
|
||||
return error_message.c_str();
|
||||
}
|
||||
} // namespace vkb
|
||||
@@ -0,0 +1,75 @@
|
||||
/* Copyright (c) 2018-2024, 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 <cassert>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
|
||||
#include "core/util/error.hpp"
|
||||
|
||||
#include "common/strings.h"
|
||||
#include "core/util/logging.hpp"
|
||||
#include "vk_common.h"
|
||||
|
||||
namespace vkb
|
||||
{
|
||||
/**
|
||||
* @brief Vulkan exception structure
|
||||
*/
|
||||
class VulkanException : public std::runtime_error
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief Vulkan exception constructor
|
||||
*/
|
||||
VulkanException(VkResult result, const std::string &msg = "Vulkan error");
|
||||
|
||||
/**
|
||||
* @brief Returns the Vulkan error code as string
|
||||
* @return String message of exception
|
||||
*/
|
||||
const char *what() const noexcept override;
|
||||
|
||||
VkResult result;
|
||||
|
||||
private:
|
||||
std::string error_message;
|
||||
};
|
||||
} // namespace vkb
|
||||
|
||||
/// @brief Helper macro to test the result of Vulkan calls which can return an error.
|
||||
#define VK_CHECK(x) \
|
||||
do \
|
||||
{ \
|
||||
VkResult err = x; \
|
||||
if (err) \
|
||||
{ \
|
||||
throw std::runtime_error("Detected Vulkan error: " + vkb::to_string(err)); \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define ASSERT_VK_HANDLE(handle) \
|
||||
do \
|
||||
{ \
|
||||
if ((handle) == VK_NULL_HANDLE) \
|
||||
{ \
|
||||
LOGE("Handle is NULL"); \
|
||||
abort(); \
|
||||
} \
|
||||
} while (0)
|
||||
@@ -0,0 +1,28 @@
|
||||
/* Copyright (c) 2019, 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
|
||||
|
||||
#ifndef GLM_FORCE_RADIANS
|
||||
# define GLM_FORCE_RADIANS
|
||||
#endif
|
||||
#ifndef GLM_FORCE_DEPTH_ZERO_TO_ONE
|
||||
# define GLM_FORCE_DEPTH_ZERO_TO_ONE
|
||||
#endif
|
||||
#include <glm/glm.hpp>
|
||||
#include <glm/gtx/rotate_vector.hpp>
|
||||
#include <glm/gtx/transform.hpp>
|
||||
@@ -0,0 +1,215 @@
|
||||
/* Copyright (c) 2018-2024, 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 <algorithm>
|
||||
#include <array>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <fstream>
|
||||
#include <functional>
|
||||
#include <iterator>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <queue>
|
||||
#include <set>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
|
||||
#include "common/error.h"
|
||||
|
||||
#include "common/glm_common.h"
|
||||
#include <glm/gtx/hash.hpp>
|
||||
|
||||
namespace vkb
|
||||
{
|
||||
template <typename T>
|
||||
inline void read(std::istringstream &is, T &value)
|
||||
{
|
||||
is.read(reinterpret_cast<char *>(&value), sizeof(T));
|
||||
}
|
||||
|
||||
inline void read(std::istringstream &is, std::string &value)
|
||||
{
|
||||
std::size_t size;
|
||||
read(is, size);
|
||||
value.resize(size);
|
||||
is.read(const_cast<char *>(value.data()), size);
|
||||
}
|
||||
|
||||
template <class T>
|
||||
inline void read(std::istringstream &is, std::set<T> &value)
|
||||
{
|
||||
std::size_t size;
|
||||
read(is, size);
|
||||
for (uint32_t i = 0; i < size; i++)
|
||||
{
|
||||
T item;
|
||||
is.read(reinterpret_cast<char *>(&item), sizeof(T));
|
||||
value.insert(std::move(item));
|
||||
}
|
||||
}
|
||||
|
||||
template <class T>
|
||||
inline void read(std::istringstream &is, std::vector<T> &value)
|
||||
{
|
||||
std::size_t size;
|
||||
read(is, size);
|
||||
value.resize(size);
|
||||
is.read(reinterpret_cast<char *>(value.data()), value.size() * sizeof(T));
|
||||
}
|
||||
|
||||
template <class T, class S>
|
||||
inline void read(std::istringstream &is, std::map<T, S> &value)
|
||||
{
|
||||
std::size_t size;
|
||||
read(is, size);
|
||||
|
||||
for (uint32_t i = 0; i < size; i++)
|
||||
{
|
||||
std::pair<T, S> item;
|
||||
read(is, item.first);
|
||||
read(is, item.second);
|
||||
|
||||
value.insert(std::move(item));
|
||||
}
|
||||
}
|
||||
|
||||
template <class T, uint32_t N>
|
||||
inline void read(std::istringstream &is, std::array<T, N> &value)
|
||||
{
|
||||
is.read(reinterpret_cast<char *>(value.data()), N * sizeof(T));
|
||||
}
|
||||
|
||||
template <typename T, typename... Args>
|
||||
inline void read(std::istringstream &is, T &first_arg, Args &...args)
|
||||
{
|
||||
read(is, first_arg);
|
||||
|
||||
read(is, args...);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline void write(std::ostringstream &os, const T &value)
|
||||
{
|
||||
os.write(reinterpret_cast<const char *>(&value), sizeof(T));
|
||||
}
|
||||
|
||||
inline void write(std::ostringstream &os, const std::string &value)
|
||||
{
|
||||
write(os, value.size());
|
||||
os.write(value.data(), value.size());
|
||||
}
|
||||
|
||||
template <class T>
|
||||
inline void write(std::ostringstream &os, const std::set<T> &value)
|
||||
{
|
||||
write(os, value.size());
|
||||
for (const T &item : value)
|
||||
{
|
||||
os.write(reinterpret_cast<const char *>(&item), sizeof(T));
|
||||
}
|
||||
}
|
||||
|
||||
template <class T>
|
||||
inline void write(std::ostringstream &os, const std::vector<T> &value)
|
||||
{
|
||||
write(os, value.size());
|
||||
os.write(reinterpret_cast<const char *>(value.data()), value.size() * sizeof(T));
|
||||
}
|
||||
|
||||
template <class T, class S>
|
||||
inline void write(std::ostringstream &os, const std::map<T, S> &value)
|
||||
{
|
||||
write(os, value.size());
|
||||
|
||||
for (const std::pair<T, S> &item : value)
|
||||
{
|
||||
write(os, item.first);
|
||||
write(os, item.second);
|
||||
}
|
||||
}
|
||||
|
||||
template <class T, uint32_t N>
|
||||
inline void write(std::ostringstream &os, const std::array<T, N> &value)
|
||||
{
|
||||
os.write(reinterpret_cast<const char *>(value.data()), N * sizeof(T));
|
||||
}
|
||||
|
||||
template <typename T, typename... Args>
|
||||
inline void write(std::ostringstream &os, const T &first_arg, const Args &...args)
|
||||
{
|
||||
write(os, first_arg);
|
||||
|
||||
write(os, args...);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Helper function to combine a given hash
|
||||
* with a generated hash for the input param.
|
||||
*/
|
||||
template <class T>
|
||||
inline void hash_combine(size_t &seed, const T &v)
|
||||
{
|
||||
std::hash<T> hasher;
|
||||
glm::detail::hash_combine(seed, hasher(v));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Helper function to convert a data type
|
||||
* to string using output stream operator.
|
||||
* @param value The object to be converted to string
|
||||
* @return String version of the given object
|
||||
*/
|
||||
template <class T>
|
||||
inline std::string to_string(const T &value)
|
||||
{
|
||||
std::stringstream ss;
|
||||
ss << std::fixed << value;
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Helper function to check size_t is correctly converted to uint32_t
|
||||
* @param value Value of type size_t to convert
|
||||
* @return An uint32_t representation of the same value
|
||||
*/
|
||||
template <class T>
|
||||
uint32_t to_u32(T value)
|
||||
{
|
||||
static_assert(std::is_arithmetic<T>::value, "T must be numeric");
|
||||
|
||||
if (static_cast<uintmax_t>(value) > static_cast<uintmax_t>(std::numeric_limits<uint32_t>::max()))
|
||||
{
|
||||
throw std::runtime_error("to_u32() failed, value is too big to be converted to uint32_t");
|
||||
}
|
||||
|
||||
return static_cast<uint32_t>(value);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
inline std::vector<uint8_t> to_bytes(const T &value)
|
||||
{
|
||||
return std::vector<uint8_t>{reinterpret_cast<const uint8_t *>(&value),
|
||||
reinterpret_cast<const uint8_t *>(&value) + sizeof(T)};
|
||||
}
|
||||
|
||||
} // namespace vkb
|
||||
@@ -0,0 +1,41 @@
|
||||
/* Copyright (c) 2022-2024, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* 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 <common/error.h>
|
||||
|
||||
#include <vulkan/vulkan.hpp>
|
||||
|
||||
namespace vkb
|
||||
{
|
||||
namespace common
|
||||
{
|
||||
/**
|
||||
* @brief facade class around vkb::VulkanException, providing a vulkan.hpp-based interface
|
||||
*
|
||||
* See vkb::VulkanException for documentation
|
||||
*/
|
||||
class HPPVulkanException : public vkb::VulkanException
|
||||
{
|
||||
public:
|
||||
HPPVulkanException(vk::Result result, std::string const &msg = "Vulkan error") :
|
||||
vkb::VulkanException(static_cast<VkResult>(result), msg)
|
||||
{}
|
||||
};
|
||||
} // namespace common
|
||||
} // namespace vkb
|
||||
@@ -0,0 +1,414 @@
|
||||
/* Copyright (c) 2023-2025, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* 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 "common/hpp_vk_common.h"
|
||||
#include "core/hpp_descriptor_set.h"
|
||||
#include "core/hpp_image_view.h"
|
||||
#include "core/hpp_render_pass.h"
|
||||
#include "core/hpp_shader_module.h"
|
||||
#include "hpp_resource_record.h"
|
||||
#include "rendering/hpp_render_target.h"
|
||||
#include "resource_caching.h"
|
||||
#include <vulkan/vulkan_hash.hpp>
|
||||
|
||||
namespace std
|
||||
{
|
||||
template <typename Key, typename Value>
|
||||
struct hash<std::map<Key, Value>>
|
||||
{
|
||||
size_t operator()(std::map<Key, Value> const &bindings) const
|
||||
{
|
||||
size_t result = 0;
|
||||
vkb::hash_combine(result, bindings.size());
|
||||
for (auto const &binding : bindings)
|
||||
{
|
||||
vkb::hash_combine(result, binding.first);
|
||||
vkb::hash_combine(result, binding.second);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
struct hash<std::vector<T>>
|
||||
{
|
||||
size_t operator()(std::vector<T> const &values) const
|
||||
{
|
||||
size_t result = 0;
|
||||
vkb::hash_combine(result, values.size());
|
||||
for (auto const &value : values)
|
||||
{
|
||||
vkb::hash_combine(result, value);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct hash<vkb::common::HPPLoadStoreInfo>
|
||||
{
|
||||
size_t operator()(vkb::common::HPPLoadStoreInfo const &lsi) const
|
||||
{
|
||||
size_t result = 0;
|
||||
vkb::hash_combine(result, lsi.load_op);
|
||||
vkb::hash_combine(result, lsi.store_op);
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
template <vkb::BindingType bindingType, typename T>
|
||||
struct hash<vkb::core::VulkanResource<bindingType, T>>
|
||||
{
|
||||
size_t operator()(const vkb::core::VulkanResource<bindingType, T> &vulkan_resource) const
|
||||
{
|
||||
return std::hash<T>()(vulkan_resource.get_handle());
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct hash<vkb::core::HPPDescriptorPool>
|
||||
{
|
||||
size_t operator()(const vkb::core::HPPDescriptorPool &descriptor_pool) const
|
||||
{
|
||||
return std::hash<vkb::DescriptorPool>()(reinterpret_cast<vkb::DescriptorPool const &>(descriptor_pool));
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct hash<vkb::core::HPPDescriptorSet>
|
||||
{
|
||||
size_t operator()(vkb::core::HPPDescriptorSet &descriptor_set) const
|
||||
{
|
||||
size_t result = 0;
|
||||
vkb::hash_combine(result, descriptor_set.get_layout());
|
||||
// descriptor_pool ?
|
||||
vkb::hash_combine(result, descriptor_set.get_buffer_infos());
|
||||
vkb::hash_combine(result, descriptor_set.get_image_infos());
|
||||
vkb::hash_combine(result, descriptor_set.get_handle());
|
||||
// write_descriptor_sets ?
|
||||
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct hash<vkb::core::HPPDescriptorSetLayout>
|
||||
{
|
||||
size_t operator()(const vkb::core::HPPDescriptorSetLayout &descriptor_set_layout) const
|
||||
{
|
||||
return std::hash<vkb::DescriptorSetLayout>()(reinterpret_cast<vkb::DescriptorSetLayout const &>(descriptor_set_layout));
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct hash<vkb::core::HPPImage>
|
||||
{
|
||||
size_t operator()(const vkb::core::HPPImage &image) const
|
||||
{
|
||||
size_t result = 0;
|
||||
vkb::hash_combine(result, image.get_memory());
|
||||
vkb::hash_combine(result, image.get_type());
|
||||
vkb::hash_combine(result, image.get_extent());
|
||||
vkb::hash_combine(result, image.get_format());
|
||||
vkb::hash_combine(result, image.get_usage());
|
||||
vkb::hash_combine(result, image.get_sample_count());
|
||||
vkb::hash_combine(result, image.get_tiling());
|
||||
vkb::hash_combine(result, image.get_subresource());
|
||||
vkb::hash_combine(result, image.get_array_layer_count());
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct hash<vkb::core::HPPImageView>
|
||||
{
|
||||
size_t operator()(const vkb::core::HPPImageView &image_view) const
|
||||
{
|
||||
size_t result = std::hash<vkb::core::VulkanResourceCpp<vk::ImageView>>()(image_view);
|
||||
vkb::hash_combine(result, image_view.get_image());
|
||||
vkb::hash_combine(result, image_view.get_format());
|
||||
vkb::hash_combine(result, image_view.get_subresource_range());
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct hash<vkb::core::HPPRenderPass>
|
||||
{
|
||||
size_t operator()(const vkb::core::HPPRenderPass &render_pass) const
|
||||
{
|
||||
return std::hash<vkb::RenderPass>()(reinterpret_cast<vkb::RenderPass const &>(render_pass));
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct hash<vkb::core::HPPShaderModule>
|
||||
{
|
||||
size_t operator()(const vkb::core::HPPShaderModule &shader_module) const
|
||||
{
|
||||
return std::hash<vkb::ShaderModule>()(reinterpret_cast<vkb::ShaderModule const &>(shader_module));
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct hash<vkb::core::HPPShaderResource>
|
||||
{
|
||||
size_t operator()(vkb::core::HPPShaderResource const &shader_resource) const
|
||||
{
|
||||
size_t result = 0;
|
||||
vkb::hash_combine(result, shader_resource.stages);
|
||||
vkb::hash_combine(result, shader_resource.type);
|
||||
vkb::hash_combine(result, shader_resource.mode);
|
||||
vkb::hash_combine(result, shader_resource.set);
|
||||
vkb::hash_combine(result, shader_resource.binding);
|
||||
vkb::hash_combine(result, shader_resource.location);
|
||||
vkb::hash_combine(result, shader_resource.input_attachment_index);
|
||||
vkb::hash_combine(result, shader_resource.vec_size);
|
||||
vkb::hash_combine(result, shader_resource.columns);
|
||||
vkb::hash_combine(result, shader_resource.array_size);
|
||||
vkb::hash_combine(result, shader_resource.offset);
|
||||
vkb::hash_combine(result, shader_resource.size);
|
||||
vkb::hash_combine(result, shader_resource.constant_id);
|
||||
vkb::hash_combine(result, shader_resource.qualifiers);
|
||||
vkb::hash_combine(result, shader_resource.name);
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct hash<vkb::core::HPPShaderSource>
|
||||
{
|
||||
size_t operator()(const vkb::core::HPPShaderSource &shader_source) const
|
||||
{
|
||||
return std::hash<vkb::ShaderSource>()(reinterpret_cast<vkb::ShaderSource const &>(shader_source));
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct hash<vkb::core::HPPShaderVariant>
|
||||
{
|
||||
size_t operator()(const vkb::core::HPPShaderVariant &shader_variant) const
|
||||
{
|
||||
return std::hash<vkb::ShaderVariant>()(reinterpret_cast<vkb::ShaderVariant const &>(shader_variant));
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct hash<vkb::core::HPPSubpassInfo>
|
||||
{
|
||||
size_t operator()(vkb::core::HPPSubpassInfo const &subpass_info) const
|
||||
{
|
||||
size_t result = 0;
|
||||
vkb::hash_combine(result, subpass_info.input_attachments);
|
||||
vkb::hash_combine(result, subpass_info.output_attachments);
|
||||
vkb::hash_combine(result, subpass_info.color_resolve_attachments);
|
||||
vkb::hash_combine(result, subpass_info.disable_depth_stencil_attachment);
|
||||
vkb::hash_combine(result, subpass_info.depth_stencil_resolve_attachment);
|
||||
vkb::hash_combine(result, subpass_info.depth_stencil_resolve_mode);
|
||||
vkb::hash_combine(result, subpass_info.debug_name);
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct hash<vkb::rendering::HPPAttachment>
|
||||
{
|
||||
size_t operator()(const vkb::rendering::HPPAttachment &attachment) const
|
||||
{
|
||||
size_t result = 0;
|
||||
vkb::hash_combine(result, attachment.format);
|
||||
vkb::hash_combine(result, attachment.samples);
|
||||
vkb::hash_combine(result, attachment.usage);
|
||||
vkb::hash_combine(result, attachment.initial_layout);
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct hash<vkb::rendering::HPPPipelineState>
|
||||
{
|
||||
size_t operator()(const vkb::rendering::HPPPipelineState &pipeline_state) const
|
||||
{
|
||||
return std::hash<vkb::PipelineState>()(reinterpret_cast<vkb::PipelineState const &>(pipeline_state));
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct hash<vkb::rendering::HPPRenderTarget>
|
||||
{
|
||||
size_t operator()(const vkb::rendering::HPPRenderTarget &render_target) const
|
||||
{
|
||||
size_t result = 0;
|
||||
vkb::hash_combine(result, render_target.get_extent());
|
||||
for (auto const &view : render_target.get_views())
|
||||
{
|
||||
vkb::hash_combine(result, view);
|
||||
}
|
||||
for (auto const &attachment : render_target.get_attachments())
|
||||
{
|
||||
vkb::hash_combine(result, attachment);
|
||||
}
|
||||
for (auto const &input : render_target.get_input_attachments())
|
||||
{
|
||||
vkb::hash_combine(result, input);
|
||||
}
|
||||
for (auto const &output : render_target.get_output_attachments())
|
||||
{
|
||||
vkb::hash_combine(result, output);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace std
|
||||
|
||||
namespace vkb
|
||||
{
|
||||
namespace common
|
||||
{
|
||||
/**
|
||||
* @brief facade helper functions and structs around the functions and structs in common/resource_caching, providing a vulkan.hpp-based interface
|
||||
*/
|
||||
|
||||
namespace
|
||||
{
|
||||
template <class T, class... A>
|
||||
struct HPPRecordHelper
|
||||
{
|
||||
size_t record(HPPResourceRecord & /*recorder*/, A &.../*args*/)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
void index(HPPResourceRecord & /*recorder*/, size_t /*index*/, T & /*resource*/)
|
||||
{}
|
||||
};
|
||||
|
||||
template <class... A>
|
||||
struct HPPRecordHelper<vkb::core::HPPShaderModule, A...>
|
||||
{
|
||||
size_t record(HPPResourceRecord &recorder, A &...args)
|
||||
{
|
||||
return recorder.register_shader_module(args...);
|
||||
}
|
||||
|
||||
void index(HPPResourceRecord &recorder, size_t index, vkb::core::HPPShaderModule &shader_module)
|
||||
{
|
||||
recorder.set_shader_module(index, shader_module);
|
||||
}
|
||||
};
|
||||
|
||||
template <class... A>
|
||||
struct HPPRecordHelper<vkb::core::HPPPipelineLayout, A...>
|
||||
{
|
||||
size_t record(HPPResourceRecord &recorder, A &...args)
|
||||
{
|
||||
return recorder.register_pipeline_layout(args...);
|
||||
}
|
||||
|
||||
void index(HPPResourceRecord &recorder, size_t index, vkb::core::HPPPipelineLayout &pipeline_layout)
|
||||
{
|
||||
recorder.set_pipeline_layout(index, pipeline_layout);
|
||||
}
|
||||
};
|
||||
|
||||
template <class... A>
|
||||
struct HPPRecordHelper<vkb::core::HPPRenderPass, A...>
|
||||
{
|
||||
size_t record(HPPResourceRecord &recorder, A &...args)
|
||||
{
|
||||
return recorder.register_render_pass(args...);
|
||||
}
|
||||
|
||||
void index(HPPResourceRecord &recorder, size_t index, vkb::core::HPPRenderPass &render_pass)
|
||||
{
|
||||
recorder.set_render_pass(index, render_pass);
|
||||
}
|
||||
};
|
||||
|
||||
template <class... A>
|
||||
struct HPPRecordHelper<vkb::core::HPPGraphicsPipeline, A...>
|
||||
{
|
||||
size_t record(HPPResourceRecord &recorder, A &...args)
|
||||
{
|
||||
return recorder.register_graphics_pipeline(args...);
|
||||
}
|
||||
|
||||
void index(HPPResourceRecord &recorder, size_t index, vkb::core::HPPGraphicsPipeline &graphics_pipeline)
|
||||
{
|
||||
recorder.set_graphics_pipeline(index, graphics_pipeline);
|
||||
}
|
||||
};
|
||||
} // namespace
|
||||
|
||||
template <class T, class... A>
|
||||
T &request_resource(vkb::core::DeviceCpp &device, vkb::HPPResourceRecord *recorder, std::unordered_map<size_t, T> &resources, A &...args)
|
||||
{
|
||||
HPPRecordHelper<T, A...> record_helper;
|
||||
|
||||
size_t hash{0U};
|
||||
hash_param(hash, args...);
|
||||
|
||||
auto res_it = resources.find(hash);
|
||||
|
||||
if (res_it != resources.end())
|
||||
{
|
||||
return res_it->second;
|
||||
}
|
||||
|
||||
// If we do not have it already, create and cache it
|
||||
const char *res_type = typeid(T).name();
|
||||
size_t res_id = resources.size();
|
||||
|
||||
LOGD("Building #{} cache object ({})", res_id, res_type);
|
||||
|
||||
// Only error handle in release
|
||||
#ifndef DEBUG
|
||||
try
|
||||
{
|
||||
#endif
|
||||
T resource(device, args...);
|
||||
|
||||
auto res_ins_it = resources.emplace(hash, std::move(resource));
|
||||
|
||||
if (!res_ins_it.second)
|
||||
{
|
||||
throw std::runtime_error{std::string{"Insertion error for #"} + std::to_string(res_id) + "cache object (" + res_type + ")"};
|
||||
}
|
||||
|
||||
res_it = res_ins_it.first;
|
||||
|
||||
if (recorder)
|
||||
{
|
||||
size_t index = record_helper.record(*recorder, args...);
|
||||
record_helper.index(*recorder, index, res_it->second);
|
||||
}
|
||||
#ifndef DEBUG
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
LOGE("Creation error for #{} cache object ({})", res_id, res_type);
|
||||
throw e;
|
||||
}
|
||||
#endif
|
||||
|
||||
return res_it->second;
|
||||
}
|
||||
} // namespace common
|
||||
} // namespace vkb
|
||||
@@ -0,0 +1,36 @@
|
||||
/* Copyright (c) 2021-2022, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* 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 <common/strings.h>
|
||||
|
||||
#include <vulkan/vulkan.hpp>
|
||||
|
||||
namespace vkb
|
||||
{
|
||||
namespace common
|
||||
{
|
||||
/**
|
||||
* @brief facade helper functions around the functions in common/strings.h, providing a vulkan.hpp-based interface
|
||||
*/
|
||||
std::string to_string(vk::Extent2D const &extent)
|
||||
{
|
||||
return vkb::to_string(static_cast<VkExtent2D const &>(extent));
|
||||
}
|
||||
} // namespace common
|
||||
} // namespace vkb
|
||||
@@ -0,0 +1,42 @@
|
||||
/* Copyright (c) 2021-2024, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* 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 <common/utils.h>
|
||||
|
||||
#include <rendering/hpp_render_context.h>
|
||||
#include <scene_graph/hpp_scene.h>
|
||||
|
||||
/**
|
||||
* @brief facade helper functions around the functions in common/utils.h, providing a vulkan.hpp-based interface
|
||||
*/
|
||||
namespace vkb
|
||||
{
|
||||
namespace common
|
||||
{
|
||||
inline sg::Node &add_free_camera(vkb::scene_graph::HPPScene &scene, const std::string &node_name, vk::Extent2D const &extent)
|
||||
{
|
||||
return vkb::add_free_camera(reinterpret_cast<vkb::sg::Scene &>(scene), node_name, static_cast<VkExtent2D>(extent));
|
||||
}
|
||||
|
||||
inline void screenshot(vkb::rendering::HPPRenderContext &render_context, const std::string &filename)
|
||||
{
|
||||
vkb::screenshot(reinterpret_cast<vkb::RenderContext &>(render_context), filename);
|
||||
}
|
||||
} // namespace common
|
||||
} // namespace vkb
|
||||
@@ -0,0 +1,453 @@
|
||||
/* Copyright (c) 2021-2025, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* 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 "common/vk_common.h"
|
||||
|
||||
#include "core/util/logging.hpp"
|
||||
#include "vulkan/vulkan.hpp"
|
||||
#include "vulkan/vulkan_format_traits.hpp"
|
||||
|
||||
namespace vkb
|
||||
{
|
||||
namespace common
|
||||
{
|
||||
/**
|
||||
* @brief facade helper functions and structs around the functions and structs in common/vk_common, providing a vulkan.hpp-based interface
|
||||
*/
|
||||
|
||||
struct HPPBufferMemoryBarrier
|
||||
{
|
||||
vk::PipelineStageFlags src_stage_mask = vk::PipelineStageFlagBits::eBottomOfPipe;
|
||||
vk::PipelineStageFlags dst_stage_mask = vk::PipelineStageFlagBits::eTopOfPipe;
|
||||
vk::AccessFlags src_access_mask = {};
|
||||
vk::AccessFlags dst_access_mask = {};
|
||||
};
|
||||
|
||||
struct HPPImageMemoryBarrier
|
||||
{
|
||||
vk::PipelineStageFlags src_stage_mask = vk::PipelineStageFlagBits::eBottomOfPipe;
|
||||
vk::PipelineStageFlags dst_stage_mask = vk::PipelineStageFlagBits::eTopOfPipe;
|
||||
vk::AccessFlags src_access_mask;
|
||||
vk::AccessFlags dst_access_mask;
|
||||
vk::ImageLayout old_layout = vk::ImageLayout::eUndefined;
|
||||
vk::ImageLayout new_layout = vk::ImageLayout::eUndefined;
|
||||
uint32_t src_queue_family = VK_QUEUE_FAMILY_IGNORED;
|
||||
uint32_t dst_queue_family = VK_QUEUE_FAMILY_IGNORED;
|
||||
};
|
||||
|
||||
struct HPPLoadStoreInfo
|
||||
{
|
||||
vk::AttachmentLoadOp load_op = vk::AttachmentLoadOp::eClear;
|
||||
vk::AttachmentStoreOp store_op = vk::AttachmentStoreOp::eStore;
|
||||
};
|
||||
|
||||
inline int32_t get_bits_per_pixel(vk::Format format)
|
||||
{
|
||||
return vkb::get_bits_per_pixel(static_cast<VkFormat>(format));
|
||||
}
|
||||
|
||||
inline vk::Format get_suitable_depth_format(vk::PhysicalDevice physical_device,
|
||||
bool depth_only = false,
|
||||
const std::vector<vk::Format> &depth_format_priority_list = {
|
||||
vk::Format::eD32Sfloat, vk::Format::eD24UnormS8Uint, vk::Format::eD16Unorm})
|
||||
{
|
||||
return static_cast<vk::Format>(
|
||||
vkb::get_suitable_depth_format(physical_device, depth_only, reinterpret_cast<std::vector<VkFormat> const &>(depth_format_priority_list)));
|
||||
}
|
||||
|
||||
inline bool is_buffer_descriptor_type(vk::DescriptorType descriptor_type)
|
||||
{
|
||||
return vkb::is_buffer_descriptor_type(static_cast<VkDescriptorType>(descriptor_type));
|
||||
}
|
||||
|
||||
inline bool is_depth_only_format(vk::Format format)
|
||||
{
|
||||
assert(vkb::is_depth_only_format(static_cast<VkFormat>(format)) ==
|
||||
((vk::componentCount(format) == 1) && (std::string(vk::componentName(format, 0)) == "D")));
|
||||
return vkb::is_depth_only_format(static_cast<VkFormat>(format));
|
||||
}
|
||||
|
||||
inline bool is_depth_stencil_format(vk::Format format)
|
||||
{
|
||||
assert(vkb::is_depth_stencil_format(static_cast<VkFormat>(format)) ==
|
||||
((vk::componentCount(format) == 2) && (std::string(vk::componentName(format, 0)) == "D") &&
|
||||
(std::string(vk::componentName(format, 1)) == "S")));
|
||||
return vkb::is_depth_stencil_format(static_cast<VkFormat>(format));
|
||||
}
|
||||
|
||||
inline bool is_depth_format(vk::Format format)
|
||||
{
|
||||
assert(vkb::is_depth_format(static_cast<VkFormat>(format)) == (std::string(vk::componentName(format, 0)) == "D"));
|
||||
return vkb::is_depth_format(static_cast<VkFormat>(format));
|
||||
}
|
||||
|
||||
inline bool is_dynamic_buffer_descriptor_type(vk::DescriptorType descriptor_type)
|
||||
{
|
||||
return vkb::is_dynamic_buffer_descriptor_type(static_cast<VkDescriptorType>(descriptor_type));
|
||||
}
|
||||
|
||||
inline vk::ShaderModule load_shader(const std::string &filename, vk::Device device, vk::ShaderStageFlagBits stage)
|
||||
{
|
||||
return static_cast<vk::ShaderModule>(vkb::load_shader(filename, device, static_cast<VkShaderStageFlagBits>(stage)));
|
||||
}
|
||||
|
||||
inline void image_layout_transition(vk::CommandBuffer command_buffer,
|
||||
vk::Image image,
|
||||
vk::ImageLayout old_layout,
|
||||
vk::ImageLayout new_layout)
|
||||
{
|
||||
vkb::image_layout_transition(static_cast<VkCommandBuffer>(command_buffer),
|
||||
static_cast<VkImage>(image),
|
||||
static_cast<VkImageLayout>(old_layout),
|
||||
static_cast<VkImageLayout>(new_layout));
|
||||
}
|
||||
|
||||
inline void image_layout_transition(vk::CommandBuffer command_buffer,
|
||||
vk::Image image,
|
||||
vk::ImageLayout old_layout,
|
||||
vk::ImageLayout new_layout,
|
||||
vk::ImageSubresourceRange subresource_range)
|
||||
{
|
||||
vkb::image_layout_transition(static_cast<VkCommandBuffer>(command_buffer),
|
||||
static_cast<VkImage>(image),
|
||||
static_cast<VkImageLayout>(old_layout),
|
||||
static_cast<VkImageLayout>(new_layout),
|
||||
static_cast<VkImageSubresourceRange>(subresource_range));
|
||||
}
|
||||
|
||||
inline void image_layout_transition(vk::CommandBuffer command_buffer,
|
||||
vk::Image image,
|
||||
vk::PipelineStageFlags src_stage_mask,
|
||||
vk::PipelineStageFlags dst_stage_mask,
|
||||
vk::AccessFlags src_access_mask,
|
||||
vk::AccessFlags dst_access_mask,
|
||||
vk::ImageLayout old_layout,
|
||||
vk::ImageLayout new_layout,
|
||||
vk::ImageSubresourceRange const &subresource_range)
|
||||
{
|
||||
vkb::image_layout_transition(static_cast<VkCommandBuffer>(command_buffer),
|
||||
static_cast<VkImage>(image),
|
||||
static_cast<VkPipelineStageFlags>(src_stage_mask),
|
||||
static_cast<VkPipelineStageFlags>(dst_stage_mask),
|
||||
static_cast<VkAccessFlags>(src_access_mask),
|
||||
static_cast<VkAccessFlags>(dst_access_mask),
|
||||
static_cast<VkImageLayout>(old_layout),
|
||||
static_cast<VkImageLayout>(new_layout),
|
||||
static_cast<VkImageSubresourceRange const &>(subresource_range));
|
||||
}
|
||||
|
||||
inline void make_filters_valid(vk::PhysicalDevice physical_device, vk::Format format, vk::Filter *filter, vk::SamplerMipmapMode *mipmapMode = nullptr)
|
||||
{
|
||||
// Not all formats support linear filtering, so we need to adjust them if they don't
|
||||
if (*filter == vk::Filter::eNearest && (mipmapMode == nullptr || *mipmapMode == vk::SamplerMipmapMode::eNearest))
|
||||
{
|
||||
return; // These must already be valid
|
||||
}
|
||||
|
||||
vk::FormatProperties properties = physical_device.getFormatProperties(format);
|
||||
|
||||
if (!(properties.optimalTilingFeatures & vk::FormatFeatureFlagBits::eSampledImageFilterLinear))
|
||||
{
|
||||
*filter = vk::Filter::eNearest;
|
||||
if (mipmapMode)
|
||||
{
|
||||
*mipmapMode = vk::SamplerMipmapMode::eNearest;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inline vk::SurfaceFormatKHR select_surface_format(vk::PhysicalDevice gpu,
|
||||
vk::SurfaceKHR surface,
|
||||
std::vector<vk::Format> const &preferred_formats = {
|
||||
vk::Format::eR8G8B8A8Srgb, vk::Format::eB8G8R8A8Srgb, vk::Format::eA8B8G8R8SrgbPack32})
|
||||
{
|
||||
std::vector<vk::SurfaceFormatKHR> supported_surface_formats = gpu.getSurfaceFormatsKHR(surface);
|
||||
assert(!supported_surface_formats.empty());
|
||||
|
||||
auto it = std::ranges::find_if(supported_surface_formats,
|
||||
[&preferred_formats](vk::SurfaceFormatKHR surface_format) {
|
||||
return std::ranges::any_of(preferred_formats,
|
||||
[&surface_format](vk::Format format) { return format == surface_format.format; });
|
||||
});
|
||||
|
||||
// We use the first supported format as a fallback in case none of the preferred formats is available
|
||||
return it != supported_surface_formats.end() ? *it : supported_surface_formats[0];
|
||||
}
|
||||
|
||||
inline vk::Format choose_blendable_format(vk::PhysicalDevice gpu, const std::vector<vk::Format> &format_priority_list)
|
||||
{
|
||||
for (const auto &format : format_priority_list)
|
||||
{
|
||||
vk::FormatProperties fmt_props = gpu.getFormatProperties(format);
|
||||
|
||||
if (fmt_props.optimalTilingFeatures & vk::FormatFeatureFlagBits::eColorAttachmentBlend)
|
||||
return format;
|
||||
}
|
||||
|
||||
throw std::runtime_error("No suitable blendable format could be determined");
|
||||
}
|
||||
|
||||
// helper functions not backed by vk_common.h
|
||||
inline vk::CommandBuffer
|
||||
allocate_command_buffer(vk::Device device, vk::CommandPool command_pool, vk::CommandBufferLevel level = vk::CommandBufferLevel::ePrimary)
|
||||
{
|
||||
vk::CommandBufferAllocateInfo command_buffer_allocate_info{.commandPool = command_pool, .level = level, .commandBufferCount = 1};
|
||||
return device.allocateCommandBuffers(command_buffer_allocate_info).front();
|
||||
}
|
||||
|
||||
inline vk::DescriptorSet allocate_descriptor_set(vk::Device device, vk::DescriptorPool descriptor_pool, vk::DescriptorSetLayout descriptor_set_layout)
|
||||
{
|
||||
vk::DescriptorSetAllocateInfo descriptor_set_allocate_info{.descriptorPool = descriptor_pool,
|
||||
.descriptorSetCount = 1,
|
||||
.pSetLayouts = &descriptor_set_layout};
|
||||
return device.allocateDescriptorSets(descriptor_set_allocate_info).front();
|
||||
}
|
||||
|
||||
inline vk::Framebuffer
|
||||
create_framebuffer(vk::Device device, vk::RenderPass render_pass, std::vector<vk::ImageView> const &attachments, vk::Extent2D const &extent)
|
||||
{
|
||||
vk::FramebufferCreateInfo framebuffer_create_info{.renderPass = render_pass,
|
||||
.attachmentCount = static_cast<uint32_t>(attachments.size()),
|
||||
.pAttachments = attachments.data(),
|
||||
.width = extent.width,
|
||||
.height = extent.height,
|
||||
.layers = 1};
|
||||
return device.createFramebuffer(framebuffer_create_info);
|
||||
}
|
||||
|
||||
inline vk::Pipeline create_graphics_pipeline(vk::Device device,
|
||||
vk::PipelineCache pipeline_cache,
|
||||
std::vector<vk::PipelineShaderStageCreateInfo> const &shader_stages,
|
||||
vk::PipelineVertexInputStateCreateInfo const &vertex_input_state,
|
||||
vk::PrimitiveTopology primitive_topology,
|
||||
uint32_t patch_control_points,
|
||||
vk::PolygonMode polygon_mode,
|
||||
vk::CullModeFlags cull_mode,
|
||||
vk::FrontFace front_face,
|
||||
std::vector<vk::PipelineColorBlendAttachmentState> const &blend_attachment_states,
|
||||
vk::PipelineDepthStencilStateCreateInfo const &depth_stencil_state,
|
||||
vk::PipelineLayout pipeline_layout,
|
||||
vk::RenderPass render_pass)
|
||||
{
|
||||
vk::PipelineInputAssemblyStateCreateInfo input_assembly_state{.topology = primitive_topology};
|
||||
|
||||
vk::PipelineTessellationStateCreateInfo tessellation_state{.patchControlPoints = patch_control_points};
|
||||
|
||||
vk::PipelineViewportStateCreateInfo viewport_state{.viewportCount = 1, .scissorCount = 1};
|
||||
|
||||
vk::PipelineRasterizationStateCreateInfo rasterization_state{
|
||||
.polygonMode = polygon_mode, .cullMode = cull_mode, .frontFace = front_face, .lineWidth = 1.0f};
|
||||
|
||||
vk::PipelineMultisampleStateCreateInfo multisample_state{.rasterizationSamples = vk::SampleCountFlagBits::e1};
|
||||
|
||||
vk::PipelineColorBlendStateCreateInfo color_blend_state{.attachmentCount = static_cast<uint32_t>(blend_attachment_states.size()),
|
||||
.pAttachments = blend_attachment_states.data()};
|
||||
|
||||
std::array<vk::DynamicState, 2> dynamic_state_enables = {vk::DynamicState::eViewport, vk::DynamicState::eScissor};
|
||||
vk::PipelineDynamicStateCreateInfo dynamic_state{.dynamicStateCount = static_cast<uint32_t>(dynamic_state_enables.size()),
|
||||
.pDynamicStates = dynamic_state_enables.data()};
|
||||
|
||||
// Final fullscreen composition pass pipeline
|
||||
vk::GraphicsPipelineCreateInfo pipeline_create_info{.stageCount = static_cast<uint32_t>(shader_stages.size()),
|
||||
.pStages = shader_stages.data(),
|
||||
.pVertexInputState = &vertex_input_state,
|
||||
.pInputAssemblyState = &input_assembly_state,
|
||||
.pTessellationState = &tessellation_state,
|
||||
.pViewportState = &viewport_state,
|
||||
.pRasterizationState = &rasterization_state,
|
||||
.pMultisampleState = &multisample_state,
|
||||
.pDepthStencilState = &depth_stencil_state,
|
||||
.pColorBlendState = &color_blend_state,
|
||||
.pDynamicState = &dynamic_state,
|
||||
.layout = pipeline_layout,
|
||||
.renderPass = render_pass,
|
||||
.basePipelineIndex = -1};
|
||||
|
||||
vk::Result result;
|
||||
vk::Pipeline pipeline;
|
||||
std::tie(result, pipeline) = device.createGraphicsPipeline(pipeline_cache, pipeline_create_info);
|
||||
assert(result == vk::Result::eSuccess);
|
||||
return pipeline;
|
||||
}
|
||||
|
||||
inline vk::ImageView create_image_view(vk::Device device,
|
||||
vk::Image image,
|
||||
vk::ImageViewType view_type,
|
||||
vk::Format format,
|
||||
vk::ImageAspectFlags aspect_mask = vk::ImageAspectFlagBits::eColor,
|
||||
uint32_t base_mip_level = 0,
|
||||
uint32_t level_count = 1,
|
||||
uint32_t base_array_layer = 0,
|
||||
uint32_t layer_count = 1)
|
||||
{
|
||||
vk::ImageViewCreateInfo image_view_create_info{.image = image,
|
||||
.viewType = view_type,
|
||||
.format = format,
|
||||
.subresourceRange = {.aspectMask = aspect_mask,
|
||||
.baseMipLevel = base_mip_level,
|
||||
.levelCount = level_count,
|
||||
.baseArrayLayer = base_array_layer,
|
||||
.layerCount = layer_count}};
|
||||
return device.createImageView(image_view_create_info);
|
||||
}
|
||||
|
||||
inline vk::QueryPool create_query_pool(vk::Device device, vk::QueryType query_type, uint32_t query_count, vk::QueryPipelineStatisticFlags pipeline_statistics = {})
|
||||
{
|
||||
vk::QueryPoolCreateInfo query_pool_create_info{.queryType = query_type, .queryCount = query_count, .pipelineStatistics = pipeline_statistics};
|
||||
return device.createQueryPool(query_pool_create_info);
|
||||
}
|
||||
|
||||
inline vk::Sampler create_sampler(vk::Device device,
|
||||
vk::Filter mag_filter,
|
||||
vk::Filter min_filter,
|
||||
vk::SamplerMipmapMode mipmap_mode,
|
||||
vk::SamplerAddressMode sampler_address_mode,
|
||||
float max_anisotropy,
|
||||
float max_LOD)
|
||||
{
|
||||
vk::SamplerCreateInfo sampler_create_info{.magFilter = mag_filter,
|
||||
.minFilter = min_filter,
|
||||
.mipmapMode = mipmap_mode,
|
||||
.addressModeU = sampler_address_mode,
|
||||
.addressModeV = sampler_address_mode,
|
||||
.addressModeW = sampler_address_mode,
|
||||
.anisotropyEnable = (1.0f < max_anisotropy),
|
||||
.maxAnisotropy = max_anisotropy,
|
||||
.compareOp = vk::CompareOp::eNever,
|
||||
.minLod = 0.0f,
|
||||
.maxLod = max_LOD,
|
||||
.borderColor = vk::BorderColor::eFloatOpaqueWhite};
|
||||
return device.createSampler(sampler_create_info);
|
||||
}
|
||||
|
||||
inline vk::Sampler create_sampler(vk::PhysicalDevice gpu,
|
||||
vk::Device device,
|
||||
vk::Format format,
|
||||
vk::Filter filter,
|
||||
vk::SamplerAddressMode sampler_address_mode,
|
||||
float max_anisotropy,
|
||||
float max_LOD)
|
||||
{
|
||||
const vk::FormatProperties fmt_props = gpu.getFormatProperties(format);
|
||||
|
||||
bool has_linear_filter = !!(fmt_props.optimalTilingFeatures & vk::FormatFeatureFlagBits::eSampledImageFilterLinear);
|
||||
|
||||
return create_sampler(device,
|
||||
has_linear_filter ? filter : vk::Filter::eNearest,
|
||||
has_linear_filter ? filter : vk::Filter::eNearest,
|
||||
has_linear_filter ? vk::SamplerMipmapMode::eLinear : vk::SamplerMipmapMode::eNearest,
|
||||
sampler_address_mode,
|
||||
max_anisotropy,
|
||||
max_LOD);
|
||||
}
|
||||
|
||||
inline vk::ImageAspectFlags get_image_aspect_flags(vk::ImageUsageFlagBits usage, vk::Format format)
|
||||
{
|
||||
vk::ImageAspectFlags image_aspect_flags;
|
||||
|
||||
switch (usage)
|
||||
{
|
||||
case vk::ImageUsageFlagBits::eColorAttachment:
|
||||
assert(!vkb::common::is_depth_format(format));
|
||||
image_aspect_flags = vk::ImageAspectFlagBits::eColor;
|
||||
break;
|
||||
case vk::ImageUsageFlagBits::eDepthStencilAttachment:
|
||||
assert(vkb::common::is_depth_format(format));
|
||||
image_aspect_flags = vk::ImageAspectFlagBits::eDepth;
|
||||
// Stencil aspect should only be set on depth + stencil formats
|
||||
if (vkb::common::is_depth_stencil_format(format))
|
||||
{
|
||||
image_aspect_flags |= vk::ImageAspectFlagBits::eStencil;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
|
||||
return image_aspect_flags;
|
||||
}
|
||||
|
||||
inline void submit_and_wait(vk::Device device, vk::Queue queue, std::vector<vk::CommandBuffer> command_buffers, std::vector<vk::Semaphore> semaphores = {})
|
||||
{
|
||||
// Submit command_buffer
|
||||
vk::SubmitInfo submit_info{.commandBufferCount = static_cast<uint32_t>(command_buffers.size()),
|
||||
.pCommandBuffers = command_buffers.data(),
|
||||
.signalSemaphoreCount = static_cast<uint32_t>(semaphores.size()),
|
||||
.pSignalSemaphores = semaphores.data()};
|
||||
|
||||
// Create fence to ensure that command_buffer has finished executing
|
||||
vk::Fence fence = device.createFence({});
|
||||
|
||||
// Submit to the queue
|
||||
queue.submit(submit_info, fence);
|
||||
|
||||
// Wait for the fence to signal that command_buffer has finished executing
|
||||
vk::Result result = device.waitForFences(fence, true, DEFAULT_FENCE_TIMEOUT);
|
||||
if (result != vk::Result::eSuccess)
|
||||
{
|
||||
LOGE("Vulkan error on waitForFences: {}", vk::to_string(result));
|
||||
abort();
|
||||
}
|
||||
|
||||
// Destroy the fence
|
||||
device.destroyFence(fence);
|
||||
}
|
||||
|
||||
inline uint32_t get_queue_family_index(std::vector<vk::QueueFamilyProperties> const &queue_family_properties, vk::QueueFlagBits queue_flag)
|
||||
{
|
||||
// Dedicated queue for compute
|
||||
// Try to find a queue family index that supports compute but not graphics
|
||||
if (queue_flag & vk::QueueFlagBits::eCompute)
|
||||
{
|
||||
auto propertyIt = std::ranges::find_if(queue_family_properties,
|
||||
[queue_flag](const vk::QueueFamilyProperties &property) { return (property.queueFlags & queue_flag) && !(property.queueFlags & vk::QueueFlagBits::eGraphics); });
|
||||
if (propertyIt != queue_family_properties.end())
|
||||
{
|
||||
return static_cast<uint32_t>(std::distance(queue_family_properties.begin(), propertyIt));
|
||||
}
|
||||
}
|
||||
|
||||
// Dedicated queue for transfer
|
||||
// Try to find a queue family index that supports transfer but not graphics and compute
|
||||
if (queue_flag & vk::QueueFlagBits::eTransfer)
|
||||
{
|
||||
auto propertyIt = std::ranges::find_if(queue_family_properties,
|
||||
[queue_flag](const vk::QueueFamilyProperties &property) {
|
||||
return (property.queueFlags & queue_flag) && !(property.queueFlags & vk::QueueFlagBits::eGraphics) &&
|
||||
!(property.queueFlags & vk::QueueFlagBits::eCompute);
|
||||
});
|
||||
if (propertyIt != queue_family_properties.end())
|
||||
{
|
||||
return static_cast<uint32_t>(std::distance(queue_family_properties.begin(), propertyIt));
|
||||
}
|
||||
}
|
||||
|
||||
// For other queue types or if no separate compute queue is present, return the first one to support the requested flags
|
||||
auto propertyIt = std::ranges::find_if(
|
||||
queue_family_properties, [queue_flag](const vk::QueueFamilyProperties &property) { return (property.queueFlags & queue_flag) == queue_flag; });
|
||||
if (propertyIt != queue_family_properties.end())
|
||||
{
|
||||
return static_cast<uint32_t>(std::distance(queue_family_properties.begin(), propertyIt));
|
||||
}
|
||||
|
||||
throw std::runtime_error("Could not find a matching queue family index");
|
||||
}
|
||||
|
||||
} // namespace common
|
||||
} // namespace vkb
|
||||
@@ -0,0 +1,36 @@
|
||||
/* Copyright (c) 2023, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* 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 "ktx_common.h"
|
||||
#include <stdexcept>
|
||||
|
||||
namespace vkb
|
||||
{
|
||||
namespace ktx
|
||||
{
|
||||
ktxTexture *load_texture(std::string const &filename)
|
||||
{
|
||||
ktxTexture *ktx_texture;
|
||||
KTX_error_code result = ktxTexture_CreateFromNamedFile(filename.c_str(), KTX_TEXTURE_CREATE_LOAD_IMAGE_DATA_BIT, &ktx_texture);
|
||||
if ((result != KTX_SUCCESS) || (ktx_texture == nullptr))
|
||||
{
|
||||
throw std::runtime_error("Couldn't load texture");
|
||||
}
|
||||
return ktx_texture;
|
||||
}
|
||||
} // namespace ktx
|
||||
} // namespace vkb
|
||||
@@ -0,0 +1,29 @@
|
||||
/* Copyright (c) 2023, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* 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 <ktx.h>
|
||||
#include <string>
|
||||
|
||||
namespace vkb
|
||||
{
|
||||
namespace ktx
|
||||
{
|
||||
ktxTexture *load_texture(std::string const &filename);
|
||||
}
|
||||
} // namespace vkb
|
||||
@@ -0,0 +1,133 @@
|
||||
/* Copyright (c) 2021, 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 <cassert>
|
||||
|
||||
namespace vkb
|
||||
{
|
||||
// Simple optional class for pre c++17 std::optional
|
||||
// May be replaced by std::optional in the future
|
||||
template <typename Type>
|
||||
class Optional
|
||||
{
|
||||
public:
|
||||
virtual ~Optional() = default;
|
||||
Optional() = default;
|
||||
|
||||
Optional(const Optional &optional);
|
||||
|
||||
template <typename U = Type>
|
||||
Optional(const U &value);
|
||||
|
||||
bool has_value() const;
|
||||
const Type &value() const;
|
||||
const Type value_or(Type &&alternative) const;
|
||||
const Type value_or(const Type &alternative = {}) const;
|
||||
|
||||
Optional &operator=(Optional &&opt)
|
||||
{
|
||||
_has_value = opt._has_value;
|
||||
_value = opt._value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename U = Type>
|
||||
Optional &operator=(U &&value)
|
||||
{
|
||||
_has_value = true;
|
||||
_value = value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
Optional &operator=(const Optional &value)
|
||||
{
|
||||
_has_value = value._has_value;
|
||||
_value = value._value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename U = Type>
|
||||
Optional &operator=(U *value)
|
||||
{
|
||||
if (value == nullptr)
|
||||
{
|
||||
_has_value = false;
|
||||
return *this;
|
||||
}
|
||||
|
||||
_has_value = true;
|
||||
_value = *value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
private:
|
||||
bool _has_value = false;
|
||||
Type _value;
|
||||
};
|
||||
|
||||
template <typename Type>
|
||||
Optional<Type>::Optional(const Optional &optional)
|
||||
{
|
||||
_has_value = optional._has_value;
|
||||
_value = optional._value;
|
||||
}
|
||||
|
||||
template <typename Type>
|
||||
template <typename U>
|
||||
Optional<Type>::Optional(const U &value)
|
||||
{
|
||||
_has_value = true;
|
||||
_value = value;
|
||||
}
|
||||
|
||||
template <typename Type>
|
||||
bool Optional<Type>::has_value() const
|
||||
{
|
||||
return _has_value;
|
||||
}
|
||||
|
||||
template <typename Type>
|
||||
const Type &Optional<Type>::value() const
|
||||
{
|
||||
assert(_has_value && "Value does not exist");
|
||||
return _value;
|
||||
}
|
||||
|
||||
template <typename Type>
|
||||
const Type Optional<Type>::value_or(Type &&alternative) const
|
||||
{
|
||||
if (has_value())
|
||||
{
|
||||
return _value;
|
||||
}
|
||||
|
||||
return alternative;
|
||||
}
|
||||
|
||||
template <typename Type>
|
||||
const Type Optional<Type>::value_or(const Type &alternative) const
|
||||
{
|
||||
if (has_value())
|
||||
{
|
||||
return _value;
|
||||
}
|
||||
|
||||
return alternative;
|
||||
}
|
||||
} // namespace vkb
|
||||
@@ -0,0 +1,781 @@
|
||||
/* Copyright (c) 2018-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 "core/descriptor_pool.h"
|
||||
#include "core/descriptor_set.h"
|
||||
#include "core/descriptor_set_layout.h"
|
||||
#include "core/framebuffer.h"
|
||||
#include "core/image.h"
|
||||
#include "core/pipeline.h"
|
||||
#include "rendering/pipeline_state.h"
|
||||
#include "rendering/render_target.h"
|
||||
#include "resource_record.h"
|
||||
|
||||
#include "common/helpers.h"
|
||||
|
||||
namespace std
|
||||
{
|
||||
template <>
|
||||
struct hash<vkb::ShaderSource>
|
||||
{
|
||||
std::size_t operator()(const vkb::ShaderSource &shader_source) const
|
||||
{
|
||||
std::size_t result = 0;
|
||||
|
||||
vkb::hash_combine(result, shader_source.get_id());
|
||||
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct hash<vkb::ShaderVariant>
|
||||
{
|
||||
std::size_t operator()(const vkb::ShaderVariant &shader_variant) const
|
||||
{
|
||||
std::size_t result = 0;
|
||||
|
||||
vkb::hash_combine(result, shader_variant.get_id());
|
||||
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct hash<vkb::ShaderModule>
|
||||
{
|
||||
std::size_t operator()(const vkb::ShaderModule &shader_module) const
|
||||
{
|
||||
std::size_t result = 0;
|
||||
|
||||
vkb::hash_combine(result, shader_module.get_id());
|
||||
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct hash<vkb::DescriptorSetLayout>
|
||||
{
|
||||
std::size_t operator()(const vkb::DescriptorSetLayout &descriptor_set_layout) const
|
||||
{
|
||||
std::size_t result = 0;
|
||||
|
||||
vkb::hash_combine(result, descriptor_set_layout.get_handle());
|
||||
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct hash<vkb::DescriptorPool>
|
||||
{
|
||||
std::size_t operator()(const vkb::DescriptorPool &descriptor_pool) const
|
||||
{
|
||||
std::size_t result = 0;
|
||||
|
||||
vkb::hash_combine(result, descriptor_pool.get_descriptor_set_layout());
|
||||
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct hash<vkb::PipelineLayout>
|
||||
{
|
||||
std::size_t operator()(const vkb::PipelineLayout &pipeline_layout) const
|
||||
{
|
||||
std::size_t result = 0;
|
||||
|
||||
vkb::hash_combine(result, pipeline_layout.get_handle());
|
||||
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct hash<vkb::RenderPass>
|
||||
{
|
||||
std::size_t operator()(const vkb::RenderPass &render_pass) const
|
||||
{
|
||||
std::size_t result = 0;
|
||||
|
||||
vkb::hash_combine(result, render_pass.get_handle());
|
||||
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct hash<vkb::Attachment>
|
||||
{
|
||||
std::size_t operator()(const vkb::Attachment &attachment) const
|
||||
{
|
||||
std::size_t result = 0;
|
||||
|
||||
vkb::hash_combine(result, static_cast<std::underlying_type<VkFormat>::type>(attachment.format));
|
||||
vkb::hash_combine(result, static_cast<std::underlying_type<VkSampleCountFlagBits>::type>(attachment.samples));
|
||||
vkb::hash_combine(result, attachment.usage);
|
||||
vkb::hash_combine(result, static_cast<std::underlying_type<VkImageLayout>::type>(attachment.initial_layout));
|
||||
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct hash<vkb::LoadStoreInfo>
|
||||
{
|
||||
std::size_t operator()(const vkb::LoadStoreInfo &load_store_info) const
|
||||
{
|
||||
std::size_t result = 0;
|
||||
|
||||
vkb::hash_combine(result, static_cast<std::underlying_type<VkAttachmentLoadOp>::type>(load_store_info.load_op));
|
||||
vkb::hash_combine(result, static_cast<std::underlying_type<VkAttachmentStoreOp>::type>(load_store_info.store_op));
|
||||
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct hash<vkb::SubpassInfo>
|
||||
{
|
||||
std::size_t operator()(const vkb::SubpassInfo &subpass_info) const
|
||||
{
|
||||
std::size_t result = 0;
|
||||
|
||||
for (uint32_t output_attachment : subpass_info.output_attachments)
|
||||
{
|
||||
vkb::hash_combine(result, output_attachment);
|
||||
}
|
||||
|
||||
for (uint32_t input_attachment : subpass_info.input_attachments)
|
||||
{
|
||||
vkb::hash_combine(result, input_attachment);
|
||||
}
|
||||
|
||||
for (uint32_t resolve_attachment : subpass_info.color_resolve_attachments)
|
||||
{
|
||||
vkb::hash_combine(result, resolve_attachment);
|
||||
}
|
||||
|
||||
vkb::hash_combine(result, subpass_info.disable_depth_stencil_attachment);
|
||||
vkb::hash_combine(result, subpass_info.depth_stencil_resolve_attachment);
|
||||
vkb::hash_combine(result, subpass_info.depth_stencil_resolve_mode);
|
||||
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct hash<vkb::SpecializationConstantState>
|
||||
{
|
||||
std::size_t operator()(const vkb::SpecializationConstantState &specialization_constant_state) const
|
||||
{
|
||||
std::size_t result = 0;
|
||||
|
||||
for (auto constants : specialization_constant_state.get_specialization_constant_state())
|
||||
{
|
||||
vkb::hash_combine(result, constants.first);
|
||||
for (const auto data : constants.second)
|
||||
{
|
||||
vkb::hash_combine(result, data);
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct hash<vkb::ShaderResource>
|
||||
{
|
||||
std::size_t operator()(const vkb::ShaderResource &shader_resource) const
|
||||
{
|
||||
std::size_t result = 0;
|
||||
|
||||
if (shader_resource.type == vkb::ShaderResourceType::Input ||
|
||||
shader_resource.type == vkb::ShaderResourceType::Output ||
|
||||
shader_resource.type == vkb::ShaderResourceType::PushConstant ||
|
||||
shader_resource.type == vkb::ShaderResourceType::SpecializationConstant)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
|
||||
vkb::hash_combine(result, shader_resource.set);
|
||||
vkb::hash_combine(result, shader_resource.binding);
|
||||
vkb::hash_combine(result, static_cast<std::underlying_type<vkb::ShaderResourceType>::type>(shader_resource.type));
|
||||
vkb::hash_combine(result, shader_resource.mode);
|
||||
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct hash<VkDescriptorBufferInfo>
|
||||
{
|
||||
std::size_t operator()(const VkDescriptorBufferInfo &descriptor_buffer_info) const
|
||||
{
|
||||
std::size_t result = 0;
|
||||
|
||||
vkb::hash_combine(result, descriptor_buffer_info.buffer);
|
||||
vkb::hash_combine(result, descriptor_buffer_info.range);
|
||||
vkb::hash_combine(result, descriptor_buffer_info.offset);
|
||||
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct hash<VkDescriptorImageInfo>
|
||||
{
|
||||
std::size_t operator()(const VkDescriptorImageInfo &descriptor_image_info) const
|
||||
{
|
||||
std::size_t result = 0;
|
||||
|
||||
vkb::hash_combine(result, descriptor_image_info.imageView);
|
||||
vkb::hash_combine(result, static_cast<std::underlying_type<VkImageLayout>::type>(descriptor_image_info.imageLayout));
|
||||
vkb::hash_combine(result, descriptor_image_info.sampler);
|
||||
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct hash<VkWriteDescriptorSet>
|
||||
{
|
||||
std::size_t operator()(const VkWriteDescriptorSet &write_descriptor_set) const
|
||||
{
|
||||
std::size_t result = 0;
|
||||
|
||||
vkb::hash_combine(result, write_descriptor_set.dstSet);
|
||||
vkb::hash_combine(result, write_descriptor_set.dstBinding);
|
||||
vkb::hash_combine(result, write_descriptor_set.dstArrayElement);
|
||||
vkb::hash_combine(result, write_descriptor_set.descriptorCount);
|
||||
vkb::hash_combine(result, write_descriptor_set.descriptorType);
|
||||
|
||||
switch (write_descriptor_set.descriptorType)
|
||||
{
|
||||
case VK_DESCRIPTOR_TYPE_SAMPLER:
|
||||
case VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER:
|
||||
case VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE:
|
||||
case VK_DESCRIPTOR_TYPE_STORAGE_IMAGE:
|
||||
case VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT:
|
||||
for (uint32_t i = 0; i < write_descriptor_set.descriptorCount; i++)
|
||||
{
|
||||
vkb::hash_combine(result, write_descriptor_set.pImageInfo[i]);
|
||||
}
|
||||
break;
|
||||
|
||||
case VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER:
|
||||
case VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER:
|
||||
for (uint32_t i = 0; i < write_descriptor_set.descriptorCount; i++)
|
||||
{
|
||||
vkb::hash_combine(result, write_descriptor_set.pTexelBufferView[i]);
|
||||
}
|
||||
break;
|
||||
|
||||
case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER:
|
||||
case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER:
|
||||
case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC:
|
||||
case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC:
|
||||
for (uint32_t i = 0; i < write_descriptor_set.descriptorCount; i++)
|
||||
{
|
||||
vkb::hash_combine(result, write_descriptor_set.pBufferInfo[i]);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
// Not implemented
|
||||
break;
|
||||
};
|
||||
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct hash<VkVertexInputAttributeDescription>
|
||||
{
|
||||
std::size_t operator()(const VkVertexInputAttributeDescription &vertex_attrib) const
|
||||
{
|
||||
std::size_t result = 0;
|
||||
|
||||
vkb::hash_combine(result, vertex_attrib.binding);
|
||||
vkb::hash_combine(result, static_cast<std::underlying_type<VkFormat>::type>(vertex_attrib.format));
|
||||
vkb::hash_combine(result, vertex_attrib.location);
|
||||
vkb::hash_combine(result, vertex_attrib.offset);
|
||||
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct hash<VkVertexInputBindingDescription>
|
||||
{
|
||||
std::size_t operator()(const VkVertexInputBindingDescription &vertex_binding) const
|
||||
{
|
||||
std::size_t result = 0;
|
||||
|
||||
vkb::hash_combine(result, vertex_binding.binding);
|
||||
vkb::hash_combine(result, static_cast<std::underlying_type<VkVertexInputRate>::type>(vertex_binding.inputRate));
|
||||
vkb::hash_combine(result, vertex_binding.stride);
|
||||
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct hash<vkb::StencilOpState>
|
||||
{
|
||||
std::size_t operator()(const vkb::StencilOpState &stencil) const
|
||||
{
|
||||
std::size_t result = 0;
|
||||
|
||||
vkb::hash_combine(result, static_cast<std::underlying_type<VkCompareOp>::type>(stencil.compare_op));
|
||||
vkb::hash_combine(result, static_cast<std::underlying_type<VkStencilOp>::type>(stencil.depth_fail_op));
|
||||
vkb::hash_combine(result, static_cast<std::underlying_type<VkStencilOp>::type>(stencil.fail_op));
|
||||
vkb::hash_combine(result, static_cast<std::underlying_type<VkStencilOp>::type>(stencil.pass_op));
|
||||
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct hash<VkExtent2D>
|
||||
{
|
||||
size_t operator()(const VkExtent2D &extent) const
|
||||
{
|
||||
size_t result = 0;
|
||||
|
||||
vkb::hash_combine(result, extent.width);
|
||||
vkb::hash_combine(result, extent.height);
|
||||
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct hash<VkOffset2D>
|
||||
{
|
||||
size_t operator()(const VkOffset2D &offset) const
|
||||
{
|
||||
size_t result = 0;
|
||||
|
||||
vkb::hash_combine(result, offset.x);
|
||||
vkb::hash_combine(result, offset.y);
|
||||
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct hash<VkRect2D>
|
||||
{
|
||||
size_t operator()(const VkRect2D &rect) const
|
||||
{
|
||||
size_t result = 0;
|
||||
|
||||
vkb::hash_combine(result, rect.extent);
|
||||
vkb::hash_combine(result, rect.offset);
|
||||
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct hash<VkViewport>
|
||||
{
|
||||
size_t operator()(const VkViewport &viewport) const
|
||||
{
|
||||
size_t result = 0;
|
||||
|
||||
vkb::hash_combine(result, viewport.width);
|
||||
vkb::hash_combine(result, viewport.height);
|
||||
vkb::hash_combine(result, viewport.maxDepth);
|
||||
vkb::hash_combine(result, viewport.minDepth);
|
||||
vkb::hash_combine(result, viewport.x);
|
||||
vkb::hash_combine(result, viewport.y);
|
||||
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct hash<vkb::ColorBlendAttachmentState>
|
||||
{
|
||||
std::size_t operator()(const vkb::ColorBlendAttachmentState &color_blend_attachment) const
|
||||
{
|
||||
std::size_t result = 0;
|
||||
|
||||
vkb::hash_combine(result, static_cast<std::underlying_type<VkBlendOp>::type>(color_blend_attachment.alpha_blend_op));
|
||||
vkb::hash_combine(result, color_blend_attachment.blend_enable);
|
||||
vkb::hash_combine(result, static_cast<std::underlying_type<VkBlendOp>::type>(color_blend_attachment.color_blend_op));
|
||||
vkb::hash_combine(result, color_blend_attachment.color_write_mask);
|
||||
vkb::hash_combine(result, static_cast<std::underlying_type<VkBlendFactor>::type>(color_blend_attachment.dst_alpha_blend_factor));
|
||||
vkb::hash_combine(result, static_cast<std::underlying_type<VkBlendFactor>::type>(color_blend_attachment.dst_color_blend_factor));
|
||||
vkb::hash_combine(result, static_cast<std::underlying_type<VkBlendFactor>::type>(color_blend_attachment.src_alpha_blend_factor));
|
||||
vkb::hash_combine(result, static_cast<std::underlying_type<VkBlendFactor>::type>(color_blend_attachment.src_color_blend_factor));
|
||||
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct hash<vkb::RenderTarget>
|
||||
{
|
||||
std::size_t operator()(const vkb::RenderTarget &render_target) const
|
||||
{
|
||||
std::size_t result = 0;
|
||||
|
||||
for (auto &view : render_target.get_views())
|
||||
{
|
||||
vkb::hash_combine(result, view.get_handle());
|
||||
vkb::hash_combine(result, view.get_image().get_handle());
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
};
|
||||
|
||||
template <>
|
||||
struct hash<vkb::PipelineState>
|
||||
{
|
||||
std::size_t operator()(const vkb::PipelineState &pipeline_state) const
|
||||
{
|
||||
std::size_t result = 0;
|
||||
|
||||
vkb::hash_combine(result, pipeline_state.get_pipeline_layout().get_handle());
|
||||
|
||||
// For graphics only
|
||||
if (auto render_pass = pipeline_state.get_render_pass())
|
||||
{
|
||||
vkb::hash_combine(result, render_pass->get_handle());
|
||||
}
|
||||
|
||||
vkb::hash_combine(result, pipeline_state.get_specialization_constant_state());
|
||||
|
||||
vkb::hash_combine(result, pipeline_state.get_subpass_index());
|
||||
|
||||
for (auto shader_module : pipeline_state.get_pipeline_layout().get_shader_modules())
|
||||
{
|
||||
vkb::hash_combine(result, shader_module->get_id());
|
||||
}
|
||||
|
||||
// VkPipelineVertexInputStateCreateInfo
|
||||
for (auto &attribute : pipeline_state.get_vertex_input_state().attributes)
|
||||
{
|
||||
vkb::hash_combine(result, attribute);
|
||||
}
|
||||
|
||||
for (auto &binding : pipeline_state.get_vertex_input_state().bindings)
|
||||
{
|
||||
vkb::hash_combine(result, binding);
|
||||
}
|
||||
|
||||
// VkPipelineInputAssemblyStateCreateInfo
|
||||
vkb::hash_combine(result, pipeline_state.get_input_assembly_state().primitive_restart_enable);
|
||||
vkb::hash_combine(result, static_cast<std::underlying_type<VkPrimitiveTopology>::type>(pipeline_state.get_input_assembly_state().topology));
|
||||
|
||||
// VkPipelineViewportStateCreateInfo
|
||||
vkb::hash_combine(result, pipeline_state.get_viewport_state().viewport_count);
|
||||
vkb::hash_combine(result, pipeline_state.get_viewport_state().scissor_count);
|
||||
|
||||
// VkPipelineRasterizationStateCreateInfo
|
||||
vkb::hash_combine(result, pipeline_state.get_rasterization_state().cull_mode);
|
||||
vkb::hash_combine(result, pipeline_state.get_rasterization_state().depth_bias_enable);
|
||||
vkb::hash_combine(result, pipeline_state.get_rasterization_state().depth_clamp_enable);
|
||||
vkb::hash_combine(result, static_cast<std::underlying_type<VkFrontFace>::type>(pipeline_state.get_rasterization_state().front_face));
|
||||
vkb::hash_combine(result, static_cast<std::underlying_type<VkPolygonMode>::type>(pipeline_state.get_rasterization_state().polygon_mode));
|
||||
vkb::hash_combine(result, pipeline_state.get_rasterization_state().rasterizer_discard_enable);
|
||||
|
||||
// VkPipelineMultisampleStateCreateInfo
|
||||
vkb::hash_combine(result, pipeline_state.get_multisample_state().alpha_to_coverage_enable);
|
||||
vkb::hash_combine(result, pipeline_state.get_multisample_state().alpha_to_one_enable);
|
||||
vkb::hash_combine(result, pipeline_state.get_multisample_state().min_sample_shading);
|
||||
vkb::hash_combine(result, static_cast<std::underlying_type<VkSampleCountFlagBits>::type>(pipeline_state.get_multisample_state().rasterization_samples));
|
||||
vkb::hash_combine(result, pipeline_state.get_multisample_state().sample_shading_enable);
|
||||
vkb::hash_combine(result, pipeline_state.get_multisample_state().sample_mask);
|
||||
|
||||
// VkPipelineDepthStencilStateCreateInfo
|
||||
vkb::hash_combine(result, pipeline_state.get_depth_stencil_state().back);
|
||||
vkb::hash_combine(result, pipeline_state.get_depth_stencil_state().depth_bounds_test_enable);
|
||||
vkb::hash_combine(result, static_cast<std::underlying_type<VkCompareOp>::type>(pipeline_state.get_depth_stencil_state().depth_compare_op));
|
||||
vkb::hash_combine(result, pipeline_state.get_depth_stencil_state().depth_test_enable);
|
||||
vkb::hash_combine(result, pipeline_state.get_depth_stencil_state().depth_write_enable);
|
||||
vkb::hash_combine(result, pipeline_state.get_depth_stencil_state().front);
|
||||
vkb::hash_combine(result, pipeline_state.get_depth_stencil_state().stencil_test_enable);
|
||||
|
||||
// VkPipelineColorBlendStateCreateInfo
|
||||
vkb::hash_combine(result, static_cast<std::underlying_type<VkLogicOp>::type>(pipeline_state.get_color_blend_state().logic_op));
|
||||
vkb::hash_combine(result, pipeline_state.get_color_blend_state().logic_op_enable);
|
||||
|
||||
for (auto &attachment : pipeline_state.get_color_blend_state().attachments)
|
||||
{
|
||||
vkb::hash_combine(result, attachment);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
};
|
||||
} // namespace std
|
||||
|
||||
namespace vkb
|
||||
{
|
||||
namespace
|
||||
{
|
||||
template <typename T>
|
||||
inline void hash_param(size_t &seed, const T &value)
|
||||
{
|
||||
hash_combine(seed, value);
|
||||
}
|
||||
|
||||
template <>
|
||||
inline void hash_param(size_t & /*seed*/, const VkPipelineCache & /*value*/)
|
||||
{
|
||||
}
|
||||
|
||||
template <>
|
||||
inline void hash_param<std::vector<uint8_t>>(
|
||||
size_t &seed,
|
||||
const std::vector<uint8_t> &value)
|
||||
{
|
||||
hash_combine(seed, std::string{value.begin(), value.end()});
|
||||
}
|
||||
|
||||
template <>
|
||||
inline void hash_param<std::vector<Attachment>>(
|
||||
size_t &seed,
|
||||
const std::vector<Attachment> &value)
|
||||
{
|
||||
for (auto &attachment : value)
|
||||
{
|
||||
hash_combine(seed, attachment);
|
||||
}
|
||||
}
|
||||
|
||||
template <>
|
||||
inline void hash_param<std::vector<LoadStoreInfo>>(
|
||||
size_t &seed,
|
||||
const std::vector<LoadStoreInfo> &value)
|
||||
{
|
||||
for (auto &load_store_info : value)
|
||||
{
|
||||
hash_combine(seed, load_store_info);
|
||||
}
|
||||
}
|
||||
|
||||
template <>
|
||||
inline void hash_param<std::vector<SubpassInfo>>(
|
||||
size_t &seed,
|
||||
const std::vector<SubpassInfo> &value)
|
||||
{
|
||||
for (auto &subpass_info : value)
|
||||
{
|
||||
hash_combine(seed, subpass_info);
|
||||
}
|
||||
}
|
||||
|
||||
template <>
|
||||
inline void hash_param<std::vector<ShaderModule *>>(
|
||||
size_t &seed,
|
||||
const std::vector<ShaderModule *> &value)
|
||||
{
|
||||
for (auto &shader_module : value)
|
||||
{
|
||||
hash_combine(seed, shader_module->get_id());
|
||||
}
|
||||
}
|
||||
|
||||
template <>
|
||||
inline void hash_param<std::vector<ShaderResource>>(
|
||||
size_t &seed,
|
||||
const std::vector<ShaderResource> &value)
|
||||
{
|
||||
for (auto &resource : value)
|
||||
{
|
||||
hash_combine(seed, resource);
|
||||
}
|
||||
}
|
||||
|
||||
template <>
|
||||
inline void hash_param<std::map<uint32_t, std::map<uint32_t, VkDescriptorBufferInfo>>>(
|
||||
size_t &seed,
|
||||
const std::map<uint32_t, std::map<uint32_t, VkDescriptorBufferInfo>> &value)
|
||||
{
|
||||
for (auto &binding_set : value)
|
||||
{
|
||||
hash_combine(seed, binding_set.first);
|
||||
|
||||
for (auto &binding_element : binding_set.second)
|
||||
{
|
||||
hash_combine(seed, binding_element.first);
|
||||
hash_combine(seed, binding_element.second);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <>
|
||||
inline void hash_param<std::map<uint32_t, std::map<uint32_t, VkDescriptorImageInfo>>>(
|
||||
size_t &seed,
|
||||
const std::map<uint32_t, std::map<uint32_t, VkDescriptorImageInfo>> &value)
|
||||
{
|
||||
for (auto &binding_set : value)
|
||||
{
|
||||
hash_combine(seed, binding_set.first);
|
||||
|
||||
for (auto &binding_element : binding_set.second)
|
||||
{
|
||||
hash_combine(seed, binding_element.first);
|
||||
hash_combine(seed, binding_element.second);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename... Args>
|
||||
inline void hash_param(size_t &seed, const T &first_arg, const Args &...args)
|
||||
{
|
||||
hash_param(seed, first_arg);
|
||||
|
||||
hash_param(seed, args...);
|
||||
}
|
||||
|
||||
template <class T, class... A>
|
||||
struct RecordHelper
|
||||
{
|
||||
size_t record(ResourceRecord & /*recorder*/, A &.../*args*/)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
void index(ResourceRecord & /*recorder*/, size_t /*index*/, T & /*resource*/)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
template <class... A>
|
||||
struct RecordHelper<ShaderModule, A...>
|
||||
{
|
||||
size_t record(ResourceRecord &recorder, A &...args)
|
||||
{
|
||||
return recorder.register_shader_module(args...);
|
||||
}
|
||||
|
||||
void index(ResourceRecord &recorder, size_t index, ShaderModule &shader_module)
|
||||
{
|
||||
recorder.set_shader_module(index, shader_module);
|
||||
}
|
||||
};
|
||||
|
||||
template <class... A>
|
||||
struct RecordHelper<PipelineLayout, A...>
|
||||
{
|
||||
size_t record(ResourceRecord &recorder, A &...args)
|
||||
{
|
||||
return recorder.register_pipeline_layout(args...);
|
||||
}
|
||||
|
||||
void index(ResourceRecord &recorder, size_t index, PipelineLayout &pipeline_layout)
|
||||
{
|
||||
recorder.set_pipeline_layout(index, pipeline_layout);
|
||||
}
|
||||
};
|
||||
|
||||
template <class... A>
|
||||
struct RecordHelper<RenderPass, A...>
|
||||
{
|
||||
size_t record(ResourceRecord &recorder, A &...args)
|
||||
{
|
||||
return recorder.register_render_pass(args...);
|
||||
}
|
||||
|
||||
void index(ResourceRecord &recorder, size_t index, RenderPass &render_pass)
|
||||
{
|
||||
recorder.set_render_pass(index, render_pass);
|
||||
}
|
||||
};
|
||||
|
||||
template <class... A>
|
||||
struct RecordHelper<GraphicsPipeline, A...>
|
||||
{
|
||||
size_t record(ResourceRecord &recorder, A &...args)
|
||||
{
|
||||
return recorder.register_graphics_pipeline(args...);
|
||||
}
|
||||
|
||||
void index(ResourceRecord &recorder, size_t index, GraphicsPipeline &graphics_pipeline)
|
||||
{
|
||||
recorder.set_graphics_pipeline(index, graphics_pipeline);
|
||||
}
|
||||
};
|
||||
} // namespace
|
||||
|
||||
template <class T, class... A>
|
||||
T &request_resource(vkb::core::DeviceC &device, ResourceRecord *recorder, std::unordered_map<std::size_t, T> &resources, A &...args)
|
||||
{
|
||||
RecordHelper<T, A...> record_helper;
|
||||
|
||||
std::size_t hash{0U};
|
||||
hash_param(hash, args...);
|
||||
|
||||
auto res_it = resources.find(hash);
|
||||
|
||||
if (res_it != resources.end())
|
||||
{
|
||||
return res_it->second;
|
||||
}
|
||||
|
||||
// If we do not have it already, create and cache it
|
||||
const char *res_type = typeid(T).name();
|
||||
size_t res_id = resources.size();
|
||||
|
||||
LOGD("Building #{} cache object ({})", res_id, res_type);
|
||||
|
||||
// Only error handle in release
|
||||
#ifndef DEBUG
|
||||
try
|
||||
{
|
||||
#endif
|
||||
T resource(device, args...);
|
||||
|
||||
auto res_ins_it = resources.emplace(hash, std::move(resource));
|
||||
|
||||
if (!res_ins_it.second)
|
||||
{
|
||||
throw std::runtime_error{std::string{"Insertion error for #"} + std::to_string(res_id) + "cache object (" + res_type + ")"};
|
||||
}
|
||||
|
||||
res_it = res_ins_it.first;
|
||||
|
||||
if (recorder)
|
||||
{
|
||||
size_t index = record_helper.record(*recorder, args...);
|
||||
record_helper.index(*recorder, index, res_it->second);
|
||||
}
|
||||
#ifndef DEBUG
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
LOGE("Creation error for #{} cache object ({})", res_id, res_type);
|
||||
throw e;
|
||||
}
|
||||
#endif
|
||||
|
||||
return res_it->second;
|
||||
}
|
||||
} // namespace vkb
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,304 @@
|
||||
/* Copyright (c) 2018-2024, 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 <map>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include <volk.h>
|
||||
|
||||
namespace vkb
|
||||
{
|
||||
enum class ShaderResourceType;
|
||||
|
||||
namespace sg
|
||||
{
|
||||
enum class AlphaMode;
|
||||
}
|
||||
|
||||
std::vector<std::string> split(const std::string &str, const std::string &delimiter);
|
||||
std::string join(const std::vector<std::string> &str, const std::string &separator);
|
||||
|
||||
/**
|
||||
* @brief Helper function to convert a VkFormat enum to a string
|
||||
* @param format Vulkan format to convert.
|
||||
* @return The string to return.
|
||||
*/
|
||||
const std::string to_string(VkFormat format);
|
||||
|
||||
/**
|
||||
* @brief Helper function to convert a VkPresentModeKHR to a string
|
||||
* @param present_mode Vulkan present mode to convert.
|
||||
* @return The string to return.
|
||||
*/
|
||||
const std::string to_string(VkPresentModeKHR present_mode);
|
||||
|
||||
/**
|
||||
* @brief Helper function to convert a VkResult enum to a string
|
||||
* @param result Vulkan result to convert.
|
||||
* @return The string to return.
|
||||
*/
|
||||
const std::string to_string(VkResult result);
|
||||
|
||||
/**
|
||||
* @brief Helper function to convert a VkPhysicalDeviceType enum to a string
|
||||
* @param type Vulkan physical device type to convert.
|
||||
* @return The string to return.
|
||||
*/
|
||||
const std::string to_string(VkPhysicalDeviceType type);
|
||||
|
||||
/**
|
||||
* @brief Helper function to convert a VkSurfaceTransformFlagBitsKHR flag to a string
|
||||
* @param transform_flag Vulkan surface transform flag bit to convert.
|
||||
* @return The string to return.
|
||||
*/
|
||||
const std::string to_string(VkSurfaceTransformFlagBitsKHR transform_flag);
|
||||
|
||||
/**
|
||||
* @brief Helper function to convert a VkSurfaceFormatKHR format to a string
|
||||
* @param surface_format Vulkan surface format to convert.
|
||||
* @return The string to return.
|
||||
*/
|
||||
const std::string to_string(VkSurfaceFormatKHR surface_format);
|
||||
|
||||
/**
|
||||
* @brief Helper function to convert a VkCompositeAlphaFlagBitsKHR flag to a string
|
||||
* @param composite_alpha Vulkan composite alpha flag bit to convert.
|
||||
* @return The string to return.
|
||||
*/
|
||||
const std::string to_string(VkCompositeAlphaFlagBitsKHR composite_alpha);
|
||||
|
||||
/**
|
||||
* @brief Helper function to convert a VkImageUsageFlagBits flag to a string
|
||||
* @param image_usage Vulkan image usage flag bit to convert.
|
||||
* @return The string to return.
|
||||
*/
|
||||
const std::string to_string(VkImageUsageFlagBits image_usage);
|
||||
|
||||
/**
|
||||
* @brief Helper function to convert a VkExtent2D flag to a string
|
||||
* @param format Vulkan format to convert.
|
||||
* @return The string to return.
|
||||
*/
|
||||
std::string to_string(VkExtent2D format);
|
||||
|
||||
/**
|
||||
* @brief Helper function to convert VkSampleCountFlagBits to a string
|
||||
* @param flags Vulkan sample count flags to convert
|
||||
* @return const std::string
|
||||
*/
|
||||
const std::string to_string(VkSampleCountFlagBits flags);
|
||||
|
||||
/**
|
||||
* @brief Helper function to convert VkImageTiling to a string
|
||||
* @param tiling Vulkan VkImageTiling to convert
|
||||
* @return The string to return
|
||||
*/
|
||||
const std::string to_string(VkImageTiling tiling);
|
||||
|
||||
/**
|
||||
* @brief Helper function to convert VkImageType to a string
|
||||
* @param type Vulkan VkImageType to convert
|
||||
* @return The string to return
|
||||
*/
|
||||
const std::string to_string(VkImageType type);
|
||||
|
||||
/**
|
||||
* @brief Helper function to convert VkBlendFactor to a string
|
||||
* @param blend Vulkan VkBlendFactor to convert
|
||||
* @return The string to return
|
||||
*/
|
||||
const std::string to_string(VkBlendFactor blend);
|
||||
|
||||
/**
|
||||
* @brief Helper function to convert VkVertexInputRate to a string
|
||||
* @param rate Vulkan VkVertexInputRate to convert
|
||||
* @return The string to return
|
||||
*/
|
||||
const std::string to_string(VkVertexInputRate rate);
|
||||
|
||||
/**
|
||||
* @brief Helper function to convert VkBool32 to a string
|
||||
* @param state Vulkan VkBool32 to convert
|
||||
* @return The string to return
|
||||
*/
|
||||
const std::string to_string_vk_bool(VkBool32 state);
|
||||
|
||||
/**
|
||||
* @brief Helper function to convert VkPrimitiveTopology to a string
|
||||
* @param topology Vulkan VkPrimitiveTopology to convert
|
||||
* @return The string to return
|
||||
*/
|
||||
const std::string to_string(VkPrimitiveTopology topology);
|
||||
|
||||
/**
|
||||
* @brief Helper function to convert VkFrontFace to a string
|
||||
* @param face Vulkan VkFrontFace to convert
|
||||
* @return The string to return
|
||||
*/
|
||||
const std::string to_string(VkFrontFace face);
|
||||
|
||||
/**
|
||||
* @brief Helper function to convert VkPolygonMode to a string
|
||||
* @param mode Vulkan VkPolygonMode to convert
|
||||
* @return The string to return
|
||||
*/
|
||||
const std::string to_string(VkPolygonMode mode);
|
||||
|
||||
/**
|
||||
* @brief Helper function to convert VkCompareOp to a string
|
||||
* @param operation Vulkan VkCompareOp to convert
|
||||
* @return The string to return
|
||||
*/
|
||||
const std::string to_string(VkCompareOp operation);
|
||||
|
||||
/**
|
||||
* @brief Helper function to convert VkStencilOp to a string
|
||||
* @param operation Vulkan VkStencilOp to convert
|
||||
* @return The string to return
|
||||
*/
|
||||
const std::string to_string(VkStencilOp operation);
|
||||
|
||||
/**
|
||||
* @brief Helper function to convert VkLogicOp to a string
|
||||
* @param operation Vulkan VkLogicOp to convert
|
||||
* @return The string to return
|
||||
*/
|
||||
const std::string to_string(VkLogicOp operation);
|
||||
|
||||
/**
|
||||
* @brief Helper function to convert VkBlendOp to a string
|
||||
* @param operation Vulkan VkBlendOp to convert
|
||||
* @return The string to return
|
||||
*/
|
||||
const std::string to_string(VkBlendOp operation);
|
||||
|
||||
/**
|
||||
* @brief Helper function to convert AlphaMode to a string
|
||||
* @param mode Vulkan AlphaMode to convert
|
||||
* @return The string to return
|
||||
*/
|
||||
const std::string to_string(sg::AlphaMode mode);
|
||||
|
||||
/**
|
||||
* @brief Helper function to convert bool to a string
|
||||
* @param flag Vulkan bool to convert (true/false)
|
||||
* @return The string to return
|
||||
*/
|
||||
const std::string to_string(bool flag);
|
||||
|
||||
/**
|
||||
* @brief Helper function to convert ShaderResourceType to a string
|
||||
* @param type Vulkan ShaderResourceType to convert
|
||||
* @return The string to return
|
||||
*/
|
||||
const std::string to_string(ShaderResourceType type);
|
||||
|
||||
/**
|
||||
* @brief Helper generic function to convert a bitmask to a string of its components
|
||||
* @param bitmask The bitmask to convert
|
||||
* @param string_map A map of bitmask bits to the string that describe the Vulkan flag
|
||||
* @returns A string of the enabled bits in the bitmask
|
||||
*/
|
||||
template <typename T>
|
||||
inline const std::string to_string(uint32_t bitmask, const std::map<T, const char *> string_map)
|
||||
{
|
||||
std::stringstream result;
|
||||
bool append = false;
|
||||
for (const auto &s : string_map)
|
||||
{
|
||||
if (bitmask & s.first)
|
||||
{
|
||||
if (append)
|
||||
{
|
||||
result << " | ";
|
||||
}
|
||||
result << s.second;
|
||||
append = true;
|
||||
}
|
||||
}
|
||||
return result.str();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Helper function to convert VkBufferUsageFlags to a string
|
||||
* @param bitmask The buffer usage bitmask to convert to strings
|
||||
* @return The converted string to return
|
||||
*/
|
||||
const std::string buffer_usage_to_string(VkBufferUsageFlags bitmask);
|
||||
|
||||
/**
|
||||
* @brief Helper function to convert VkShaderStageFlags to a string
|
||||
* @param bitmask The shader stage bitmask to convert
|
||||
* @return The converted string to return
|
||||
*/
|
||||
const std::string shader_stage_to_string(VkShaderStageFlags bitmask);
|
||||
|
||||
/**
|
||||
* @brief Helper function to convert VkImageUsageFlags to a string
|
||||
* @param bitmask The image usage bitmask to convert
|
||||
* @return The converted string to return
|
||||
*/
|
||||
const std::string image_usage_to_string(VkImageUsageFlags bitmask);
|
||||
|
||||
/**
|
||||
* @brief Helper function to convert VkImageAspectFlags to a string
|
||||
* @param bitmask The image aspect bitmask to convert
|
||||
* @return The converted string to return
|
||||
*/
|
||||
const std::string image_aspect_to_string(VkImageAspectFlags bitmask);
|
||||
|
||||
/**
|
||||
* @brief Helper function to convert VkCullModeFlags to a string
|
||||
* @param bitmask The cull mode bitmask to convert
|
||||
* @return The converted string to return
|
||||
*/
|
||||
const std::string cull_mode_to_string(VkCullModeFlags bitmask);
|
||||
|
||||
/**
|
||||
* @brief Helper function to convert VkColorComponentFlags to a string
|
||||
* @param bitmask The color component bitmask to convert
|
||||
* @return The converted string to return
|
||||
*/
|
||||
const std::string color_component_to_string(VkColorComponentFlags bitmask);
|
||||
|
||||
/**
|
||||
* @brief Helper function to convert VkImageCompressionFlagsEXT to a string
|
||||
* @param flags The flags to convert
|
||||
* @return The converted string to return
|
||||
*/
|
||||
const std::string image_compression_flags_to_string(VkImageCompressionFlagsEXT flags);
|
||||
|
||||
/**
|
||||
* @brief Helper function to convert VkImageCompressionFixedRateFlagsEXT to a string
|
||||
* @param flags The flags to convert
|
||||
* @return The converted string to return
|
||||
*/
|
||||
const std::string image_compression_fixed_rate_flags_to_string(VkImageCompressionFixedRateFlagsEXT flags);
|
||||
|
||||
/**
|
||||
* @brief Helper function to split a single string into a vector of strings by a delimiter
|
||||
* @param input The input string to be split
|
||||
* @param delim The character to delimit by
|
||||
* @return The vector of tokenized strings
|
||||
*/
|
||||
std::vector<std::string> split(const std::string &input, char delim);
|
||||
} // namespace vkb
|
||||
@@ -0,0 +1,85 @@
|
||||
/* Copyright (c) 2020-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 <algorithm>
|
||||
#include <vector>
|
||||
|
||||
/**
|
||||
* @brief Used to represent a tag
|
||||
*/
|
||||
typedef void (*TagID)();
|
||||
|
||||
/**
|
||||
* @brief Tag acts as a unique identifier to categories objects
|
||||
*
|
||||
* Tags are uniquely defined using different type names. The easiest way of creating a new tag is to use an empty struct
|
||||
* struct TagName{};
|
||||
* struct DifferentTag{};
|
||||
* Tag<TagName>::ID == Tag<TagName>::member != Tag<DifferentTag>:ID
|
||||
*
|
||||
* @tparam TAGS A set of tags
|
||||
*/
|
||||
template <typename... TAGS>
|
||||
class Tag
|
||||
{
|
||||
public:
|
||||
Tag()
|
||||
{
|
||||
tags = {Tag<TAGS>::ID...};
|
||||
}
|
||||
|
||||
static void member(){};
|
||||
|
||||
/**
|
||||
* @brief Unique TagID for a given Tag<TagName>
|
||||
*/
|
||||
constexpr static TagID ID = &member;
|
||||
|
||||
static bool has_tag(TagID id)
|
||||
{
|
||||
return std::ranges::find(tags, id) != tags.end();
|
||||
}
|
||||
|
||||
template <typename C>
|
||||
static bool has_tag()
|
||||
{
|
||||
return has_tag(Tag<C>::ID);
|
||||
}
|
||||
|
||||
template <typename... C>
|
||||
static bool has_tags()
|
||||
{
|
||||
std::vector<TagID> query = {Tag<C>::ID...};
|
||||
bool res = true;
|
||||
for (auto id : query)
|
||||
{
|
||||
res &= has_tag(id);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
private:
|
||||
static std::vector<TagID> tags;
|
||||
};
|
||||
|
||||
#ifndef DOXYGEN_SKIP
|
||||
|
||||
template <typename... TAGS>
|
||||
std::vector<TagID> Tag<TAGS...>::tags;
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,311 @@
|
||||
/* Copyright (c) 2018-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 "utils.h"
|
||||
|
||||
#include <queue>
|
||||
#include <stdexcept>
|
||||
|
||||
#include "scene_graph/components/material.h"
|
||||
#include "scene_graph/components/perspective_camera.h"
|
||||
#include "scene_graph/components/sub_mesh.h"
|
||||
#include "scene_graph/node.h"
|
||||
#include "scene_graph/script.h"
|
||||
#include "scene_graph/scripts/free_camera.h"
|
||||
|
||||
namespace vkb
|
||||
{
|
||||
std::string get_extension(const std::string &uri)
|
||||
{
|
||||
auto dot_pos = uri.find_last_of('.');
|
||||
if (dot_pos == std::string::npos)
|
||||
{
|
||||
throw std::runtime_error{"Uri has no extension"};
|
||||
}
|
||||
|
||||
return uri.substr(dot_pos + 1);
|
||||
}
|
||||
|
||||
void screenshot(RenderContext &render_context, const std::string &filename)
|
||||
{
|
||||
assert(render_context.get_format() == VK_FORMAT_R8G8B8A8_UNORM ||
|
||||
render_context.get_format() == VK_FORMAT_B8G8R8A8_UNORM ||
|
||||
render_context.get_format() == VK_FORMAT_R8G8B8A8_SRGB ||
|
||||
render_context.get_format() == VK_FORMAT_B8G8R8A8_SRGB);
|
||||
|
||||
// We want the last completed frame since we don't want to be reading from an incomplete framebuffer
|
||||
auto &frame = render_context.get_last_rendered_frame();
|
||||
assert(!frame.get_render_target().get_views().empty());
|
||||
auto &src_image_view = frame.get_render_target().get_views()[0];
|
||||
|
||||
auto width = render_context.get_surface_extent().width;
|
||||
auto height = render_context.get_surface_extent().height;
|
||||
auto dst_size = width * height * 4;
|
||||
|
||||
vkb::core::BufferC dst_buffer{render_context.get_device(),
|
||||
dst_size,
|
||||
VK_BUFFER_USAGE_TRANSFER_DST_BIT,
|
||||
VMA_MEMORY_USAGE_GPU_TO_CPU,
|
||||
VMA_ALLOCATION_CREATE_MAPPED_BIT};
|
||||
|
||||
const auto &queue = render_context.get_device().get_queue_by_flags(VK_QUEUE_GRAPHICS_BIT, 0);
|
||||
|
||||
auto cmd_buf = render_context.get_device().get_command_pool().request_command_buffer();
|
||||
|
||||
cmd_buf->begin(VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT);
|
||||
|
||||
// Enable destination buffer to be written to
|
||||
{
|
||||
BufferMemoryBarrier memory_barrier{};
|
||||
memory_barrier.src_access_mask = 0;
|
||||
memory_barrier.dst_access_mask = VK_ACCESS_TRANSFER_WRITE_BIT;
|
||||
memory_barrier.src_stage_mask = VK_PIPELINE_STAGE_TRANSFER_BIT;
|
||||
memory_barrier.dst_stage_mask = VK_PIPELINE_STAGE_TRANSFER_BIT;
|
||||
|
||||
cmd_buf->buffer_memory_barrier(dst_buffer, 0, dst_size, memory_barrier);
|
||||
}
|
||||
|
||||
// Enable framebuffer image view to be read from
|
||||
{
|
||||
ImageMemoryBarrier memory_barrier{};
|
||||
memory_barrier.old_layout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
|
||||
memory_barrier.new_layout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
|
||||
memory_barrier.src_stage_mask = VK_PIPELINE_STAGE_TRANSFER_BIT;
|
||||
memory_barrier.dst_stage_mask = VK_PIPELINE_STAGE_TRANSFER_BIT;
|
||||
|
||||
cmd_buf->image_memory_barrier(src_image_view, memory_barrier);
|
||||
}
|
||||
|
||||
// Check if framebuffer images are in a BGR format
|
||||
auto bgr_formats = {VK_FORMAT_B8G8R8A8_SRGB, VK_FORMAT_B8G8R8A8_UNORM, VK_FORMAT_B8G8R8A8_SNORM};
|
||||
bool swizzle = std::ranges::find(bgr_formats, src_image_view.get_format()) != bgr_formats.end();
|
||||
|
||||
// Copy framebuffer image memory
|
||||
VkBufferImageCopy image_copy_region{};
|
||||
image_copy_region.bufferRowLength = width;
|
||||
image_copy_region.bufferImageHeight = height;
|
||||
image_copy_region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||
image_copy_region.imageSubresource.layerCount = 1;
|
||||
image_copy_region.imageExtent.width = width;
|
||||
image_copy_region.imageExtent.height = height;
|
||||
image_copy_region.imageExtent.depth = 1;
|
||||
|
||||
cmd_buf->copy_image_to_buffer(src_image_view.get_image(), VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, dst_buffer, {image_copy_region});
|
||||
|
||||
// Enable destination buffer to map memory
|
||||
{
|
||||
BufferMemoryBarrier memory_barrier{};
|
||||
memory_barrier.src_access_mask = VK_ACCESS_TRANSFER_WRITE_BIT;
|
||||
memory_barrier.dst_access_mask = VK_ACCESS_HOST_READ_BIT;
|
||||
memory_barrier.src_stage_mask = VK_PIPELINE_STAGE_TRANSFER_BIT;
|
||||
memory_barrier.dst_stage_mask = VK_PIPELINE_STAGE_HOST_BIT;
|
||||
|
||||
cmd_buf->buffer_memory_barrier(dst_buffer, 0, dst_size, memory_barrier);
|
||||
}
|
||||
|
||||
// Revert back the framebuffer image view from transfer to present
|
||||
{
|
||||
ImageMemoryBarrier memory_barrier{};
|
||||
memory_barrier.old_layout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
|
||||
memory_barrier.new_layout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
|
||||
memory_barrier.src_stage_mask = VK_PIPELINE_STAGE_TRANSFER_BIT;
|
||||
memory_barrier.dst_stage_mask = VK_PIPELINE_STAGE_TRANSFER_BIT;
|
||||
|
||||
cmd_buf->image_memory_barrier(src_image_view, memory_barrier);
|
||||
}
|
||||
|
||||
cmd_buf->end();
|
||||
|
||||
queue.submit(*cmd_buf, frame.get_fence_pool().request_fence());
|
||||
|
||||
queue.wait_idle();
|
||||
|
||||
auto raw_data = dst_buffer.map();
|
||||
|
||||
// Creates a pointer to the address of the first byte of the image data
|
||||
// Replace the A component with 255 (remove transparency)
|
||||
// If swapchain format is BGR, swapping the R and B components
|
||||
uint8_t *data = raw_data;
|
||||
if (swizzle)
|
||||
{
|
||||
for (size_t i = 0; i < height; ++i)
|
||||
{
|
||||
// Iterate over each pixel, swapping R and B components and writing the max value for alpha
|
||||
for (size_t j = 0; j < width; ++j)
|
||||
{
|
||||
auto temp = *(data + 2);
|
||||
*(data + 2) = *(data);
|
||||
*(data) = temp;
|
||||
*(data + 3) = 255;
|
||||
|
||||
// Get next pixel
|
||||
data += 4;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (size_t i = 0; i < height; ++i)
|
||||
{
|
||||
// Iterate over each pixel, writing the max value for alpha
|
||||
for (size_t j = 0; j < width; ++j)
|
||||
{
|
||||
*(data + 3) = 255;
|
||||
|
||||
// Get next pixel
|
||||
data += 4;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
vkb::fs::write_image(raw_data,
|
||||
filename,
|
||||
width,
|
||||
height,
|
||||
4,
|
||||
width * 4);
|
||||
|
||||
dst_buffer.unmap();
|
||||
} // namespace vkb
|
||||
|
||||
std::string to_snake_case(const std::string &text)
|
||||
{
|
||||
std::stringstream result;
|
||||
|
||||
for (const auto ch : text)
|
||||
{
|
||||
if (std::isalpha(ch))
|
||||
{
|
||||
if (std::isspace(ch))
|
||||
{
|
||||
result << "_";
|
||||
}
|
||||
else
|
||||
{
|
||||
if (std::isupper(ch))
|
||||
{
|
||||
result << "_";
|
||||
}
|
||||
|
||||
result << static_cast<char>(std::tolower(ch));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
result << ch;
|
||||
}
|
||||
}
|
||||
|
||||
return result.str();
|
||||
}
|
||||
|
||||
sg::Light &add_light(sg::Scene &scene, sg::LightType type, const glm::vec3 &position, const glm::quat &rotation, const sg::LightProperties &props, sg::Node *parent_node)
|
||||
{
|
||||
auto light_ptr = std::make_unique<sg::Light>("light");
|
||||
auto node = std::make_unique<sg::Node>(-1, "light node");
|
||||
|
||||
if (parent_node)
|
||||
{
|
||||
node->set_parent(*parent_node);
|
||||
}
|
||||
|
||||
light_ptr->set_node(*node);
|
||||
light_ptr->set_light_type(type);
|
||||
light_ptr->set_properties(props);
|
||||
|
||||
auto &t = node->get_transform();
|
||||
t.set_translation(position);
|
||||
t.set_rotation(rotation);
|
||||
|
||||
// Storing the light component because the unique_ptr will be moved to the scene
|
||||
auto &light = *light_ptr;
|
||||
|
||||
node->set_component(light);
|
||||
scene.add_child(*node);
|
||||
scene.add_component(std::move(light_ptr));
|
||||
scene.add_node(std::move(node));
|
||||
|
||||
return light;
|
||||
}
|
||||
|
||||
sg::Light &add_point_light(sg::Scene &scene, const glm::vec3 &position, const sg::LightProperties &props, sg::Node *parent_node)
|
||||
{
|
||||
return add_light(scene, sg::LightType::Point, position, {}, props, parent_node);
|
||||
}
|
||||
|
||||
sg::Light &add_directional_light(sg::Scene &scene, const glm::quat &rotation, const sg::LightProperties &props, sg::Node *parent_node)
|
||||
{
|
||||
return add_light(scene, sg::LightType::Directional, {}, rotation, props, parent_node);
|
||||
}
|
||||
|
||||
sg::Light &add_spot_light(sg::Scene &scene, const glm::vec3 &position, const glm::quat &rotation, const sg::LightProperties &props, sg::Node *parent_node)
|
||||
{
|
||||
return add_light(scene, sg::LightType::Spot, position, rotation, props, parent_node);
|
||||
}
|
||||
|
||||
sg::Node &add_free_camera(sg::Scene &scene, const std::string &node_name, VkExtent2D extent)
|
||||
{
|
||||
auto camera_node = scene.find_node(node_name);
|
||||
|
||||
if (!camera_node)
|
||||
{
|
||||
LOGW("Camera node `{}` not found. Looking for `default_camera` node.", node_name.c_str());
|
||||
|
||||
camera_node = scene.find_node("default_camera");
|
||||
}
|
||||
|
||||
if (!camera_node)
|
||||
{
|
||||
throw std::runtime_error("Camera node with name `" + node_name + "` not found.");
|
||||
}
|
||||
|
||||
if (!camera_node->has_component<sg::Camera>())
|
||||
{
|
||||
throw std::runtime_error("No camera component found for `" + node_name + "` node.");
|
||||
}
|
||||
|
||||
auto free_camera_script = std::make_unique<sg::FreeCamera>(*camera_node);
|
||||
|
||||
free_camera_script->resize(extent.width, extent.height);
|
||||
|
||||
scene.add_component(std::move(free_camera_script), *camera_node);
|
||||
|
||||
return *camera_node;
|
||||
}
|
||||
|
||||
size_t calculate_hash(const std::vector<uint8_t> &data)
|
||||
{
|
||||
static_assert(sizeof(data[0]) == 1);
|
||||
constexpr size_t chunk_size = sizeof(size_t) / sizeof(data[0]);
|
||||
size_t data_hash = 0;
|
||||
size_t offset = 0;
|
||||
|
||||
for (; offset + chunk_size < data.size(); offset += chunk_size)
|
||||
{
|
||||
glm::detail::hash_combine(data_hash, *reinterpret_cast<size_t const *>(&data[offset]));
|
||||
}
|
||||
|
||||
if (offset < data.size())
|
||||
{
|
||||
size_t it = 0;
|
||||
std::memcpy(&it, &data[offset], data.size() - offset);
|
||||
glm::detail::hash_combine(data_hash, it);
|
||||
}
|
||||
return data_hash;
|
||||
}
|
||||
|
||||
} // namespace vkb
|
||||
@@ -0,0 +1,112 @@
|
||||
/* Copyright (c) 2018-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 "common/error.h"
|
||||
|
||||
#include "common/glm_common.h"
|
||||
#include "glm/gtx/quaternion.hpp"
|
||||
|
||||
#include "filesystem/legacy.h"
|
||||
#include "rendering/pipeline_state.h"
|
||||
#include "rendering/render_context.h"
|
||||
#include "scene_graph/components/sub_mesh.h"
|
||||
#include "scene_graph/scene.h"
|
||||
|
||||
namespace vkb
|
||||
{
|
||||
/**
|
||||
* @brief Extracts the extension from an uri
|
||||
* @param uri An uniform Resource Identifier
|
||||
* @return The extension
|
||||
*/
|
||||
std::string get_extension(const std::string &uri);
|
||||
/**
|
||||
* @brief Generates a hash from an array
|
||||
* @param data
|
||||
* @return data_hash hash of the data
|
||||
*/
|
||||
size_t calculate_hash(const std::vector<uint8_t> &data);
|
||||
|
||||
/**
|
||||
* @param name String to convert to snake case
|
||||
* @return a snake case version of the string
|
||||
*/
|
||||
std::string to_snake_case(const std::string &name);
|
||||
|
||||
/**
|
||||
* @brief Takes a screenshot of the app by writing the swapchain image to file (slow function)
|
||||
* @param render_context The RenderContext to use
|
||||
* @param filename The name of the file to save the output to
|
||||
*/
|
||||
void screenshot(RenderContext &render_context, const std::string &filename);
|
||||
|
||||
/**
|
||||
* @brief Adds a light to the scene with the specified parameters
|
||||
* @param scene The scene to add the light to
|
||||
* @param type The light type
|
||||
* @param position The position of the light
|
||||
* @param rotation The rotation of the light
|
||||
* @param props The light properties, such as color and intensity
|
||||
* @param parent_node The parent node for the line, defaults to root
|
||||
* @return The newly created light component
|
||||
*/
|
||||
sg::Light &add_light(sg::Scene &scene, sg::LightType type, const glm::vec3 &position, const glm::quat &rotation = {}, const sg::LightProperties &props = {}, sg::Node *parent_node = nullptr);
|
||||
|
||||
/**
|
||||
* @brief Adds a point light to the scene with the specified parameters
|
||||
* @param scene The scene to add the light to
|
||||
* @param position The position of the light
|
||||
* @param props The light properties, such as color and intensity
|
||||
* @param parent_node The parent node for the line, defaults to root
|
||||
* @return The newly created light component
|
||||
*/
|
||||
sg::Light &add_point_light(sg::Scene &scene, const glm::vec3 &position, const sg::LightProperties &props = {}, sg::Node *parent_node = nullptr);
|
||||
|
||||
/**
|
||||
* @brief Adds a directional light to the scene with the specified parameters
|
||||
* @param scene The scene to add the light to
|
||||
* @param rotation The rotation of the light
|
||||
* @param props The light properties, such as color and intensity
|
||||
* @param parent_node The parent node for the line, defaults to root
|
||||
* @return The newly created light component
|
||||
*/
|
||||
sg::Light &add_directional_light(sg::Scene &scene, const glm::quat &rotation, const sg::LightProperties &props = {}, sg::Node *parent_node = nullptr);
|
||||
|
||||
/**
|
||||
* @brief Adds a spot light to the scene with the specified parameters
|
||||
* @param scene The scene to add the light to
|
||||
* @param position The position of the light
|
||||
* @param rotation The rotation of the light
|
||||
* @param props The light properties, such as color and intensity
|
||||
* @param parent_node The parent node for the line, defaults to root
|
||||
* @return The newly created light component
|
||||
*/
|
||||
sg::Light &add_spot_light(sg::Scene &scene, const glm::vec3 &position, const glm::quat &rotation, const sg::LightProperties &props = {}, sg::Node *parent_node = nullptr);
|
||||
|
||||
/**
|
||||
* @brief Add free camera script to a node with a camera object.
|
||||
* Fallback to the default_camera if node not found.
|
||||
* @param scene The scene to add the camera to
|
||||
* @param node_name The scene node name
|
||||
* @param extent The initial resolution of the camera
|
||||
* @return Node where the script was attached as component
|
||||
*/
|
||||
sg::Node &add_free_camera(sg::Scene &scene, const std::string &node_name, VkExtent2D extent);
|
||||
|
||||
} // namespace vkb
|
||||
@@ -0,0 +1,719 @@
|
||||
/* Copyright (c) 2018-2025, Arm Limited and Contributors
|
||||
* Copyright (c) 2019-2025, 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.
|
||||
*/
|
||||
|
||||
#include "vk_common.h"
|
||||
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include "filesystem/legacy.h"
|
||||
|
||||
std::ostream &operator<<(std::ostream &os, const VkResult result)
|
||||
{
|
||||
#define WRITE_VK_ENUM(r) \
|
||||
case VK_##r: \
|
||||
os << #r; \
|
||||
break;
|
||||
|
||||
switch (result)
|
||||
{
|
||||
WRITE_VK_ENUM(NOT_READY);
|
||||
WRITE_VK_ENUM(TIMEOUT);
|
||||
WRITE_VK_ENUM(EVENT_SET);
|
||||
WRITE_VK_ENUM(EVENT_RESET);
|
||||
WRITE_VK_ENUM(INCOMPLETE);
|
||||
WRITE_VK_ENUM(ERROR_OUT_OF_HOST_MEMORY);
|
||||
WRITE_VK_ENUM(ERROR_OUT_OF_DEVICE_MEMORY);
|
||||
WRITE_VK_ENUM(ERROR_INITIALIZATION_FAILED);
|
||||
WRITE_VK_ENUM(ERROR_DEVICE_LOST);
|
||||
WRITE_VK_ENUM(ERROR_MEMORY_MAP_FAILED);
|
||||
WRITE_VK_ENUM(ERROR_LAYER_NOT_PRESENT);
|
||||
WRITE_VK_ENUM(ERROR_EXTENSION_NOT_PRESENT);
|
||||
WRITE_VK_ENUM(ERROR_FEATURE_NOT_PRESENT);
|
||||
WRITE_VK_ENUM(ERROR_INCOMPATIBLE_DRIVER);
|
||||
WRITE_VK_ENUM(ERROR_TOO_MANY_OBJECTS);
|
||||
WRITE_VK_ENUM(ERROR_FORMAT_NOT_SUPPORTED);
|
||||
WRITE_VK_ENUM(ERROR_SURFACE_LOST_KHR);
|
||||
WRITE_VK_ENUM(ERROR_NATIVE_WINDOW_IN_USE_KHR);
|
||||
WRITE_VK_ENUM(SUBOPTIMAL_KHR);
|
||||
WRITE_VK_ENUM(ERROR_OUT_OF_DATE_KHR);
|
||||
WRITE_VK_ENUM(ERROR_INCOMPATIBLE_DISPLAY_KHR);
|
||||
WRITE_VK_ENUM(ERROR_VALIDATION_FAILED_EXT);
|
||||
WRITE_VK_ENUM(ERROR_INVALID_SHADER_NV);
|
||||
default:
|
||||
os << "UNKNOWN_ERROR";
|
||||
}
|
||||
|
||||
#undef WRITE_VK_ENUM
|
||||
|
||||
return os;
|
||||
}
|
||||
|
||||
namespace vkb
|
||||
{
|
||||
bool is_depth_only_format(VkFormat format)
|
||||
{
|
||||
return format == VK_FORMAT_D16_UNORM ||
|
||||
format == VK_FORMAT_D32_SFLOAT;
|
||||
}
|
||||
|
||||
bool is_depth_stencil_format(VkFormat format)
|
||||
{
|
||||
return format == VK_FORMAT_D16_UNORM_S8_UINT ||
|
||||
format == VK_FORMAT_D24_UNORM_S8_UINT ||
|
||||
format == VK_FORMAT_D32_SFLOAT_S8_UINT;
|
||||
}
|
||||
|
||||
bool is_depth_format(VkFormat format)
|
||||
{
|
||||
return is_depth_only_format(format) || is_depth_stencil_format(format);
|
||||
}
|
||||
|
||||
VkFormat get_suitable_depth_format(VkPhysicalDevice physical_device, bool depth_only, const std::vector<VkFormat> &depth_format_priority_list)
|
||||
{
|
||||
VkFormat depth_format{VK_FORMAT_UNDEFINED};
|
||||
|
||||
for (auto &format : depth_format_priority_list)
|
||||
{
|
||||
if (depth_only && !is_depth_only_format(format))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
VkFormatProperties properties;
|
||||
vkGetPhysicalDeviceFormatProperties(physical_device, format, &properties);
|
||||
|
||||
// Format must support depth stencil attachment for optimal tiling
|
||||
if (properties.optimalTilingFeatures & VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT)
|
||||
{
|
||||
depth_format = format;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (depth_format != VK_FORMAT_UNDEFINED)
|
||||
{
|
||||
LOGI("Depth format selected: {}", to_string(depth_format));
|
||||
return depth_format;
|
||||
}
|
||||
|
||||
throw std::runtime_error("No suitable depth format could be determined");
|
||||
}
|
||||
|
||||
VkFormat choose_blendable_format(VkPhysicalDevice physical_device, const std::vector<VkFormat> &format_priority_list)
|
||||
{
|
||||
for (const auto &format : format_priority_list)
|
||||
{
|
||||
VkFormatProperties properties;
|
||||
vkGetPhysicalDeviceFormatProperties(physical_device, format, &properties);
|
||||
if (properties.optimalTilingFeatures & VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT)
|
||||
{
|
||||
return format;
|
||||
}
|
||||
}
|
||||
|
||||
throw std::runtime_error("No suitable blendable format could be determined");
|
||||
}
|
||||
|
||||
void make_filters_valid(VkPhysicalDevice physical_device, VkFormat format, VkFilter *filter, VkSamplerMipmapMode *mipmapMode)
|
||||
{
|
||||
// Not all formats support linear filtering, so we need to adjust them if they don't
|
||||
if (*filter == VK_FILTER_NEAREST && (mipmapMode == nullptr || *mipmapMode == VK_SAMPLER_MIPMAP_MODE_NEAREST))
|
||||
{
|
||||
return; // These must already be valid
|
||||
}
|
||||
|
||||
VkFormatProperties properties;
|
||||
vkGetPhysicalDeviceFormatProperties(physical_device, format, &properties);
|
||||
|
||||
if (!(properties.optimalTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT))
|
||||
{
|
||||
*filter = VK_FILTER_NEAREST;
|
||||
if (mipmapMode)
|
||||
{
|
||||
*mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool is_dynamic_buffer_descriptor_type(VkDescriptorType descriptor_type)
|
||||
{
|
||||
return descriptor_type == VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC ||
|
||||
descriptor_type == VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC;
|
||||
}
|
||||
|
||||
bool is_buffer_descriptor_type(VkDescriptorType descriptor_type)
|
||||
{
|
||||
return descriptor_type == VK_DESCRIPTOR_TYPE_STORAGE_BUFFER ||
|
||||
descriptor_type == VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER ||
|
||||
is_dynamic_buffer_descriptor_type(descriptor_type);
|
||||
}
|
||||
|
||||
int32_t get_bits_per_pixel(VkFormat format)
|
||||
{
|
||||
switch (format)
|
||||
{
|
||||
case VK_FORMAT_R4G4_UNORM_PACK8:
|
||||
return 8;
|
||||
case VK_FORMAT_R4G4B4A4_UNORM_PACK16:
|
||||
case VK_FORMAT_B4G4R4A4_UNORM_PACK16:
|
||||
case VK_FORMAT_R5G6B5_UNORM_PACK16:
|
||||
case VK_FORMAT_B5G6R5_UNORM_PACK16:
|
||||
case VK_FORMAT_R5G5B5A1_UNORM_PACK16:
|
||||
case VK_FORMAT_B5G5R5A1_UNORM_PACK16:
|
||||
case VK_FORMAT_A1R5G5B5_UNORM_PACK16:
|
||||
return 16;
|
||||
case VK_FORMAT_R8_UNORM:
|
||||
case VK_FORMAT_R8_SNORM:
|
||||
case VK_FORMAT_R8_USCALED:
|
||||
case VK_FORMAT_R8_SSCALED:
|
||||
case VK_FORMAT_R8_UINT:
|
||||
case VK_FORMAT_R8_SINT:
|
||||
case VK_FORMAT_R8_SRGB:
|
||||
return 8;
|
||||
case VK_FORMAT_R8G8_UNORM:
|
||||
case VK_FORMAT_R8G8_SNORM:
|
||||
case VK_FORMAT_R8G8_USCALED:
|
||||
case VK_FORMAT_R8G8_SSCALED:
|
||||
case VK_FORMAT_R8G8_UINT:
|
||||
case VK_FORMAT_R8G8_SINT:
|
||||
case VK_FORMAT_R8G8_SRGB:
|
||||
return 16;
|
||||
case VK_FORMAT_R8G8B8_UNORM:
|
||||
case VK_FORMAT_R8G8B8_SNORM:
|
||||
case VK_FORMAT_R8G8B8_USCALED:
|
||||
case VK_FORMAT_R8G8B8_SSCALED:
|
||||
case VK_FORMAT_R8G8B8_UINT:
|
||||
case VK_FORMAT_R8G8B8_SINT:
|
||||
case VK_FORMAT_R8G8B8_SRGB:
|
||||
case VK_FORMAT_B8G8R8_UNORM:
|
||||
case VK_FORMAT_B8G8R8_SNORM:
|
||||
case VK_FORMAT_B8G8R8_USCALED:
|
||||
case VK_FORMAT_B8G8R8_SSCALED:
|
||||
case VK_FORMAT_B8G8R8_UINT:
|
||||
case VK_FORMAT_B8G8R8_SINT:
|
||||
case VK_FORMAT_B8G8R8_SRGB:
|
||||
return 24;
|
||||
case VK_FORMAT_R8G8B8A8_UNORM:
|
||||
case VK_FORMAT_R8G8B8A8_SNORM:
|
||||
case VK_FORMAT_R8G8B8A8_USCALED:
|
||||
case VK_FORMAT_R8G8B8A8_SSCALED:
|
||||
case VK_FORMAT_R8G8B8A8_UINT:
|
||||
case VK_FORMAT_R8G8B8A8_SINT:
|
||||
case VK_FORMAT_R8G8B8A8_SRGB:
|
||||
case VK_FORMAT_B8G8R8A8_UNORM:
|
||||
case VK_FORMAT_B8G8R8A8_SNORM:
|
||||
case VK_FORMAT_B8G8R8A8_USCALED:
|
||||
case VK_FORMAT_B8G8R8A8_SSCALED:
|
||||
case VK_FORMAT_B8G8R8A8_UINT:
|
||||
case VK_FORMAT_B8G8R8A8_SINT:
|
||||
case VK_FORMAT_B8G8R8A8_SRGB:
|
||||
case VK_FORMAT_A8B8G8R8_UNORM_PACK32:
|
||||
case VK_FORMAT_A8B8G8R8_SNORM_PACK32:
|
||||
case VK_FORMAT_A8B8G8R8_USCALED_PACK32:
|
||||
case VK_FORMAT_A8B8G8R8_SSCALED_PACK32:
|
||||
case VK_FORMAT_A8B8G8R8_UINT_PACK32:
|
||||
case VK_FORMAT_A8B8G8R8_SINT_PACK32:
|
||||
case VK_FORMAT_A8B8G8R8_SRGB_PACK32:
|
||||
return 32;
|
||||
case VK_FORMAT_A2R10G10B10_UNORM_PACK32:
|
||||
case VK_FORMAT_A2R10G10B10_SNORM_PACK32:
|
||||
case VK_FORMAT_A2R10G10B10_USCALED_PACK32:
|
||||
case VK_FORMAT_A2R10G10B10_SSCALED_PACK32:
|
||||
case VK_FORMAT_A2R10G10B10_UINT_PACK32:
|
||||
case VK_FORMAT_A2R10G10B10_SINT_PACK32:
|
||||
case VK_FORMAT_A2B10G10R10_UNORM_PACK32:
|
||||
case VK_FORMAT_A2B10G10R10_SNORM_PACK32:
|
||||
case VK_FORMAT_A2B10G10R10_USCALED_PACK32:
|
||||
case VK_FORMAT_A2B10G10R10_SSCALED_PACK32:
|
||||
case VK_FORMAT_A2B10G10R10_UINT_PACK32:
|
||||
case VK_FORMAT_A2B10G10R10_SINT_PACK32:
|
||||
return 32;
|
||||
case VK_FORMAT_R16_UNORM:
|
||||
case VK_FORMAT_R16_SNORM:
|
||||
case VK_FORMAT_R16_USCALED:
|
||||
case VK_FORMAT_R16_SSCALED:
|
||||
case VK_FORMAT_R16_UINT:
|
||||
case VK_FORMAT_R16_SINT:
|
||||
case VK_FORMAT_R16_SFLOAT:
|
||||
return 16;
|
||||
case VK_FORMAT_R16G16_UNORM:
|
||||
case VK_FORMAT_R16G16_SNORM:
|
||||
case VK_FORMAT_R16G16_USCALED:
|
||||
case VK_FORMAT_R16G16_SSCALED:
|
||||
case VK_FORMAT_R16G16_UINT:
|
||||
case VK_FORMAT_R16G16_SINT:
|
||||
case VK_FORMAT_R16G16_SFLOAT:
|
||||
return 32;
|
||||
case VK_FORMAT_R16G16B16_UNORM:
|
||||
case VK_FORMAT_R16G16B16_SNORM:
|
||||
case VK_FORMAT_R16G16B16_USCALED:
|
||||
case VK_FORMAT_R16G16B16_SSCALED:
|
||||
case VK_FORMAT_R16G16B16_UINT:
|
||||
case VK_FORMAT_R16G16B16_SINT:
|
||||
case VK_FORMAT_R16G16B16_SFLOAT:
|
||||
return 48;
|
||||
case VK_FORMAT_R16G16B16A16_UNORM:
|
||||
case VK_FORMAT_R16G16B16A16_SNORM:
|
||||
case VK_FORMAT_R16G16B16A16_USCALED:
|
||||
case VK_FORMAT_R16G16B16A16_SSCALED:
|
||||
case VK_FORMAT_R16G16B16A16_UINT:
|
||||
case VK_FORMAT_R16G16B16A16_SINT:
|
||||
case VK_FORMAT_R16G16B16A16_SFLOAT:
|
||||
return 64;
|
||||
case VK_FORMAT_R32_UINT:
|
||||
case VK_FORMAT_R32_SINT:
|
||||
case VK_FORMAT_R32_SFLOAT:
|
||||
return 32;
|
||||
case VK_FORMAT_R32G32_UINT:
|
||||
case VK_FORMAT_R32G32_SINT:
|
||||
case VK_FORMAT_R32G32_SFLOAT:
|
||||
return 64;
|
||||
case VK_FORMAT_R32G32B32_UINT:
|
||||
case VK_FORMAT_R32G32B32_SINT:
|
||||
case VK_FORMAT_R32G32B32_SFLOAT:
|
||||
return 96;
|
||||
case VK_FORMAT_R32G32B32A32_UINT:
|
||||
case VK_FORMAT_R32G32B32A32_SINT:
|
||||
case VK_FORMAT_R32G32B32A32_SFLOAT:
|
||||
return 128;
|
||||
case VK_FORMAT_R64_UINT:
|
||||
case VK_FORMAT_R64_SINT:
|
||||
case VK_FORMAT_R64_SFLOAT:
|
||||
return 64;
|
||||
case VK_FORMAT_R64G64_UINT:
|
||||
case VK_FORMAT_R64G64_SINT:
|
||||
case VK_FORMAT_R64G64_SFLOAT:
|
||||
return 128;
|
||||
case VK_FORMAT_R64G64B64_UINT:
|
||||
case VK_FORMAT_R64G64B64_SINT:
|
||||
case VK_FORMAT_R64G64B64_SFLOAT:
|
||||
return 192;
|
||||
case VK_FORMAT_R64G64B64A64_UINT:
|
||||
case VK_FORMAT_R64G64B64A64_SINT:
|
||||
case VK_FORMAT_R64G64B64A64_SFLOAT:
|
||||
return 256;
|
||||
case VK_FORMAT_B10G11R11_UFLOAT_PACK32:
|
||||
return 32;
|
||||
case VK_FORMAT_E5B9G9R9_UFLOAT_PACK32:
|
||||
return 32;
|
||||
case VK_FORMAT_D16_UNORM:
|
||||
return 16;
|
||||
case VK_FORMAT_X8_D24_UNORM_PACK32:
|
||||
return 32;
|
||||
case VK_FORMAT_D32_SFLOAT:
|
||||
return 32;
|
||||
case VK_FORMAT_S8_UINT:
|
||||
return 8;
|
||||
case VK_FORMAT_D16_UNORM_S8_UINT:
|
||||
return 24;
|
||||
case VK_FORMAT_D24_UNORM_S8_UINT:
|
||||
return 32;
|
||||
case VK_FORMAT_D32_SFLOAT_S8_UINT:
|
||||
return 40;
|
||||
case VK_FORMAT_UNDEFINED:
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
VkShaderModule load_shader(const std::string &filename, VkDevice device, VkShaderStageFlagBits stage)
|
||||
{
|
||||
auto spirv = vkb::fs::read_shader_binary_u32(filename);
|
||||
|
||||
VkShaderModule shader_module;
|
||||
VkShaderModuleCreateInfo module_create_info{};
|
||||
module_create_info.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
|
||||
module_create_info.codeSize = spirv.size() * sizeof(uint32_t);
|
||||
module_create_info.pCode = spirv.data();
|
||||
|
||||
VK_CHECK(vkCreateShaderModule(device, &module_create_info, NULL, &shader_module));
|
||||
|
||||
return shader_module;
|
||||
}
|
||||
|
||||
VkAccessFlags getAccessFlags(VkImageLayout layout)
|
||||
{
|
||||
switch (layout)
|
||||
{
|
||||
case VK_IMAGE_LAYOUT_UNDEFINED:
|
||||
case VK_IMAGE_LAYOUT_PRESENT_SRC_KHR:
|
||||
return 0;
|
||||
case VK_IMAGE_LAYOUT_PREINITIALIZED:
|
||||
return VK_ACCESS_HOST_WRITE_BIT;
|
||||
case VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL:
|
||||
return VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
|
||||
case VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL:
|
||||
return VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
|
||||
case VK_IMAGE_LAYOUT_FRAGMENT_SHADING_RATE_ATTACHMENT_OPTIMAL_KHR:
|
||||
return VK_ACCESS_FRAGMENT_SHADING_RATE_ATTACHMENT_READ_BIT_KHR;
|
||||
case VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL:
|
||||
return VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_INPUT_ATTACHMENT_READ_BIT;
|
||||
case VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL:
|
||||
return VK_ACCESS_TRANSFER_READ_BIT;
|
||||
case VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL:
|
||||
return VK_ACCESS_TRANSFER_WRITE_BIT;
|
||||
case VK_IMAGE_LAYOUT_GENERAL:
|
||||
assert(false && "Don't know how to get a meaningful VkAccessFlags for VK_IMAGE_LAYOUT_GENERAL! Don't use it!");
|
||||
return 0;
|
||||
default:
|
||||
assert(false);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
VkPipelineStageFlags getPipelineStageFlags(VkImageLayout layout)
|
||||
{
|
||||
switch (layout)
|
||||
{
|
||||
case VK_IMAGE_LAYOUT_UNDEFINED:
|
||||
return VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
|
||||
case VK_IMAGE_LAYOUT_PREINITIALIZED:
|
||||
return VK_PIPELINE_STAGE_HOST_BIT;
|
||||
case VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL:
|
||||
case VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL:
|
||||
return VK_PIPELINE_STAGE_TRANSFER_BIT;
|
||||
case VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL:
|
||||
return VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
|
||||
case VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL:
|
||||
return VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT;
|
||||
case VK_IMAGE_LAYOUT_FRAGMENT_SHADING_RATE_ATTACHMENT_OPTIMAL_KHR:
|
||||
return VK_PIPELINE_STAGE_FRAGMENT_SHADING_RATE_ATTACHMENT_BIT_KHR;
|
||||
case VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL:
|
||||
return VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
|
||||
case VK_IMAGE_LAYOUT_PRESENT_SRC_KHR:
|
||||
return VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT;
|
||||
case VK_IMAGE_LAYOUT_GENERAL:
|
||||
assert(false && "Don't know how to get a meaningful VkPipelineStageFlags for VK_IMAGE_LAYOUT_GENERAL! Don't use it!");
|
||||
return 0;
|
||||
default:
|
||||
assert(false);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Create an image memory barrier for changing the layout of
|
||||
// an image and put it into an active command buffer
|
||||
// See chapter 12.4 "Image Layout" for details
|
||||
|
||||
void image_layout_transition(VkCommandBuffer command_buffer,
|
||||
VkImage image,
|
||||
VkPipelineStageFlags src_stage_mask,
|
||||
VkPipelineStageFlags dst_stage_mask,
|
||||
VkAccessFlags src_access_mask,
|
||||
VkAccessFlags dst_access_mask,
|
||||
VkImageLayout old_layout,
|
||||
VkImageLayout new_layout,
|
||||
VkImageSubresourceRange const &subresource_range)
|
||||
{
|
||||
// Create an image barrier object
|
||||
VkImageMemoryBarrier image_memory_barrier{};
|
||||
image_memory_barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
|
||||
image_memory_barrier.srcAccessMask = src_access_mask;
|
||||
image_memory_barrier.dstAccessMask = dst_access_mask;
|
||||
image_memory_barrier.oldLayout = old_layout;
|
||||
image_memory_barrier.newLayout = new_layout;
|
||||
image_memory_barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
||||
image_memory_barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
||||
image_memory_barrier.image = image;
|
||||
image_memory_barrier.subresourceRange = subresource_range;
|
||||
|
||||
// Put barrier inside setup command buffer
|
||||
vkCmdPipelineBarrier(command_buffer, src_stage_mask, dst_stage_mask, 0, 0, nullptr, 0, nullptr, 1, &image_memory_barrier);
|
||||
}
|
||||
|
||||
void image_layout_transition(VkCommandBuffer command_buffer,
|
||||
VkImage image,
|
||||
VkImageLayout old_layout,
|
||||
VkImageLayout new_layout,
|
||||
VkImageSubresourceRange const &subresource_range)
|
||||
{
|
||||
VkPipelineStageFlags src_stage_mask = getPipelineStageFlags(old_layout);
|
||||
VkPipelineStageFlags dst_stage_mask = getPipelineStageFlags(new_layout);
|
||||
VkAccessFlags src_access_mask = getAccessFlags(old_layout);
|
||||
VkAccessFlags dst_access_mask = getAccessFlags(new_layout);
|
||||
|
||||
image_layout_transition(command_buffer, image, src_stage_mask, dst_stage_mask, src_access_mask, dst_access_mask, old_layout, new_layout, subresource_range);
|
||||
}
|
||||
|
||||
// Fixed sub resource on first mip level and layer
|
||||
void image_layout_transition(VkCommandBuffer command_buffer,
|
||||
VkImage image,
|
||||
VkImageLayout old_layout,
|
||||
VkImageLayout new_layout)
|
||||
{
|
||||
VkImageSubresourceRange subresource_range = {};
|
||||
subresource_range.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||
subresource_range.baseMipLevel = 0;
|
||||
subresource_range.levelCount = 1;
|
||||
subresource_range.baseArrayLayer = 0;
|
||||
subresource_range.layerCount = 1;
|
||||
image_layout_transition(command_buffer, image, old_layout, new_layout, subresource_range);
|
||||
}
|
||||
|
||||
void image_layout_transition(VkCommandBuffer command_buffer,
|
||||
std::vector<std::pair<VkImage, VkImageSubresourceRange>> const &imagesAndRanges,
|
||||
VkImageLayout old_layout,
|
||||
VkImageLayout new_layout)
|
||||
{
|
||||
VkPipelineStageFlags src_stage_mask = getPipelineStageFlags(old_layout);
|
||||
VkPipelineStageFlags dst_stage_mask = getPipelineStageFlags(new_layout);
|
||||
VkAccessFlags src_access_mask = getAccessFlags(old_layout);
|
||||
VkAccessFlags dst_access_mask = getAccessFlags(new_layout);
|
||||
|
||||
// Create image barrier objects
|
||||
std::vector<VkImageMemoryBarrier> image_memory_barriers;
|
||||
image_memory_barriers.reserve(imagesAndRanges.size());
|
||||
for (size_t i = 0; i < imagesAndRanges.size(); i++)
|
||||
{
|
||||
image_memory_barriers.emplace_back(VkImageMemoryBarrier{VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
|
||||
nullptr,
|
||||
src_access_mask,
|
||||
dst_access_mask,
|
||||
old_layout,
|
||||
new_layout,
|
||||
VK_QUEUE_FAMILY_IGNORED,
|
||||
VK_QUEUE_FAMILY_IGNORED,
|
||||
imagesAndRanges[i].first,
|
||||
imagesAndRanges[i].second});
|
||||
}
|
||||
|
||||
// Put barriers inside setup command buffer
|
||||
vkCmdPipelineBarrier(command_buffer,
|
||||
src_stage_mask,
|
||||
dst_stage_mask,
|
||||
0,
|
||||
0,
|
||||
nullptr,
|
||||
0,
|
||||
nullptr,
|
||||
static_cast<uint32_t>(image_memory_barriers.size()),
|
||||
image_memory_barriers.data());
|
||||
}
|
||||
|
||||
std::vector<VkImageCompressionFixedRateFlagBitsEXT> fixed_rate_compression_flags_to_vector(VkImageCompressionFixedRateFlagsEXT flags)
|
||||
{
|
||||
const std::vector<VkImageCompressionFixedRateFlagBitsEXT> all_flags = {VK_IMAGE_COMPRESSION_FIXED_RATE_1BPC_BIT_EXT, VK_IMAGE_COMPRESSION_FIXED_RATE_2BPC_BIT_EXT,
|
||||
VK_IMAGE_COMPRESSION_FIXED_RATE_3BPC_BIT_EXT, VK_IMAGE_COMPRESSION_FIXED_RATE_4BPC_BIT_EXT,
|
||||
VK_IMAGE_COMPRESSION_FIXED_RATE_5BPC_BIT_EXT, VK_IMAGE_COMPRESSION_FIXED_RATE_6BPC_BIT_EXT,
|
||||
VK_IMAGE_COMPRESSION_FIXED_RATE_7BPC_BIT_EXT, VK_IMAGE_COMPRESSION_FIXED_RATE_8BPC_BIT_EXT,
|
||||
VK_IMAGE_COMPRESSION_FIXED_RATE_9BPC_BIT_EXT, VK_IMAGE_COMPRESSION_FIXED_RATE_10BPC_BIT_EXT,
|
||||
VK_IMAGE_COMPRESSION_FIXED_RATE_11BPC_BIT_EXT, VK_IMAGE_COMPRESSION_FIXED_RATE_12BPC_BIT_EXT,
|
||||
VK_IMAGE_COMPRESSION_FIXED_RATE_13BPC_BIT_EXT, VK_IMAGE_COMPRESSION_FIXED_RATE_14BPC_BIT_EXT,
|
||||
VK_IMAGE_COMPRESSION_FIXED_RATE_15BPC_BIT_EXT, VK_IMAGE_COMPRESSION_FIXED_RATE_16BPC_BIT_EXT,
|
||||
VK_IMAGE_COMPRESSION_FIXED_RATE_17BPC_BIT_EXT, VK_IMAGE_COMPRESSION_FIXED_RATE_18BPC_BIT_EXT,
|
||||
VK_IMAGE_COMPRESSION_FIXED_RATE_19BPC_BIT_EXT, VK_IMAGE_COMPRESSION_FIXED_RATE_20BPC_BIT_EXT,
|
||||
VK_IMAGE_COMPRESSION_FIXED_RATE_21BPC_BIT_EXT, VK_IMAGE_COMPRESSION_FIXED_RATE_22BPC_BIT_EXT,
|
||||
VK_IMAGE_COMPRESSION_FIXED_RATE_23BPC_BIT_EXT, VK_IMAGE_COMPRESSION_FIXED_RATE_24BPC_BIT_EXT};
|
||||
|
||||
std::vector<VkImageCompressionFixedRateFlagBitsEXT> flags_vector;
|
||||
|
||||
for (size_t i = 0; i < all_flags.size(); i++)
|
||||
{
|
||||
if (all_flags[i] & flags)
|
||||
{
|
||||
flags_vector.push_back(all_flags[i]);
|
||||
}
|
||||
}
|
||||
|
||||
return flags_vector;
|
||||
}
|
||||
|
||||
VkImageCompressionPropertiesEXT query_supported_fixed_rate_compression(VkPhysicalDevice gpu, const VkImageCreateInfo &create_info)
|
||||
{
|
||||
VkImageCompressionPropertiesEXT supported_compression_properties{VK_STRUCTURE_TYPE_IMAGE_COMPRESSION_PROPERTIES_EXT};
|
||||
|
||||
VkImageCompressionControlEXT compression_control{VK_STRUCTURE_TYPE_IMAGE_COMPRESSION_CONTROL_EXT};
|
||||
compression_control.flags = VK_IMAGE_COMPRESSION_FIXED_RATE_DEFAULT_EXT;
|
||||
|
||||
VkPhysicalDeviceImageFormatInfo2 image_format_info{VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_FORMAT_INFO_2};
|
||||
image_format_info.format = create_info.format;
|
||||
image_format_info.type = create_info.imageType;
|
||||
image_format_info.tiling = create_info.tiling;
|
||||
image_format_info.usage = create_info.usage;
|
||||
image_format_info.pNext = &compression_control;
|
||||
|
||||
VkImageFormatProperties2 image_format_properties{VK_STRUCTURE_TYPE_IMAGE_FORMAT_PROPERTIES_2};
|
||||
image_format_properties.pNext = &supported_compression_properties;
|
||||
|
||||
vkGetPhysicalDeviceImageFormatProperties2KHR(gpu, &image_format_info, &image_format_properties);
|
||||
|
||||
return supported_compression_properties;
|
||||
}
|
||||
|
||||
VkImageCompressionPropertiesEXT query_applied_compression(VkDevice device, VkImage image)
|
||||
{
|
||||
VkImageSubresource2EXT image_subresource{VK_STRUCTURE_TYPE_IMAGE_SUBRESOURCE_2_KHR};
|
||||
image_subresource.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||
image_subresource.imageSubresource.mipLevel = 0;
|
||||
image_subresource.imageSubresource.arrayLayer = 0;
|
||||
|
||||
VkImageCompressionPropertiesEXT compression_properties{VK_STRUCTURE_TYPE_IMAGE_COMPRESSION_PROPERTIES_EXT};
|
||||
VkSubresourceLayout2EXT subresource_layout{VK_STRUCTURE_TYPE_SUBRESOURCE_LAYOUT_2_KHR};
|
||||
subresource_layout.pNext = &compression_properties;
|
||||
|
||||
vkGetImageSubresourceLayout2EXT(device, image, &image_subresource, &subresource_layout);
|
||||
|
||||
return compression_properties;
|
||||
}
|
||||
|
||||
VkSurfaceFormatKHR select_surface_format(VkPhysicalDevice gpu, VkSurfaceKHR surface, std::vector<VkFormat> const &preferred_formats)
|
||||
{
|
||||
uint32_t surface_format_count;
|
||||
vkGetPhysicalDeviceSurfaceFormatsKHR(gpu, surface, &surface_format_count, nullptr);
|
||||
assert(0 < surface_format_count);
|
||||
std::vector<VkSurfaceFormatKHR> supported_surface_formats(surface_format_count);
|
||||
vkGetPhysicalDeviceSurfaceFormatsKHR(gpu, surface, &surface_format_count, supported_surface_formats.data());
|
||||
|
||||
auto it = std::ranges::find_if(supported_surface_formats,
|
||||
[&preferred_formats](VkSurfaceFormatKHR surface_format) {
|
||||
return std::ranges::any_of(preferred_formats,
|
||||
[&surface_format](VkFormat format) { return format == surface_format.format; });
|
||||
});
|
||||
|
||||
// We use the first supported format as a fallback in case none of the preferred formats is available
|
||||
return it != supported_surface_formats.end() ? *it : supported_surface_formats[0];
|
||||
}
|
||||
|
||||
namespace gbuffer
|
||||
{
|
||||
std::vector<LoadStoreInfo> get_load_all_store_swapchain()
|
||||
{
|
||||
// Load every attachment and store only swapchain
|
||||
std::vector<LoadStoreInfo> load_store{4};
|
||||
|
||||
// Swapchain
|
||||
load_store[0].load_op = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
|
||||
load_store[0].store_op = VK_ATTACHMENT_STORE_OP_STORE;
|
||||
|
||||
// Depth
|
||||
load_store[1].load_op = VK_ATTACHMENT_LOAD_OP_LOAD;
|
||||
load_store[1].store_op = VK_ATTACHMENT_STORE_OP_DONT_CARE;
|
||||
|
||||
// Albedo
|
||||
load_store[2].load_op = VK_ATTACHMENT_LOAD_OP_LOAD;
|
||||
load_store[2].store_op = VK_ATTACHMENT_STORE_OP_DONT_CARE;
|
||||
|
||||
// Normal
|
||||
load_store[3].load_op = VK_ATTACHMENT_LOAD_OP_LOAD;
|
||||
load_store[3].store_op = VK_ATTACHMENT_STORE_OP_DONT_CARE;
|
||||
|
||||
return load_store;
|
||||
}
|
||||
|
||||
std::vector<LoadStoreInfo> get_clear_all_store_swapchain()
|
||||
{
|
||||
// Clear every attachment and store only swapchain
|
||||
std::vector<LoadStoreInfo> load_store{4};
|
||||
|
||||
// Swapchain
|
||||
load_store[0].load_op = VK_ATTACHMENT_LOAD_OP_CLEAR;
|
||||
load_store[0].store_op = VK_ATTACHMENT_STORE_OP_STORE;
|
||||
|
||||
// Depth
|
||||
load_store[1].load_op = VK_ATTACHMENT_LOAD_OP_CLEAR;
|
||||
load_store[1].store_op = VK_ATTACHMENT_STORE_OP_DONT_CARE;
|
||||
|
||||
// Albedo
|
||||
load_store[2].load_op = VK_ATTACHMENT_LOAD_OP_CLEAR;
|
||||
load_store[2].store_op = VK_ATTACHMENT_STORE_OP_DONT_CARE;
|
||||
|
||||
// Normal
|
||||
load_store[3].load_op = VK_ATTACHMENT_LOAD_OP_CLEAR;
|
||||
load_store[3].store_op = VK_ATTACHMENT_STORE_OP_DONT_CARE;
|
||||
|
||||
return load_store;
|
||||
}
|
||||
|
||||
std::vector<LoadStoreInfo> get_clear_store_all()
|
||||
{
|
||||
// Clear and store every attachment
|
||||
std::vector<LoadStoreInfo> load_store{4};
|
||||
|
||||
// Swapchain
|
||||
load_store[0].load_op = VK_ATTACHMENT_LOAD_OP_CLEAR;
|
||||
load_store[0].store_op = VK_ATTACHMENT_STORE_OP_STORE;
|
||||
|
||||
// Depth
|
||||
load_store[1].load_op = VK_ATTACHMENT_LOAD_OP_CLEAR;
|
||||
load_store[1].store_op = VK_ATTACHMENT_STORE_OP_STORE;
|
||||
|
||||
// Albedo
|
||||
load_store[2].load_op = VK_ATTACHMENT_LOAD_OP_CLEAR;
|
||||
load_store[2].store_op = VK_ATTACHMENT_STORE_OP_STORE;
|
||||
|
||||
// Normal
|
||||
load_store[3].load_op = VK_ATTACHMENT_LOAD_OP_CLEAR;
|
||||
load_store[3].store_op = VK_ATTACHMENT_STORE_OP_STORE;
|
||||
|
||||
return load_store;
|
||||
}
|
||||
|
||||
std::vector<VkClearValue> get_clear_value()
|
||||
{
|
||||
// Clear values
|
||||
std::vector<VkClearValue> clear_value{4};
|
||||
clear_value[0].color = {{0.0f, 0.0f, 0.0f, 1.0f}};
|
||||
clear_value[1].depthStencil = {0.0f, ~0U};
|
||||
clear_value[2].color = {{0.0f, 0.0f, 0.0f, 1.0f}};
|
||||
clear_value[3].color = {{0.0f, 0.0f, 0.0f, 1.0f}};
|
||||
|
||||
return clear_value;
|
||||
}
|
||||
} // namespace gbuffer
|
||||
|
||||
uint32_t get_queue_family_index(std::vector<VkQueueFamilyProperties> const &queue_family_properties, VkQueueFlagBits queue_flag)
|
||||
{
|
||||
// Dedicated queue for compute
|
||||
// Try to find a queue family index that supports compute but not graphics
|
||||
if (queue_flag & VK_QUEUE_COMPUTE_BIT)
|
||||
{
|
||||
auto propertyIt = std::ranges::find_if(queue_family_properties,
|
||||
[queue_flag](const VkQueueFamilyProperties &property) { return (property.queueFlags & queue_flag) && !(property.queueFlags & VK_QUEUE_GRAPHICS_BIT); });
|
||||
if (propertyIt != queue_family_properties.end())
|
||||
{
|
||||
return static_cast<uint32_t>(std::distance(queue_family_properties.begin(), propertyIt));
|
||||
}
|
||||
}
|
||||
|
||||
// Dedicated queue for transfer
|
||||
// Try to find a queue family index that supports transfer but not graphics and compute
|
||||
if (queue_flag & VK_QUEUE_TRANSFER_BIT)
|
||||
{
|
||||
auto propertyIt = std::ranges::find_if(queue_family_properties,
|
||||
[queue_flag](const VkQueueFamilyProperties &property) {
|
||||
return (property.queueFlags & queue_flag) && !(property.queueFlags & VK_QUEUE_GRAPHICS_BIT) &&
|
||||
!(property.queueFlags & VK_QUEUE_COMPUTE_BIT);
|
||||
});
|
||||
if (propertyIt != queue_family_properties.end())
|
||||
{
|
||||
return static_cast<uint32_t>(std::distance(queue_family_properties.begin(), propertyIt));
|
||||
}
|
||||
}
|
||||
|
||||
// For other queue types or if no separate compute queue is present, return the first one to support the requested flags
|
||||
auto propertyIt = std::ranges::find_if(
|
||||
queue_family_properties, [queue_flag](const VkQueueFamilyProperties &property) { return (property.queueFlags & queue_flag) == queue_flag; });
|
||||
if (propertyIt != queue_family_properties.end())
|
||||
{
|
||||
return static_cast<uint32_t>(std::distance(queue_family_properties.begin(), propertyIt));
|
||||
}
|
||||
|
||||
throw std::runtime_error("Could not find a matching queue family index");
|
||||
}
|
||||
|
||||
} // namespace vkb
|
||||
@@ -0,0 +1,311 @@
|
||||
/* Copyright (c) 2018-2025, Arm Limited and Contributors
|
||||
* Copyright (c) 2019-2025, Sascha Willems
|
||||
* Copyright (c) 2024-2025, Mobica Limited
|
||||
*
|
||||
* 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 <cstdio>
|
||||
#include <map>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
#include "common/error.h"
|
||||
#include <vk_mem_alloc.h>
|
||||
#include <volk.h>
|
||||
|
||||
#define VK_FLAGS_NONE 0 // Custom define for better code readability
|
||||
|
||||
#define DEFAULT_FENCE_TIMEOUT 100000000000 // Default fence timeout in nanoseconds
|
||||
|
||||
template <class T>
|
||||
using ShaderStageMap = std::map<VkShaderStageFlagBits, T>;
|
||||
|
||||
template <class T>
|
||||
using BindingMap = std::map<uint32_t, std::map<uint32_t, T>>;
|
||||
|
||||
namespace vkb
|
||||
{
|
||||
enum class BindingType
|
||||
{
|
||||
C,
|
||||
Cpp
|
||||
};
|
||||
|
||||
enum class CommandBufferResetMode
|
||||
{
|
||||
ResetPool,
|
||||
ResetIndividually,
|
||||
AlwaysAllocate,
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Helper function to determine if a Vulkan format is depth only.
|
||||
* @param format Vulkan format to check.
|
||||
* @return True if format is a depth only, false otherwise.
|
||||
*/
|
||||
bool is_depth_only_format(VkFormat format);
|
||||
|
||||
/**
|
||||
* @brief Helper function to determine if a Vulkan format is depth with stencil.
|
||||
* @param format Vulkan format to check.
|
||||
* @return True if format is a depth with stencil, false otherwise.
|
||||
*/
|
||||
bool is_depth_stencil_format(VkFormat format);
|
||||
|
||||
/**
|
||||
* @brief Helper function to determine if a Vulkan format is depth.
|
||||
* @param format Vulkan format to check.
|
||||
* @return True if format is a depth, false otherwise.
|
||||
*/
|
||||
bool is_depth_format(VkFormat format);
|
||||
|
||||
/**
|
||||
* @brief Helper function to determine a suitable supported depth format based on a priority list
|
||||
* @param physical_device The physical device to check the depth formats against
|
||||
* @param depth_only (Optional) Wether to include the stencil component in the format or not
|
||||
* @param depth_format_priority_list (Optional) The list of depth formats to prefer over one another
|
||||
* By default we start with the highest precision packed format
|
||||
* @return The valid suited depth format
|
||||
*/
|
||||
VkFormat get_suitable_depth_format(VkPhysicalDevice physical_device,
|
||||
bool depth_only = false,
|
||||
const std::vector<VkFormat> &depth_format_priority_list = {
|
||||
VK_FORMAT_D32_SFLOAT,
|
||||
VK_FORMAT_D24_UNORM_S8_UINT,
|
||||
VK_FORMAT_D16_UNORM});
|
||||
|
||||
/**
|
||||
* @brief Helper function to pick a blendable format from a priority ordered list
|
||||
* @param physical_device The physical device to check the formats against
|
||||
* @param format_priority_list List of formats in order of priority
|
||||
* @return The selected format
|
||||
*/
|
||||
VkFormat choose_blendable_format(VkPhysicalDevice physical_device, const std::vector<VkFormat> &format_priority_list);
|
||||
|
||||
/**
|
||||
* @brief Helper function to check support for linear filtering and adjust its parameters if required
|
||||
* @param physical_device The physical device to check the depth formats against
|
||||
* @param format The format to check against
|
||||
* @param filter The preferred filter to adjust
|
||||
* @param mipmapMode (Optional) The preferred mipmap mode to adjust
|
||||
*/
|
||||
void make_filters_valid(VkPhysicalDevice physical_device, VkFormat format, VkFilter *filter, VkSamplerMipmapMode *mipmapMode = nullptr);
|
||||
|
||||
/**
|
||||
* @brief Helper function to determine if a Vulkan descriptor type is a dynamic storage buffer or dynamic uniform buffer.
|
||||
* @param descriptor_type Vulkan descriptor type to check.
|
||||
* @return True if type is dynamic buffer, false otherwise.
|
||||
*/
|
||||
bool is_dynamic_buffer_descriptor_type(VkDescriptorType descriptor_type);
|
||||
|
||||
/**
|
||||
* @brief Helper function to determine if a Vulkan descriptor type is a buffer (either uniform or storage buffer, dynamic or not).
|
||||
* @param descriptor_type Vulkan descriptor type to check.
|
||||
* @return True if type is buffer, false otherwise.
|
||||
*/
|
||||
bool is_buffer_descriptor_type(VkDescriptorType descriptor_type);
|
||||
|
||||
/**
|
||||
* @brief Helper function to get the bits per pixel of a Vulkan format.
|
||||
* @param format Vulkan format to check.
|
||||
* @return The bits per pixel of the given format, -1 for invalid formats.
|
||||
*/
|
||||
int32_t get_bits_per_pixel(VkFormat format);
|
||||
|
||||
enum class ShadingLanguage
|
||||
{
|
||||
GLSL,
|
||||
HLSL,
|
||||
SLANG,
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Helper function to create a VkShaderModule from a SPIR-V shader file
|
||||
* @param filename The shader location
|
||||
* @param device The logical device
|
||||
* @param stage The shader stage
|
||||
* @return The shader module containing the loaded shader
|
||||
*/
|
||||
VkShaderModule load_shader(const std::string &filename, VkDevice device, VkShaderStageFlagBits stage);
|
||||
|
||||
/**
|
||||
* @brief Helper function to select a VkSurfaceFormatKHR
|
||||
* @param gpu The VkPhysicalDevice to select a format for.
|
||||
* @param surface The VkSurfaceKHR to select a format for.
|
||||
* @param preferred_formats List of preferred VkFormats to use.
|
||||
* @return The preferred VkSurfaceFormatKHR.
|
||||
*/
|
||||
VkSurfaceFormatKHR select_surface_format(VkPhysicalDevice gpu,
|
||||
VkSurfaceKHR surface,
|
||||
std::vector<VkFormat> const &preferred_formats = {
|
||||
VK_FORMAT_R8G8B8A8_SRGB, VK_FORMAT_B8G8R8A8_SRGB, VK_FORMAT_A8B8G8R8_SRGB_PACK32});
|
||||
|
||||
/**
|
||||
* @brief Image memory barrier structure used to define
|
||||
* memory access for an image view during command recording.
|
||||
*/
|
||||
struct ImageMemoryBarrier
|
||||
{
|
||||
VkPipelineStageFlags src_stage_mask{VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT};
|
||||
|
||||
VkPipelineStageFlags dst_stage_mask{VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT};
|
||||
|
||||
VkAccessFlags src_access_mask{0};
|
||||
|
||||
VkAccessFlags dst_access_mask{0};
|
||||
|
||||
VkImageLayout old_layout{VK_IMAGE_LAYOUT_UNDEFINED};
|
||||
|
||||
VkImageLayout new_layout{VK_IMAGE_LAYOUT_UNDEFINED};
|
||||
|
||||
uint32_t src_queue_family{VK_QUEUE_FAMILY_IGNORED};
|
||||
|
||||
uint32_t dst_queue_family{VK_QUEUE_FAMILY_IGNORED};
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Buffer memory barrier structure used to define
|
||||
* memory access for a buffer during command recording.
|
||||
*/
|
||||
struct BufferMemoryBarrier
|
||||
{
|
||||
VkPipelineStageFlags src_stage_mask{VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT};
|
||||
|
||||
VkPipelineStageFlags dst_stage_mask{VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT};
|
||||
|
||||
VkAccessFlags src_access_mask{0};
|
||||
|
||||
VkAccessFlags dst_access_mask{0};
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Put an image memory barrier for a layout transition of an image, using explicitly give transition parameters.
|
||||
* @param command_buffer The VkCommandBuffer to record the barrier.
|
||||
* @param image The VkImage to transition.
|
||||
* @param src_stage_mask The VkPipelineStageFlags to use as source.
|
||||
* @param dst_stage_mask The VkPipelineStageFlags to use as destination.
|
||||
* @param src_access_mask The VkAccessFlags to use as source.
|
||||
* @param dst_access_mask The VkAccessFlags to use as destination.
|
||||
* @param old_layout The VkImageLayout to transition from.
|
||||
* @param new_layout The VkImageLayout to transition to.
|
||||
* @param subresource_range The VkImageSubresourceRange to use with the transition.
|
||||
*/
|
||||
void image_layout_transition(VkCommandBuffer command_buffer,
|
||||
VkImage image,
|
||||
VkPipelineStageFlags src_stage_mask,
|
||||
VkPipelineStageFlags dst_stage_mask,
|
||||
VkAccessFlags src_access_mask,
|
||||
VkAccessFlags dst_access_mask,
|
||||
VkImageLayout old_layout,
|
||||
VkImageLayout new_layout,
|
||||
VkImageSubresourceRange const &subresource_range);
|
||||
|
||||
/**
|
||||
* @brief Put an image memory barrier for a layout transition of an image, on a given subresource range.
|
||||
*
|
||||
* The src_stage_mask, dst_stage_mask, src_access_mask, and dst_access_mask used are determined from old_layout and new_layout.
|
||||
*
|
||||
* @param command_buffer The VkCommandBuffer to record the barrier.
|
||||
* @param image The VkImage to transition.
|
||||
* @param old_layout The VkImageLayout to transition from.
|
||||
* @param new_layout The VkImageLayout to transition to.
|
||||
* @param subresource_range The VkImageSubresourceRange to use with the transition.
|
||||
*/
|
||||
void image_layout_transition(VkCommandBuffer command_buffer,
|
||||
VkImage image,
|
||||
VkImageLayout old_layout,
|
||||
VkImageLayout new_layout,
|
||||
VkImageSubresourceRange const &subresource_range);
|
||||
|
||||
/**
|
||||
* @brief Put an image memory barrier for a layout transition of an image, on a fixed subresource with first mip level and layer.
|
||||
*
|
||||
* The src_stage_mask, dst_stage_mask, src_access_mask, and dst_access_mask used are determined from old_layout and new_layout.
|
||||
*
|
||||
* @param command_buffer The VkCommandBuffer to record the barrier.
|
||||
* @param image The VkImage to transition.
|
||||
* @param old_layout The VkImageLayout to transition from.
|
||||
* @param new_layout The VkImageLayout to transition to.
|
||||
*/
|
||||
void image_layout_transition(VkCommandBuffer command_buffer,
|
||||
VkImage image,
|
||||
VkImageLayout old_layout,
|
||||
VkImageLayout new_layout);
|
||||
|
||||
/**
|
||||
* @brief Put an image memory barrier for a layout transition of a vector of images, with a given subresource range per image.
|
||||
*
|
||||
* The src_stage_mask, dst_stage_mask, src_access_mask, and dst_access_mask used are determined from old_layout and new_layout.
|
||||
*
|
||||
* @param command_buffer The VkCommandBuffer to record the barrier.
|
||||
* @param imagesAndRanges The images to transition, with accompanying subresource ranges.
|
||||
* @param old_layout The VkImageLayout to transition from.
|
||||
* @param new_layout The VkImageLayout to transition to.
|
||||
*/
|
||||
void image_layout_transition(VkCommandBuffer command_buffer,
|
||||
std::vector<std::pair<VkImage, VkImageSubresourceRange>> const &imagesAndRanges,
|
||||
VkImageLayout old_layout,
|
||||
VkImageLayout new_layout);
|
||||
|
||||
/**
|
||||
* @brief Helper functions for compression controls
|
||||
*/
|
||||
std::vector<VkImageCompressionFixedRateFlagBitsEXT> fixed_rate_compression_flags_to_vector(VkImageCompressionFixedRateFlagsEXT flags);
|
||||
|
||||
VkImageCompressionPropertiesEXT query_supported_fixed_rate_compression(VkPhysicalDevice gpu, const VkImageCreateInfo &create_info);
|
||||
|
||||
VkImageCompressionPropertiesEXT query_applied_compression(VkDevice device, VkImage image);
|
||||
|
||||
/**
|
||||
* @brief Load and store info for a render pass attachment.
|
||||
*/
|
||||
struct LoadStoreInfo
|
||||
{
|
||||
VkAttachmentLoadOp load_op = VK_ATTACHMENT_LOAD_OP_CLEAR;
|
||||
|
||||
VkAttachmentStoreOp store_op = VK_ATTACHMENT_STORE_OP_STORE;
|
||||
};
|
||||
|
||||
namespace gbuffer
|
||||
{
|
||||
/**
|
||||
* @return Load store info to load all and store only the swapchain
|
||||
*/
|
||||
std::vector<LoadStoreInfo> get_load_all_store_swapchain();
|
||||
|
||||
/**
|
||||
* @return Load store info to clear all and store only the swapchain
|
||||
*/
|
||||
std::vector<LoadStoreInfo> get_clear_all_store_swapchain();
|
||||
|
||||
/**
|
||||
* @return Load store info to clear and store all images
|
||||
*/
|
||||
std::vector<LoadStoreInfo> get_clear_store_all();
|
||||
|
||||
/**
|
||||
* @return Default clear values for the G-buffer
|
||||
*/
|
||||
std::vector<VkClearValue> get_clear_value();
|
||||
} // namespace gbuffer
|
||||
|
||||
uint32_t get_queue_family_index(std::vector<VkQueueFamilyProperties> const &queue_family_properties, VkQueueFlagBits queue_flag);
|
||||
|
||||
} // namespace vkb
|
||||
@@ -0,0 +1,667 @@
|
||||
/* Copyright (c) 2019-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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "volk.h"
|
||||
#include <vector>
|
||||
|
||||
namespace vkb
|
||||
{
|
||||
namespace initializers
|
||||
{
|
||||
inline VkMemoryAllocateInfo memory_allocate_info()
|
||||
{
|
||||
VkMemoryAllocateInfo memory_allocation{};
|
||||
memory_allocation.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
|
||||
return memory_allocation;
|
||||
}
|
||||
|
||||
inline VkMappedMemoryRange mapped_memory_range()
|
||||
{
|
||||
VkMappedMemoryRange mapped_memory_range{};
|
||||
mapped_memory_range.sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE;
|
||||
return mapped_memory_range;
|
||||
}
|
||||
|
||||
inline VkCommandBufferAllocateInfo command_buffer_allocate_info(
|
||||
VkCommandPool command_pool,
|
||||
VkCommandBufferLevel level,
|
||||
uint32_t buffer_count)
|
||||
{
|
||||
VkCommandBufferAllocateInfo command_buffer_allocate_info{};
|
||||
command_buffer_allocate_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
|
||||
command_buffer_allocate_info.commandPool = command_pool;
|
||||
command_buffer_allocate_info.level = level;
|
||||
command_buffer_allocate_info.commandBufferCount = buffer_count;
|
||||
return command_buffer_allocate_info;
|
||||
}
|
||||
|
||||
inline VkCommandPoolCreateInfo command_pool_create_info()
|
||||
{
|
||||
VkCommandPoolCreateInfo command_pool_create_info{};
|
||||
command_pool_create_info.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
|
||||
return command_pool_create_info;
|
||||
}
|
||||
|
||||
inline VkCommandBufferBeginInfo command_buffer_begin_info()
|
||||
{
|
||||
VkCommandBufferBeginInfo cmdBufferBeginInfo{};
|
||||
cmdBufferBeginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
|
||||
return cmdBufferBeginInfo;
|
||||
}
|
||||
|
||||
inline VkCommandBufferInheritanceInfo command_buffer_inheritance_info()
|
||||
{
|
||||
VkCommandBufferInheritanceInfo command_buffer_inheritance_info{};
|
||||
command_buffer_inheritance_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_INHERITANCE_INFO;
|
||||
return command_buffer_inheritance_info;
|
||||
}
|
||||
|
||||
inline VkRenderPassBeginInfo render_pass_begin_info()
|
||||
{
|
||||
VkRenderPassBeginInfo render_pass_begin_info{};
|
||||
render_pass_begin_info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
|
||||
return render_pass_begin_info;
|
||||
}
|
||||
|
||||
inline VkRenderPassCreateInfo render_pass_create_info()
|
||||
{
|
||||
VkRenderPassCreateInfo render_pass_create_info{};
|
||||
render_pass_create_info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
|
||||
return render_pass_create_info;
|
||||
}
|
||||
|
||||
/** @brief Initialize rendering_attachment_info */
|
||||
inline VkRenderingAttachmentInfoKHR rendering_attachment_info()
|
||||
{
|
||||
VkRenderingAttachmentInfoKHR attachment_info{};
|
||||
attachment_info.sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO_KHR;
|
||||
attachment_info.pNext = VK_NULL_HANDLE;
|
||||
return attachment_info;
|
||||
}
|
||||
|
||||
/** @brief Initialize VkRenderingInfoKHR, e.g. for use with dynamic rendering extension */
|
||||
inline VkRenderingInfoKHR rendering_info(VkRect2D render_area = {},
|
||||
uint32_t color_attachment_count = 0,
|
||||
const VkRenderingAttachmentInfoKHR *pColorAttachments = VK_NULL_HANDLE,
|
||||
VkRenderingFlagsKHR flags = 0)
|
||||
{
|
||||
VkRenderingInfoKHR rendering_info = {};
|
||||
rendering_info.sType = VK_STRUCTURE_TYPE_RENDERING_INFO_KHR;
|
||||
rendering_info.pNext = VK_NULL_HANDLE;
|
||||
rendering_info.flags = flags;
|
||||
rendering_info.renderArea = render_area;
|
||||
rendering_info.layerCount = 0;
|
||||
rendering_info.viewMask = 0;
|
||||
rendering_info.colorAttachmentCount = color_attachment_count;
|
||||
rendering_info.pColorAttachments = pColorAttachments;
|
||||
rendering_info.pDepthAttachment = VK_NULL_HANDLE;
|
||||
rendering_info.pStencilAttachment = VK_NULL_HANDLE;
|
||||
return rendering_info;
|
||||
}
|
||||
|
||||
/** @brief Initialize an image memory barrier with no image transfer ownership */
|
||||
inline VkImageMemoryBarrier image_memory_barrier()
|
||||
{
|
||||
VkImageMemoryBarrier image_memory_barrier{};
|
||||
image_memory_barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
|
||||
image_memory_barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
||||
image_memory_barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
||||
return image_memory_barrier;
|
||||
}
|
||||
|
||||
/** @brief Initialize a buffer memory barrier with no image transfer ownership */
|
||||
inline VkBufferMemoryBarrier buffer_memory_barrier()
|
||||
{
|
||||
VkBufferMemoryBarrier buffer_memory_barrier{};
|
||||
buffer_memory_barrier.sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER;
|
||||
buffer_memory_barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
||||
buffer_memory_barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
|
||||
return buffer_memory_barrier;
|
||||
}
|
||||
|
||||
inline VkMemoryBarrier memory_barrier()
|
||||
{
|
||||
VkMemoryBarrier memory_barrier{};
|
||||
memory_barrier.sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER;
|
||||
return memory_barrier;
|
||||
}
|
||||
|
||||
inline VkImageCreateInfo image_create_info()
|
||||
{
|
||||
VkImageCreateInfo image_create_info{};
|
||||
image_create_info.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
|
||||
return image_create_info;
|
||||
}
|
||||
|
||||
inline VkSamplerCreateInfo sampler_create_info()
|
||||
{
|
||||
VkSamplerCreateInfo sampler_create_info{};
|
||||
sampler_create_info.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO;
|
||||
sampler_create_info.maxAnisotropy = 1.0f;
|
||||
return sampler_create_info;
|
||||
}
|
||||
|
||||
inline VkImageViewCreateInfo image_view_create_info()
|
||||
{
|
||||
VkImageViewCreateInfo image_view_create_info{};
|
||||
image_view_create_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
|
||||
return image_view_create_info;
|
||||
}
|
||||
|
||||
inline VkFramebufferCreateInfo framebuffer_create_info()
|
||||
{
|
||||
VkFramebufferCreateInfo framebuffer_create_info{};
|
||||
framebuffer_create_info.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
|
||||
return framebuffer_create_info;
|
||||
}
|
||||
|
||||
inline VkSemaphoreCreateInfo semaphore_create_info()
|
||||
{
|
||||
VkSemaphoreCreateInfo semaphore_create_info{};
|
||||
semaphore_create_info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
|
||||
return semaphore_create_info;
|
||||
}
|
||||
|
||||
inline VkFenceCreateInfo fence_create_info(VkFenceCreateFlags flags = 0)
|
||||
{
|
||||
VkFenceCreateInfo fence_create_info{};
|
||||
fence_create_info.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
|
||||
fence_create_info.flags = flags;
|
||||
return fence_create_info;
|
||||
}
|
||||
|
||||
inline VkEventCreateInfo event_create_info()
|
||||
{
|
||||
VkEventCreateInfo event_create_info{};
|
||||
event_create_info.sType = VK_STRUCTURE_TYPE_EVENT_CREATE_INFO;
|
||||
return event_create_info;
|
||||
}
|
||||
|
||||
inline VkSubmitInfo submit_info()
|
||||
{
|
||||
VkSubmitInfo submit_info{};
|
||||
submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
|
||||
return submit_info;
|
||||
}
|
||||
|
||||
inline VkViewport viewport(
|
||||
float width,
|
||||
float height,
|
||||
float min_depth,
|
||||
float max_depth)
|
||||
{
|
||||
VkViewport viewport{};
|
||||
viewport.width = width;
|
||||
viewport.height = height;
|
||||
viewport.minDepth = min_depth;
|
||||
viewport.maxDepth = max_depth;
|
||||
return viewport;
|
||||
}
|
||||
|
||||
inline VkRect2D rect2D(
|
||||
int32_t width,
|
||||
int32_t height,
|
||||
int32_t offset_x,
|
||||
int32_t offset_y)
|
||||
{
|
||||
VkRect2D rect2D{};
|
||||
rect2D.extent.width = width;
|
||||
rect2D.extent.height = height;
|
||||
rect2D.offset.x = offset_x;
|
||||
rect2D.offset.y = offset_y;
|
||||
return rect2D;
|
||||
}
|
||||
|
||||
inline VkBufferCreateInfo buffer_create_info()
|
||||
{
|
||||
VkBufferCreateInfo buffer_create_info{};
|
||||
buffer_create_info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
|
||||
return buffer_create_info;
|
||||
}
|
||||
|
||||
inline VkBufferCreateInfo buffer_create_info(
|
||||
VkBufferUsageFlags usage,
|
||||
VkDeviceSize size)
|
||||
{
|
||||
VkBufferCreateInfo buffer_create_info{};
|
||||
buffer_create_info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
|
||||
buffer_create_info.usage = usage;
|
||||
buffer_create_info.size = size;
|
||||
return buffer_create_info;
|
||||
}
|
||||
|
||||
inline VkDescriptorPoolCreateInfo descriptor_pool_create_info(
|
||||
uint32_t count,
|
||||
VkDescriptorPoolSize *pool_sizes,
|
||||
uint32_t max_sets)
|
||||
{
|
||||
VkDescriptorPoolCreateInfo descriptor_pool_info{};
|
||||
descriptor_pool_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
|
||||
descriptor_pool_info.poolSizeCount = count;
|
||||
descriptor_pool_info.pPoolSizes = pool_sizes;
|
||||
descriptor_pool_info.maxSets = max_sets;
|
||||
return descriptor_pool_info;
|
||||
}
|
||||
|
||||
inline VkDescriptorPoolCreateInfo descriptor_pool_create_info(
|
||||
const std::vector<VkDescriptorPoolSize> &pool_sizes,
|
||||
uint32_t max_sets)
|
||||
{
|
||||
VkDescriptorPoolCreateInfo descriptor_pool_info{};
|
||||
descriptor_pool_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
|
||||
descriptor_pool_info.poolSizeCount = static_cast<uint32_t>(pool_sizes.size());
|
||||
descriptor_pool_info.pPoolSizes = pool_sizes.data();
|
||||
descriptor_pool_info.maxSets = max_sets;
|
||||
return descriptor_pool_info;
|
||||
}
|
||||
|
||||
inline VkDescriptorPoolSize descriptor_pool_size(
|
||||
VkDescriptorType type,
|
||||
uint32_t count)
|
||||
{
|
||||
VkDescriptorPoolSize descriptor_pool_size{};
|
||||
descriptor_pool_size.type = type;
|
||||
descriptor_pool_size.descriptorCount = count;
|
||||
return descriptor_pool_size;
|
||||
}
|
||||
|
||||
inline VkDescriptorSetLayoutBinding descriptor_set_layout_binding(
|
||||
VkDescriptorType type,
|
||||
VkShaderStageFlags flags,
|
||||
uint32_t binding,
|
||||
uint32_t count = 1)
|
||||
{
|
||||
VkDescriptorSetLayoutBinding set_layout_binding{};
|
||||
set_layout_binding.descriptorType = type;
|
||||
set_layout_binding.stageFlags = flags;
|
||||
set_layout_binding.binding = binding;
|
||||
set_layout_binding.descriptorCount = count;
|
||||
return set_layout_binding;
|
||||
}
|
||||
|
||||
inline VkDescriptorSetLayoutCreateInfo descriptor_set_layout_create_info(
|
||||
const VkDescriptorSetLayoutBinding *bindings,
|
||||
uint32_t binding_count)
|
||||
{
|
||||
VkDescriptorSetLayoutCreateInfo descriptor_set_layout_create_info{};
|
||||
descriptor_set_layout_create_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
|
||||
descriptor_set_layout_create_info.pBindings = bindings;
|
||||
descriptor_set_layout_create_info.bindingCount = binding_count;
|
||||
return descriptor_set_layout_create_info;
|
||||
}
|
||||
|
||||
inline VkDescriptorSetLayoutCreateInfo descriptor_set_layout_create_info(
|
||||
const std::vector<VkDescriptorSetLayoutBinding> &bindings)
|
||||
{
|
||||
VkDescriptorSetLayoutCreateInfo descriptor_set_layout_create_info{};
|
||||
descriptor_set_layout_create_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
|
||||
descriptor_set_layout_create_info.pBindings = bindings.data();
|
||||
descriptor_set_layout_create_info.bindingCount = static_cast<uint32_t>(bindings.size());
|
||||
return descriptor_set_layout_create_info;
|
||||
}
|
||||
|
||||
inline VkPipelineLayoutCreateInfo pipeline_layout_create_info(
|
||||
const VkDescriptorSetLayout *set_layouts,
|
||||
uint32_t set_layout_count = 1)
|
||||
{
|
||||
VkPipelineLayoutCreateInfo pipeline_layout_create_info{};
|
||||
pipeline_layout_create_info.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
|
||||
pipeline_layout_create_info.setLayoutCount = set_layout_count;
|
||||
pipeline_layout_create_info.pSetLayouts = set_layouts;
|
||||
return pipeline_layout_create_info;
|
||||
}
|
||||
|
||||
inline VkPipelineLayoutCreateInfo pipeline_layout_create_info(
|
||||
uint32_t set_layout_count = 1)
|
||||
{
|
||||
VkPipelineLayoutCreateInfo pipeline_layout_create_info{};
|
||||
pipeline_layout_create_info.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
|
||||
pipeline_layout_create_info.setLayoutCount = set_layout_count;
|
||||
return pipeline_layout_create_info;
|
||||
}
|
||||
|
||||
inline VkDescriptorSetAllocateInfo descriptor_set_allocate_info(
|
||||
VkDescriptorPool descriptor_pool,
|
||||
const VkDescriptorSetLayout *set_layouts,
|
||||
uint32_t descriptor_set_count)
|
||||
{
|
||||
VkDescriptorSetAllocateInfo descriptor_set_allocate_info{};
|
||||
descriptor_set_allocate_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
|
||||
descriptor_set_allocate_info.descriptorPool = descriptor_pool;
|
||||
descriptor_set_allocate_info.pSetLayouts = set_layouts;
|
||||
descriptor_set_allocate_info.descriptorSetCount = descriptor_set_count;
|
||||
return descriptor_set_allocate_info;
|
||||
}
|
||||
|
||||
inline VkDescriptorImageInfo descriptor_image_info(VkSampler sampler, VkImageView image_view, VkImageLayout image_layout)
|
||||
{
|
||||
VkDescriptorImageInfo descriptor_image_info{};
|
||||
descriptor_image_info.sampler = sampler;
|
||||
descriptor_image_info.imageView = image_view;
|
||||
descriptor_image_info.imageLayout = image_layout;
|
||||
return descriptor_image_info;
|
||||
}
|
||||
|
||||
inline VkWriteDescriptorSet write_descriptor_set(
|
||||
VkDescriptorSet dst_set,
|
||||
VkDescriptorType type,
|
||||
uint32_t binding,
|
||||
VkDescriptorBufferInfo *buffer_info,
|
||||
uint32_t descriptor_count = 1)
|
||||
{
|
||||
VkWriteDescriptorSet write_descriptor_set{};
|
||||
write_descriptor_set.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
|
||||
write_descriptor_set.dstSet = dst_set;
|
||||
write_descriptor_set.descriptorType = type;
|
||||
write_descriptor_set.dstBinding = binding;
|
||||
write_descriptor_set.pBufferInfo = buffer_info;
|
||||
write_descriptor_set.descriptorCount = descriptor_count;
|
||||
return write_descriptor_set;
|
||||
}
|
||||
|
||||
inline VkWriteDescriptorSet write_descriptor_set(
|
||||
VkDescriptorSet dst_set,
|
||||
VkDescriptorType type,
|
||||
uint32_t binding,
|
||||
VkDescriptorImageInfo *image_info,
|
||||
uint32_t descriptor_count = 1)
|
||||
{
|
||||
VkWriteDescriptorSet write_descriptor_set{};
|
||||
write_descriptor_set.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
|
||||
write_descriptor_set.dstSet = dst_set;
|
||||
write_descriptor_set.descriptorType = type;
|
||||
write_descriptor_set.dstBinding = binding;
|
||||
write_descriptor_set.pImageInfo = image_info;
|
||||
write_descriptor_set.descriptorCount = descriptor_count;
|
||||
return write_descriptor_set;
|
||||
}
|
||||
|
||||
inline VkVertexInputBindingDescription vertex_input_binding_description(
|
||||
uint32_t binding,
|
||||
uint32_t stride,
|
||||
VkVertexInputRate input_rate)
|
||||
{
|
||||
VkVertexInputBindingDescription vertex_input_binding_description{};
|
||||
vertex_input_binding_description.binding = binding;
|
||||
vertex_input_binding_description.stride = stride;
|
||||
vertex_input_binding_description.inputRate = input_rate;
|
||||
return vertex_input_binding_description;
|
||||
}
|
||||
|
||||
inline VkVertexInputBindingDescription2EXT vertex_input_binding_description2ext(
|
||||
uint32_t binding,
|
||||
uint32_t stride,
|
||||
VkVertexInputRate input_rate,
|
||||
uint32_t divisor)
|
||||
{
|
||||
VkVertexInputBindingDescription2EXT vertex_input_binding_description2ext{};
|
||||
vertex_input_binding_description2ext.sType = VK_STRUCTURE_TYPE_VERTEX_INPUT_BINDING_DESCRIPTION_2_EXT;
|
||||
vertex_input_binding_description2ext.binding = binding;
|
||||
vertex_input_binding_description2ext.stride = stride;
|
||||
vertex_input_binding_description2ext.inputRate = input_rate;
|
||||
vertex_input_binding_description2ext.divisor = divisor;
|
||||
return vertex_input_binding_description2ext;
|
||||
}
|
||||
|
||||
inline VkVertexInputAttributeDescription vertex_input_attribute_description(
|
||||
uint32_t binding,
|
||||
uint32_t location,
|
||||
VkFormat format,
|
||||
uint32_t offset)
|
||||
{
|
||||
VkVertexInputAttributeDescription vertex_input_attribute_description{};
|
||||
vertex_input_attribute_description.location = location;
|
||||
vertex_input_attribute_description.binding = binding;
|
||||
vertex_input_attribute_description.format = format;
|
||||
vertex_input_attribute_description.offset = offset;
|
||||
return vertex_input_attribute_description;
|
||||
}
|
||||
|
||||
inline VkVertexInputAttributeDescription2EXT vertex_input_attribute_description2ext(
|
||||
uint32_t binding,
|
||||
uint32_t location,
|
||||
VkFormat format,
|
||||
uint32_t offset)
|
||||
{
|
||||
VkVertexInputAttributeDescription2EXT vertex_input_attribute_description2ext{};
|
||||
vertex_input_attribute_description2ext.sType = VK_STRUCTURE_TYPE_VERTEX_INPUT_ATTRIBUTE_DESCRIPTION_2_EXT;
|
||||
vertex_input_attribute_description2ext.location = location;
|
||||
vertex_input_attribute_description2ext.binding = binding;
|
||||
vertex_input_attribute_description2ext.format = format;
|
||||
vertex_input_attribute_description2ext.offset = offset;
|
||||
return vertex_input_attribute_description2ext;
|
||||
}
|
||||
|
||||
inline VkPipelineVertexInputStateCreateInfo pipeline_vertex_input_state_create_info()
|
||||
{
|
||||
VkPipelineVertexInputStateCreateInfo pipeline_vertex_input_state_create_info{};
|
||||
pipeline_vertex_input_state_create_info.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
|
||||
return pipeline_vertex_input_state_create_info;
|
||||
}
|
||||
|
||||
inline VkPipelineInputAssemblyStateCreateInfo pipeline_input_assembly_state_create_info(
|
||||
VkPrimitiveTopology topology,
|
||||
VkPipelineInputAssemblyStateCreateFlags flags,
|
||||
VkBool32 primitive_restart_enable)
|
||||
{
|
||||
VkPipelineInputAssemblyStateCreateInfo pipeline_input_assembly_state_create_info{};
|
||||
pipeline_input_assembly_state_create_info.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
|
||||
pipeline_input_assembly_state_create_info.topology = topology;
|
||||
pipeline_input_assembly_state_create_info.flags = flags;
|
||||
pipeline_input_assembly_state_create_info.primitiveRestartEnable = primitive_restart_enable;
|
||||
return pipeline_input_assembly_state_create_info;
|
||||
}
|
||||
|
||||
inline VkPipelineRasterizationStateCreateInfo pipeline_rasterization_state_create_info(
|
||||
VkPolygonMode polygon_mode,
|
||||
VkCullModeFlags cull_mode,
|
||||
VkFrontFace front_face,
|
||||
VkPipelineRasterizationStateCreateFlags flags = 0)
|
||||
{
|
||||
VkPipelineRasterizationStateCreateInfo pipeline_rasterization_state_create_info{};
|
||||
pipeline_rasterization_state_create_info.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
|
||||
pipeline_rasterization_state_create_info.polygonMode = polygon_mode;
|
||||
pipeline_rasterization_state_create_info.cullMode = cull_mode;
|
||||
pipeline_rasterization_state_create_info.frontFace = front_face;
|
||||
pipeline_rasterization_state_create_info.flags = flags;
|
||||
pipeline_rasterization_state_create_info.depthClampEnable = VK_FALSE;
|
||||
pipeline_rasterization_state_create_info.lineWidth = 1.0f;
|
||||
return pipeline_rasterization_state_create_info;
|
||||
}
|
||||
|
||||
inline VkPipelineColorBlendAttachmentState pipeline_color_blend_attachment_state(
|
||||
VkColorComponentFlags color_write_mask,
|
||||
VkBool32 blend_enable)
|
||||
{
|
||||
VkPipelineColorBlendAttachmentState pipeline_color_blend_attachment_state{};
|
||||
pipeline_color_blend_attachment_state.colorWriteMask = color_write_mask;
|
||||
pipeline_color_blend_attachment_state.blendEnable = blend_enable;
|
||||
return pipeline_color_blend_attachment_state;
|
||||
}
|
||||
|
||||
inline VkPipelineColorBlendStateCreateInfo pipeline_color_blend_state_create_info(
|
||||
uint32_t attachment_count,
|
||||
const VkPipelineColorBlendAttachmentState *attachments)
|
||||
{
|
||||
VkPipelineColorBlendStateCreateInfo pipeline_color_blend_state_create_info{};
|
||||
pipeline_color_blend_state_create_info.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
|
||||
pipeline_color_blend_state_create_info.attachmentCount = attachment_count;
|
||||
pipeline_color_blend_state_create_info.pAttachments = attachments;
|
||||
return pipeline_color_blend_state_create_info;
|
||||
}
|
||||
|
||||
inline VkPipelineDepthStencilStateCreateInfo pipeline_depth_stencil_state_create_info(
|
||||
VkBool32 depth_test_enable,
|
||||
VkBool32 depth_write_enable,
|
||||
VkCompareOp depth_compare_op)
|
||||
{
|
||||
VkPipelineDepthStencilStateCreateInfo pipeline_depth_stencil_state_create_info{};
|
||||
pipeline_depth_stencil_state_create_info.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO;
|
||||
pipeline_depth_stencil_state_create_info.depthTestEnable = depth_test_enable;
|
||||
pipeline_depth_stencil_state_create_info.depthWriteEnable = depth_write_enable;
|
||||
pipeline_depth_stencil_state_create_info.depthCompareOp = depth_compare_op;
|
||||
pipeline_depth_stencil_state_create_info.front = pipeline_depth_stencil_state_create_info.back;
|
||||
pipeline_depth_stencil_state_create_info.back.compareOp = VK_COMPARE_OP_ALWAYS;
|
||||
return pipeline_depth_stencil_state_create_info;
|
||||
}
|
||||
|
||||
inline VkPipelineViewportStateCreateInfo pipeline_viewport_state_create_info(
|
||||
uint32_t viewport_count,
|
||||
uint32_t scissor_count,
|
||||
VkPipelineViewportStateCreateFlags flags = 0)
|
||||
{
|
||||
VkPipelineViewportStateCreateInfo pipeline_viewport_state_create_info{};
|
||||
pipeline_viewport_state_create_info.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
|
||||
pipeline_viewport_state_create_info.viewportCount = viewport_count;
|
||||
pipeline_viewport_state_create_info.scissorCount = scissor_count;
|
||||
pipeline_viewport_state_create_info.flags = flags;
|
||||
return pipeline_viewport_state_create_info;
|
||||
}
|
||||
|
||||
inline VkPipelineMultisampleStateCreateInfo pipeline_multisample_state_create_info(
|
||||
VkSampleCountFlagBits rasterization_samples,
|
||||
VkPipelineMultisampleStateCreateFlags flags = 0)
|
||||
{
|
||||
VkPipelineMultisampleStateCreateInfo pipeline_multisample_state_create_info{};
|
||||
pipeline_multisample_state_create_info.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
|
||||
pipeline_multisample_state_create_info.rasterizationSamples = rasterization_samples;
|
||||
pipeline_multisample_state_create_info.flags = flags;
|
||||
return pipeline_multisample_state_create_info;
|
||||
}
|
||||
|
||||
inline VkPipelineDynamicStateCreateInfo pipeline_dynamic_state_create_info(
|
||||
const VkDynamicState *dynamic_states,
|
||||
uint32_t dynamicStateCount,
|
||||
VkPipelineDynamicStateCreateFlags flags = 0)
|
||||
{
|
||||
VkPipelineDynamicStateCreateInfo pipeline_dynamic_state_create_info{};
|
||||
pipeline_dynamic_state_create_info.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO;
|
||||
pipeline_dynamic_state_create_info.pDynamicStates = dynamic_states;
|
||||
pipeline_dynamic_state_create_info.dynamicStateCount = dynamicStateCount;
|
||||
pipeline_dynamic_state_create_info.flags = flags;
|
||||
return pipeline_dynamic_state_create_info;
|
||||
}
|
||||
|
||||
inline VkPipelineDynamicStateCreateInfo pipeline_dynamic_state_create_info(
|
||||
const std::vector<VkDynamicState> &dynamic_states,
|
||||
VkPipelineDynamicStateCreateFlags flags = 0)
|
||||
{
|
||||
VkPipelineDynamicStateCreateInfo pipeline_dynamic_state_create_info{};
|
||||
pipeline_dynamic_state_create_info.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO;
|
||||
pipeline_dynamic_state_create_info.pDynamicStates = dynamic_states.data();
|
||||
pipeline_dynamic_state_create_info.dynamicStateCount = static_cast<uint32_t>(dynamic_states.size());
|
||||
pipeline_dynamic_state_create_info.flags = flags;
|
||||
return pipeline_dynamic_state_create_info;
|
||||
}
|
||||
|
||||
inline VkPipelineTessellationStateCreateInfo pipeline_tessellation_state_create_info(uint32_t patch_control_points)
|
||||
{
|
||||
VkPipelineTessellationStateCreateInfo pipeline_tessellation_state_create_info{};
|
||||
pipeline_tessellation_state_create_info.sType = VK_STRUCTURE_TYPE_PIPELINE_TESSELLATION_STATE_CREATE_INFO;
|
||||
pipeline_tessellation_state_create_info.patchControlPoints = patch_control_points;
|
||||
return pipeline_tessellation_state_create_info;
|
||||
}
|
||||
|
||||
inline VkGraphicsPipelineCreateInfo pipeline_create_info(
|
||||
VkPipelineLayout layout,
|
||||
VkRenderPass render_pass,
|
||||
VkPipelineCreateFlags flags = 0)
|
||||
{
|
||||
VkGraphicsPipelineCreateInfo pipeline_create_info{};
|
||||
pipeline_create_info.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
|
||||
pipeline_create_info.layout = layout;
|
||||
pipeline_create_info.renderPass = render_pass;
|
||||
pipeline_create_info.flags = flags;
|
||||
pipeline_create_info.basePipelineIndex = -1;
|
||||
pipeline_create_info.basePipelineHandle = VK_NULL_HANDLE;
|
||||
return pipeline_create_info;
|
||||
}
|
||||
|
||||
inline VkGraphicsPipelineCreateInfo pipeline_create_info()
|
||||
{
|
||||
VkGraphicsPipelineCreateInfo pipeline_create_info{};
|
||||
pipeline_create_info.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
|
||||
pipeline_create_info.basePipelineIndex = -1;
|
||||
pipeline_create_info.basePipelineHandle = VK_NULL_HANDLE;
|
||||
return pipeline_create_info;
|
||||
}
|
||||
|
||||
inline VkComputePipelineCreateInfo compute_pipeline_create_info(
|
||||
VkPipelineLayout layout,
|
||||
VkPipelineCreateFlags flags = 0)
|
||||
{
|
||||
VkComputePipelineCreateInfo compute_pipeline_create_info{};
|
||||
compute_pipeline_create_info.sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO;
|
||||
compute_pipeline_create_info.layout = layout;
|
||||
compute_pipeline_create_info.flags = flags;
|
||||
return compute_pipeline_create_info;
|
||||
}
|
||||
|
||||
inline VkPushConstantRange push_constant_range(
|
||||
VkShaderStageFlags stage_flags,
|
||||
uint32_t size,
|
||||
uint32_t offset)
|
||||
{
|
||||
VkPushConstantRange push_constant_range{};
|
||||
push_constant_range.stageFlags = stage_flags;
|
||||
push_constant_range.offset = offset;
|
||||
push_constant_range.size = size;
|
||||
return push_constant_range;
|
||||
}
|
||||
|
||||
inline VkBindSparseInfo bind_sparse_info()
|
||||
{
|
||||
VkBindSparseInfo bind_sparse_info{};
|
||||
bind_sparse_info.sType = VK_STRUCTURE_TYPE_BIND_SPARSE_INFO;
|
||||
return bind_sparse_info;
|
||||
}
|
||||
|
||||
/** @brief Initialize a map entry for a shader specialization constant */
|
||||
inline VkSpecializationMapEntry specialization_map_entry(uint32_t constant_id, uint32_t offset, size_t size)
|
||||
{
|
||||
VkSpecializationMapEntry specialization_map_entry{};
|
||||
specialization_map_entry.constantID = constant_id;
|
||||
specialization_map_entry.offset = offset;
|
||||
specialization_map_entry.size = size;
|
||||
return specialization_map_entry;
|
||||
}
|
||||
|
||||
/** @brief Initialize a specialization constant info structure to pass to a shader stage */
|
||||
inline VkSpecializationInfo specialization_info(uint32_t map_entry_count, const VkSpecializationMapEntry *map_entries, size_t data_size, const void *data)
|
||||
{
|
||||
VkSpecializationInfo specialization_info{};
|
||||
specialization_info.mapEntryCount = map_entry_count;
|
||||
specialization_info.pMapEntries = map_entries;
|
||||
specialization_info.dataSize = data_size;
|
||||
specialization_info.pData = data;
|
||||
return specialization_info;
|
||||
}
|
||||
|
||||
inline VkTimelineSemaphoreSubmitInfo timeline_semaphore_submit_info(uint32_t wait_value_count, uint64_t *wait_values, uint32_t signal_value_count, uint64_t *signal_values)
|
||||
{
|
||||
return VkTimelineSemaphoreSubmitInfo{
|
||||
VK_STRUCTURE_TYPE_TIMELINE_SEMAPHORE_SUBMIT_INFO,
|
||||
NULL,
|
||||
wait_value_count,
|
||||
wait_values,
|
||||
signal_value_count,
|
||||
signal_values};
|
||||
}
|
||||
} // namespace initializers
|
||||
} // namespace vkb
|
||||
@@ -0,0 +1,246 @@
|
||||
/* Copyright (c) 2021-2025, 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.
|
||||
*/
|
||||
|
||||
#include "acceleration_structure.h"
|
||||
|
||||
#include "device.h"
|
||||
|
||||
namespace vkb
|
||||
{
|
||||
namespace core
|
||||
{
|
||||
AccelerationStructure::AccelerationStructure(vkb::core::DeviceC &device,
|
||||
VkAccelerationStructureTypeKHR type) :
|
||||
device{device},
|
||||
type{type}
|
||||
{
|
||||
}
|
||||
|
||||
AccelerationStructure::~AccelerationStructure()
|
||||
{
|
||||
if (handle != VK_NULL_HANDLE)
|
||||
{
|
||||
vkDestroyAccelerationStructureKHR(device.get_handle(), handle, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t AccelerationStructure::add_triangle_geometry(vkb::core::BufferC &vertex_buffer,
|
||||
vkb::core::BufferC &index_buffer,
|
||||
vkb::core::BufferC &transform_buffer,
|
||||
uint32_t triangle_count,
|
||||
uint32_t max_vertex,
|
||||
VkDeviceSize vertex_stride,
|
||||
uint32_t transform_offset,
|
||||
VkFormat vertex_format,
|
||||
VkIndexType index_type,
|
||||
VkGeometryFlagsKHR flags,
|
||||
uint64_t vertex_buffer_data_address,
|
||||
uint64_t index_buffer_data_address,
|
||||
uint64_t transform_buffer_data_address)
|
||||
{
|
||||
VkAccelerationStructureGeometryKHR geometry{};
|
||||
geometry.sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_GEOMETRY_KHR;
|
||||
geometry.geometryType = VK_GEOMETRY_TYPE_TRIANGLES_KHR;
|
||||
geometry.flags = flags;
|
||||
geometry.geometry.triangles.sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_GEOMETRY_TRIANGLES_DATA_KHR;
|
||||
geometry.geometry.triangles.vertexFormat = vertex_format;
|
||||
geometry.geometry.triangles.maxVertex = max_vertex;
|
||||
geometry.geometry.triangles.vertexStride = vertex_stride;
|
||||
geometry.geometry.triangles.indexType = index_type;
|
||||
geometry.geometry.triangles.vertexData.deviceAddress = vertex_buffer_data_address == 0 ? vertex_buffer.get_device_address() : vertex_buffer_data_address;
|
||||
geometry.geometry.triangles.indexData.deviceAddress = index_buffer_data_address == 0 ? index_buffer.get_device_address() : index_buffer_data_address;
|
||||
geometry.geometry.triangles.transformData.deviceAddress = transform_buffer_data_address == 0 ? transform_buffer.get_device_address() : transform_buffer_data_address;
|
||||
|
||||
uint64_t index = geometries.size();
|
||||
geometries.insert({index, {geometry, triangle_count, transform_offset}});
|
||||
return index;
|
||||
}
|
||||
|
||||
void AccelerationStructure::update_triangle_geometry(uint64_t triangleUUID,
|
||||
std::unique_ptr<vkb::core::BufferC> &vertex_buffer,
|
||||
std::unique_ptr<vkb::core::BufferC> &index_buffer,
|
||||
std::unique_ptr<vkb::core::BufferC> &transform_buffer,
|
||||
uint32_t triangle_count, uint32_t max_vertex,
|
||||
VkDeviceSize vertex_stride, uint32_t transform_offset,
|
||||
VkFormat vertex_format, VkGeometryFlagsKHR flags,
|
||||
uint64_t vertex_buffer_data_address,
|
||||
uint64_t index_buffer_data_address,
|
||||
uint64_t transform_buffer_data_address)
|
||||
{
|
||||
VkAccelerationStructureGeometryKHR *geometry = &geometries[triangleUUID].geometry;
|
||||
geometry->sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_GEOMETRY_KHR;
|
||||
geometry->geometryType = VK_GEOMETRY_TYPE_TRIANGLES_KHR;
|
||||
geometry->flags = flags;
|
||||
geometry->geometry.triangles.sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_GEOMETRY_TRIANGLES_DATA_KHR;
|
||||
geometry->geometry.triangles.vertexFormat = vertex_format;
|
||||
geometry->geometry.triangles.maxVertex = max_vertex;
|
||||
geometry->geometry.triangles.vertexStride = vertex_stride;
|
||||
geometry->geometry.triangles.indexType = VK_INDEX_TYPE_UINT32;
|
||||
geometry->geometry.triangles.vertexData.deviceAddress = vertex_buffer_data_address == 0 ? vertex_buffer->get_device_address() : vertex_buffer_data_address;
|
||||
geometry->geometry.triangles.indexData.deviceAddress = index_buffer_data_address == 0 ? index_buffer->get_device_address() : index_buffer_data_address;
|
||||
geometry->geometry.triangles.transformData.deviceAddress = transform_buffer_data_address == 0 ? transform_buffer->get_device_address() : transform_buffer_data_address;
|
||||
geometries[triangleUUID].primitive_count = triangle_count;
|
||||
geometries[triangleUUID].transform_offset = transform_offset;
|
||||
geometries[triangleUUID].updated = true;
|
||||
}
|
||||
|
||||
uint64_t AccelerationStructure::add_instance_geometry(std::unique_ptr<vkb::core::BufferC> &instance_buffer, uint32_t instance_count, uint32_t transform_offset, VkGeometryFlagsKHR flags)
|
||||
{
|
||||
VkAccelerationStructureGeometryKHR geometry{};
|
||||
geometry.sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_GEOMETRY_KHR;
|
||||
geometry.geometryType = VK_GEOMETRY_TYPE_INSTANCES_KHR;
|
||||
geometry.flags = flags;
|
||||
geometry.geometry.instances.sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_GEOMETRY_INSTANCES_DATA_KHR;
|
||||
geometry.geometry.instances.arrayOfPointers = VK_FALSE;
|
||||
geometry.geometry.instances.data.deviceAddress = instance_buffer->get_device_address();
|
||||
|
||||
uint64_t index = geometries.size();
|
||||
geometries.insert({index, {geometry, instance_count, transform_offset}});
|
||||
return index;
|
||||
}
|
||||
|
||||
void AccelerationStructure::update_instance_geometry(uint64_t instance_UID,
|
||||
std::unique_ptr<vkb::core::BufferC> &instance_buffer,
|
||||
uint32_t instance_count, uint32_t transform_offset,
|
||||
VkGeometryFlagsKHR flags)
|
||||
{
|
||||
VkAccelerationStructureGeometryKHR *geometry = &geometries[instance_UID].geometry;
|
||||
geometry->sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_GEOMETRY_KHR;
|
||||
geometry->geometryType = VK_GEOMETRY_TYPE_INSTANCES_KHR;
|
||||
geometry->flags = flags;
|
||||
geometry->geometry.instances.sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_GEOMETRY_INSTANCES_DATA_KHR;
|
||||
geometry->geometry.instances.arrayOfPointers = VK_FALSE;
|
||||
geometry->geometry.instances.data.deviceAddress = instance_buffer->get_device_address();
|
||||
geometries[instance_UID].primitive_count = instance_count;
|
||||
geometries[instance_UID].transform_offset = transform_offset;
|
||||
geometries[instance_UID].updated = true;
|
||||
}
|
||||
|
||||
void AccelerationStructure::build(VkQueue queue, VkBuildAccelerationStructureFlagsKHR flags, VkBuildAccelerationStructureModeKHR mode)
|
||||
{
|
||||
assert(!geometries.empty());
|
||||
|
||||
std::vector<VkAccelerationStructureGeometryKHR> acceleration_structure_geometries;
|
||||
std::vector<VkAccelerationStructureBuildRangeInfoKHR> acceleration_structure_build_range_infos;
|
||||
std::vector<uint32_t> primitive_counts;
|
||||
for (auto &geometry : geometries)
|
||||
{
|
||||
if (mode == VK_BUILD_ACCELERATION_STRUCTURE_MODE_UPDATE_KHR && !geometry.second.updated)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
acceleration_structure_geometries.push_back(geometry.second.geometry);
|
||||
// Infer build range info from geometry
|
||||
VkAccelerationStructureBuildRangeInfoKHR build_range_info;
|
||||
build_range_info.primitiveCount = geometry.second.primitive_count;
|
||||
build_range_info.primitiveOffset = 0;
|
||||
build_range_info.firstVertex = 0;
|
||||
build_range_info.transformOffset = geometry.second.transform_offset;
|
||||
acceleration_structure_build_range_infos.push_back(build_range_info);
|
||||
primitive_counts.push_back(geometry.second.primitive_count);
|
||||
geometry.second.updated = false;
|
||||
}
|
||||
|
||||
VkAccelerationStructureBuildGeometryInfoKHR build_geometry_info{};
|
||||
build_geometry_info.sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_BUILD_GEOMETRY_INFO_KHR;
|
||||
build_geometry_info.type = type;
|
||||
build_geometry_info.flags = flags;
|
||||
build_geometry_info.mode = mode;
|
||||
if (mode == VK_BUILD_ACCELERATION_STRUCTURE_MODE_UPDATE_KHR && handle != VK_NULL_HANDLE)
|
||||
{
|
||||
build_geometry_info.srcAccelerationStructure = handle;
|
||||
build_geometry_info.dstAccelerationStructure = handle;
|
||||
}
|
||||
build_geometry_info.geometryCount = static_cast<uint32_t>(acceleration_structure_geometries.size());
|
||||
build_geometry_info.pGeometries = acceleration_structure_geometries.data();
|
||||
|
||||
// Get required build sizes
|
||||
build_sizes_info.sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_BUILD_SIZES_INFO_KHR;
|
||||
vkGetAccelerationStructureBuildSizesKHR(
|
||||
device.get_handle(),
|
||||
VK_ACCELERATION_STRUCTURE_BUILD_TYPE_DEVICE_KHR,
|
||||
&build_geometry_info,
|
||||
primitive_counts.data(),
|
||||
&build_sizes_info);
|
||||
|
||||
// Create a buffer for the acceleration structure
|
||||
if (!buffer || buffer->get_size() != build_sizes_info.accelerationStructureSize)
|
||||
{
|
||||
buffer = std::make_unique<vkb::core::BufferC>(
|
||||
device,
|
||||
build_sizes_info.accelerationStructureSize,
|
||||
VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_STORAGE_BIT_KHR | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT,
|
||||
VMA_MEMORY_USAGE_GPU_ONLY);
|
||||
|
||||
VkAccelerationStructureCreateInfoKHR acceleration_structure_create_info{};
|
||||
acceleration_structure_create_info.sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_CREATE_INFO_KHR;
|
||||
acceleration_structure_create_info.buffer = buffer->get_handle();
|
||||
acceleration_structure_create_info.size = build_sizes_info.accelerationStructureSize;
|
||||
acceleration_structure_create_info.type = type;
|
||||
VkResult result = vkCreateAccelerationStructureKHR(device.get_handle(), &acceleration_structure_create_info, nullptr, &handle);
|
||||
|
||||
if (result != VK_SUCCESS)
|
||||
{
|
||||
throw VulkanException{result, "Could not create acceleration structure"};
|
||||
}
|
||||
}
|
||||
|
||||
// Get the acceleration structure's handle
|
||||
VkAccelerationStructureDeviceAddressInfoKHR acceleration_device_address_info{};
|
||||
acceleration_device_address_info.sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_DEVICE_ADDRESS_INFO_KHR;
|
||||
acceleration_device_address_info.accelerationStructure = handle;
|
||||
device_address = vkGetAccelerationStructureDeviceAddressKHR(device.get_handle(), &acceleration_device_address_info);
|
||||
|
||||
// Create a scratch buffer as a temporary storage for the acceleration structure build
|
||||
scratch_buffer = std::make_unique<vkb::core::BufferC>(
|
||||
device,
|
||||
build_sizes_info.buildScratchSize,
|
||||
VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT,
|
||||
VMA_MEMORY_USAGE_GPU_ONLY);
|
||||
|
||||
build_geometry_info.scratchData.deviceAddress = scratch_buffer->get_device_address();
|
||||
build_geometry_info.dstAccelerationStructure = handle;
|
||||
|
||||
// Build the acceleration structure on the device via a one-time command buffer submission
|
||||
VkCommandBuffer command_buffer = device.create_command_buffer(VK_COMMAND_BUFFER_LEVEL_PRIMARY, true);
|
||||
auto as_build_range_infos = &*acceleration_structure_build_range_infos.data();
|
||||
vkCmdBuildAccelerationStructuresKHR(
|
||||
command_buffer,
|
||||
1,
|
||||
&build_geometry_info,
|
||||
&as_build_range_infos);
|
||||
device.flush_command_buffer(command_buffer, queue);
|
||||
scratch_buffer.reset();
|
||||
}
|
||||
|
||||
VkAccelerationStructureKHR AccelerationStructure::get_handle() const
|
||||
{
|
||||
return handle;
|
||||
}
|
||||
|
||||
const VkAccelerationStructureKHR *AccelerationStructure::get() const
|
||||
{
|
||||
return &handle;
|
||||
}
|
||||
|
||||
uint64_t AccelerationStructure::get_device_address() const
|
||||
{
|
||||
return device_address;
|
||||
}
|
||||
|
||||
} // namespace core
|
||||
} // namespace vkb
|
||||
@@ -0,0 +1,162 @@
|
||||
/* Copyright (c) 2021-2025, 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.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "common/helpers.h"
|
||||
#include "common/vk_common.h"
|
||||
#include "core/buffer.h"
|
||||
|
||||
namespace vkb
|
||||
{
|
||||
namespace core
|
||||
{
|
||||
template <vkb::BindingType bindingType>
|
||||
class Device;
|
||||
using DeviceC = Device<vkb::BindingType::C>;
|
||||
|
||||
/**
|
||||
* @brief Wraps setup and access for a ray tracing top- or bottom-level acceleration structure
|
||||
*/
|
||||
class AccelerationStructure
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief Creates a acceleration structure and the required buffer to store it's geometries
|
||||
* @param device A valid Vulkan device
|
||||
* @param type The type of the acceleration structure (top- or bottom-level)
|
||||
*/
|
||||
AccelerationStructure(vkb::core::DeviceC &device,
|
||||
VkAccelerationStructureTypeKHR type);
|
||||
|
||||
~AccelerationStructure();
|
||||
|
||||
/**
|
||||
* @brief Adds triangle geometry to the acceleration structure (only valid for bottom level)
|
||||
* @returns UUID for the geometry instance for the case of multiple geometries to look up in the map
|
||||
* @param vertex_buffer Buffer containing vertices
|
||||
* @param index_buffer Buffer containing indices
|
||||
* @param transform_buffer Buffer containing transform data
|
||||
* @param triangle_count Number of triangles for this geometry
|
||||
* @param max_vertex Index of the last vertex in the geometry
|
||||
* @param vertex_stride Stride of the vertex structure
|
||||
* @param transform_offset Offset of this geometry in the transform data buffer
|
||||
* @param vertex_format Format of the vertex structure
|
||||
* @param index_type Type of the indices
|
||||
* @param flags Ray tracing geometry flags
|
||||
* @param vertex_buffer_data_address set this if don't want the vertex_buffer data_address
|
||||
* @param index_buffer_data_address set this if don't want the index_buffer data_address
|
||||
* @param transform_buffer_data_address set this if don't want the transform_buffer data_address
|
||||
*/
|
||||
uint64_t add_triangle_geometry(vkb::core::BufferC &vertex_buffer,
|
||||
vkb::core::BufferC &index_buffer,
|
||||
vkb::core::BufferC &transform_buffer,
|
||||
uint32_t triangle_count,
|
||||
uint32_t max_vertex,
|
||||
VkDeviceSize vertex_stride,
|
||||
uint32_t transform_offset = 0,
|
||||
VkFormat vertex_format = VK_FORMAT_R32G32B32_SFLOAT,
|
||||
VkIndexType index_type = VK_INDEX_TYPE_UINT32,
|
||||
VkGeometryFlagsKHR flags = VK_GEOMETRY_OPAQUE_BIT_KHR,
|
||||
uint64_t vertex_buffer_data_address = 0,
|
||||
uint64_t index_buffer_data_address = 0,
|
||||
uint64_t transform_buffer_data_address = 0);
|
||||
|
||||
void update_triangle_geometry(uint64_t triangleUUID, std::unique_ptr<vkb::core::BufferC> &vertex_buffer,
|
||||
std::unique_ptr<vkb::core::BufferC> &index_buffer,
|
||||
std::unique_ptr<vkb::core::BufferC> &transform_buffer,
|
||||
uint32_t triangle_count,
|
||||
uint32_t max_vertex,
|
||||
VkDeviceSize vertex_stride,
|
||||
uint32_t transform_offset = 0,
|
||||
VkFormat vertex_format = VK_FORMAT_R32G32B32_SFLOAT,
|
||||
VkGeometryFlagsKHR flags = VK_GEOMETRY_OPAQUE_BIT_KHR,
|
||||
uint64_t vertex_buffer_data_address = 0,
|
||||
uint64_t index_buffer_data_address = 0,
|
||||
uint64_t transform_buffer_data_address = 0);
|
||||
|
||||
/**
|
||||
* @brief Adds instance geometry to the acceleration structure (only valid for top level)
|
||||
* @returns index of the instance geometry into the structure.
|
||||
* @param instance_buffer Buffer containing instances
|
||||
* @param instance_count Number of instances for this geometry
|
||||
* @param transform_offset Offset of this geometry in the transform data buffer
|
||||
* @param flags Ray tracing geometry flags
|
||||
*/
|
||||
uint64_t add_instance_geometry(std::unique_ptr<vkb::core::BufferC> &instance_buffer,
|
||||
uint32_t instance_count,
|
||||
uint32_t transform_offset = 0,
|
||||
VkGeometryFlagsKHR flags = VK_GEOMETRY_OPAQUE_BIT_KHR);
|
||||
|
||||
void update_instance_geometry(uint64_t instance_UID, std::unique_ptr<vkb::core::BufferC> &instance_buffer,
|
||||
uint32_t instance_count,
|
||||
uint32_t transform_offset = 0,
|
||||
VkGeometryFlagsKHR flags = VK_GEOMETRY_OPAQUE_BIT_KHR);
|
||||
|
||||
/**
|
||||
* @brief Builds the acceleration structure on the device (requires at least one geometry to be added)
|
||||
* @param queue Queue to use for the build process
|
||||
* @param flags Build flags
|
||||
* @param mode Build mode (build or update)
|
||||
*/
|
||||
void build(VkQueue queue,
|
||||
VkBuildAccelerationStructureFlagsKHR flags = VK_BUILD_ACCELERATION_STRUCTURE_PREFER_FAST_TRACE_BIT_KHR,
|
||||
VkBuildAccelerationStructureModeKHR mode = VK_BUILD_ACCELERATION_STRUCTURE_MODE_BUILD_KHR);
|
||||
|
||||
VkAccelerationStructureKHR get_handle() const;
|
||||
|
||||
const VkAccelerationStructureKHR *get() const;
|
||||
|
||||
uint64_t get_device_address() const;
|
||||
|
||||
vkb::core::BufferC *get_buffer() const
|
||||
{
|
||||
return buffer.get();
|
||||
}
|
||||
|
||||
void resetGeometries()
|
||||
{
|
||||
geometries.clear();
|
||||
}
|
||||
|
||||
private:
|
||||
vkb::core::DeviceC &device;
|
||||
|
||||
VkAccelerationStructureKHR handle{VK_NULL_HANDLE};
|
||||
|
||||
uint64_t device_address{0};
|
||||
|
||||
VkAccelerationStructureTypeKHR type{};
|
||||
|
||||
VkAccelerationStructureBuildSizesInfoKHR build_sizes_info{};
|
||||
|
||||
struct Geometry
|
||||
{
|
||||
VkAccelerationStructureGeometryKHR geometry{};
|
||||
uint32_t primitive_count{};
|
||||
uint32_t transform_offset{};
|
||||
bool updated = false;
|
||||
};
|
||||
|
||||
std::unique_ptr<vkb::core::BufferC> scratch_buffer;
|
||||
|
||||
std::map<uint64_t, Geometry> geometries{};
|
||||
|
||||
std::unique_ptr<vkb::core::BufferC> buffer{nullptr};
|
||||
};
|
||||
} // namespace core
|
||||
} // namespace vkb
|
||||
@@ -0,0 +1,61 @@
|
||||
/* Copyright (c) 2021-2024, NVIDIA CORPORATION. All rights reserved.
|
||||
* Copyright (c) 2024, Bradley Austin Davis. All rights reserved.
|
||||
*
|
||||
* 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 "allocated.h"
|
||||
#include "common/error.h"
|
||||
|
||||
namespace vkb
|
||||
{
|
||||
|
||||
namespace allocated
|
||||
{
|
||||
|
||||
VmaAllocator &get_memory_allocator()
|
||||
{
|
||||
static VmaAllocator memory_allocator = VK_NULL_HANDLE;
|
||||
return memory_allocator;
|
||||
}
|
||||
|
||||
void init(const VmaAllocatorCreateInfo &create_info)
|
||||
{
|
||||
auto &allocator = get_memory_allocator();
|
||||
if (allocator == VK_NULL_HANDLE)
|
||||
{
|
||||
VkResult result = vmaCreateAllocator(&create_info, &allocator);
|
||||
if (result != VK_SUCCESS)
|
||||
{
|
||||
throw VulkanException{result, "Cannot create allocator"};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void shutdown()
|
||||
{
|
||||
auto &allocator = get_memory_allocator();
|
||||
if (allocator != VK_NULL_HANDLE)
|
||||
{
|
||||
VmaTotalStatistics stats;
|
||||
vmaCalculateStatistics(allocator, &stats);
|
||||
LOGI("Total device memory leaked: {} bytes.", stats.total.statistics.allocationBytes);
|
||||
vmaDestroyAllocator(allocator);
|
||||
allocator = VK_NULL_HANDLE;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace allocated
|
||||
} // namespace vkb
|
||||
@@ -0,0 +1,633 @@
|
||||
/* Copyright (c) 2021-2025, NVIDIA CORPORATION. All rights reserved.
|
||||
* Copyright (c) 2024-2025, Bradley Austin Davis. All rights reserved.
|
||||
*
|
||||
* 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 "common/error.h"
|
||||
#include "core/physical_device.h"
|
||||
#include "core/vulkan_resource.h"
|
||||
|
||||
namespace vkb
|
||||
{
|
||||
namespace allocated
|
||||
{
|
||||
/**
|
||||
* @brief Retrieves a reference to the VMA allocator singleton. It will hold an opaque handle to the VMA
|
||||
* allocator between calls to `init` and `shutdown`. Otherwise it contains a null pointer.
|
||||
* @return A reference to the VMA allocator singleton handle.
|
||||
*/
|
||||
VmaAllocator &get_memory_allocator();
|
||||
|
||||
/**
|
||||
* @brief The non-templatized VMA initializer function, referenced by the template version to smooth
|
||||
* over the differences between the `vkb::Device` and `vkb::core::HPPDevice` classes.
|
||||
* Idempotent, but should be paired with `shutdown`.
|
||||
* @param create_info The VMA allocator create info.
|
||||
*/
|
||||
void init(const VmaAllocatorCreateInfo &create_info);
|
||||
|
||||
/**
|
||||
* @brief Initializes the VMA allocator with the specified device, expressed
|
||||
* as the `vkb` wrapper class, which might be `vkb::Device` or `vkb::core::HPPDevice`.
|
||||
* @tparam DeviceType The type of the device.
|
||||
* @param device The Vulkan device.
|
||||
*/
|
||||
template <typename DeviceType = vkb::core::DeviceC>
|
||||
void init(const DeviceType &device)
|
||||
{
|
||||
VmaVulkanFunctions vma_vulkan_func{};
|
||||
vma_vulkan_func.vkGetInstanceProcAddr = vkGetInstanceProcAddr;
|
||||
vma_vulkan_func.vkGetDeviceProcAddr = vkGetDeviceProcAddr;
|
||||
|
||||
VmaAllocatorCreateInfo allocator_info{};
|
||||
allocator_info.pVulkanFunctions = &vma_vulkan_func;
|
||||
allocator_info.physicalDevice = static_cast<VkPhysicalDevice>(device.get_gpu().get_handle());
|
||||
allocator_info.device = static_cast<VkDevice>(device.get_handle());
|
||||
allocator_info.instance = static_cast<VkInstance>(device.get_gpu().get_instance().get_handle());
|
||||
|
||||
bool can_get_memory_requirements = device.get_gpu().is_extension_supported(VK_KHR_GET_MEMORY_REQUIREMENTS_2_EXTENSION_NAME);
|
||||
bool has_dedicated_allocation = device.get_gpu().is_extension_supported(VK_KHR_DEDICATED_ALLOCATION_EXTENSION_NAME);
|
||||
if (can_get_memory_requirements && has_dedicated_allocation && device.is_extension_enabled(VK_KHR_DEDICATED_ALLOCATION_EXTENSION_NAME))
|
||||
{
|
||||
allocator_info.flags |= VMA_ALLOCATOR_CREATE_KHR_DEDICATED_ALLOCATION_BIT;
|
||||
}
|
||||
|
||||
if (device.get_gpu().is_extension_supported(VK_KHR_BUFFER_DEVICE_ADDRESS_EXTENSION_NAME) &&
|
||||
device.is_extension_enabled(VK_KHR_BUFFER_DEVICE_ADDRESS_EXTENSION_NAME))
|
||||
{
|
||||
allocator_info.flags |= VMA_ALLOCATOR_CREATE_BUFFER_DEVICE_ADDRESS_BIT;
|
||||
}
|
||||
|
||||
if (device.get_gpu().is_extension_supported(VK_EXT_MEMORY_BUDGET_EXTENSION_NAME) && device.is_extension_enabled(VK_EXT_MEMORY_BUDGET_EXTENSION_NAME))
|
||||
{
|
||||
allocator_info.flags |= VMA_ALLOCATOR_CREATE_EXT_MEMORY_BUDGET_BIT;
|
||||
}
|
||||
|
||||
if (device.get_gpu().is_extension_supported(VK_EXT_MEMORY_PRIORITY_EXTENSION_NAME) &&
|
||||
device.is_extension_enabled(VK_EXT_MEMORY_PRIORITY_EXTENSION_NAME))
|
||||
{
|
||||
allocator_info.flags |= VMA_ALLOCATOR_CREATE_EXT_MEMORY_PRIORITY_BIT;
|
||||
}
|
||||
|
||||
if (device.get_gpu().is_extension_supported(VK_KHR_BIND_MEMORY_2_EXTENSION_NAME) && device.is_extension_enabled(VK_KHR_BIND_MEMORY_2_EXTENSION_NAME))
|
||||
{
|
||||
allocator_info.flags |= VMA_ALLOCATOR_CREATE_KHR_BIND_MEMORY2_BIT;
|
||||
}
|
||||
|
||||
if (device.get_gpu().is_extension_supported(VK_AMD_DEVICE_COHERENT_MEMORY_EXTENSION_NAME) &&
|
||||
device.is_extension_enabled(VK_AMD_DEVICE_COHERENT_MEMORY_EXTENSION_NAME))
|
||||
{
|
||||
allocator_info.flags |= VMA_ALLOCATOR_CREATE_AMD_DEVICE_COHERENT_MEMORY_BIT;
|
||||
}
|
||||
|
||||
init(allocator_info);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Shuts down the VMA allocator and releases all resources. Should be preceeded with a call to `init`.
|
||||
*/
|
||||
void shutdown();
|
||||
|
||||
/**
|
||||
* @brief The `Allocated` class serves as a base class for wrappers around Vulkan that require memory allocation
|
||||
* (`VkImage` and `VkBuffer`). This class mostly ensures proper behavior for a RAII pattern, preventing double-release by
|
||||
* preventing copy assignment and copy construction in favor of move semantics, as well as preventing default construction
|
||||
* in favor of explicit construction with a pre-existing handle or a populated create info struct.
|
||||
*
|
||||
* This project uses the [VMA](https://gpuopen.com/vulkan-memory-allocator/) to handle the low
|
||||
* level details of memory allocation and management, as it hides away many of the messyy details of
|
||||
* memory allocation when a user is first learning Vulkan, but still allows for fine grained control
|
||||
* when a user becomes more experienced and the situation calls for it.
|
||||
*
|
||||
* @note Constants used in this documentation in the form of `HOST_COHERENT` are shorthand for
|
||||
* `VK_MEMORY_PROPERTY_HOST_COHERENT_BIT` used for the sake of brevity.
|
||||
*
|
||||
* @tparam bindingType A flag indicating whether this is being used with the C or C++ API
|
||||
*/
|
||||
template <vkb::BindingType bindingType, typename HandleType>
|
||||
class Allocated : public vkb::core::VulkanResource<bindingType, HandleType>
|
||||
{
|
||||
public:
|
||||
using ParentType = vkb::core::VulkanResource<bindingType, HandleType>;
|
||||
|
||||
using BufferType = typename std::conditional<bindingType == vkb::BindingType::Cpp, vk::Buffer, VkBuffer>::type;
|
||||
using BufferCreateInfoType = typename std::conditional<bindingType == vkb::BindingType::Cpp, vk::BufferCreateInfo, VkBufferCreateInfo>::type;
|
||||
using DeviceMemoryType = typename std::conditional<bindingType == vkb::BindingType::Cpp, vk::DeviceMemory, VkDeviceMemory>::type;
|
||||
using DeviceSizeType = typename std::conditional<bindingType == vkb::BindingType::Cpp, vk::DeviceSize, VkDeviceSize>::type;
|
||||
using ImageCreateInfoType = typename std::conditional<bindingType == vkb::BindingType::Cpp, vk::ImageCreateInfo, VkImageCreateInfo>::type;
|
||||
using ImageType = typename std::conditional<bindingType == vkb::BindingType::Cpp, vk::Image, VkImage>::type;
|
||||
|
||||
public:
|
||||
Allocated() = delete;
|
||||
Allocated(const Allocated &) = delete;
|
||||
Allocated(Allocated &&other) noexcept;
|
||||
Allocated &operator=(Allocated const &other) = delete;
|
||||
Allocated &operator=(Allocated &&other) = default;
|
||||
|
||||
protected:
|
||||
/**
|
||||
* @brief The VMA-specific constructor for new objects. This should only be visible to derived classes.
|
||||
* @param allocation_create_info All of the non-resource-specific information needed by the VMA to allocate the memory.
|
||||
* @param args Additional constructor arguments needed for the derived class. Typically a `VkImageCreateInfo` or `VkBufferCreateInfo` struct.
|
||||
*/
|
||||
template <typename... Args>
|
||||
Allocated(const VmaAllocationCreateInfo &allocation_create_info, Args &&...args);
|
||||
|
||||
/**
|
||||
* @brief This constructor is used when the handle is already created, and the user wants to wrap it in an `Allocated` object.
|
||||
* @note This constructor is used when the API provides us a pre-existing handle to something we didn't actually allocate, for instance
|
||||
* when we allocate a swapchain and access the images in it. In these cases the `allocation` member variable will remain null for the
|
||||
* lifetime of the wrapper object (which is NOT necessarily the lifetime of the handle) and the wrapper will make no attempt to apply
|
||||
* RAII semantics.
|
||||
*/
|
||||
Allocated(HandleType handle, vkb::core::Device<bindingType> *device_ = nullptr);
|
||||
|
||||
public:
|
||||
const HandleType *get() const;
|
||||
|
||||
/**
|
||||
* @brief Flushes memory if it is NOT `HOST_COHERENT` (which also implies `HOST_VISIBLE`).
|
||||
* This is a no-op for `HOST_COHERENT` memory.
|
||||
*
|
||||
* @param offset The offset into the memory to flush. Defaults to 0.
|
||||
* @param size The size of the memory to flush. Defaults to the entire block of memory.
|
||||
*/
|
||||
void flush(DeviceSizeType offset = 0, DeviceSizeType size = VK_WHOLE_SIZE);
|
||||
|
||||
/**
|
||||
* @brief Retrieves a pointer to the host visible memory as an unsigned byte array.
|
||||
* @return The pointer to the host visible memory.
|
||||
* @note This performs no checking that the memory is actually mapped, so it's possible to get a nullptr
|
||||
*/
|
||||
const uint8_t *get_data() const;
|
||||
/**
|
||||
* @brief Retrieves the raw Vulkan memory object.
|
||||
* @return The Vulkan memory object.
|
||||
*/
|
||||
DeviceMemoryType get_memory() const;
|
||||
|
||||
/**
|
||||
* @brief Maps Vulkan memory if it isn't already mapped to a host visible address. Does nothing if the
|
||||
* allocation is already mapped (including persistently mapped allocations).
|
||||
* @return Pointer to host visible memory.
|
||||
*/
|
||||
uint8_t *map();
|
||||
|
||||
/**
|
||||
* @brief Returns true if the memory is mapped (i.e. the object contains a pointer for the mapping).
|
||||
* This is true for both objects where `map` has been called as well as objects created with persistent
|
||||
* mapping, where no call to `map` is necessary.
|
||||
* @return mapping status.
|
||||
*/
|
||||
bool mapped() const;
|
||||
|
||||
/**
|
||||
* @brief Unmaps Vulkan memory from the host visible address. Does nothing if the memory is not mapped or
|
||||
* if the allocation is persistently mapped.
|
||||
*/
|
||||
void unmap();
|
||||
|
||||
/**
|
||||
* @brief Copies the specified unsigned byte data into the mapped memory region.
|
||||
* @note For non-persistently mapped memory, this function will call the `map` and `unmap` methods and SHOULD NOT
|
||||
* be used if the user intends to make multiple updates to the memory region. In that case, the user should call
|
||||
* `map` once, make all the updates against the pointer returned by `get_data`, and then call `unmap`. This may
|
||||
* be a poor design choice as it creates a side effect of using the method (that mapped memory will
|
||||
* unexpectedly be unmapped), but it is the current design of the method and changing it would be burdensome.
|
||||
* Refactoring could be eased by creating a new method with a more explicit name, and then removing this method
|
||||
* entirely.
|
||||
*
|
||||
* @param data The data to copy from.
|
||||
* @param size The amount of bytes to copy.
|
||||
* @param offset The offset to start the copying into the mapped data. Defaults to 0.
|
||||
*/
|
||||
size_t update(const uint8_t *data, size_t size, size_t offset = 0);
|
||||
|
||||
/**
|
||||
* @brief Converts any non-byte data into bytes and then updates the buffer. This allows the user to pass
|
||||
* arbitrary structure pointers to the update method, which will then be copied into the buffer as bytes.
|
||||
* @param data The data to copy from.
|
||||
* @param size The amount of bytes to copy.
|
||||
* @param offset The offset to start the copying into the mapped data. Defaults to 0.
|
||||
*/
|
||||
size_t update(void const *data, size_t size, size_t offset = 0);
|
||||
|
||||
/**
|
||||
* @brief Copies a vector of items into the buffer. This is a convenience method that allows the user to
|
||||
* pass a vector of items to the update method, which will then be copied into the buffer as bytes.
|
||||
*
|
||||
* This function DOES NOT automatically manage adhering to the alignment requirements of the items being copied,
|
||||
* for instance the `minUniformBufferOffsetAlignment` property of the [device](https://vulkan.gpuinfo.org/displaydevicelimit.php?name=minUniformBufferOffsetAlignment&platform=all).
|
||||
* If the data needs to be aligned on something other than `sizeof(T)`, the user must manage that themselves.
|
||||
* @param data The data vector to upload
|
||||
* @param offset The offset to start the copying into the mapped data
|
||||
* @deprecated Use the `updateTyped` method that uses the `vk::ArrayProxy` class instead.
|
||||
*/
|
||||
template <typename T>
|
||||
size_t update(std::vector<T> const &data, size_t offset = 0)
|
||||
{
|
||||
return update(data.data(), data.size() * sizeof(T), offset);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Another convenience method, similar to the vector update method, but for std::array. The same caveats apply.
|
||||
* @param data The data vector to upload
|
||||
* @param offset The offset to start the copying into the mapped data
|
||||
* @see update(std::vector<T> const &data, size_t offset = 0)
|
||||
* @deprecated Use the `updateTyped` method that uses the `vk::ArrayProxy` class instead.
|
||||
*/
|
||||
template <typename T, size_t N>
|
||||
size_t update(std::array<T, N> const &data, size_t offset = 0)
|
||||
{
|
||||
return update(data.data(), data.size() * sizeof(T), offset);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Copies an object as byte data into the buffer. This is a convenience method that allows the user to
|
||||
* pass an object to the update method, which will then be copied into the buffer as bytes. The name difference
|
||||
* is to avoid amibuity with the `update` method signatures (including the non-templated version)
|
||||
* @param object The object to convert into byte data
|
||||
* @param offset The offset to start the copying into the mapped data
|
||||
* @deprecated Use the `updateTyped` method that uses the `vk::ArrayProxy` class instead.
|
||||
*/
|
||||
template <class T>
|
||||
size_t convert_and_update(const T &object, size_t offset = 0)
|
||||
{
|
||||
return update(reinterpret_cast<const uint8_t *>(&object), sizeof(T), offset);
|
||||
}
|
||||
/**
|
||||
* @brief Copies an object as byte data into the buffer. This is a convenience method that allows the user to
|
||||
* pass an object to the update method, which will then be copied into the buffer as bytes. The use of the `vk::ArrayProxy`
|
||||
* type here to wrap the passed data means you can use any type related to T that can be used as a constructor to `vk::ArrayProxy`.
|
||||
* This includes `T`, `std::vector<T>`, `std::array<T, N>`, and `vk::ArrayProxy<T>`.
|
||||
*
|
||||
* @remark This was previously not feasible as it would have been undesirable to create a strong coupling with the
|
||||
* C++ Vulkan bindings where the `vk::ArrayProxy` type is defined. However, structural changes have ensured that this
|
||||
* coupling is always present, so the `vk::ArrayProxy` may as well be used to our advantage here.
|
||||
*
|
||||
* @note This function DOES NOT automatically manage adhering to the alignment requirements of the items being copied,
|
||||
* for instance the `minUniformBufferOffsetAlignment` property of the [device](https://vulkan.gpuinfo.org/displaydevicelimit.php?name=minUniformBufferOffsetAlignment&platform=all).
|
||||
* If the data needs to be aligned on something other than `sizeof(T)`, the user must manage that themselves.
|
||||
*
|
||||
* @todo create `updateTypedAligned` which has an additional argument specifying the required GPU alignment of the elements of the array.
|
||||
*/
|
||||
template <class T>
|
||||
size_t updateTyped(const vk::ArrayProxy<T> &object, size_t offset = 0)
|
||||
{
|
||||
return update(reinterpret_cast<const uint8_t *>(object.data()), object.size() * sizeof(T), offset);
|
||||
}
|
||||
|
||||
protected:
|
||||
/**
|
||||
* @brief Internal method to actually create the buffer, allocate the memory and bind them.
|
||||
* Should only be called from the `Buffer` derived class.
|
||||
*
|
||||
* Present in this common base class in order to allow the internal state members to remain `private`
|
||||
* instead of `protected`, and because it (mostly) isolates interaction with the VMA to a single class
|
||||
*/
|
||||
[[nodiscard]] BufferType create_buffer(BufferCreateInfoType const &create_info);
|
||||
/**
|
||||
* @brief Internal method to actually create the image, allocate the memory and bind them.
|
||||
* Should only be called from the `Image` derived class.
|
||||
*
|
||||
* Present in this common base class in order to allow the internal state members to remain `private`
|
||||
* instead of `protected`, and because it (mostly) isolates interaction with the VMA to a single class
|
||||
*/
|
||||
[[nodiscard]] ImageType create_image(ImageCreateInfoType const &create_info);
|
||||
/**
|
||||
* @brief The post_create method is called after the creation of a buffer or image to store the allocation info internally. Derived classes
|
||||
* could in theory override this to ensure any post-allocation operations are performed, but the base class should always be called to ensure
|
||||
* the allocation info is stored.
|
||||
* Should only be called in the corresponding `create_xxx` methods.
|
||||
*/
|
||||
virtual void post_create(VmaAllocationInfo const &allocation_info);
|
||||
|
||||
/**
|
||||
* @brief Internal method to actually destroy the buffer and release the allocated memory. Should
|
||||
* only be called from the `Buffer` derived class.
|
||||
* Present in this common base class in order to allow the internal state members to remain `private`
|
||||
* instead of `protected`, and because it (mostly) isolates interaction with the VMA to a single class
|
||||
*/
|
||||
void destroy_buffer(BufferType buffer);
|
||||
/**
|
||||
* @brief Internal method to actually destroy the image and release the allocated memory. Should
|
||||
* only be called from the `Image` derived class.
|
||||
* Present in this common base class in order to allow the internal state members to remain `private`
|
||||
* instead of `protected`, and because it (mostly) isolates interaction with the VMA to a single class
|
||||
*/
|
||||
void destroy_image(ImageType image);
|
||||
/**
|
||||
* @brief Clears the internal state. Can be overridden by derived classes to perform additional cleanup of members.
|
||||
* Should only be called in the corresping `destroy_xxx` methods.
|
||||
*/
|
||||
void clear();
|
||||
|
||||
private:
|
||||
vk::Buffer create_buffer_impl(vk::BufferCreateInfo const &create_info);
|
||||
vk::Image create_image_impl(vk::ImageCreateInfo const &create_info);
|
||||
|
||||
VmaAllocationCreateInfo allocation_create_info = {};
|
||||
VmaAllocation allocation = VK_NULL_HANDLE;
|
||||
/**
|
||||
* @brief A pointer to the allocation memory, if the memory is HOST_VISIBLE and is currently (or persistently) mapped.
|
||||
* Contains null otherwise.
|
||||
*/
|
||||
uint8_t *mapped_data = nullptr;
|
||||
/**
|
||||
* @brief This flag is set to true if the memory is coherent and doesn't need to be flushed after writes.
|
||||
*
|
||||
* @note This is initialized at allocation time to avoid subsequent need to call a function to fetch the
|
||||
* allocation information from the VMA, since this property won't change for the lifetime of the allocation.
|
||||
*/
|
||||
bool coherent = false;
|
||||
/**
|
||||
* @brief This flag is set to true if the memory is persistently mapped (i.e. not just HOST_VISIBLE, but available
|
||||
* as a pointer to the application for the lifetime of the allocation).
|
||||
*
|
||||
* @note This is initialized at allocation time to avoid subsequent need to call a function to fetch the
|
||||
* allocation information from the VMA, since this property won't change for the lifetime of the allocation.
|
||||
*/
|
||||
bool persistent = false;
|
||||
};
|
||||
|
||||
template <vkb::BindingType bindingType, typename HandleType>
|
||||
inline Allocated<bindingType, HandleType>::Allocated(Allocated &&other) noexcept :
|
||||
ParentType{static_cast<ParentType &&>(other)},
|
||||
allocation_create_info(std::exchange(other.allocation_create_info, {})),
|
||||
allocation(std::exchange(other.allocation, {})),
|
||||
mapped_data(std::exchange(other.mapped_data, {})),
|
||||
coherent(std::exchange(other.coherent, {})),
|
||||
persistent(std::exchange(other.persistent, {}))
|
||||
{
|
||||
}
|
||||
|
||||
template <vkb::BindingType bindingType, typename HandleType>
|
||||
template <typename... Args>
|
||||
inline Allocated<bindingType, HandleType>::Allocated(const VmaAllocationCreateInfo &allocation_create_info, Args &&...args) :
|
||||
ParentType{std::forward<Args>(args)...},
|
||||
allocation_create_info(allocation_create_info)
|
||||
{}
|
||||
|
||||
template <vkb::BindingType bindingType, typename HandleType>
|
||||
inline Allocated<bindingType, HandleType>::Allocated(HandleType handle, vkb::core::Device<bindingType> *device_) :
|
||||
ParentType(handle, device_)
|
||||
{}
|
||||
|
||||
template <vkb::BindingType bindingType, typename HandleType>
|
||||
inline const HandleType *Allocated<bindingType, HandleType>::get() const
|
||||
{
|
||||
return &ParentType::get_handle();
|
||||
}
|
||||
|
||||
template <vkb::BindingType bindingType, typename HandleType>
|
||||
inline void Allocated<bindingType, HandleType>::clear()
|
||||
{
|
||||
mapped_data = nullptr;
|
||||
persistent = false;
|
||||
allocation_create_info = {};
|
||||
}
|
||||
|
||||
template <vkb::BindingType bindingType, typename HandleType>
|
||||
inline typename Allocated<bindingType, HandleType>::BufferType Allocated<bindingType, HandleType>::create_buffer(BufferCreateInfoType const &create_info)
|
||||
{
|
||||
if constexpr (bindingType == vkb::BindingType::Cpp)
|
||||
{
|
||||
return create_buffer_impl(create_info);
|
||||
}
|
||||
else
|
||||
{
|
||||
return static_cast<VkBuffer>(create_buffer_impl(reinterpret_cast<vk::BufferCreateInfo const &>(create_info)));
|
||||
}
|
||||
}
|
||||
|
||||
template <vkb::BindingType bindingType, typename HandleType>
|
||||
inline vk::Buffer Allocated<bindingType, HandleType>::create_buffer_impl(vk::BufferCreateInfo const &create_info)
|
||||
{
|
||||
vk::Buffer buffer = VK_NULL_HANDLE;
|
||||
VmaAllocationInfo allocation_info{};
|
||||
|
||||
auto result = vmaCreateBuffer(
|
||||
get_memory_allocator(),
|
||||
reinterpret_cast<VkBufferCreateInfo const *>(&create_info),
|
||||
&allocation_create_info,
|
||||
reinterpret_cast<VkBuffer *>(&buffer),
|
||||
&allocation,
|
||||
&allocation_info);
|
||||
|
||||
if (result != VK_SUCCESS)
|
||||
{
|
||||
throw VulkanException{result, "Cannot create Buffer"};
|
||||
}
|
||||
post_create(allocation_info);
|
||||
return buffer;
|
||||
}
|
||||
|
||||
template <vkb::BindingType bindingType, typename HandleType>
|
||||
inline typename Allocated<bindingType, HandleType>::ImageType Allocated<bindingType, HandleType>::create_image(ImageCreateInfoType const &create_info)
|
||||
{
|
||||
if constexpr (bindingType == vkb::BindingType::Cpp)
|
||||
{
|
||||
return create_image_impl(create_info);
|
||||
}
|
||||
else
|
||||
{
|
||||
return static_cast<VkImage>(create_image_impl(reinterpret_cast<vk::ImageCreateInfo const &>(create_info)));
|
||||
}
|
||||
}
|
||||
|
||||
template <vkb::BindingType bindingType, typename HandleType>
|
||||
inline vk::Image Allocated<bindingType, HandleType>::create_image_impl(vk::ImageCreateInfo const &create_info)
|
||||
{
|
||||
assert(0 < create_info.mipLevels && "Images should have at least one level");
|
||||
assert(0 < create_info.arrayLayers && "Images should have at least one layer");
|
||||
assert(create_info.usage && "Images should have at least one usage type");
|
||||
|
||||
vk::Image image = VK_NULL_HANDLE;
|
||||
VmaAllocationInfo allocation_info{};
|
||||
|
||||
#if 0
|
||||
// If the image is an attachment, prefer dedicated memory
|
||||
constexpr vk::ImageUsageFlags attachment_only_flags = vk::ImageUsageFlagBits::eColorAttachment | vk::ImageUsageFlagBits::eDepthStencilAttachment | vk::ImageUsageFlagBits::eTransientAttachment;
|
||||
if (create_info.usage & attachment_only_flags)
|
||||
{
|
||||
allocation_create_info.flags |= VMA_ALLOCATION_CREATE_DEDICATED_MEMORY_BIT;
|
||||
}
|
||||
|
||||
if (create_info.usage & vk::ImageUsageFlagBits::eTransientAttachment)
|
||||
{
|
||||
allocation_create_info.preferredFlags |= VK_MEMORY_PROPERTY_LAZILY_ALLOCATED_BIT;
|
||||
}
|
||||
#endif
|
||||
|
||||
VkResult result = vmaCreateImage(get_memory_allocator(),
|
||||
reinterpret_cast<VkImageCreateInfo const *>(&create_info),
|
||||
&allocation_create_info,
|
||||
reinterpret_cast<VkImage *>(&image),
|
||||
&allocation,
|
||||
&allocation_info);
|
||||
|
||||
if (result != VK_SUCCESS)
|
||||
{
|
||||
throw VulkanException{result, "Cannot create Image"};
|
||||
}
|
||||
|
||||
post_create(allocation_info);
|
||||
return image;
|
||||
}
|
||||
|
||||
template <vkb::BindingType bindingType, typename HandleType>
|
||||
inline void Allocated<bindingType, HandleType>::destroy_buffer(BufferType handle)
|
||||
{
|
||||
if (handle != VK_NULL_HANDLE && allocation != VK_NULL_HANDLE)
|
||||
{
|
||||
unmap();
|
||||
if constexpr (bindingType == vkb::BindingType::Cpp)
|
||||
{
|
||||
vmaDestroyBuffer(get_memory_allocator(), static_cast<VkBuffer>(handle), allocation);
|
||||
}
|
||||
else
|
||||
{
|
||||
vmaDestroyBuffer(get_memory_allocator(), handle, allocation);
|
||||
}
|
||||
clear();
|
||||
}
|
||||
}
|
||||
|
||||
template <vkb::BindingType bindingType, typename HandleType>
|
||||
inline void Allocated<bindingType, HandleType>::destroy_image(ImageType image)
|
||||
{
|
||||
if (image != VK_NULL_HANDLE && allocation != VK_NULL_HANDLE)
|
||||
{
|
||||
unmap();
|
||||
if constexpr (bindingType == vkb::BindingType::Cpp)
|
||||
{
|
||||
vmaDestroyImage(get_memory_allocator(), static_cast<VkImage>(image), allocation);
|
||||
}
|
||||
else
|
||||
{
|
||||
vmaDestroyImage(get_memory_allocator(), image, allocation);
|
||||
}
|
||||
clear();
|
||||
}
|
||||
}
|
||||
|
||||
template <vkb::BindingType bindingType, typename HandleType>
|
||||
inline void Allocated<bindingType, HandleType>::flush(DeviceSizeType offset, DeviceSizeType size)
|
||||
{
|
||||
if (!coherent)
|
||||
{
|
||||
if constexpr (bindingType == vkb::BindingType::Cpp)
|
||||
{
|
||||
vmaFlushAllocation(get_memory_allocator(), allocation, static_cast<VkDeviceSize>(offset), static_cast<VkDeviceSize>(size));
|
||||
}
|
||||
else
|
||||
{
|
||||
vmaFlushAllocation(get_memory_allocator(), allocation, offset, size);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <vkb::BindingType bindingType, typename HandleType>
|
||||
inline const uint8_t *Allocated<bindingType, HandleType>::get_data() const
|
||||
{
|
||||
return mapped_data;
|
||||
}
|
||||
|
||||
template <vkb::BindingType bindingType, typename HandleType>
|
||||
inline typename Allocated<bindingType, HandleType>::DeviceMemoryType Allocated<bindingType, HandleType>::get_memory() const
|
||||
{
|
||||
VmaAllocationInfo alloc_info;
|
||||
vmaGetAllocationInfo(get_memory_allocator(), allocation, &alloc_info);
|
||||
if constexpr (bindingType == vkb::BindingType::Cpp)
|
||||
{
|
||||
return static_cast<vk::DeviceMemory>(alloc_info.deviceMemory);
|
||||
}
|
||||
else
|
||||
{
|
||||
return alloc_info.deviceMemory;
|
||||
}
|
||||
}
|
||||
|
||||
template <vkb::BindingType bindingType, typename HandleType>
|
||||
inline uint8_t *Allocated<bindingType, HandleType>::map()
|
||||
{
|
||||
if (!persistent && !mapped())
|
||||
{
|
||||
VK_CHECK(vmaMapMemory(get_memory_allocator(), allocation, reinterpret_cast<void **>(&mapped_data)));
|
||||
assert(mapped_data);
|
||||
}
|
||||
return mapped_data;
|
||||
}
|
||||
|
||||
template <vkb::BindingType bindingType, typename HandleType>
|
||||
inline bool Allocated<bindingType, HandleType>::mapped() const
|
||||
{
|
||||
return mapped_data != nullptr;
|
||||
}
|
||||
|
||||
template <vkb::BindingType bindingType, typename HandleType>
|
||||
inline void Allocated<bindingType, HandleType>::post_create(VmaAllocationInfo const &allocation_info)
|
||||
{
|
||||
VkMemoryPropertyFlags memory_properties;
|
||||
vmaGetAllocationMemoryProperties(get_memory_allocator(), allocation, &memory_properties);
|
||||
coherent = (memory_properties & VK_MEMORY_PROPERTY_HOST_COHERENT_BIT) == VK_MEMORY_PROPERTY_HOST_COHERENT_BIT;
|
||||
mapped_data = static_cast<uint8_t *>(allocation_info.pMappedData);
|
||||
persistent = mapped();
|
||||
}
|
||||
|
||||
template <vkb::BindingType bindingType, typename HandleType>
|
||||
inline void Allocated<bindingType, HandleType>::unmap()
|
||||
{
|
||||
if (!persistent && mapped())
|
||||
{
|
||||
vmaUnmapMemory(get_memory_allocator(), allocation);
|
||||
mapped_data = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
template <vkb::BindingType bindingType, typename HandleType>
|
||||
inline size_t Allocated<bindingType, HandleType>::update(const uint8_t *data, size_t size, size_t offset)
|
||||
{
|
||||
if (persistent)
|
||||
{
|
||||
std::copy(data, data + size, mapped_data + offset);
|
||||
flush();
|
||||
}
|
||||
else
|
||||
{
|
||||
map();
|
||||
std::copy(data, data + size, mapped_data + offset);
|
||||
flush();
|
||||
unmap();
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
template <vkb::BindingType bindingType, typename HandleType>
|
||||
inline size_t Allocated<bindingType, HandleType>::update(void const *data, size_t size, size_t offset)
|
||||
{
|
||||
return update(reinterpret_cast<const uint8_t *>(data), size, offset);
|
||||
}
|
||||
|
||||
template <typename HandleType>
|
||||
using AllocatedC = Allocated<vkb::BindingType::C, HandleType>;
|
||||
template <typename HandleType>
|
||||
using AllocatedCpp = Allocated<vkb::BindingType::Cpp, HandleType>;
|
||||
|
||||
} // namespace allocated
|
||||
} // namespace vkb
|
||||
@@ -0,0 +1,273 @@
|
||||
/* Copyright (c) 2019-2025, Arm Limited and Contributors
|
||||
* Copyright (c) 2021-2025, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* 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 "builder_base.h"
|
||||
#include "common/vk_common.h"
|
||||
#include "core/allocated.h"
|
||||
|
||||
namespace vkb
|
||||
{
|
||||
namespace core
|
||||
{
|
||||
template <vkb::BindingType bindingType>
|
||||
class Buffer;
|
||||
template <vkb::BindingType bindingType>
|
||||
using BufferPtr = std::unique_ptr<Buffer<bindingType>>;
|
||||
template <vkb::BindingType bindingType>
|
||||
class Device;
|
||||
|
||||
template <vkb::BindingType bindingType>
|
||||
struct BufferBuilder
|
||||
: public vkb::allocated::BuilderBase<bindingType,
|
||||
BufferBuilder<bindingType>,
|
||||
typename std::conditional<bindingType == vkb::BindingType::Cpp, vk::BufferCreateInfo, VkBufferCreateInfo>::type>
|
||||
{
|
||||
public:
|
||||
using BufferCreateFlagsType = typename std::conditional<bindingType == vkb::BindingType::Cpp, vk::BufferCreateFlags, VkBufferCreateFlags>::type;
|
||||
using BufferCreateInfoType = typename std::conditional<bindingType == vkb::BindingType::Cpp, vk::BufferCreateInfo, VkBufferCreateInfo>::type;
|
||||
using BufferUsageFlagsType = typename std::conditional<bindingType == vkb::BindingType::Cpp, vk::BufferUsageFlags, VkBufferUsageFlags>::type;
|
||||
using DeviceSizeType = typename std::conditional<bindingType == vkb::BindingType::Cpp, vk::DeviceSize, VkDeviceSize>::type;
|
||||
using SharingModeType = typename std::conditional<bindingType == vkb::BindingType::Cpp, vk::SharingMode, VkSharingMode>::type;
|
||||
|
||||
private:
|
||||
using ParentType = vkb::allocated::BuilderBase<bindingType, BufferBuilder<bindingType>, BufferCreateInfoType>;
|
||||
|
||||
public:
|
||||
BufferBuilder(DeviceSizeType size);
|
||||
|
||||
Buffer<bindingType> build(vkb::core::Device<bindingType> &device) const;
|
||||
BufferPtr<bindingType> build_unique(vkb::core::Device<bindingType> &device) const;
|
||||
BufferBuilder &with_flags(BufferCreateFlagsType flags);
|
||||
BufferBuilder &with_usage(BufferUsageFlagsType usage);
|
||||
};
|
||||
|
||||
using BufferBuilderC = BufferBuilder<vkb::BindingType::C>;
|
||||
using BufferBuilderCpp = BufferBuilder<vkb::BindingType::Cpp>;
|
||||
|
||||
template <>
|
||||
inline BufferBuilder<vkb::BindingType::Cpp>::BufferBuilder(vk::DeviceSize size) :
|
||||
ParentType(BufferCreateInfoType{.size = size})
|
||||
{
|
||||
}
|
||||
|
||||
template <>
|
||||
inline BufferBuilder<vkb::BindingType::C>::BufferBuilder(VkDeviceSize size) :
|
||||
ParentType(VkBufferCreateInfo{VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, nullptr, 0, size})
|
||||
{}
|
||||
|
||||
template <vkb::BindingType bindingType>
|
||||
inline Buffer<bindingType> BufferBuilder<bindingType>::build(vkb::core::Device<bindingType> &device) const
|
||||
{
|
||||
return Buffer<bindingType>{device, *this};
|
||||
}
|
||||
|
||||
template <vkb::BindingType bindingType>
|
||||
inline BufferPtr<bindingType> BufferBuilder<bindingType>::build_unique(vkb::core::Device<bindingType> &device) const
|
||||
{
|
||||
return std::make_unique<Buffer<bindingType>>(device, *this);
|
||||
}
|
||||
|
||||
template <vkb::BindingType bindingType>
|
||||
inline BufferBuilder<bindingType> &BufferBuilder<bindingType>::with_flags(BufferCreateFlagsType flags)
|
||||
{
|
||||
this->create_info.flags = flags;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <vkb::BindingType bindingType>
|
||||
inline BufferBuilder<bindingType> &BufferBuilder<bindingType>::with_usage(BufferUsageFlagsType usage)
|
||||
{
|
||||
this->get_create_info().usage = usage;
|
||||
return *this;
|
||||
}
|
||||
|
||||
/*=========================================================*/
|
||||
|
||||
template <vkb::BindingType bindingType>
|
||||
class Buffer
|
||||
: public vkb::allocated::Allocated<bindingType, typename std::conditional<bindingType == vkb::BindingType::Cpp, vk::Buffer, VkBuffer>::type>
|
||||
{
|
||||
public:
|
||||
using BufferType = typename std::conditional<bindingType == vkb::BindingType::Cpp, vk::Buffer, VkBuffer>::type;
|
||||
using BufferUsageFlagsType = typename std::conditional<bindingType == vkb::BindingType::Cpp, vk::BufferUsageFlags, VkBufferUsageFlags>::type;
|
||||
using DeviceSizeType = typename std::conditional<bindingType == vkb::BindingType::Cpp, vk::DeviceSize, VkDeviceSize>::type;
|
||||
|
||||
private:
|
||||
using ParentType = vkb::allocated::Allocated<bindingType, BufferType>;
|
||||
|
||||
public:
|
||||
static Buffer<bindingType> create_staging_buffer(vkb::core::Device<bindingType> &device, DeviceSizeType size, const void *data);
|
||||
|
||||
template <typename T>
|
||||
static Buffer create_staging_buffer(vkb::core::Device<bindingType> &device, std::vector<T> const &data);
|
||||
|
||||
template <typename T>
|
||||
static Buffer create_staging_buffer(vkb::core::Device<bindingType> &device, const T &data);
|
||||
|
||||
Buffer() = delete;
|
||||
Buffer(const Buffer &) = delete;
|
||||
Buffer(Buffer &&other) = default;
|
||||
Buffer &operator=(const Buffer &) = delete;
|
||||
Buffer &operator=(Buffer &&) = default;
|
||||
|
||||
/**
|
||||
* @brief Creates a buffer using VMA
|
||||
* @param device A valid Vulkan device
|
||||
* @param size The size in bytes of the buffer
|
||||
* @param buffer_usage The usage flags for the VkBuffer
|
||||
* @param memory_usage The memory usage of the buffer
|
||||
* @param flags The allocation create flags
|
||||
* @param queue_family_indices optional queue family indices
|
||||
*/
|
||||
// [[deprecated("Use the BufferBuilder ctor instead")]]
|
||||
Buffer(vkb::core::Device<bindingType> &device,
|
||||
DeviceSizeType size,
|
||||
BufferUsageFlagsType buffer_usage,
|
||||
VmaMemoryUsage memory_usage,
|
||||
VmaAllocationCreateFlags flags = VMA_ALLOCATION_CREATE_MAPPED_BIT | VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT,
|
||||
const std::vector<uint32_t> &queue_family_indices = {});
|
||||
|
||||
Buffer(vkb::core::Device<bindingType> &device, BufferBuilder<bindingType> const &builder);
|
||||
|
||||
~Buffer();
|
||||
|
||||
/**
|
||||
* @return Return the buffer's device address (note: requires that the buffer has been created with the VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT usage fla)
|
||||
*/
|
||||
uint64_t get_device_address() const;
|
||||
|
||||
/**
|
||||
* @return The size of the buffer
|
||||
*/
|
||||
DeviceSizeType get_size() const;
|
||||
|
||||
private:
|
||||
static Buffer<vkb::BindingType::Cpp> create_staging_buffer_impl(vkb::core::DeviceCpp &device, vk::DeviceSize size, const void *data);
|
||||
|
||||
private:
|
||||
vk::DeviceSize size = 0;
|
||||
};
|
||||
|
||||
using BufferC = Buffer<vkb::BindingType::C>;
|
||||
using BufferCpp = Buffer<vkb::BindingType::Cpp>;
|
||||
|
||||
template <vkb::BindingType bindingType>
|
||||
template <typename T>
|
||||
inline Buffer<bindingType> Buffer<bindingType>::create_staging_buffer(vkb::core::Device<bindingType> &device, const T &data)
|
||||
{
|
||||
return create_staging_buffer(device, sizeof(T), &data);
|
||||
}
|
||||
|
||||
template <vkb::BindingType bindingType>
|
||||
inline Buffer<bindingType> Buffer<bindingType>::create_staging_buffer(vkb::core::Device<bindingType> &device, DeviceSizeType size, const void *data)
|
||||
{
|
||||
if constexpr (bindingType == vkb::BindingType::Cpp)
|
||||
{
|
||||
return create_staging_buffer_impl(device, size, data);
|
||||
}
|
||||
else
|
||||
{
|
||||
BufferCpp buffer = create_staging_buffer_impl(reinterpret_cast<vkb::core::DeviceCpp &>(device), static_cast<vk::DeviceSize>(size), data);
|
||||
return std::move(*reinterpret_cast<BufferC *>(&buffer));
|
||||
}
|
||||
}
|
||||
|
||||
template <vkb::BindingType bindingType>
|
||||
inline BufferCpp Buffer<bindingType>::create_staging_buffer_impl(vkb::core::DeviceCpp &device, vk::DeviceSize size, const void *data)
|
||||
{
|
||||
BufferBuilderCpp builder(size);
|
||||
builder.with_vma_flags(VMA_ALLOCATION_CREATE_MAPPED_BIT | VMA_ALLOCATION_CREATE_HOST_ACCESS_SEQUENTIAL_WRITE_BIT)
|
||||
.with_usage(vk::BufferUsageFlagBits::eTransferSrc);
|
||||
BufferCpp result(device, builder);
|
||||
if (data != nullptr)
|
||||
{
|
||||
result.update(data, size);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
template <vkb::BindingType bindingType>
|
||||
template <typename T>
|
||||
inline Buffer<bindingType> Buffer<bindingType>::create_staging_buffer(vkb::core::Device<bindingType> &device, std::vector<T> const &data)
|
||||
{
|
||||
return create_staging_buffer(device, data.size() * sizeof(T), data.data());
|
||||
}
|
||||
|
||||
template <vkb::BindingType bindingType>
|
||||
inline Buffer<bindingType>::Buffer(vkb::core::Device<bindingType> &device,
|
||||
DeviceSizeType size,
|
||||
BufferUsageFlagsType buffer_usage,
|
||||
VmaMemoryUsage memory_usage,
|
||||
VmaAllocationCreateFlags flags,
|
||||
const std::vector<uint32_t> &queue_family_indices) :
|
||||
Buffer(device,
|
||||
BufferBuilder<bindingType>(size)
|
||||
.with_usage(buffer_usage)
|
||||
.with_vma_usage(memory_usage)
|
||||
.with_vma_flags(flags)
|
||||
.with_queue_families(queue_family_indices)
|
||||
.with_implicit_sharing_mode())
|
||||
{}
|
||||
|
||||
template <vkb::BindingType bindingType>
|
||||
inline Buffer<bindingType>::Buffer(vkb::core::Device<bindingType> &device, const BufferBuilder<bindingType> &builder) :
|
||||
ParentType(builder.get_allocation_create_info(), nullptr, &device), size(builder.get_create_info().size)
|
||||
{
|
||||
this->set_handle(this->create_buffer(builder.get_create_info()));
|
||||
if (!builder.get_debug_name().empty())
|
||||
{
|
||||
this->set_debug_name(builder.get_debug_name());
|
||||
}
|
||||
}
|
||||
|
||||
template <vkb::BindingType bindingType>
|
||||
inline Buffer<bindingType>::~Buffer()
|
||||
{
|
||||
this->destroy_buffer(this->get_handle());
|
||||
}
|
||||
|
||||
template <vkb::BindingType bindingType>
|
||||
inline uint64_t Buffer<bindingType>::get_device_address() const
|
||||
{
|
||||
if constexpr (bindingType == vkb::BindingType::Cpp)
|
||||
{
|
||||
return this->get_device().get_handle().getBufferAddressKHR({.buffer = this->get_handle()});
|
||||
}
|
||||
else
|
||||
{
|
||||
return static_cast<vk::Device>(this->get_device().get_handle()).getBufferAddressKHR({.buffer = static_cast<vk::Buffer>(this->get_handle())});
|
||||
}
|
||||
}
|
||||
|
||||
template <vkb::BindingType bindingType>
|
||||
inline typename Buffer<bindingType>::DeviceSizeType Buffer<bindingType>::get_size() const
|
||||
{
|
||||
if constexpr (bindingType == vkb::BindingType::Cpp)
|
||||
{
|
||||
return size;
|
||||
}
|
||||
else
|
||||
{
|
||||
return static_cast<VkDeviceSize>(size);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace core
|
||||
} // namespace vkb
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,183 @@
|
||||
/* Copyright (c) 2019-2025, Arm Limited and Contributors
|
||||
* Copyright (c) 2024-2025, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* 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 "core/command_pool_base.h"
|
||||
#include "core/device.h"
|
||||
#include <vulkan/vulkan.hpp>
|
||||
|
||||
namespace vkb
|
||||
{
|
||||
namespace rendering
|
||||
{
|
||||
template <vkb::BindingType bindingType>
|
||||
class RenderFrame;
|
||||
using RenderFrameC = RenderFrame<vkb::BindingType::C>;
|
||||
using RenderFrameCpp = RenderFrame<vkb::BindingType::Cpp>;
|
||||
} // namespace rendering
|
||||
|
||||
namespace core
|
||||
{
|
||||
template <vkb::BindingType bindingType>
|
||||
class CommandBuffer;
|
||||
using CommandBufferC = CommandBuffer<vkb::BindingType::C>;
|
||||
using CommandBufferCpp = CommandBuffer<vkb::BindingType::Cpp>;
|
||||
|
||||
namespace
|
||||
{
|
||||
// type trait to get the default value for request_command_buffer
|
||||
template <typename T>
|
||||
struct DefaultCommandBufferLevelValue;
|
||||
template <>
|
||||
struct DefaultCommandBufferLevelValue<vk::CommandBufferLevel>
|
||||
{
|
||||
static constexpr vk::CommandBufferLevel value = vk::CommandBufferLevel::ePrimary;
|
||||
};
|
||||
template <>
|
||||
struct DefaultCommandBufferLevelValue<VkCommandBufferLevel>
|
||||
{
|
||||
static constexpr VkCommandBufferLevel value = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
template <vkb::BindingType bindingType>
|
||||
class CommandPool : private vkb::core::CommandPoolBase
|
||||
{
|
||||
public:
|
||||
using CommandBufferLevelType = typename std::conditional<bindingType == vkb::BindingType::Cpp, vk::CommandBufferLevel, VkCommandBufferLevel>::type;
|
||||
using CommandPoolType = typename std::conditional<bindingType == vkb::BindingType::Cpp, vk::CommandPool, VkCommandPool>::type;
|
||||
|
||||
public:
|
||||
CommandPool(vkb::core::Device<bindingType> &device,
|
||||
uint32_t queue_family_index,
|
||||
vkb::rendering::RenderFrame<bindingType> *render_frame = nullptr,
|
||||
size_t thread_index = 0,
|
||||
vkb::CommandBufferResetMode reset_mode = vkb::CommandBufferResetMode::ResetPool);
|
||||
CommandPool(CommandPool<bindingType> const &) = delete;
|
||||
CommandPool(CommandPool<bindingType> &&other) = default;
|
||||
CommandPool &operator=(CommandPool<bindingType> const &) = delete;
|
||||
CommandPool &operator=(CommandPool<bindingType> &&other) = default;
|
||||
~CommandPool() = default;
|
||||
|
||||
vkb::core::Device<bindingType> &get_device();
|
||||
CommandPoolType get_handle() const;
|
||||
uint32_t get_queue_family_index() const;
|
||||
vkb::rendering::RenderFrame<bindingType> *get_render_frame();
|
||||
vkb::CommandBufferResetMode get_reset_mode() const;
|
||||
size_t get_thread_index() const;
|
||||
std::shared_ptr<vkb::core::CommandBuffer<bindingType>> request_command_buffer(CommandBufferLevelType level = DefaultCommandBufferLevelValue<CommandBufferLevelType>::value);
|
||||
void reset_pool();
|
||||
};
|
||||
|
||||
using CommandPoolC = CommandPool<vkb::BindingType::C>;
|
||||
using CommandPoolCpp = CommandPool<vkb::BindingType::Cpp>;
|
||||
|
||||
template <vkb::BindingType bindingType>
|
||||
inline vkb::core::CommandPool<bindingType>::CommandPool(vkb::core::Device<bindingType> &device,
|
||||
uint32_t queue_family_index,
|
||||
vkb::rendering::RenderFrame<bindingType> *render_frame,
|
||||
size_t thread_index,
|
||||
vkb::CommandBufferResetMode reset_mode) :
|
||||
CommandPoolBase(reinterpret_cast<vkb::core::DeviceCpp &>(device),
|
||||
queue_family_index,
|
||||
reinterpret_cast<vkb::rendering::RenderFrameCpp *>(render_frame),
|
||||
thread_index,
|
||||
reset_mode)
|
||||
{}
|
||||
|
||||
template <vkb::BindingType bindingType>
|
||||
inline typename vkb::core::Device<bindingType> &CommandPool<bindingType>::get_device()
|
||||
{
|
||||
if constexpr (bindingType == vkb::BindingType::Cpp)
|
||||
{
|
||||
return CommandPoolBase::get_device();
|
||||
}
|
||||
else
|
||||
{
|
||||
return reinterpret_cast<vkb::core::DeviceC &>(CommandPoolBase::get_device());
|
||||
}
|
||||
}
|
||||
|
||||
template <vkb::BindingType bindingType>
|
||||
inline typename vkb::core::CommandPool<bindingType>::CommandPoolType CommandPool<bindingType>::get_handle() const
|
||||
{
|
||||
if constexpr (bindingType == vkb::BindingType::Cpp)
|
||||
{
|
||||
return CommandPoolBase::get_handle();
|
||||
}
|
||||
else
|
||||
{
|
||||
return static_cast<VkCommandPool>(CommandPoolBase::get_handle());
|
||||
}
|
||||
}
|
||||
|
||||
template <vkb::BindingType bindingType>
|
||||
inline uint32_t CommandPool<bindingType>::get_queue_family_index() const
|
||||
{
|
||||
return CommandPoolBase::get_queue_family_index();
|
||||
}
|
||||
|
||||
template <vkb::BindingType bindingType>
|
||||
inline vkb::rendering::RenderFrame<bindingType> *CommandPool<bindingType>::get_render_frame()
|
||||
{
|
||||
if constexpr (bindingType == vkb::BindingType::Cpp)
|
||||
{
|
||||
return CommandPoolBase::get_render_frame();
|
||||
}
|
||||
else
|
||||
{
|
||||
return reinterpret_cast<vkb::rendering::RenderFrameC *>(CommandPoolBase::get_render_frame());
|
||||
}
|
||||
}
|
||||
|
||||
template <vkb::BindingType bindingType>
|
||||
inline vkb::CommandBufferResetMode CommandPool<bindingType>::get_reset_mode() const
|
||||
{
|
||||
return CommandPoolBase::get_reset_mode();
|
||||
}
|
||||
|
||||
template <vkb::BindingType bindingType>
|
||||
inline size_t CommandPool<bindingType>::get_thread_index() const
|
||||
{
|
||||
return CommandPoolBase::get_thread_index();
|
||||
}
|
||||
|
||||
template <vkb::BindingType bindingType>
|
||||
std::shared_ptr<vkb::core::CommandBuffer<bindingType>> CommandPool<bindingType>::request_command_buffer(CommandBufferLevelType level)
|
||||
{
|
||||
if constexpr (bindingType == vkb::BindingType::Cpp)
|
||||
{
|
||||
return CommandPoolBase::request_command_buffer(*this, level);
|
||||
}
|
||||
else
|
||||
{
|
||||
std::shared_ptr<vkb::core::CommandBufferCpp> command_buffer =
|
||||
CommandPoolBase::request_command_buffer(reinterpret_cast<vkb::core::CommandPoolCpp &>(*this), static_cast<vk::CommandBufferLevel>(level));
|
||||
return *reinterpret_cast<std::shared_ptr<CommandBufferC> *>(&command_buffer);
|
||||
}
|
||||
}
|
||||
|
||||
template <vkb::BindingType bindingType>
|
||||
inline void CommandPool<bindingType>::reset_pool()
|
||||
{
|
||||
CommandPoolBase::reset_pool();
|
||||
}
|
||||
|
||||
} // namespace core
|
||||
} // namespace vkb
|
||||
@@ -0,0 +1,175 @@
|
||||
/* Copyright (c) 2025, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* 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 "core/command_pool_base.h"
|
||||
#include "core/command_buffer.h"
|
||||
|
||||
namespace vkb
|
||||
{
|
||||
namespace core
|
||||
{
|
||||
vkb::core::CommandPoolBase::CommandPoolBase(vkb::core::DeviceCpp &device_,
|
||||
uint32_t queue_family_index,
|
||||
vkb::rendering::RenderFrameCpp *render_frame_,
|
||||
size_t thread_index,
|
||||
vkb::CommandBufferResetMode reset_mode) :
|
||||
device{device_}, render_frame{render_frame_}, thread_index{thread_index}, reset_mode{reset_mode}
|
||||
{
|
||||
vk::CommandPoolCreateFlags flags;
|
||||
switch (reset_mode)
|
||||
{
|
||||
case vkb::CommandBufferResetMode::ResetIndividually:
|
||||
case vkb::CommandBufferResetMode::AlwaysAllocate:
|
||||
flags = vk::CommandPoolCreateFlagBits::eResetCommandBuffer;
|
||||
break;
|
||||
case vkb::CommandBufferResetMode::ResetPool:
|
||||
default:
|
||||
flags = vk::CommandPoolCreateFlagBits::eTransient;
|
||||
break;
|
||||
}
|
||||
|
||||
vk::CommandPoolCreateInfo command_pool_create_info{.flags = flags, .queueFamilyIndex = queue_family_index};
|
||||
|
||||
handle = device.get_handle().createCommandPool(command_pool_create_info);
|
||||
}
|
||||
|
||||
CommandPoolBase::CommandPoolBase(CommandPoolBase &&other) :
|
||||
device{other.device},
|
||||
handle{other.handle},
|
||||
render_frame{other.render_frame},
|
||||
thread_index{other.thread_index},
|
||||
queue_family_index{other.queue_family_index},
|
||||
primary_command_buffers{std::move(other.primary_command_buffers)},
|
||||
active_primary_command_buffer_count{other.active_primary_command_buffer_count},
|
||||
secondary_command_buffers{std::move(other.secondary_command_buffers)},
|
||||
active_secondary_command_buffer_count{other.active_secondary_command_buffer_count},
|
||||
reset_mode{other.reset_mode}
|
||||
{
|
||||
other.handle = nullptr;
|
||||
}
|
||||
|
||||
CommandPoolBase::~CommandPoolBase()
|
||||
{
|
||||
// clear command buffers before destroying the command pool
|
||||
primary_command_buffers.clear();
|
||||
secondary_command_buffers.clear();
|
||||
|
||||
// Destroy command pool
|
||||
if (handle)
|
||||
{
|
||||
device.get_handle().destroyCommandPool(handle);
|
||||
}
|
||||
}
|
||||
|
||||
vkb::core::DeviceCpp &CommandPoolBase::get_device()
|
||||
{
|
||||
return device;
|
||||
}
|
||||
|
||||
vk::CommandPool CommandPoolBase::get_handle() const
|
||||
{
|
||||
return handle;
|
||||
}
|
||||
|
||||
uint32_t CommandPoolBase::get_queue_family_index() const
|
||||
{
|
||||
return queue_family_index;
|
||||
}
|
||||
|
||||
vkb::rendering::RenderFrameCpp *CommandPoolBase::get_render_frame()
|
||||
{
|
||||
return render_frame;
|
||||
}
|
||||
|
||||
vkb::CommandBufferResetMode CommandPoolBase::get_reset_mode() const
|
||||
{
|
||||
return reset_mode;
|
||||
}
|
||||
|
||||
size_t CommandPoolBase::get_thread_index() const
|
||||
{
|
||||
return thread_index;
|
||||
}
|
||||
|
||||
std::shared_ptr<vkb::core::CommandBufferCpp> CommandPoolBase::request_command_buffer(vkb::core::CommandPoolCpp &commandPool, vk::CommandBufferLevel level)
|
||||
{
|
||||
if (static_cast<vk::CommandBufferLevel>(level) == vk::CommandBufferLevel::ePrimary)
|
||||
{
|
||||
if (active_primary_command_buffer_count < primary_command_buffers.size())
|
||||
{
|
||||
return primary_command_buffers[active_primary_command_buffer_count++];
|
||||
}
|
||||
|
||||
primary_command_buffers.emplace_back(std::make_shared<vkb::core::CommandBufferCpp>(commandPool, level));
|
||||
|
||||
active_primary_command_buffer_count++;
|
||||
|
||||
return primary_command_buffers.back();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (active_secondary_command_buffer_count < secondary_command_buffers.size())
|
||||
{
|
||||
return secondary_command_buffers[active_secondary_command_buffer_count++];
|
||||
}
|
||||
|
||||
secondary_command_buffers.emplace_back(std::make_shared<vkb::core::CommandBufferCpp>(commandPool, level));
|
||||
|
||||
active_secondary_command_buffer_count++;
|
||||
|
||||
return secondary_command_buffers.back();
|
||||
}
|
||||
}
|
||||
|
||||
void CommandPoolBase::reset_pool()
|
||||
{
|
||||
switch (reset_mode)
|
||||
{
|
||||
case vkb::CommandBufferResetMode::ResetIndividually:
|
||||
for (auto &cmd_buf : primary_command_buffers)
|
||||
{
|
||||
cmd_buf->reset(reset_mode);
|
||||
}
|
||||
active_primary_command_buffer_count = 0;
|
||||
|
||||
for (auto &cmd_buf : secondary_command_buffers)
|
||||
{
|
||||
cmd_buf->reset(reset_mode);
|
||||
}
|
||||
active_secondary_command_buffer_count = 0;
|
||||
break;
|
||||
|
||||
case vkb::CommandBufferResetMode::ResetPool:
|
||||
device.get_handle().resetCommandPool(handle);
|
||||
active_primary_command_buffer_count = 0;
|
||||
active_secondary_command_buffer_count = 0;
|
||||
break;
|
||||
|
||||
case vkb::CommandBufferResetMode::AlwaysAllocate:
|
||||
primary_command_buffers.clear();
|
||||
active_primary_command_buffer_count = 0;
|
||||
secondary_command_buffers.clear();
|
||||
active_secondary_command_buffer_count = 0;
|
||||
break;
|
||||
|
||||
default:
|
||||
throw std::runtime_error("Unknown reset mode for command pools");
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace core
|
||||
} // namespace vkb
|
||||
@@ -0,0 +1,84 @@
|
||||
/* Copyright (c) 2025, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* 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 "common/vk_common.h"
|
||||
#include <memory>
|
||||
#include <vulkan/vulkan.hpp>
|
||||
|
||||
namespace vkb
|
||||
{
|
||||
namespace rendering
|
||||
{
|
||||
template <vkb::BindingType bindingType>
|
||||
class RenderFrame;
|
||||
using RenderFrameCpp = RenderFrame<vkb::BindingType::Cpp>;
|
||||
} // namespace rendering
|
||||
|
||||
namespace core
|
||||
{
|
||||
template <vkb::BindingType bindingType>
|
||||
class CommandBuffer;
|
||||
using CommandBufferCpp = CommandBuffer<vkb::BindingType::Cpp>;
|
||||
|
||||
template <vkb::BindingType bindingType>
|
||||
class CommandPool;
|
||||
using CommandPoolCpp = CommandPool<vkb::BindingType::Cpp>;
|
||||
|
||||
template <vkb::BindingType bindingType>
|
||||
class Device;
|
||||
using DeviceCpp = Device<vkb::BindingType::Cpp>;
|
||||
|
||||
class CommandPoolBase
|
||||
{
|
||||
public:
|
||||
CommandPoolBase(vkb::core::DeviceCpp &device,
|
||||
uint32_t queue_family_index,
|
||||
vkb::rendering::RenderFrameCpp *render_frame = nullptr,
|
||||
size_t thread_index = 0,
|
||||
vkb::CommandBufferResetMode reset_mode = vkb::CommandBufferResetMode::ResetPool);
|
||||
CommandPoolBase(CommandPoolBase const &) = delete;
|
||||
CommandPoolBase(CommandPoolBase &&other);
|
||||
CommandPoolBase &operator=(CommandPoolBase const &) = delete;
|
||||
CommandPoolBase &operator=(CommandPoolBase &&other) = delete;
|
||||
~CommandPoolBase();
|
||||
|
||||
protected:
|
||||
vkb::core::DeviceCpp &get_device();
|
||||
vk::CommandPool get_handle() const;
|
||||
uint32_t get_queue_family_index() const;
|
||||
vkb::rendering::RenderFrameCpp *get_render_frame();
|
||||
vkb::CommandBufferResetMode get_reset_mode() const;
|
||||
size_t get_thread_index() const;
|
||||
std::shared_ptr<vkb::core::CommandBufferCpp> request_command_buffer(vkb::core::CommandPoolCpp &commandPool, vk::CommandBufferLevel level);
|
||||
void reset_pool();
|
||||
|
||||
private:
|
||||
vkb::core::DeviceCpp &device;
|
||||
vk::CommandPool handle = nullptr;
|
||||
vkb::rendering::RenderFrameCpp *render_frame = nullptr;
|
||||
size_t thread_index = 0;
|
||||
uint32_t queue_family_index = 0;
|
||||
std::vector<std::shared_ptr<vkb::core::CommandBufferCpp>> primary_command_buffers;
|
||||
uint32_t active_primary_command_buffer_count = 0;
|
||||
std::vector<std::shared_ptr<vkb::core::CommandBufferCpp>> secondary_command_buffers;
|
||||
uint32_t active_secondary_command_buffer_count = 0;
|
||||
vkb::CommandBufferResetMode reset_mode = vkb::CommandBufferResetMode::ResetPool;
|
||||
};
|
||||
} // namespace core
|
||||
} // namespace vkb
|
||||
@@ -0,0 +1,207 @@
|
||||
/* Copyright (c) 2021-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 "debug.h"
|
||||
|
||||
#include "core/command_buffer.h"
|
||||
#include "core/device.h"
|
||||
|
||||
#include <glm/gtc/type_ptr.hpp>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace vkb
|
||||
{
|
||||
void DebugUtilsExtDebugUtils::set_debug_name(VkDevice device, VkObjectType object_type, uint64_t object_handle,
|
||||
const char *name) const
|
||||
{
|
||||
VkDebugUtilsObjectNameInfoEXT name_info{};
|
||||
name_info.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT;
|
||||
name_info.objectType = object_type;
|
||||
name_info.objectHandle = object_handle;
|
||||
name_info.pObjectName = name;
|
||||
|
||||
assert(vkSetDebugUtilsObjectNameEXT);
|
||||
vkSetDebugUtilsObjectNameEXT(device, &name_info);
|
||||
}
|
||||
|
||||
void DebugUtilsExtDebugUtils::set_debug_tag(VkDevice device, VkObjectType object_type, uint64_t object_handle,
|
||||
uint64_t tag_name, const void *tag_data, size_t tag_data_size) const
|
||||
{
|
||||
VkDebugUtilsObjectTagInfoEXT tag_info{};
|
||||
tag_info.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_TAG_INFO_EXT;
|
||||
tag_info.objectType = object_type;
|
||||
tag_info.objectHandle = object_handle;
|
||||
tag_info.tagName = tag_name;
|
||||
tag_info.tagSize = tag_data_size;
|
||||
tag_info.pTag = tag_data;
|
||||
|
||||
assert(vkSetDebugUtilsObjectTagEXT);
|
||||
vkSetDebugUtilsObjectTagEXT(device, &tag_info);
|
||||
}
|
||||
|
||||
void DebugUtilsExtDebugUtils::cmd_begin_label(VkCommandBuffer command_buffer,
|
||||
const char *name, glm::vec4 color) const
|
||||
{
|
||||
VkDebugUtilsLabelEXT label_info{};
|
||||
label_info.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_LABEL_EXT;
|
||||
label_info.pLabelName = name;
|
||||
memcpy(label_info.color, glm::value_ptr(color), sizeof(glm::vec4));
|
||||
|
||||
assert(vkCmdBeginDebugUtilsLabelEXT);
|
||||
vkCmdBeginDebugUtilsLabelEXT(command_buffer, &label_info);
|
||||
}
|
||||
|
||||
void DebugUtilsExtDebugUtils::cmd_end_label(VkCommandBuffer command_buffer) const
|
||||
{
|
||||
assert(vkCmdEndDebugUtilsLabelEXT);
|
||||
vkCmdEndDebugUtilsLabelEXT(command_buffer);
|
||||
}
|
||||
|
||||
void DebugUtilsExtDebugUtils::cmd_insert_label(VkCommandBuffer command_buffer,
|
||||
const char *name, glm::vec4 color) const
|
||||
{
|
||||
VkDebugUtilsLabelEXT label_info{};
|
||||
label_info.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_LABEL_EXT;
|
||||
label_info.pLabelName = name;
|
||||
memcpy(label_info.color, glm::value_ptr(color), sizeof(glm::vec4));
|
||||
|
||||
assert(vkCmdInsertDebugUtilsLabelEXT);
|
||||
vkCmdInsertDebugUtilsLabelEXT(command_buffer, &label_info);
|
||||
}
|
||||
|
||||
// See https://www.khronos.org/registry/vulkan/specs/1.2-extensions/man/html/VkDebugReportObjectTypeEXT.html
|
||||
static const std::unordered_map<VkObjectType, VkDebugReportObjectTypeEXT> VK_OBJECT_TYPE_TO_DEBUG_REPORT_TYPE{
|
||||
{VK_OBJECT_TYPE_UNKNOWN, VK_DEBUG_REPORT_OBJECT_TYPE_UNKNOWN_EXT},
|
||||
{VK_OBJECT_TYPE_INSTANCE, VK_DEBUG_REPORT_OBJECT_TYPE_INSTANCE_EXT},
|
||||
{VK_OBJECT_TYPE_PHYSICAL_DEVICE, VK_DEBUG_REPORT_OBJECT_TYPE_PHYSICAL_DEVICE_EXT},
|
||||
{VK_OBJECT_TYPE_DEVICE, VK_DEBUG_REPORT_OBJECT_TYPE_DEVICE_EXT},
|
||||
{VK_OBJECT_TYPE_QUEUE, VK_DEBUG_REPORT_OBJECT_TYPE_QUEUE_EXT},
|
||||
{VK_OBJECT_TYPE_SEMAPHORE, VK_DEBUG_REPORT_OBJECT_TYPE_SEMAPHORE_EXT},
|
||||
{VK_OBJECT_TYPE_COMMAND_BUFFER, VK_DEBUG_REPORT_OBJECT_TYPE_COMMAND_BUFFER_EXT},
|
||||
{VK_OBJECT_TYPE_FENCE, VK_DEBUG_REPORT_OBJECT_TYPE_FENCE_EXT},
|
||||
{VK_OBJECT_TYPE_DEVICE_MEMORY, VK_DEBUG_REPORT_OBJECT_TYPE_DEVICE_MEMORY_EXT},
|
||||
{VK_OBJECT_TYPE_BUFFER, VK_DEBUG_REPORT_OBJECT_TYPE_BUFFER_EXT},
|
||||
{VK_OBJECT_TYPE_IMAGE, VK_DEBUG_REPORT_OBJECT_TYPE_IMAGE_EXT},
|
||||
{VK_OBJECT_TYPE_EVENT, VK_DEBUG_REPORT_OBJECT_TYPE_EVENT_EXT},
|
||||
{VK_OBJECT_TYPE_QUERY_POOL, VK_DEBUG_REPORT_OBJECT_TYPE_QUERY_POOL_EXT},
|
||||
{VK_OBJECT_TYPE_BUFFER_VIEW, VK_DEBUG_REPORT_OBJECT_TYPE_BUFFER_VIEW_EXT},
|
||||
{VK_OBJECT_TYPE_IMAGE_VIEW, VK_DEBUG_REPORT_OBJECT_TYPE_IMAGE_VIEW_EXT},
|
||||
{VK_OBJECT_TYPE_SHADER_MODULE, VK_DEBUG_REPORT_OBJECT_TYPE_SHADER_MODULE_EXT},
|
||||
{VK_OBJECT_TYPE_PIPELINE_CACHE, VK_DEBUG_REPORT_OBJECT_TYPE_PIPELINE_CACHE_EXT},
|
||||
{VK_OBJECT_TYPE_PIPELINE_LAYOUT, VK_DEBUG_REPORT_OBJECT_TYPE_PIPELINE_LAYOUT_EXT},
|
||||
{VK_OBJECT_TYPE_RENDER_PASS, VK_DEBUG_REPORT_OBJECT_TYPE_RENDER_PASS_EXT},
|
||||
{VK_OBJECT_TYPE_PIPELINE, VK_DEBUG_REPORT_OBJECT_TYPE_PIPELINE_EXT},
|
||||
{VK_OBJECT_TYPE_DESCRIPTOR_SET_LAYOUT, VK_DEBUG_REPORT_OBJECT_TYPE_DESCRIPTOR_SET_LAYOUT_EXT},
|
||||
{VK_OBJECT_TYPE_SAMPLER, VK_DEBUG_REPORT_OBJECT_TYPE_SAMPLER_EXT},
|
||||
{VK_OBJECT_TYPE_DESCRIPTOR_POOL, VK_DEBUG_REPORT_OBJECT_TYPE_DESCRIPTOR_POOL_EXT},
|
||||
{VK_OBJECT_TYPE_DESCRIPTOR_SET, VK_DEBUG_REPORT_OBJECT_TYPE_DESCRIPTOR_SET_EXT},
|
||||
{VK_OBJECT_TYPE_FRAMEBUFFER, VK_DEBUG_REPORT_OBJECT_TYPE_FRAMEBUFFER_EXT},
|
||||
{VK_OBJECT_TYPE_COMMAND_POOL, VK_DEBUG_REPORT_OBJECT_TYPE_COMMAND_POOL_EXT},
|
||||
{VK_OBJECT_TYPE_SURFACE_KHR, VK_DEBUG_REPORT_OBJECT_TYPE_SURFACE_KHR_EXT},
|
||||
{VK_OBJECT_TYPE_SWAPCHAIN_KHR, VK_DEBUG_REPORT_OBJECT_TYPE_SWAPCHAIN_KHR_EXT},
|
||||
{VK_OBJECT_TYPE_DEBUG_REPORT_CALLBACK_EXT, VK_DEBUG_REPORT_OBJECT_TYPE_DEBUG_REPORT_CALLBACK_EXT_EXT},
|
||||
{VK_OBJECT_TYPE_DISPLAY_KHR, VK_DEBUG_REPORT_OBJECT_TYPE_DISPLAY_KHR_EXT},
|
||||
{VK_OBJECT_TYPE_DISPLAY_MODE_KHR, VK_DEBUG_REPORT_OBJECT_TYPE_DISPLAY_MODE_KHR_EXT},
|
||||
{VK_OBJECT_TYPE_DESCRIPTOR_UPDATE_TEMPLATE_KHR, VK_DEBUG_REPORT_OBJECT_TYPE_DESCRIPTOR_UPDATE_TEMPLATE_EXT},
|
||||
};
|
||||
|
||||
void DebugMarkerExtDebugUtils::set_debug_name(VkDevice device, VkObjectType object_type, uint64_t object_handle,
|
||||
const char *name) const
|
||||
{
|
||||
VkDebugMarkerObjectNameInfoEXT name_info{};
|
||||
name_info.sType = VK_STRUCTURE_TYPE_DEBUG_MARKER_OBJECT_NAME_INFO_EXT;
|
||||
name_info.objectType = VK_OBJECT_TYPE_TO_DEBUG_REPORT_TYPE.at(object_type);
|
||||
name_info.object = object_handle;
|
||||
name_info.pObjectName = name;
|
||||
|
||||
assert(vkDebugMarkerSetObjectNameEXT);
|
||||
vkDebugMarkerSetObjectNameEXT(device, &name_info);
|
||||
}
|
||||
|
||||
void DebugMarkerExtDebugUtils::set_debug_tag(VkDevice device, VkObjectType object_type, uint64_t object_handle,
|
||||
uint64_t tag_name, const void *tag_data, size_t tag_data_size) const
|
||||
{
|
||||
VkDebugMarkerObjectTagInfoEXT tag_info{};
|
||||
tag_info.sType = VK_STRUCTURE_TYPE_DEBUG_MARKER_OBJECT_TAG_INFO_EXT;
|
||||
tag_info.objectType = VK_OBJECT_TYPE_TO_DEBUG_REPORT_TYPE.at(object_type);
|
||||
tag_info.object = object_handle;
|
||||
tag_info.tagName = tag_name;
|
||||
tag_info.tagSize = tag_data_size;
|
||||
tag_info.pTag = tag_data;
|
||||
|
||||
assert(vkDebugMarkerSetObjectTagEXT);
|
||||
vkDebugMarkerSetObjectTagEXT(device, &tag_info);
|
||||
}
|
||||
|
||||
void DebugMarkerExtDebugUtils::cmd_begin_label(VkCommandBuffer command_buffer,
|
||||
const char *name, glm::vec4 color) const
|
||||
{
|
||||
VkDebugMarkerMarkerInfoEXT marker_info{};
|
||||
marker_info.sType = VK_STRUCTURE_TYPE_DEBUG_MARKER_MARKER_INFO_EXT;
|
||||
marker_info.pMarkerName = name;
|
||||
memcpy(marker_info.color, glm::value_ptr(color), sizeof(glm::vec4));
|
||||
|
||||
assert(vkCmdDebugMarkerBeginEXT);
|
||||
vkCmdDebugMarkerBeginEXT(command_buffer, &marker_info);
|
||||
}
|
||||
|
||||
void DebugMarkerExtDebugUtils::cmd_end_label(VkCommandBuffer command_buffer) const
|
||||
{
|
||||
assert(vkCmdDebugMarkerEndEXT);
|
||||
vkCmdDebugMarkerEndEXT(command_buffer);
|
||||
}
|
||||
|
||||
void DebugMarkerExtDebugUtils::cmd_insert_label(VkCommandBuffer command_buffer,
|
||||
const char *name, glm::vec4 color) const
|
||||
{
|
||||
VkDebugMarkerMarkerInfoEXT marker_info{};
|
||||
marker_info.sType = VK_STRUCTURE_TYPE_DEBUG_MARKER_MARKER_INFO_EXT;
|
||||
marker_info.pMarkerName = name;
|
||||
memcpy(marker_info.color, glm::value_ptr(color), sizeof(glm::vec4));
|
||||
|
||||
assert(vkCmdDebugMarkerInsertEXT);
|
||||
vkCmdDebugMarkerInsertEXT(command_buffer, &marker_info);
|
||||
}
|
||||
|
||||
ScopedDebugLabel::ScopedDebugLabel(const DebugUtils &debug_utils, VkCommandBuffer command_buffer,
|
||||
const char *name, glm::vec4 color) :
|
||||
debug_utils{&debug_utils},
|
||||
command_buffer{VK_NULL_HANDLE}
|
||||
{
|
||||
if (name && *name != '\0')
|
||||
{
|
||||
assert(command_buffer != VK_NULL_HANDLE);
|
||||
this->command_buffer = command_buffer;
|
||||
|
||||
debug_utils.cmd_begin_label(command_buffer, name, color);
|
||||
}
|
||||
}
|
||||
|
||||
ScopedDebugLabel::ScopedDebugLabel(const vkb::core::CommandBufferC &command_buffer, const char *name, glm::vec4 color) :
|
||||
ScopedDebugLabel{command_buffer.get_device().get_debug_utils(), command_buffer.get_handle(), name, color}
|
||||
{
|
||||
}
|
||||
|
||||
ScopedDebugLabel::~ScopedDebugLabel()
|
||||
{
|
||||
if (command_buffer != VK_NULL_HANDLE)
|
||||
{
|
||||
debug_utils->cmd_end_label(command_buffer);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace vkb
|
||||
@@ -0,0 +1,165 @@
|
||||
/* Copyright (c) 2021-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 "common/glm_common.h"
|
||||
#include "common/vk_common.h"
|
||||
#include <cassert>
|
||||
|
||||
namespace vkb
|
||||
{
|
||||
namespace core
|
||||
{
|
||||
template <vkb::BindingType bindingType>
|
||||
class CommandBuffer;
|
||||
using CommandBufferC = CommandBuffer<vkb::BindingType::C>;
|
||||
} // namespace core
|
||||
|
||||
/**
|
||||
* @brief An interface over platform-specific debug extensions.
|
||||
*/
|
||||
class DebugUtils
|
||||
{
|
||||
public:
|
||||
virtual ~DebugUtils() = default;
|
||||
|
||||
/**
|
||||
* @brief Sets the debug name for a Vulkan object.
|
||||
*/
|
||||
virtual void set_debug_name(VkDevice device, VkObjectType object_type, uint64_t object_handle,
|
||||
const char *name) const = 0;
|
||||
|
||||
/**
|
||||
* @brief Tags the given Vulkan object with some data.
|
||||
*/
|
||||
virtual void set_debug_tag(VkDevice device, VkObjectType object_type, uint64_t object_handle,
|
||||
uint64_t tag_name, const void *tag_data, size_t tag_data_size) const = 0;
|
||||
|
||||
/**
|
||||
* @brief Inserts a command to begin a new debug label/marker scope.
|
||||
*/
|
||||
virtual void cmd_begin_label(VkCommandBuffer command_buffer,
|
||||
const char *name, glm::vec4 color = {}) const = 0;
|
||||
|
||||
/**
|
||||
* @brief Inserts a command to end the current debug label/marker scope.
|
||||
*/
|
||||
virtual void cmd_end_label(VkCommandBuffer command_buffer) const = 0;
|
||||
|
||||
/**
|
||||
* @brief Inserts a (non-scoped) debug label/marker in the command buffer.
|
||||
*/
|
||||
virtual void cmd_insert_label(VkCommandBuffer command_buffer,
|
||||
const char *name, glm::vec4 color = {}) const = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief DebugUtils implemented on top of VK_EXT_debug_utils.
|
||||
*/
|
||||
class DebugUtilsExtDebugUtils final : public DebugUtils
|
||||
{
|
||||
public:
|
||||
~DebugUtilsExtDebugUtils() override = default;
|
||||
|
||||
void set_debug_name(VkDevice device, VkObjectType object_type, uint64_t object_handle,
|
||||
const char *name) const override;
|
||||
|
||||
void set_debug_tag(VkDevice device, VkObjectType object_type, uint64_t object_handle,
|
||||
uint64_t tag_name, const void *tag_data, size_t tag_data_size) const override;
|
||||
|
||||
void cmd_begin_label(VkCommandBuffer command_buffer,
|
||||
const char *name, glm::vec4 color) const override;
|
||||
|
||||
void cmd_end_label(VkCommandBuffer command_buffer) const override;
|
||||
|
||||
void cmd_insert_label(VkCommandBuffer command_buffer,
|
||||
const char *name, glm::vec4 color) const override;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief DebugUtils implemented on top of VK_EXT_debug_marker.
|
||||
*/
|
||||
class DebugMarkerExtDebugUtils final : public DebugUtils
|
||||
{
|
||||
public:
|
||||
~DebugMarkerExtDebugUtils() override = default;
|
||||
|
||||
void set_debug_name(VkDevice device, VkObjectType object_type, uint64_t object_handle,
|
||||
const char *name) const override;
|
||||
|
||||
void set_debug_tag(VkDevice device, VkObjectType object_type, uint64_t object_handle,
|
||||
uint64_t tag_name, const void *tag_data, size_t tag_data_size) const override;
|
||||
|
||||
void cmd_begin_label(VkCommandBuffer command_buffer,
|
||||
const char *name, glm::vec4 color) const override;
|
||||
|
||||
void cmd_end_label(VkCommandBuffer command_buffer) const override;
|
||||
|
||||
void cmd_insert_label(VkCommandBuffer command_buffer,
|
||||
const char *name, glm::vec4 color) const override;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief No-op DebugUtils.
|
||||
*/
|
||||
class DummyDebugUtils final : public DebugUtils
|
||||
{
|
||||
public:
|
||||
~DummyDebugUtils() override = default;
|
||||
|
||||
inline void set_debug_name(VkDevice, VkObjectType, uint64_t, const char *) const override
|
||||
{}
|
||||
|
||||
inline void set_debug_tag(VkDevice, VkObjectType, uint64_t,
|
||||
uint64_t, const void *, size_t) const override
|
||||
{}
|
||||
|
||||
inline void cmd_begin_label(VkCommandBuffer,
|
||||
const char *, glm::vec4) const override
|
||||
{}
|
||||
|
||||
inline void cmd_end_label(VkCommandBuffer) const override
|
||||
{}
|
||||
|
||||
inline void cmd_insert_label(VkCommandBuffer,
|
||||
const char *, glm::vec4) const override
|
||||
{}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A RAII debug label.
|
||||
* If any of EXT_debug_utils or EXT_debug_marker is available, this:
|
||||
* - Begins a debug label / marker on construction
|
||||
* - Ends it on destruction
|
||||
*/
|
||||
class ScopedDebugLabel final
|
||||
{
|
||||
public:
|
||||
ScopedDebugLabel(const DebugUtils &debug_utils, VkCommandBuffer command_buffer,
|
||||
const char *name, glm::vec4 color = {});
|
||||
|
||||
ScopedDebugLabel(const vkb::core::CommandBufferC &command_buffer, const char *name, glm::vec4 color = {});
|
||||
|
||||
~ScopedDebugLabel();
|
||||
|
||||
private:
|
||||
const DebugUtils *debug_utils;
|
||||
VkCommandBuffer command_buffer;
|
||||
};
|
||||
|
||||
} // namespace vkb
|
||||
@@ -0,0 +1,205 @@
|
||||
/* Copyright (c) 2019-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 "descriptor_pool.h"
|
||||
|
||||
#include "descriptor_set_layout.h"
|
||||
#include "device.h"
|
||||
|
||||
namespace vkb
|
||||
{
|
||||
DescriptorPool::DescriptorPool(vkb::core::DeviceC &device,
|
||||
const DescriptorSetLayout &descriptor_set_layout,
|
||||
uint32_t pool_size) :
|
||||
device{device},
|
||||
descriptor_set_layout{&descriptor_set_layout}
|
||||
{
|
||||
const auto &bindings = descriptor_set_layout.get_bindings();
|
||||
|
||||
std::map<VkDescriptorType, std::uint32_t> descriptor_type_counts;
|
||||
|
||||
// Count each type of descriptor set
|
||||
for (auto &binding : bindings)
|
||||
{
|
||||
descriptor_type_counts[binding.descriptorType] += binding.descriptorCount;
|
||||
}
|
||||
|
||||
// Allocate pool sizes array
|
||||
pool_sizes.resize(descriptor_type_counts.size());
|
||||
|
||||
auto pool_size_it = pool_sizes.begin();
|
||||
|
||||
// Fill pool size for each descriptor type count multiplied by the pool size
|
||||
for (auto &it : descriptor_type_counts)
|
||||
{
|
||||
pool_size_it->type = it.first;
|
||||
|
||||
pool_size_it->descriptorCount = it.second * pool_size;
|
||||
|
||||
++pool_size_it;
|
||||
}
|
||||
|
||||
pool_max_sets = pool_size;
|
||||
}
|
||||
|
||||
DescriptorPool::~DescriptorPool()
|
||||
{
|
||||
// Destroy all descriptor pools
|
||||
for (auto pool : pools)
|
||||
{
|
||||
vkDestroyDescriptorPool(device.get_handle(), pool, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
void DescriptorPool::reset()
|
||||
{
|
||||
// Reset all descriptor pools
|
||||
for (auto pool : pools)
|
||||
{
|
||||
vkResetDescriptorPool(device.get_handle(), pool, 0);
|
||||
}
|
||||
|
||||
// Clear internal tracking of descriptor set allocations
|
||||
std::fill(pool_sets_count.begin(), pool_sets_count.end(), 0);
|
||||
set_pool_mapping.clear();
|
||||
|
||||
// Reset the pool index from which descriptor sets are allocated
|
||||
pool_index = 0;
|
||||
}
|
||||
|
||||
const DescriptorSetLayout &DescriptorPool::get_descriptor_set_layout() const
|
||||
{
|
||||
assert(descriptor_set_layout && "Descriptor set layout is invalid");
|
||||
return *descriptor_set_layout;
|
||||
}
|
||||
|
||||
void DescriptorPool::set_descriptor_set_layout(const DescriptorSetLayout &set_layout)
|
||||
{
|
||||
descriptor_set_layout = &set_layout;
|
||||
}
|
||||
|
||||
VkDescriptorSet DescriptorPool::allocate()
|
||||
{
|
||||
pool_index = find_available_pool(pool_index);
|
||||
|
||||
// Increment allocated set count for the current pool
|
||||
++pool_sets_count[pool_index];
|
||||
|
||||
VkDescriptorSetLayout set_layout = get_descriptor_set_layout().get_handle();
|
||||
|
||||
VkDescriptorSetAllocateInfo alloc_info{VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO};
|
||||
alloc_info.descriptorPool = pools[pool_index];
|
||||
alloc_info.descriptorSetCount = 1;
|
||||
alloc_info.pSetLayouts = &set_layout;
|
||||
|
||||
VkDescriptorSet handle = VK_NULL_HANDLE;
|
||||
|
||||
// Allocate a new descriptor set from the current pool
|
||||
auto result = vkAllocateDescriptorSets(device.get_handle(), &alloc_info, &handle);
|
||||
|
||||
if (result != VK_SUCCESS)
|
||||
{
|
||||
// Decrement allocated set count for the current pool
|
||||
--pool_sets_count[pool_index];
|
||||
|
||||
return VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
// Store mapping between the descriptor set and the pool
|
||||
set_pool_mapping.emplace(handle, pool_index);
|
||||
|
||||
return handle;
|
||||
}
|
||||
|
||||
VkResult DescriptorPool::free(VkDescriptorSet descriptor_set)
|
||||
{
|
||||
// Get the pool index of the descriptor set
|
||||
auto it = set_pool_mapping.find(descriptor_set);
|
||||
|
||||
if (it == set_pool_mapping.end())
|
||||
{
|
||||
return VK_INCOMPLETE;
|
||||
}
|
||||
|
||||
auto desc_pool_index = it->second;
|
||||
|
||||
// Free descriptor set from the pool
|
||||
vkFreeDescriptorSets(device.get_handle(), pools[desc_pool_index], 1, &descriptor_set);
|
||||
|
||||
// Remove descriptor set mapping to the pool
|
||||
set_pool_mapping.erase(it);
|
||||
|
||||
// Decrement allocated set count for the pool
|
||||
--pool_sets_count[desc_pool_index];
|
||||
|
||||
// Change the current pool index to use the available pool
|
||||
pool_index = desc_pool_index;
|
||||
|
||||
return VK_SUCCESS;
|
||||
}
|
||||
|
||||
std::uint32_t DescriptorPool::find_available_pool(std::uint32_t search_index)
|
||||
{
|
||||
// Create a new pool
|
||||
if (pools.size() <= search_index)
|
||||
{
|
||||
VkDescriptorPoolCreateInfo create_info{VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO};
|
||||
|
||||
create_info.poolSizeCount = to_u32(pool_sizes.size());
|
||||
create_info.pPoolSizes = pool_sizes.data();
|
||||
create_info.maxSets = pool_max_sets;
|
||||
|
||||
// We do not set FREE_DESCRIPTOR_SET_BIT as we do not need to free individual descriptor sets
|
||||
create_info.flags = 0;
|
||||
|
||||
// Check descriptor set layout and enable the required flags
|
||||
auto &binding_flags = descriptor_set_layout->get_binding_flags();
|
||||
for (auto binding_flag : binding_flags)
|
||||
{
|
||||
if (binding_flag & VK_DESCRIPTOR_BINDING_UPDATE_AFTER_BIND_BIT_EXT)
|
||||
{
|
||||
create_info.flags |= VK_DESCRIPTOR_POOL_CREATE_UPDATE_AFTER_BIND_BIT_EXT;
|
||||
}
|
||||
}
|
||||
|
||||
VkDescriptorPool handle = VK_NULL_HANDLE;
|
||||
|
||||
// Create the Vulkan descriptor pool
|
||||
auto result = vkCreateDescriptorPool(device.get_handle(), &create_info, nullptr, &handle);
|
||||
|
||||
if (result != VK_SUCCESS)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Store internally the Vulkan handle
|
||||
pools.push_back(handle);
|
||||
|
||||
// Add set count for the descriptor pool
|
||||
pool_sets_count.push_back(0);
|
||||
|
||||
return search_index;
|
||||
}
|
||||
else if (pool_sets_count[search_index] < pool_max_sets)
|
||||
{
|
||||
return search_index;
|
||||
}
|
||||
|
||||
// Increment pool index
|
||||
return find_available_pool(++search_index);
|
||||
}
|
||||
} // namespace vkb
|
||||
@@ -0,0 +1,94 @@
|
||||
/* Copyright (c) 2019-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 <unordered_map>
|
||||
|
||||
#include "common/helpers.h"
|
||||
#include "common/vk_common.h"
|
||||
|
||||
namespace vkb
|
||||
{
|
||||
class DescriptorSetLayout;
|
||||
|
||||
namespace core
|
||||
{
|
||||
template <vkb::BindingType bindingType>
|
||||
class Device;
|
||||
using DeviceC = Device<vkb::BindingType::C>;
|
||||
} // namespace core
|
||||
|
||||
/**
|
||||
* @brief Manages an array of fixed size VkDescriptorPool and is able to allocate descriptor sets
|
||||
*/
|
||||
class DescriptorPool
|
||||
{
|
||||
public:
|
||||
static const uint32_t MAX_SETS_PER_POOL = 16;
|
||||
|
||||
DescriptorPool(vkb::core::DeviceC &device,
|
||||
const DescriptorSetLayout &descriptor_set_layout,
|
||||
uint32_t pool_size = MAX_SETS_PER_POOL);
|
||||
|
||||
DescriptorPool(const DescriptorPool &) = delete;
|
||||
|
||||
DescriptorPool(DescriptorPool &&) = default;
|
||||
|
||||
~DescriptorPool();
|
||||
|
||||
DescriptorPool &operator=(const DescriptorPool &) = delete;
|
||||
|
||||
DescriptorPool &operator=(DescriptorPool &&) = delete;
|
||||
|
||||
void reset();
|
||||
|
||||
const DescriptorSetLayout &get_descriptor_set_layout() const;
|
||||
|
||||
void set_descriptor_set_layout(const DescriptorSetLayout &set_layout);
|
||||
|
||||
VkDescriptorSet allocate();
|
||||
|
||||
VkResult free(VkDescriptorSet descriptor_set);
|
||||
|
||||
private:
|
||||
vkb::core::DeviceC &device;
|
||||
|
||||
const DescriptorSetLayout *descriptor_set_layout{nullptr};
|
||||
|
||||
// Descriptor pool size
|
||||
std::vector<VkDescriptorPoolSize> pool_sizes;
|
||||
|
||||
// Number of sets to allocate for each pool
|
||||
uint32_t pool_max_sets{0};
|
||||
|
||||
// Total descriptor pools created
|
||||
std::vector<VkDescriptorPool> pools;
|
||||
|
||||
// Count sets for each pool
|
||||
std::vector<uint32_t> pool_sets_count;
|
||||
|
||||
// Current pool index to allocate descriptor set
|
||||
uint32_t pool_index{0};
|
||||
|
||||
// Map between descriptor set and pool index
|
||||
std::unordered_map<VkDescriptorSet, uint32_t> set_pool_mapping;
|
||||
|
||||
// Find next pool index or create new pool
|
||||
uint32_t find_available_pool(uint32_t pool_index);
|
||||
};
|
||||
} // namespace vkb
|
||||
@@ -0,0 +1,256 @@
|
||||
/* Copyright (c) 2019-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 "descriptor_set.h"
|
||||
#include "common/resource_caching.h"
|
||||
#include "core/device.h"
|
||||
#include "core/physical_device.h"
|
||||
|
||||
namespace vkb
|
||||
{
|
||||
DescriptorSet::DescriptorSet(vkb::core::DeviceC &device,
|
||||
const DescriptorSetLayout &descriptor_set_layout,
|
||||
DescriptorPool &descriptor_pool,
|
||||
const BindingMap<VkDescriptorBufferInfo> &buffer_infos,
|
||||
const BindingMap<VkDescriptorImageInfo> &image_infos) :
|
||||
device{device},
|
||||
descriptor_set_layout{descriptor_set_layout},
|
||||
descriptor_pool{descriptor_pool},
|
||||
buffer_infos{buffer_infos},
|
||||
image_infos{image_infos},
|
||||
handle{descriptor_pool.allocate()}
|
||||
{
|
||||
prepare();
|
||||
}
|
||||
|
||||
void DescriptorSet::reset(const BindingMap<VkDescriptorBufferInfo> &new_buffer_infos, const BindingMap<VkDescriptorImageInfo> &new_image_infos)
|
||||
{
|
||||
if (!new_buffer_infos.empty() || !new_image_infos.empty())
|
||||
{
|
||||
buffer_infos = new_buffer_infos;
|
||||
image_infos = new_image_infos;
|
||||
}
|
||||
else
|
||||
{
|
||||
LOGW("Calling reset on Descriptor Set with no new buffer infos and no new image infos.");
|
||||
}
|
||||
|
||||
this->write_descriptor_sets.clear();
|
||||
this->updated_bindings.clear();
|
||||
|
||||
prepare();
|
||||
}
|
||||
|
||||
void DescriptorSet::prepare()
|
||||
{
|
||||
// We don't want to prepare twice during the life cycle of a Descriptor Set
|
||||
if (!write_descriptor_sets.empty())
|
||||
{
|
||||
LOGW("Trying to prepare a descriptor set that has already been prepared, skipping.");
|
||||
return;
|
||||
}
|
||||
|
||||
// Iterate over all buffer bindings
|
||||
for (auto &binding_it : buffer_infos)
|
||||
{
|
||||
auto binding_index = binding_it.first;
|
||||
auto &buffer_bindings = binding_it.second;
|
||||
|
||||
if (auto binding_info = descriptor_set_layout.get_layout_binding(binding_index))
|
||||
{
|
||||
// Iterate over all binding buffers in array
|
||||
for (auto &element_it : buffer_bindings)
|
||||
{
|
||||
auto &buffer_info = element_it.second;
|
||||
|
||||
size_t uniform_buffer_range_limit = device.get_gpu().get_properties().limits.maxUniformBufferRange;
|
||||
size_t storage_buffer_range_limit = device.get_gpu().get_properties().limits.maxStorageBufferRange;
|
||||
|
||||
size_t buffer_range_limit = static_cast<size_t>(buffer_info.range);
|
||||
|
||||
if ((binding_info->descriptorType == VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER || binding_info->descriptorType == VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC) && buffer_range_limit > uniform_buffer_range_limit)
|
||||
{
|
||||
LOGE("Set {} binding {} cannot be updated: buffer size {} exceeds the uniform buffer range limit {}", descriptor_set_layout.get_index(), binding_index, buffer_info.range, uniform_buffer_range_limit);
|
||||
buffer_range_limit = uniform_buffer_range_limit;
|
||||
}
|
||||
else if ((binding_info->descriptorType == VK_DESCRIPTOR_TYPE_STORAGE_BUFFER || binding_info->descriptorType == VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC) && buffer_range_limit > storage_buffer_range_limit)
|
||||
{
|
||||
LOGE("Set {} binding {} cannot be updated: buffer size {} exceeds the storage buffer range limit {}", descriptor_set_layout.get_index(), binding_index, buffer_info.range, storage_buffer_range_limit);
|
||||
buffer_range_limit = storage_buffer_range_limit;
|
||||
}
|
||||
|
||||
// Clip the buffers range to the limit if one exists as otherwise we will receive a Vulkan validation error
|
||||
buffer_info.range = buffer_range_limit;
|
||||
|
||||
VkWriteDescriptorSet write_descriptor_set{VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET};
|
||||
|
||||
write_descriptor_set.dstBinding = binding_index;
|
||||
write_descriptor_set.descriptorType = binding_info->descriptorType;
|
||||
write_descriptor_set.pBufferInfo = &buffer_info;
|
||||
write_descriptor_set.dstSet = handle;
|
||||
write_descriptor_set.dstArrayElement = element_it.first;
|
||||
write_descriptor_set.descriptorCount = 1;
|
||||
|
||||
write_descriptor_sets.push_back(write_descriptor_set);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LOGE("Shader layout set does not use buffer binding at #{}", binding_index);
|
||||
}
|
||||
}
|
||||
|
||||
// Iterate over all image bindings
|
||||
for (auto &binding_it : image_infos)
|
||||
{
|
||||
auto binding_index = binding_it.first;
|
||||
auto &binding_resources = binding_it.second;
|
||||
|
||||
if (auto binding_info = descriptor_set_layout.get_layout_binding(binding_index))
|
||||
{
|
||||
// Iterate over all binding images in array
|
||||
for (auto &element_it : binding_resources)
|
||||
{
|
||||
auto &image_info = element_it.second;
|
||||
|
||||
VkWriteDescriptorSet write_descriptor_set{VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET};
|
||||
|
||||
write_descriptor_set.dstBinding = binding_index;
|
||||
write_descriptor_set.descriptorType = binding_info->descriptorType;
|
||||
write_descriptor_set.pImageInfo = &image_info;
|
||||
write_descriptor_set.dstSet = handle;
|
||||
write_descriptor_set.dstArrayElement = element_it.first;
|
||||
write_descriptor_set.descriptorCount = 1;
|
||||
|
||||
write_descriptor_sets.push_back(write_descriptor_set);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LOGE("Shader layout set does not use image binding at #{}", binding_index);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DescriptorSet::update(const std::vector<uint32_t> &bindings_to_update)
|
||||
{
|
||||
std::vector<VkWriteDescriptorSet> write_operations;
|
||||
std::vector<size_t> write_operation_hashes;
|
||||
|
||||
// If the 'bindings_to_update' vector is empty, we want to write to all the bindings
|
||||
// (but skipping all to-update bindings that haven't been written yet)
|
||||
if (bindings_to_update.empty())
|
||||
{
|
||||
for (size_t i = 0; i < write_descriptor_sets.size(); i++)
|
||||
{
|
||||
const auto &write_operation = write_descriptor_sets[i];
|
||||
|
||||
size_t write_operation_hash = 0;
|
||||
hash_param(write_operation_hash, write_operation);
|
||||
|
||||
auto update_pair_it = updated_bindings.find(write_operation.dstBinding);
|
||||
if (update_pair_it == updated_bindings.end() || update_pair_it->second != write_operation_hash)
|
||||
{
|
||||
write_operations.push_back(write_operation);
|
||||
write_operation_hashes.push_back(write_operation_hash);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Otherwise we want to update the binding indices present in the 'bindings_to_update' vector.
|
||||
// (again, skipping those to update but not updated yet)
|
||||
for (size_t i = 0; i < write_descriptor_sets.size(); i++)
|
||||
{
|
||||
const auto &write_operation = write_descriptor_sets[i];
|
||||
|
||||
if (std::ranges::find(bindings_to_update, write_operation.dstBinding) != bindings_to_update.end())
|
||||
{
|
||||
size_t write_operation_hash = 0;
|
||||
hash_param(write_operation_hash, write_operation);
|
||||
|
||||
auto update_pair_it = updated_bindings.find(write_operation.dstBinding);
|
||||
if (update_pair_it == updated_bindings.end() || update_pair_it->second != write_operation_hash)
|
||||
{
|
||||
write_operations.push_back(write_operation);
|
||||
write_operation_hashes.push_back(write_operation_hash);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Perform the Vulkan call to update the DescriptorSet by executing the write operations
|
||||
if (!write_operations.empty())
|
||||
{
|
||||
vkUpdateDescriptorSets(device.get_handle(),
|
||||
to_u32(write_operations.size()),
|
||||
write_operations.data(),
|
||||
0,
|
||||
nullptr);
|
||||
}
|
||||
|
||||
// Store the bindings from the write operations that were executed by vkUpdateDescriptorSets (and their hash)
|
||||
// to prevent overwriting by future calls to "update()"
|
||||
for (size_t i = 0; i < write_operations.size(); i++)
|
||||
{
|
||||
updated_bindings[write_operations[i].dstBinding] = write_operation_hashes[i];
|
||||
}
|
||||
}
|
||||
|
||||
void DescriptorSet::apply_writes() const
|
||||
{
|
||||
vkUpdateDescriptorSets(device.get_handle(),
|
||||
to_u32(write_descriptor_sets.size()),
|
||||
write_descriptor_sets.data(),
|
||||
0,
|
||||
nullptr);
|
||||
}
|
||||
|
||||
DescriptorSet::DescriptorSet(DescriptorSet &&other) :
|
||||
device{other.device},
|
||||
descriptor_set_layout{other.descriptor_set_layout},
|
||||
descriptor_pool{other.descriptor_pool},
|
||||
buffer_infos{std::move(other.buffer_infos)},
|
||||
image_infos{std::move(other.image_infos)},
|
||||
handle{other.handle},
|
||||
write_descriptor_sets{std::move(other.write_descriptor_sets)},
|
||||
updated_bindings{std::move(other.updated_bindings)}
|
||||
{
|
||||
other.handle = VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
VkDescriptorSet DescriptorSet::get_handle() const
|
||||
{
|
||||
return handle;
|
||||
}
|
||||
|
||||
const DescriptorSetLayout &DescriptorSet::get_layout() const
|
||||
{
|
||||
return descriptor_set_layout;
|
||||
}
|
||||
|
||||
BindingMap<VkDescriptorBufferInfo> &DescriptorSet::get_buffer_infos()
|
||||
{
|
||||
return buffer_infos;
|
||||
}
|
||||
|
||||
BindingMap<VkDescriptorImageInfo> &DescriptorSet::get_image_infos()
|
||||
{
|
||||
return image_infos;
|
||||
}
|
||||
|
||||
} // namespace vkb
|
||||
@@ -0,0 +1,125 @@
|
||||
/* Copyright (c) 2019-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 "common/helpers.h"
|
||||
#include "common/vk_common.h"
|
||||
|
||||
namespace vkb
|
||||
{
|
||||
class DescriptorSetLayout;
|
||||
class DescriptorPool;
|
||||
|
||||
namespace core
|
||||
{
|
||||
template <vkb::BindingType bindingType>
|
||||
class Device;
|
||||
using DeviceC = Device<vkb::BindingType::C>;
|
||||
} // namespace core
|
||||
|
||||
/**
|
||||
* @brief A descriptor set handle allocated from a \ref DescriptorPool.
|
||||
* Destroying the handle has no effect, as the pool manages the lifecycle of its descriptor sets.
|
||||
*
|
||||
* Keeps track of what bindings were written to prevent a double write.
|
||||
*/
|
||||
class DescriptorSet
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief Constructs a descriptor set from buffer infos and image infos
|
||||
* Implicitly calls prepare()
|
||||
* @param device A valid Vulkan device
|
||||
* @param descriptor_set_layout The Vulkan descriptor set layout this descriptor set has
|
||||
* @param descriptor_pool The Vulkan descriptor pool the descriptor set is allocated from
|
||||
* @param buffer_infos The descriptors that describe buffer data
|
||||
* @param image_infos The descriptors that describe image data
|
||||
*/
|
||||
DescriptorSet(vkb::core::DeviceC &device,
|
||||
const DescriptorSetLayout &descriptor_set_layout,
|
||||
DescriptorPool &descriptor_pool,
|
||||
const BindingMap<VkDescriptorBufferInfo> &buffer_infos = {},
|
||||
const BindingMap<VkDescriptorImageInfo> &image_infos = {});
|
||||
|
||||
DescriptorSet(const DescriptorSet &) = delete;
|
||||
|
||||
DescriptorSet(DescriptorSet &&other);
|
||||
|
||||
// The descriptor set handle is managed by the pool, and will be destroyed when the pool is reset
|
||||
~DescriptorSet() = default;
|
||||
|
||||
DescriptorSet &operator=(const DescriptorSet &) = delete;
|
||||
|
||||
DescriptorSet &operator=(DescriptorSet &&) = delete;
|
||||
|
||||
/**
|
||||
* @brief Resets the DescriptorSet state
|
||||
* Optionally prepares a new set of buffer infos and/or image infos
|
||||
* @param new_buffer_infos A map of buffer descriptors and their respective bindings
|
||||
* @param new_image_infos A map of image descriptors and their respective bindings
|
||||
*/
|
||||
void reset(const BindingMap<VkDescriptorBufferInfo> &new_buffer_infos = {},
|
||||
const BindingMap<VkDescriptorImageInfo> &new_image_infos = {});
|
||||
|
||||
/**
|
||||
* @brief Updates the contents of the DescriptorSet by performing the write operations
|
||||
* @param bindings_to_update If empty. we update all bindings. Otherwise, only write the specified bindings if they haven't already been written
|
||||
*/
|
||||
void update(const std::vector<uint32_t> &bindings_to_update = {});
|
||||
|
||||
/**
|
||||
* @brief Applies pending write operations without updating the state
|
||||
*/
|
||||
void apply_writes() const;
|
||||
|
||||
const DescriptorSetLayout &get_layout() const;
|
||||
|
||||
VkDescriptorSet get_handle() const;
|
||||
|
||||
BindingMap<VkDescriptorBufferInfo> &get_buffer_infos();
|
||||
|
||||
BindingMap<VkDescriptorImageInfo> &get_image_infos();
|
||||
|
||||
protected:
|
||||
/**
|
||||
* @brief Prepares the descriptor set to have its contents updated by loading a vector of write operations
|
||||
* Cannot be called twice during the lifetime of a DescriptorSet
|
||||
*/
|
||||
void prepare();
|
||||
|
||||
private:
|
||||
vkb::core::DeviceC &device;
|
||||
|
||||
const DescriptorSetLayout &descriptor_set_layout;
|
||||
|
||||
DescriptorPool &descriptor_pool;
|
||||
|
||||
BindingMap<VkDescriptorBufferInfo> buffer_infos;
|
||||
|
||||
BindingMap<VkDescriptorImageInfo> image_infos;
|
||||
|
||||
VkDescriptorSet handle{VK_NULL_HANDLE};
|
||||
|
||||
// The list of write operations for the descriptor set
|
||||
std::vector<VkWriteDescriptorSet> write_descriptor_sets;
|
||||
|
||||
// The bindings of the write descriptors that have had vkUpdateDescriptorSets since the last call to update().
|
||||
// Each binding number is mapped to a hash of the binding description that it will be updated to.
|
||||
std::unordered_map<uint32_t, size_t> updated_bindings;
|
||||
};
|
||||
} // namespace vkb
|
||||
@@ -0,0 +1,275 @@
|
||||
/* Copyright (c) 2019-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 "descriptor_set_layout.h"
|
||||
|
||||
#include "device.h"
|
||||
#include "physical_device.h"
|
||||
#include "shader_module.h"
|
||||
|
||||
namespace vkb
|
||||
{
|
||||
namespace
|
||||
{
|
||||
inline VkDescriptorType find_descriptor_type(ShaderResourceType resource_type, bool dynamic)
|
||||
{
|
||||
switch (resource_type)
|
||||
{
|
||||
case ShaderResourceType::InputAttachment:
|
||||
return VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT;
|
||||
break;
|
||||
case ShaderResourceType::Image:
|
||||
return VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE;
|
||||
break;
|
||||
case ShaderResourceType::ImageSampler:
|
||||
return VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;
|
||||
break;
|
||||
case ShaderResourceType::ImageStorage:
|
||||
return VK_DESCRIPTOR_TYPE_STORAGE_IMAGE;
|
||||
break;
|
||||
case ShaderResourceType::Sampler:
|
||||
return VK_DESCRIPTOR_TYPE_SAMPLER;
|
||||
break;
|
||||
case ShaderResourceType::BufferUniform:
|
||||
if (dynamic)
|
||||
{
|
||||
return VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC;
|
||||
}
|
||||
else
|
||||
{
|
||||
return VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER;
|
||||
}
|
||||
break;
|
||||
case ShaderResourceType::BufferStorage:
|
||||
if (dynamic)
|
||||
{
|
||||
return VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC;
|
||||
}
|
||||
else
|
||||
{
|
||||
return VK_DESCRIPTOR_TYPE_STORAGE_BUFFER;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw std::runtime_error("No conversion possible for the shader resource type.");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
inline bool validate_binding(const VkDescriptorSetLayoutBinding &binding, const std::vector<VkDescriptorType> &blacklist)
|
||||
{
|
||||
return !(std::ranges::find_if(blacklist, [binding](const VkDescriptorType &type) { return type == binding.descriptorType; }) != blacklist.end());
|
||||
}
|
||||
|
||||
inline bool validate_flags(const PhysicalDevice &gpu, const std::vector<VkDescriptorSetLayoutBinding> &bindings, const std::vector<VkDescriptorBindingFlagsEXT> &flags)
|
||||
{
|
||||
// Assume bindings are valid if there are no flags
|
||||
if (flags.empty())
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// Binding count has to equal flag count as its a 1:1 mapping
|
||||
if (bindings.size() != flags.size())
|
||||
{
|
||||
LOGE("Binding count has to be equal to flag count.");
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
DescriptorSetLayout::DescriptorSetLayout(vkb::core::DeviceC &device,
|
||||
const uint32_t set_index,
|
||||
const std::vector<ShaderModule *> &shader_modules,
|
||||
const std::vector<ShaderResource> &resource_set) :
|
||||
device{device},
|
||||
set_index{set_index},
|
||||
shader_modules{shader_modules}
|
||||
{
|
||||
// NOTE: `shader_modules` is passed in mainly for hashing their handles in `request_resource`.
|
||||
// This way, different pipelines (with different shaders / shader variants) will get
|
||||
// different descriptor set layouts (incl. appropriate name -> binding lookups)
|
||||
|
||||
for (auto &resource : resource_set)
|
||||
{
|
||||
// Skip shader resources whitout a binding point
|
||||
if (resource.type == ShaderResourceType::Input ||
|
||||
resource.type == ShaderResourceType::Output ||
|
||||
resource.type == ShaderResourceType::PushConstant ||
|
||||
resource.type == ShaderResourceType::SpecializationConstant)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Convert from ShaderResourceType to VkDescriptorType.
|
||||
auto descriptor_type = find_descriptor_type(resource.type, resource.mode == ShaderResourceMode::Dynamic);
|
||||
|
||||
if (resource.mode == ShaderResourceMode::UpdateAfterBind)
|
||||
{
|
||||
binding_flags.push_back(VK_DESCRIPTOR_BINDING_UPDATE_AFTER_BIND_BIT_EXT);
|
||||
}
|
||||
else
|
||||
{
|
||||
// When creating a descriptor set layout, if we give a structure to create_info.pNext, each binding needs to have a binding flag
|
||||
// (pBindings[i] uses the flags in pBindingFlags[i])
|
||||
// Adding 0 ensures the bindings that dont use any flags are mapped correctly.
|
||||
binding_flags.push_back(0);
|
||||
}
|
||||
|
||||
// Convert ShaderResource to VkDescriptorSetLayoutBinding
|
||||
VkDescriptorSetLayoutBinding layout_binding{};
|
||||
|
||||
layout_binding.binding = resource.binding;
|
||||
layout_binding.descriptorCount = resource.array_size;
|
||||
layout_binding.descriptorType = descriptor_type;
|
||||
layout_binding.stageFlags = static_cast<VkShaderStageFlags>(resource.stages);
|
||||
|
||||
bindings.push_back(layout_binding);
|
||||
|
||||
// Store mapping between binding and the binding point
|
||||
bindings_lookup.emplace(resource.binding, layout_binding);
|
||||
|
||||
binding_flags_lookup.emplace(resource.binding, binding_flags.back());
|
||||
|
||||
resources_lookup.emplace(resource.name, resource.binding);
|
||||
}
|
||||
|
||||
VkDescriptorSetLayoutCreateInfo create_info{VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO};
|
||||
create_info.flags = 0;
|
||||
create_info.bindingCount = to_u32(bindings.size());
|
||||
create_info.pBindings = bindings.data();
|
||||
|
||||
// Handle update-after-bind extensions
|
||||
VkDescriptorSetLayoutBindingFlagsCreateInfoEXT binding_flags_create_info{VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_BINDING_FLAGS_CREATE_INFO_EXT};
|
||||
if (std::ranges::find_if(resource_set,
|
||||
[](const ShaderResource &shader_resource) { return shader_resource.mode == ShaderResourceMode::UpdateAfterBind; }) != resource_set.end())
|
||||
{
|
||||
// Spec states you can't have ANY dynamic resources if you have one of the bindings set to update-after-bind
|
||||
if (std::ranges::find_if(resource_set,
|
||||
[](const ShaderResource &shader_resource) { return shader_resource.mode == ShaderResourceMode::Dynamic; }) != resource_set.end())
|
||||
{
|
||||
throw std::runtime_error("Cannot create descriptor set layout, dynamic resources are not allowed if at least one resource is update-after-bind.");
|
||||
}
|
||||
|
||||
if (!validate_flags(device.get_gpu(), bindings, binding_flags))
|
||||
{
|
||||
throw std::runtime_error("Invalid binding, couldn't create descriptor set layout.");
|
||||
}
|
||||
|
||||
binding_flags_create_info.bindingCount = to_u32(binding_flags.size());
|
||||
binding_flags_create_info.pBindingFlags = binding_flags.data();
|
||||
|
||||
create_info.pNext = &binding_flags_create_info;
|
||||
create_info.flags |= std::ranges::find(binding_flags, VK_DESCRIPTOR_BINDING_UPDATE_AFTER_BIND_BIT_EXT) != binding_flags.end() ? VK_DESCRIPTOR_SET_LAYOUT_CREATE_UPDATE_AFTER_BIND_POOL_BIT_EXT : 0;
|
||||
}
|
||||
|
||||
// Create the Vulkan descriptor set layout handle
|
||||
VkResult result = vkCreateDescriptorSetLayout(device.get_handle(), &create_info, nullptr, &handle);
|
||||
|
||||
if (result != VK_SUCCESS)
|
||||
{
|
||||
throw VulkanException{result, "Cannot create DescriptorSetLayout"};
|
||||
}
|
||||
}
|
||||
|
||||
DescriptorSetLayout::DescriptorSetLayout(DescriptorSetLayout &&other) :
|
||||
device{other.device},
|
||||
shader_modules{other.shader_modules},
|
||||
handle{other.handle},
|
||||
set_index{other.set_index},
|
||||
bindings{std::move(other.bindings)},
|
||||
binding_flags{std::move(other.binding_flags)},
|
||||
bindings_lookup{std::move(other.bindings_lookup)},
|
||||
binding_flags_lookup{std::move(other.binding_flags_lookup)},
|
||||
resources_lookup{std::move(other.resources_lookup)}
|
||||
{
|
||||
other.handle = VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
DescriptorSetLayout::~DescriptorSetLayout()
|
||||
{
|
||||
// Destroy descriptor set layout
|
||||
if (handle != VK_NULL_HANDLE)
|
||||
{
|
||||
vkDestroyDescriptorSetLayout(device.get_handle(), handle, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
VkDescriptorSetLayout DescriptorSetLayout::get_handle() const
|
||||
{
|
||||
return handle;
|
||||
}
|
||||
|
||||
const uint32_t DescriptorSetLayout::get_index() const
|
||||
{
|
||||
return set_index;
|
||||
}
|
||||
|
||||
const std::vector<VkDescriptorSetLayoutBinding> &DescriptorSetLayout::get_bindings() const
|
||||
{
|
||||
return bindings;
|
||||
}
|
||||
|
||||
const std::vector<VkDescriptorBindingFlagsEXT> &DescriptorSetLayout::get_binding_flags() const
|
||||
{
|
||||
return binding_flags;
|
||||
}
|
||||
|
||||
std::unique_ptr<VkDescriptorSetLayoutBinding> DescriptorSetLayout::get_layout_binding(uint32_t binding_index) const
|
||||
{
|
||||
auto it = bindings_lookup.find(binding_index);
|
||||
|
||||
if (it == bindings_lookup.end())
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return std::make_unique<VkDescriptorSetLayoutBinding>(it->second);
|
||||
}
|
||||
|
||||
std::unique_ptr<VkDescriptorSetLayoutBinding> DescriptorSetLayout::get_layout_binding(const std::string &name) const
|
||||
{
|
||||
auto it = resources_lookup.find(name);
|
||||
|
||||
if (it == resources_lookup.end())
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return get_layout_binding(it->second);
|
||||
}
|
||||
|
||||
VkDescriptorBindingFlagsEXT DescriptorSetLayout::get_layout_binding_flag(const uint32_t binding_index) const
|
||||
{
|
||||
auto it = binding_flags_lookup.find(binding_index);
|
||||
|
||||
if (it == binding_flags_lookup.end())
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
return it->second;
|
||||
}
|
||||
|
||||
const std::vector<ShaderModule *> &DescriptorSetLayout::get_shader_modules() const
|
||||
{
|
||||
return shader_modules;
|
||||
}
|
||||
|
||||
} // namespace vkb
|
||||
@@ -0,0 +1,101 @@
|
||||
/* Copyright (c) 2019-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 "common/helpers.h"
|
||||
#include "common/vk_common.h"
|
||||
|
||||
namespace vkb
|
||||
{
|
||||
class DescriptorPool;
|
||||
class ShaderModule;
|
||||
|
||||
struct ShaderResource;
|
||||
|
||||
namespace core
|
||||
{
|
||||
template <vkb::BindingType bindingType>
|
||||
class Device;
|
||||
using DeviceC = Device<vkb::BindingType::C>;
|
||||
} // namespace core
|
||||
|
||||
/**
|
||||
* @brief Caches DescriptorSet objects for the shader's set index.
|
||||
* Creates a DescriptorPool to allocate the DescriptorSet objects
|
||||
*/
|
||||
class DescriptorSetLayout
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief Creates a descriptor set layout from a set of resources
|
||||
* @param device A valid Vulkan device
|
||||
* @param set_index The descriptor set index this layout maps to
|
||||
* @param shader_modules The shader modules this set layout will be used for
|
||||
* @param resource_set A grouping of shader resources belonging to the same set
|
||||
*/
|
||||
DescriptorSetLayout(vkb::core::DeviceC &device,
|
||||
const uint32_t set_index,
|
||||
const std::vector<ShaderModule *> &shader_modules,
|
||||
const std::vector<ShaderResource> &resource_set);
|
||||
|
||||
DescriptorSetLayout(const DescriptorSetLayout &) = delete;
|
||||
|
||||
DescriptorSetLayout(DescriptorSetLayout &&other);
|
||||
|
||||
~DescriptorSetLayout();
|
||||
|
||||
DescriptorSetLayout &operator=(const DescriptorSetLayout &) = delete;
|
||||
|
||||
DescriptorSetLayout &operator=(DescriptorSetLayout &&) = delete;
|
||||
|
||||
VkDescriptorSetLayout get_handle() const;
|
||||
|
||||
const uint32_t get_index() const;
|
||||
|
||||
const std::vector<VkDescriptorSetLayoutBinding> &get_bindings() const;
|
||||
|
||||
std::unique_ptr<VkDescriptorSetLayoutBinding> get_layout_binding(const uint32_t binding_index) const;
|
||||
|
||||
std::unique_ptr<VkDescriptorSetLayoutBinding> get_layout_binding(const std::string &name) const;
|
||||
|
||||
const std::vector<VkDescriptorBindingFlagsEXT> &get_binding_flags() const;
|
||||
|
||||
VkDescriptorBindingFlagsEXT get_layout_binding_flag(const uint32_t binding_index) const;
|
||||
|
||||
const std::vector<ShaderModule *> &get_shader_modules() const;
|
||||
|
||||
private:
|
||||
vkb::core::DeviceC &device;
|
||||
|
||||
VkDescriptorSetLayout handle{VK_NULL_HANDLE};
|
||||
|
||||
const uint32_t set_index;
|
||||
|
||||
std::vector<VkDescriptorSetLayoutBinding> bindings;
|
||||
|
||||
std::vector<VkDescriptorBindingFlagsEXT> binding_flags;
|
||||
|
||||
std::unordered_map<uint32_t, VkDescriptorSetLayoutBinding> bindings_lookup;
|
||||
|
||||
std::unordered_map<uint32_t, VkDescriptorBindingFlagsEXT> binding_flags_lookup;
|
||||
|
||||
std::unordered_map<std::string, uint32_t> resources_lookup;
|
||||
|
||||
std::vector<ShaderModule *> shader_modules;
|
||||
};
|
||||
} // namespace vkb
|
||||
@@ -0,0 +1,773 @@
|
||||
/* Copyright (c) 2025, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* 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 "common/hpp_vk_common.h"
|
||||
#include "common/vk_common.h"
|
||||
#include "core/buffer.h"
|
||||
#include "core/hpp_physical_device.h"
|
||||
#include "hpp_debug.h"
|
||||
#include "hpp_fence_pool.h"
|
||||
#include "hpp_queue.h"
|
||||
#include "hpp_resource_cache.h"
|
||||
#include "queue.h"
|
||||
#include <utility>
|
||||
#include <vulkan/vulkan.hpp>
|
||||
|
||||
namespace vkb
|
||||
{
|
||||
class FencePool;
|
||||
class DebugUtils;
|
||||
class PhysicalDevice;
|
||||
class ResourceCache;
|
||||
|
||||
namespace core
|
||||
{
|
||||
template <vkb::BindingType bindingType>
|
||||
class CommandPool;
|
||||
using CommandPoolC = CommandPool<vkb::BindingType::C>;
|
||||
using CommandPoolCpp = CommandPool<vkb::BindingType::Cpp>;
|
||||
|
||||
template <vkb::BindingType bindingType>
|
||||
class Device
|
||||
: public vkb::core::VulkanResource<bindingType, typename std::conditional<bindingType == vkb::BindingType::Cpp, vk::Device, VkDevice>::type>
|
||||
{
|
||||
public:
|
||||
using Bool32Type = typename std::conditional<bindingType == vkb::BindingType::Cpp, vk::Bool32, VkBool32>::type;
|
||||
using BufferCopyType = typename std::conditional<bindingType == vkb::BindingType::Cpp, vk::BufferCopy, VkBufferCopy>::type;
|
||||
using CommandBufferLevelType = typename std::conditional<bindingType == vkb::BindingType::Cpp, vk::CommandBufferLevel, VkCommandBufferLevel>::type;
|
||||
using CommandBufferType = typename std::conditional<bindingType == vkb::BindingType::Cpp, vk::CommandBuffer, VkCommandBuffer>::type;
|
||||
using CommandPoolCreateFlagsType = typename std::conditional<bindingType == vkb::BindingType::Cpp, vk::CommandPoolCreateFlags, VkCommandPoolCreateFlags>::type;
|
||||
using CommandPoolType = typename std::conditional<bindingType == vkb::BindingType::Cpp, vk::CommandPool, VkCommandPool>::type;
|
||||
using DeviceMemoryType = typename std::conditional<bindingType == vkb::BindingType::Cpp, vk::DeviceMemory, VkDeviceMemory>::type;
|
||||
using DeviceType = typename std::conditional<bindingType == vkb::BindingType::Cpp, vk::Device, VkDevice>::type;
|
||||
using Extent2DType = typename std::conditional<bindingType == vkb::BindingType::Cpp, vk::Extent2D, VkExtent2D>::type;
|
||||
using FenceType = typename std::conditional<bindingType == vkb::BindingType::Cpp, vk::Fence, VkFence>::type;
|
||||
using FormatType = typename std::conditional<bindingType == vkb::BindingType::Cpp, vk::Format, VkFormat>::type;
|
||||
using ImageType = typename std::conditional<bindingType == vkb::BindingType::Cpp, vk::Image, VkImage>::type;
|
||||
using ImageUsageFlagsType = typename std::conditional<bindingType == vkb::BindingType::Cpp, vk::ImageUsageFlags, VkImageUsageFlags>::type;
|
||||
using MemoryPropertyFlagsType = typename std::conditional<bindingType == vkb::BindingType::Cpp, vk::MemoryPropertyFlags, VkMemoryPropertyFlags>::type;
|
||||
using QueueFamilyPropertiesType = typename std::conditional<bindingType == vkb::BindingType::Cpp, vk::QueueFamilyProperties, VkQueueFamilyProperties>::type;
|
||||
using QueueFlagBitsType = typename std::conditional<bindingType == vkb::BindingType::Cpp, vk::QueueFlagBits, VkQueueFlagBits>::type;
|
||||
using QueueFlagsType = typename std::conditional<bindingType == vkb::BindingType::Cpp, vk::QueueFlags, VkQueueFlags>::type;
|
||||
using QueueType = typename std::conditional<bindingType == vkb::BindingType::Cpp, vk::Queue, VkQueue>::type;
|
||||
using ResultType = typename std::conditional<bindingType == vkb::BindingType::Cpp, vk::Result, VkResult>::type;
|
||||
using SemaphoreType = typename std::conditional<bindingType == vkb::BindingType::Cpp, vk::Semaphore, VkSemaphore>::type;
|
||||
using SurfaceType = typename std::conditional<bindingType == vkb::BindingType::Cpp, vk::SurfaceKHR, VkSurfaceKHR>::type;
|
||||
|
||||
using DebugUtilsType = typename std::conditional<bindingType == vkb::BindingType::Cpp, vkb::core::HPPDebugUtils, vkb::DebugUtils>::type;
|
||||
using FencePoolType = typename std::conditional<bindingType == vkb::BindingType::Cpp, vkb::HPPFencePool, vkb::FencePool>::type;
|
||||
using PhysicalDeviceType = typename std::conditional<bindingType == vkb::BindingType::Cpp, vkb::core::HPPPhysicalDevice, vkb::PhysicalDevice>::type;
|
||||
using CoreQueueType = typename std::conditional<bindingType == vkb::BindingType::Cpp, vkb::core::HPPQueue, vkb::Queue>::type;
|
||||
using ResourceCacheType = typename std::conditional<bindingType == vkb::BindingType::Cpp, vkb::HPPResourceCache, vkb::ResourceCache>::type;
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief Device constructor
|
||||
* @param gpu A valid Vulkan physical device and the requested gpu features
|
||||
* @param surface The surface
|
||||
* @param debug_utils The debug utils to be associated to this device
|
||||
* @param requested_extensions (Optional) List of required device extensions and whether support is optional or not
|
||||
*/
|
||||
Device(PhysicalDeviceType &gpu,
|
||||
SurfaceType surface,
|
||||
std::unique_ptr<DebugUtilsType> &&debug_utils,
|
||||
std::unordered_map<const char *, bool> const &requested_extensions = {});
|
||||
|
||||
/**
|
||||
* @brief Device constructor
|
||||
* @param gpu A valid Vulkan physical device and the requested gpu features
|
||||
* @param vulkan_device A valid Vulkan device
|
||||
* @param surface The surface
|
||||
*/
|
||||
Device(PhysicalDeviceType &gpu, DeviceType &vulkan_device, SurfaceType surface);
|
||||
|
||||
Device(const Device &) = delete;
|
||||
Device(Device &&) = delete;
|
||||
~Device();
|
||||
|
||||
Device &operator=(const Device &) = delete;
|
||||
Device &operator=(Device &&) = delete;
|
||||
|
||||
void add_queue(size_t global_index, uint32_t family_index, QueueFamilyPropertiesType const &properties, Bool32Type can_present);
|
||||
void copy_buffer(
|
||||
vkb::core::Buffer<bindingType> const &src, vkb::core::Buffer<bindingType> &dst, QueueType queue, BufferCopyType const *copy_region = nullptr);
|
||||
CommandBufferType create_command_buffer(CommandBufferLevelType level, bool begin = false) const;
|
||||
CommandPoolType create_command_pool(uint32_t queue_index, CommandPoolCreateFlagsType flags = 0);
|
||||
std::pair<ImageType, DeviceMemoryType> create_image(
|
||||
FormatType format, Extent2DType const &extent, uint32_t mip_levels, ImageUsageFlagsType usage, MemoryPropertyFlagsType properties) const;
|
||||
void create_internal_command_pool();
|
||||
void create_internal_fence_pool();
|
||||
void flush_command_buffer(CommandBufferType command_buffer, QueueType queue, bool free = true, SemaphoreType signal_semaphore = VK_NULL_HANDLE) const;
|
||||
vkb::core::CommandPool<bindingType> &get_command_pool() const;
|
||||
DebugUtilsType const &get_debug_utils() const;
|
||||
FencePoolType &get_fence_pool() const;
|
||||
PhysicalDeviceType const &get_gpu() const;
|
||||
CoreQueueType const &get_queue(uint32_t queue_family_index, uint32_t queue_index) const;
|
||||
CoreQueueType const &get_queue_by_flags(QueueFlagsType queue_flags, uint32_t queue_index) const;
|
||||
CoreQueueType const &get_queue_by_present(uint32_t queue_index) const;
|
||||
ResourceCacheType &get_resource_cache();
|
||||
bool is_extension_enabled(const char *extension) const;
|
||||
bool is_image_format_supported(FormatType format) const;
|
||||
void wait_idle() const;
|
||||
|
||||
private:
|
||||
void copy_buffer_impl(vk::Device device, vkb::core::BufferCpp const &src, vkb::core::BufferCpp &dst, vk::Queue queue, vk::BufferCopy const *copy_region);
|
||||
vk::CommandBuffer create_command_buffer_impl(vk::Device device, vk::CommandBufferLevel level, bool begin) const;
|
||||
std::pair<vk::Image, vk::DeviceMemory> create_image_impl(
|
||||
vk::Device device, vk::Format format, vk::Extent2D const &extent, uint32_t mip_levels, vk::ImageUsageFlags usage, vk::MemoryPropertyFlags properties)
|
||||
const;
|
||||
void flush_command_buffer_impl(
|
||||
vk::Device device, vk::CommandBuffer command_buffer, vk::Queue queue, bool free = true, vk::Semaphore signal_semaphore = nullptr) const;
|
||||
vkb::core::HPPQueue const &get_queue_by_flags_impl(vk::QueueFlags queue_flags, uint32_t queue_index) const;
|
||||
void init(std::unordered_map<const char *, bool> const &requested_extensions);
|
||||
|
||||
private:
|
||||
std::unique_ptr<vkb::core::CommandPoolCpp> command_pool;
|
||||
std::unique_ptr<vkb::core::HPPDebugUtils> debug_utils;
|
||||
std::vector<const char *> enabled_extensions{};
|
||||
std::unique_ptr<vkb::HPPFencePool> fence_pool;
|
||||
vkb::core::HPPPhysicalDevice &gpu;
|
||||
std::vector<std::vector<vkb::core::HPPQueue>> queues;
|
||||
vkb::HPPResourceCache resource_cache;
|
||||
vk::SurfaceKHR surface = nullptr;
|
||||
};
|
||||
|
||||
using DeviceC = Device<vkb::BindingType::C>;
|
||||
using DeviceCpp = Device<vkb::BindingType::Cpp>;
|
||||
|
||||
} // namespace core
|
||||
} // namespace vkb
|
||||
|
||||
#include "core/command_pool.h"
|
||||
|
||||
namespace vkb
|
||||
{
|
||||
namespace core
|
||||
{
|
||||
|
||||
template <>
|
||||
inline Device<vkb::BindingType::Cpp>::Device(vkb::core::HPPPhysicalDevice &gpu,
|
||||
vk::SurfaceKHR surface,
|
||||
std::unique_ptr<vkb::core::HPPDebugUtils> &&debug_utils,
|
||||
std::unordered_map<const char *, bool> const &requested_extensions) :
|
||||
vkb::core::VulkanResourceCpp<vk::Device>{nullptr, this}, debug_utils{std::move(debug_utils)}, gpu{gpu}, resource_cache{*this}, surface(surface)
|
||||
{
|
||||
init(requested_extensions);
|
||||
}
|
||||
|
||||
template <>
|
||||
inline Device<vkb::BindingType::C>::Device(vkb::PhysicalDevice &gpu,
|
||||
VkSurfaceKHR surface,
|
||||
std::unique_ptr<vkb::DebugUtils> &&debug_utils,
|
||||
std::unordered_map<const char *, bool> const &requested_extensions) :
|
||||
vkb::core::VulkanResourceC<VkDevice>{VK_NULL_HANDLE, this}, debug_utils{reinterpret_cast<vkb::core::HPPDebugUtils *>(debug_utils.release())}, gpu{reinterpret_cast<vkb::core::HPPPhysicalDevice &>(gpu)}, resource_cache{*reinterpret_cast<vkb::core::DeviceCpp *>(this)}, surface(static_cast<vk::SurfaceKHR>(surface))
|
||||
{
|
||||
init(requested_extensions);
|
||||
}
|
||||
|
||||
template <>
|
||||
inline Device<vkb::BindingType::Cpp>::Device(vkb::core::HPPPhysicalDevice &gpu, vk::Device &vulkan_device, vk::SurfaceKHR surface) :
|
||||
VulkanResource{vulkan_device}, gpu{gpu}, surface{surface}, resource_cache{*this}
|
||||
{
|
||||
debug_utils = std::make_unique<HPPDummyDebugUtils>();
|
||||
}
|
||||
|
||||
template <>
|
||||
inline Device<vkb::BindingType::C>::Device(vkb::PhysicalDevice &gpu, VkDevice &vulkan_device, VkSurfaceKHR surface) :
|
||||
VulkanResource{vulkan_device}, gpu{reinterpret_cast<vkb::core::HPPPhysicalDevice &>(gpu)}, resource_cache{*reinterpret_cast<vkb::core::DeviceCpp *>(this)}, surface{static_cast<vk::SurfaceKHR>(surface)}
|
||||
{
|
||||
debug_utils = std::make_unique<HPPDummyDebugUtils>();
|
||||
}
|
||||
|
||||
template <vkb::BindingType bindingType>
|
||||
inline Device<bindingType>::~Device()
|
||||
{
|
||||
resource_cache.clear();
|
||||
command_pool.reset();
|
||||
fence_pool.reset();
|
||||
vkb::allocated::shutdown();
|
||||
if (this->get_handle())
|
||||
{
|
||||
if constexpr (bindingType == vkb::BindingType::Cpp)
|
||||
{
|
||||
this->get_handle().destroy();
|
||||
}
|
||||
else
|
||||
{
|
||||
static_cast<vk::Device>(this->get_handle()).destroy();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <vkb::BindingType bindingType>
|
||||
inline void Device<bindingType>::add_queue(size_t global_index, uint32_t family_index, QueueFamilyPropertiesType const &properties, Bool32Type can_present)
|
||||
{
|
||||
if (queues.size() <= global_index)
|
||||
{
|
||||
queues.resize(global_index + 1);
|
||||
}
|
||||
if constexpr (bindingType == vkb::BindingType::Cpp)
|
||||
{
|
||||
queues[global_index].emplace_back(*this, family_index, properties, can_present, 0);
|
||||
}
|
||||
else
|
||||
{
|
||||
queues[global_index].emplace_back(*reinterpret_cast<vkb::core::DeviceCpp *>(this),
|
||||
family_index,
|
||||
reinterpret_cast<vk::QueueFamilyProperties const &>(properties),
|
||||
static_cast<vk::Bool32>(can_present),
|
||||
0);
|
||||
}
|
||||
}
|
||||
|
||||
template <vkb::BindingType bindingType>
|
||||
inline void Device<bindingType>::copy_buffer(vkb::core::Buffer<bindingType> const &src,
|
||||
vkb::core::Buffer<bindingType> &dst,
|
||||
QueueType queue,
|
||||
BufferCopyType const *copy_region)
|
||||
{
|
||||
assert(dst.get_size() <= src.get_size());
|
||||
assert(src.get_handle());
|
||||
|
||||
if constexpr (bindingType == vkb::BindingType::Cpp)
|
||||
{
|
||||
copy_buffer_impl(this->get_handle(), src, dst, queue, copy_region);
|
||||
}
|
||||
else
|
||||
{
|
||||
copy_buffer_impl(static_cast<vk::Device>(this->get_handle()), reinterpret_cast<vkb::core::BufferCpp const &>(src),
|
||||
reinterpret_cast<vkb::core::BufferCpp &>(dst),
|
||||
static_cast<vk::Queue>(queue),
|
||||
reinterpret_cast<vk::BufferCopy const *>(copy_region));
|
||||
}
|
||||
}
|
||||
|
||||
template <vkb::BindingType bindingType>
|
||||
inline typename Device<bindingType>::CommandBufferType Device<bindingType>::create_command_buffer(CommandBufferLevelType level, bool begin) const
|
||||
{
|
||||
if constexpr (bindingType == vkb::BindingType::Cpp)
|
||||
{
|
||||
return create_command_buffer_impl(this->get_handle(), level, begin);
|
||||
}
|
||||
else
|
||||
{
|
||||
return static_cast<VkCommandBuffer>(
|
||||
create_command_buffer_impl(static_cast<vk::Device>(this->get_handle()), static_cast<vk::CommandBufferLevel>(level), begin));
|
||||
}
|
||||
}
|
||||
|
||||
template <vkb::BindingType bindingType>
|
||||
inline typename Device<bindingType>::CommandPoolType Device<bindingType>::create_command_pool(uint32_t queue_index, CommandPoolCreateFlagsType flags)
|
||||
{
|
||||
if constexpr (bindingType == vkb::BindingType::Cpp)
|
||||
{
|
||||
vk::CommandPoolCreateInfo command_pool_info{.flags = flags, .queueFamilyIndex = queue_index};
|
||||
return this->get_handle().createCommandPool(command_pool_info);
|
||||
}
|
||||
else
|
||||
{
|
||||
vk::CommandPoolCreateInfo command_pool_info{.flags = static_cast<vk::CommandPoolCreateFlags>(flags), .queueFamilyIndex = queue_index};
|
||||
return static_cast<vk::Device>(this->get_handle()).createCommandPool(command_pool_info);
|
||||
}
|
||||
}
|
||||
|
||||
template <vkb::BindingType bindingType>
|
||||
inline std::pair<typename Device<bindingType>::ImageType, typename Device<bindingType>::DeviceMemoryType> Device<bindingType>::create_image(
|
||||
FormatType format, Extent2DType const &extent, uint32_t mip_levels, ImageUsageFlagsType usage, MemoryPropertyFlagsType properties) const
|
||||
{
|
||||
if constexpr (bindingType == vkb::BindingType::Cpp)
|
||||
{
|
||||
return create_image_impl(this->get_handle(), format, extent, mip_levels, usage, properties);
|
||||
}
|
||||
else
|
||||
{
|
||||
return static_cast<std::pair<VkImage, VkDeviceMemory>>(create_image_impl(static_cast<vk::Device>(this->get_handle()),
|
||||
static_cast<VkFormat>(format),
|
||||
static_cast<VkExtent2D>(extent),
|
||||
mip_levels,
|
||||
static_cast<VkImageUsageFlags>(usage),
|
||||
static_cast<VkMemoryPropertyFlags>(properties)));
|
||||
}
|
||||
}
|
||||
|
||||
template <vkb::BindingType bindingType>
|
||||
inline void Device<bindingType>::create_internal_command_pool()
|
||||
{
|
||||
uint32_t familyIndex = get_queue_by_flags_impl(vk::QueueFlagBits::eGraphics | vk::QueueFlagBits::eCompute, 0).get_family_index();
|
||||
if constexpr (bindingType == vkb::BindingType::Cpp)
|
||||
{
|
||||
command_pool = std::make_unique<vkb::core::CommandPoolCpp>(*this, familyIndex);
|
||||
}
|
||||
else
|
||||
{
|
||||
command_pool = std::make_unique<vkb::core::CommandPoolCpp>(*reinterpret_cast<vkb::core::DeviceCpp *>(this), familyIndex);
|
||||
}
|
||||
}
|
||||
|
||||
template <vkb::BindingType bindingType>
|
||||
inline void Device<bindingType>::create_internal_fence_pool()
|
||||
{
|
||||
if constexpr (bindingType == vkb::BindingType::Cpp)
|
||||
{
|
||||
fence_pool = std::make_unique<vkb::HPPFencePool>(*this);
|
||||
}
|
||||
else
|
||||
{
|
||||
fence_pool = std::make_unique<vkb::HPPFencePool>(*reinterpret_cast<vkb::core::DeviceCpp *>(this));
|
||||
}
|
||||
}
|
||||
|
||||
template <vkb::BindingType bindingType>
|
||||
inline void Device<bindingType>::flush_command_buffer(CommandBufferType command_buffer, QueueType queue, bool free, SemaphoreType signal_semaphore) const
|
||||
{
|
||||
if constexpr (bindingType == vkb::BindingType::Cpp)
|
||||
{
|
||||
flush_command_buffer_impl(this->get_handle(), command_buffer, queue, free, signal_semaphore);
|
||||
}
|
||||
else
|
||||
{
|
||||
flush_command_buffer_impl(static_cast<vk::Device>(this->get_handle()),
|
||||
static_cast<vk::CommandBuffer>(command_buffer),
|
||||
static_cast<vk::Queue>(queue),
|
||||
free,
|
||||
static_cast<vk::Semaphore>(signal_semaphore));
|
||||
}
|
||||
}
|
||||
|
||||
template <vkb::BindingType bindingType>
|
||||
inline vkb::core::CommandPool<bindingType> &Device<bindingType>::get_command_pool() const
|
||||
{
|
||||
if constexpr (bindingType == vkb::BindingType::Cpp)
|
||||
{
|
||||
return *command_pool;
|
||||
}
|
||||
else
|
||||
{
|
||||
return reinterpret_cast<vkb::core::CommandPoolC &>(*command_pool);
|
||||
}
|
||||
}
|
||||
|
||||
template <vkb::BindingType bindingType>
|
||||
inline typename Device<bindingType>::DebugUtilsType const &Device<bindingType>::get_debug_utils() const
|
||||
{
|
||||
if constexpr (bindingType == vkb::BindingType::Cpp)
|
||||
{
|
||||
return *debug_utils;
|
||||
}
|
||||
else
|
||||
{
|
||||
return reinterpret_cast<vkb::DebugUtils const &>(*debug_utils);
|
||||
}
|
||||
}
|
||||
|
||||
template <vkb::BindingType bindingType>
|
||||
inline typename Device<bindingType>::FencePoolType &Device<bindingType>::get_fence_pool() const
|
||||
{
|
||||
if constexpr (bindingType == vkb::BindingType::Cpp)
|
||||
{
|
||||
return *fence_pool;
|
||||
}
|
||||
else
|
||||
{
|
||||
return reinterpret_cast<vkb::FencePool &>(*fence_pool);
|
||||
}
|
||||
}
|
||||
|
||||
template <vkb::BindingType bindingType>
|
||||
inline typename Device<bindingType>::PhysicalDeviceType const &Device<bindingType>::get_gpu() const
|
||||
{
|
||||
if constexpr (bindingType == vkb::BindingType::Cpp)
|
||||
{
|
||||
return gpu;
|
||||
}
|
||||
else
|
||||
{
|
||||
return reinterpret_cast<vkb::PhysicalDevice const &>(gpu);
|
||||
}
|
||||
}
|
||||
|
||||
template <vkb::BindingType bindingType>
|
||||
inline typename Device<bindingType>::CoreQueueType const &Device<bindingType>::get_queue(uint32_t queue_family_index, uint32_t queue_index) const
|
||||
{
|
||||
assert(queue_family_index < queues.size() && "Queue family index out of bounds");
|
||||
assert(queue_index < queues[queue_family_index].size() && "Queue index out of bounds");
|
||||
|
||||
if constexpr (bindingType == vkb::BindingType::Cpp)
|
||||
{
|
||||
return queues[queue_family_index][queue_index];
|
||||
}
|
||||
else
|
||||
{
|
||||
return reinterpret_cast<vkb::Queue const &>(queues[queue_family_index][queue_index]);
|
||||
}
|
||||
}
|
||||
|
||||
template <vkb::BindingType bindingType>
|
||||
inline typename Device<bindingType>::CoreQueueType const &Device<bindingType>::get_queue_by_flags(QueueFlagsType required_queue_flags,
|
||||
uint32_t queue_index) const
|
||||
{
|
||||
if constexpr (bindingType == vkb::BindingType::Cpp)
|
||||
{
|
||||
return get_queue_by_flags_impl(required_queue_flags, queue_index);
|
||||
}
|
||||
else
|
||||
{
|
||||
return reinterpret_cast<vkb::Queue const &>(get_queue_by_flags_impl(static_cast<vk::QueueFlags>(required_queue_flags), queue_index));
|
||||
}
|
||||
}
|
||||
|
||||
template <vkb::BindingType bindingType>
|
||||
inline typename Device<bindingType>::CoreQueueType const &Device<bindingType>::get_queue_by_present(uint32_t queue_index) const
|
||||
{
|
||||
auto queueIt =
|
||||
std::ranges::find_if(queues,
|
||||
[queue_index](const std::vector<vkb::core::HPPQueue> &queue_family) { return !queue_family.empty() && queue_index < queue_family[0].get_properties().queueCount && queue_family[0].support_present(); });
|
||||
if (queueIt != queues.end())
|
||||
{
|
||||
if constexpr (bindingType == vkb::BindingType::Cpp)
|
||||
{
|
||||
return (*queueIt)[queue_index];
|
||||
}
|
||||
else
|
||||
{
|
||||
return reinterpret_cast<vkb::Queue const &>((*queueIt)[queue_index]);
|
||||
}
|
||||
}
|
||||
|
||||
throw std::runtime_error("Queue not found");
|
||||
}
|
||||
|
||||
template <vkb::BindingType bindingType>
|
||||
inline typename Device<bindingType>::ResourceCacheType &Device<bindingType>::get_resource_cache()
|
||||
{
|
||||
if constexpr (bindingType == vkb::BindingType::Cpp)
|
||||
{
|
||||
return resource_cache;
|
||||
}
|
||||
else
|
||||
{
|
||||
return reinterpret_cast<vkb::ResourceCache &>(resource_cache);
|
||||
}
|
||||
}
|
||||
|
||||
template <vkb::BindingType bindingType>
|
||||
inline bool Device<bindingType>::is_extension_enabled(const char *extension) const
|
||||
{
|
||||
return std::ranges::find_if(enabled_extensions, [extension](const char *enabled_extension) { return strcmp(extension, enabled_extension) == 0; }) !=
|
||||
enabled_extensions.end();
|
||||
}
|
||||
|
||||
template <vkb::BindingType bindingType>
|
||||
inline bool Device<bindingType>::is_image_format_supported(FormatType format) const
|
||||
{
|
||||
// as we want to check for an error (vk::Result::eErrorFormatNotSupported) we use the non-throwing version of getImageFormatProperties here
|
||||
vk::ImageFormatProperties format_properties;
|
||||
return vk::Result::eErrorFormatNotSupported !=
|
||||
gpu.get_handle().getImageFormatProperties(
|
||||
static_cast<vk::Format>(format), vk::ImageType::e2D, vk::ImageTiling::eOptimal, vk::ImageUsageFlagBits::eSampled, {}, &format_properties);
|
||||
}
|
||||
|
||||
template <vkb::BindingType bindingType>
|
||||
inline void Device<bindingType>::wait_idle() const
|
||||
{
|
||||
if constexpr (bindingType == vkb::BindingType::Cpp)
|
||||
{
|
||||
this->get_handle().waitIdle();
|
||||
}
|
||||
else
|
||||
{
|
||||
static_cast<vk::Device>(this->get_handle()).waitIdle();
|
||||
}
|
||||
}
|
||||
|
||||
template <vkb::BindingType bindingType>
|
||||
inline void Device<bindingType>::copy_buffer_impl(
|
||||
vk::Device device, vkb::core::BufferCpp const &src, vkb::core::BufferCpp &dst, vk::Queue queue, vk::BufferCopy const *copy_region)
|
||||
{
|
||||
vk::CommandBuffer command_buffer = create_command_buffer_impl(device, vk::CommandBufferLevel::ePrimary, true);
|
||||
|
||||
vk::BufferCopy buffer_copy;
|
||||
if (copy_region == nullptr)
|
||||
{
|
||||
buffer_copy.size = src.get_size();
|
||||
}
|
||||
else
|
||||
{
|
||||
buffer_copy = *copy_region;
|
||||
}
|
||||
|
||||
command_buffer.copyBuffer(src.get_handle(), dst.get_handle(), buffer_copy);
|
||||
|
||||
flush_command_buffer_impl(device, command_buffer, queue);
|
||||
}
|
||||
|
||||
template <vkb::BindingType bindingType>
|
||||
inline vk::CommandBuffer Device<bindingType>::create_command_buffer_impl(vk::Device device, vk::CommandBufferLevel level, bool begin) const
|
||||
{
|
||||
assert(command_pool && "No command pool exists in the device");
|
||||
|
||||
vk::CommandBufferAllocateInfo command_buffer_allocate_info{.commandPool = command_pool->get_handle(), .level = level, .commandBufferCount = 1};
|
||||
vk::CommandBuffer command_buffer = device.allocateCommandBuffers(command_buffer_allocate_info).front();
|
||||
|
||||
// If requested, also start recording for the new command buffer
|
||||
if (begin)
|
||||
{
|
||||
command_buffer.begin(vk::CommandBufferBeginInfo());
|
||||
}
|
||||
|
||||
return command_buffer;
|
||||
}
|
||||
|
||||
template <vkb::BindingType bindingType>
|
||||
inline std::pair<vk::Image, vk::DeviceMemory> Device<bindingType>::create_image_impl(
|
||||
vk::Device device, vk::Format format, vk::Extent2D const &extent, uint32_t mip_levels, vk::ImageUsageFlags usage, vk::MemoryPropertyFlags properties) const
|
||||
{
|
||||
vk::ImageCreateInfo image_create_info{.imageType = vk::ImageType::e2D,
|
||||
.format = format,
|
||||
.extent = {.width = extent.width, .height = extent.height, .depth = 1},
|
||||
.mipLevels = mip_levels,
|
||||
.arrayLayers = 1,
|
||||
.samples = vk::SampleCountFlagBits::e1,
|
||||
.tiling = vk::ImageTiling::eOptimal,
|
||||
.usage = usage};
|
||||
|
||||
vk::Image image = device.createImage(image_create_info);
|
||||
|
||||
vk::MemoryRequirements memory_requirements = device.getImageMemoryRequirements(image);
|
||||
|
||||
vk::MemoryAllocateInfo memory_allocation{.allocationSize = memory_requirements.size,
|
||||
.memoryTypeIndex = gpu.get_memory_type(memory_requirements.memoryTypeBits, properties)};
|
||||
vk::DeviceMemory memory = device.allocateMemory(memory_allocation);
|
||||
device.bindImageMemory(image, memory, 0);
|
||||
|
||||
return std::make_pair(image, memory);
|
||||
}
|
||||
|
||||
template <vkb::BindingType bindingType>
|
||||
inline void Device<bindingType>::flush_command_buffer_impl(
|
||||
vk::Device device, vk::CommandBuffer command_buffer, vk::Queue queue, bool free, vk::Semaphore signal_semaphore) const
|
||||
{
|
||||
if (command_buffer)
|
||||
{
|
||||
command_buffer.end();
|
||||
|
||||
vk::SubmitInfo submit_info{.commandBufferCount = 1, .pCommandBuffers = &command_buffer};
|
||||
if (signal_semaphore)
|
||||
{
|
||||
submit_info.setSignalSemaphores(signal_semaphore);
|
||||
}
|
||||
|
||||
// Create fence to ensure that the command buffer has finished executing
|
||||
vk::Fence fence = device.createFence({});
|
||||
|
||||
// Submit to the queue
|
||||
queue.submit(submit_info, fence);
|
||||
|
||||
// Wait for the fence to signal that command buffer has finished executing
|
||||
vk::Result result = device.waitForFences(fence, true, DEFAULT_FENCE_TIMEOUT);
|
||||
if (result != vk::Result::eSuccess)
|
||||
{
|
||||
LOGE("Detected Vulkan error: {}", vkb::to_string(result));
|
||||
abort();
|
||||
}
|
||||
|
||||
device.destroyFence(fence);
|
||||
|
||||
if (command_pool && free)
|
||||
{
|
||||
device.freeCommandBuffers(command_pool->get_handle(), command_buffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <vkb::BindingType bindingType>
|
||||
vkb::core::HPPQueue const &Device<bindingType>::get_queue_by_flags_impl(vk::QueueFlags required_queue_flags, uint32_t queue_index) const
|
||||
{
|
||||
auto queueIt =
|
||||
std::ranges::find_if(queues,
|
||||
[required_queue_flags, queue_index](const std::vector<vkb::core::HPPQueue> &queue) {
|
||||
assert(!queue.empty());
|
||||
vk::QueueFamilyProperties const &properties = queue[0].get_properties();
|
||||
return ((properties.queueFlags & required_queue_flags) == required_queue_flags) && (queue_index < properties.queueCount);
|
||||
});
|
||||
|
||||
if (queueIt == queues.end())
|
||||
{
|
||||
throw std::runtime_error("Queue not found");
|
||||
}
|
||||
|
||||
return (*queueIt)[queue_index];
|
||||
}
|
||||
|
||||
template <vkb::BindingType bindingType>
|
||||
inline void Device<bindingType>::init(std::unordered_map<const char *, bool> const &requested_extensions)
|
||||
{
|
||||
LOGI("Selected GPU: {}", *gpu.get_properties().deviceName);
|
||||
|
||||
// Prepare the device queues
|
||||
std::vector<vk::QueueFamilyProperties> queue_family_properties = gpu.get_queue_family_properties();
|
||||
std::vector<vk::DeviceQueueCreateInfo> queue_create_infos;
|
||||
std::vector<std::vector<float>> queue_priorities;
|
||||
|
||||
queue_create_infos.reserve(queue_family_properties.size());
|
||||
queue_priorities.reserve(queue_family_properties.size());
|
||||
for (uint32_t queue_family_index = 0U; queue_family_index < queue_family_properties.size(); ++queue_family_index)
|
||||
{
|
||||
auto const &queue_family_property = queue_family_properties[queue_family_index];
|
||||
|
||||
queue_priorities.push_back(std::vector<float>(queue_family_property.queueCount, 0.5f));
|
||||
if (gpu.has_high_priority_graphics_queue() &&
|
||||
(vkb::common::get_queue_family_index(queue_family_properties, vk::QueueFlagBits::eGraphics) == queue_family_index))
|
||||
{
|
||||
queue_priorities.back()[0] = 1.0f;
|
||||
}
|
||||
|
||||
queue_create_infos.push_back({.queueFamilyIndex = queue_family_index,
|
||||
.queueCount = queue_family_property.queueCount,
|
||||
.pQueuePriorities = queue_priorities[queue_family_index].data()});
|
||||
}
|
||||
|
||||
// Check extensions to enable Vma Dedicated Allocation
|
||||
bool can_get_memory_requirements = gpu.is_extension_supported("VK_KHR_get_memory_requirements2");
|
||||
bool has_dedicated_allocation = gpu.is_extension_supported("VK_KHR_dedicated_allocation");
|
||||
|
||||
if (can_get_memory_requirements && has_dedicated_allocation)
|
||||
{
|
||||
enabled_extensions.push_back("VK_KHR_get_memory_requirements2");
|
||||
enabled_extensions.push_back("VK_KHR_dedicated_allocation");
|
||||
|
||||
LOGI("Dedicated Allocation enabled");
|
||||
}
|
||||
|
||||
// For performance queries, we also use host query reset since queryPool resets cannot
|
||||
// live in the same command buffer as beginQuery
|
||||
if (gpu.is_extension_supported("VK_KHR_performance_query") &&
|
||||
gpu.is_extension_supported("VK_EXT_host_query_reset"))
|
||||
{
|
||||
auto perf_counter_features = gpu.get_extension_features<vk::PhysicalDevicePerformanceQueryFeaturesKHR>();
|
||||
auto host_query_reset_features = gpu.get_extension_features<vk::PhysicalDeviceHostQueryResetFeatures>();
|
||||
|
||||
if (perf_counter_features.performanceCounterQueryPools && host_query_reset_features.hostQueryReset)
|
||||
{
|
||||
gpu.add_extension_features<vk::PhysicalDevicePerformanceQueryFeaturesKHR>().performanceCounterQueryPools = VK_TRUE;
|
||||
gpu.add_extension_features<vk::PhysicalDeviceHostQueryResetFeatures>().hostQueryReset = VK_TRUE;
|
||||
enabled_extensions.push_back("VK_KHR_performance_query");
|
||||
enabled_extensions.push_back("VK_EXT_host_query_reset");
|
||||
LOGI("Performance query enabled");
|
||||
}
|
||||
}
|
||||
|
||||
// Check that extensions are supported before trying to create the device
|
||||
std::vector<const char *> unsupported_extensions{};
|
||||
for (auto &extension : requested_extensions)
|
||||
{
|
||||
if (gpu.is_extension_supported(extension.first))
|
||||
{
|
||||
enabled_extensions.emplace_back(extension.first);
|
||||
}
|
||||
else
|
||||
{
|
||||
unsupported_extensions.emplace_back(extension.first);
|
||||
}
|
||||
}
|
||||
|
||||
if (enabled_extensions.size() > 0)
|
||||
{
|
||||
LOGI("Device supports the following requested extensions:");
|
||||
for (auto &extension : enabled_extensions)
|
||||
{
|
||||
LOGI(" \t{}", extension);
|
||||
}
|
||||
}
|
||||
|
||||
if (unsupported_extensions.size() > 0)
|
||||
{
|
||||
auto error = false;
|
||||
for (auto &extension : unsupported_extensions)
|
||||
{
|
||||
auto extIt = requested_extensions.find(extension);
|
||||
assert(extIt != requested_extensions.end());
|
||||
if (extIt->second)
|
||||
{
|
||||
LOGW("Optional device extension {} not available, some features may be disabled", extension);
|
||||
}
|
||||
else
|
||||
{
|
||||
LOGE("Required device extension {} not available, cannot run", extension);
|
||||
error = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (error)
|
||||
{
|
||||
throw VulkanException(VK_ERROR_EXTENSION_NOT_PRESENT, "Extensions not present");
|
||||
}
|
||||
}
|
||||
|
||||
// Latest requested feature will have the pNext's all set up for device creation.
|
||||
vk::DeviceCreateInfo create_info{.pNext = gpu.get_extension_feature_chain(),
|
||||
.queueCreateInfoCount = static_cast<uint32_t>(queue_create_infos.size()),
|
||||
.pQueueCreateInfos = queue_create_infos.data(),
|
||||
.enabledExtensionCount = static_cast<uint32_t>(enabled_extensions.size()),
|
||||
.ppEnabledExtensionNames = enabled_extensions.data(),
|
||||
.pEnabledFeatures = &gpu.get_requested_features()};
|
||||
|
||||
this->set_handle(gpu.get_handle().createDevice(create_info));
|
||||
|
||||
queues.resize(queue_family_properties.size());
|
||||
|
||||
for (uint32_t queue_family_index = 0U; queue_family_index < queue_family_properties.size(); ++queue_family_index)
|
||||
{
|
||||
const vk::QueueFamilyProperties &queue_family_property = gpu.get_queue_family_properties()[queue_family_index];
|
||||
|
||||
vk::Bool32 present_supported = gpu.get_handle().getSurfaceSupportKHR(queue_family_index, surface);
|
||||
|
||||
for (uint32_t queue_index = 0U; queue_index < queue_family_property.queueCount; ++queue_index)
|
||||
{
|
||||
if constexpr (bindingType == BindingType::Cpp)
|
||||
{
|
||||
queues[queue_family_index].emplace_back(*this, queue_family_index, queue_family_property, present_supported, queue_index);
|
||||
}
|
||||
else
|
||||
{
|
||||
queues[queue_family_index].emplace_back(
|
||||
*reinterpret_cast<vkb::core::DeviceCpp *>(this), queue_family_index, queue_family_property, present_supported, queue_index);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
vkb::allocated::init(*this);
|
||||
|
||||
if constexpr (bindingType == BindingType::Cpp)
|
||||
{
|
||||
command_pool = std::make_unique<vkb::core::CommandPoolCpp>(
|
||||
*this, get_queue_by_flags_impl(vk::QueueFlagBits::eGraphics | vk::QueueFlagBits::eCompute, 0).get_family_index());
|
||||
fence_pool = std::make_unique<vkb::HPPFencePool>(*this);
|
||||
}
|
||||
else
|
||||
{
|
||||
command_pool = std::make_unique<vkb::core::CommandPoolCpp>(
|
||||
*reinterpret_cast<vkb::core::DeviceCpp *>(this),
|
||||
get_queue_by_flags_impl(vk::QueueFlagBits::eGraphics | vk::QueueFlagBits::eCompute, 0).get_family_index());
|
||||
fence_pool = std::make_unique<vkb::HPPFencePool>(*reinterpret_cast<vkb::core::DeviceCpp *>(this));
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace core
|
||||
} // namespace vkb
|
||||
@@ -0,0 +1,77 @@
|
||||
/* Copyright (c) 2019-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 "framebuffer.h"
|
||||
|
||||
#include "device.h"
|
||||
|
||||
namespace vkb
|
||||
{
|
||||
VkFramebuffer Framebuffer::get_handle() const
|
||||
{
|
||||
return handle;
|
||||
}
|
||||
|
||||
const VkExtent2D &Framebuffer::get_extent() const
|
||||
{
|
||||
return extent;
|
||||
}
|
||||
|
||||
Framebuffer::Framebuffer(vkb::core::DeviceC &device, const RenderTarget &render_target, const RenderPass &render_pass) :
|
||||
device{device},
|
||||
extent{render_target.get_extent()}
|
||||
{
|
||||
std::vector<VkImageView> attachments;
|
||||
|
||||
for (auto &view : render_target.get_views())
|
||||
{
|
||||
attachments.emplace_back(view.get_handle());
|
||||
}
|
||||
|
||||
VkFramebufferCreateInfo create_info{VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO};
|
||||
|
||||
create_info.renderPass = render_pass.get_handle();
|
||||
create_info.attachmentCount = to_u32(attachments.size());
|
||||
create_info.pAttachments = attachments.data();
|
||||
create_info.width = extent.width;
|
||||
create_info.height = extent.height;
|
||||
create_info.layers = 1;
|
||||
|
||||
auto result = vkCreateFramebuffer(device.get_handle(), &create_info, nullptr, &handle);
|
||||
|
||||
if (result != VK_SUCCESS)
|
||||
{
|
||||
throw VulkanException{result, "Cannot create Framebuffer"};
|
||||
}
|
||||
}
|
||||
|
||||
Framebuffer::Framebuffer(Framebuffer &&other) :
|
||||
device{other.device},
|
||||
handle{other.handle},
|
||||
extent{other.extent}
|
||||
{
|
||||
other.handle = VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
Framebuffer::~Framebuffer()
|
||||
{
|
||||
if (handle != VK_NULL_HANDLE)
|
||||
{
|
||||
vkDestroyFramebuffer(device.get_handle(), handle, nullptr);
|
||||
}
|
||||
}
|
||||
} // namespace vkb
|
||||
@@ -0,0 +1,61 @@
|
||||
/* Copyright (c) 2019-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 "common/helpers.h"
|
||||
#include "common/vk_common.h"
|
||||
|
||||
namespace vkb
|
||||
{
|
||||
class RenderPass;
|
||||
class RenderTarget;
|
||||
|
||||
namespace core
|
||||
{
|
||||
template <vkb::BindingType bindingType>
|
||||
class Device;
|
||||
using DeviceC = Device<vkb::BindingType::C>;
|
||||
} // namespace core
|
||||
|
||||
class Framebuffer
|
||||
{
|
||||
public:
|
||||
Framebuffer(vkb::core::DeviceC &device, const RenderTarget &render_target, const RenderPass &render_pass);
|
||||
|
||||
Framebuffer(const Framebuffer &) = delete;
|
||||
|
||||
Framebuffer(Framebuffer &&other);
|
||||
|
||||
~Framebuffer();
|
||||
|
||||
Framebuffer &operator=(const Framebuffer &) = delete;
|
||||
|
||||
Framebuffer &operator=(Framebuffer &&) = delete;
|
||||
|
||||
VkFramebuffer get_handle() const;
|
||||
|
||||
const VkExtent2D &get_extent() const;
|
||||
|
||||
private:
|
||||
vkb::core::DeviceC &device;
|
||||
|
||||
VkFramebuffer handle{VK_NULL_HANDLE};
|
||||
|
||||
VkExtent2D extent{};
|
||||
};
|
||||
} // namespace vkb
|
||||
@@ -0,0 +1,118 @@
|
||||
/* Copyright (c) 2023-2025, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* 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 "core/hpp_debug.h"
|
||||
#include "core/command_buffer.h"
|
||||
#include "core/device.h"
|
||||
|
||||
namespace vkb
|
||||
{
|
||||
namespace core
|
||||
{
|
||||
void HPPDebugUtilsExtDebugUtils::set_debug_name(vk::Device device, vk::ObjectType object_type, uint64_t object_handle, const char *name) const
|
||||
{
|
||||
vk::DebugUtilsObjectNameInfoEXT name_info{.objectType = object_type, .objectHandle = object_handle, .pObjectName = name};
|
||||
device.setDebugUtilsObjectNameEXT(name_info);
|
||||
}
|
||||
|
||||
void HPPDebugUtilsExtDebugUtils::set_debug_tag(
|
||||
vk::Device device, vk::ObjectType object_type, uint64_t object_handle, uint64_t tag_name, const void *tag_data, size_t tag_data_size) const
|
||||
{
|
||||
vk::DebugUtilsObjectTagInfoEXT tag_info{.objectType = object_type, .objectHandle = object_handle, .tagName = tag_name, .tagSize = tag_data_size, .pTag = tag_data};
|
||||
device.setDebugUtilsObjectTagEXT(tag_info);
|
||||
}
|
||||
|
||||
void HPPDebugUtilsExtDebugUtils::cmd_begin_label(vk::CommandBuffer command_buffer, const char *name, glm::vec4 const color) const
|
||||
{
|
||||
vk::DebugUtilsLabelEXT label_info{.pLabelName = name, .color = reinterpret_cast<std::array<float, 4> const &>(*&color[0])};
|
||||
command_buffer.beginDebugUtilsLabelEXT(label_info);
|
||||
}
|
||||
|
||||
void HPPDebugUtilsExtDebugUtils::cmd_end_label(vk::CommandBuffer command_buffer) const
|
||||
{
|
||||
command_buffer.endDebugUtilsLabelEXT();
|
||||
}
|
||||
|
||||
void HPPDebugUtilsExtDebugUtils::cmd_insert_label(vk::CommandBuffer command_buffer, const char *name, glm::vec4 const color) const
|
||||
{
|
||||
vk::DebugUtilsLabelEXT label_info{.pLabelName = name, .color = reinterpret_cast<std::array<float, 4> const &>(*&color[0])};
|
||||
command_buffer.insertDebugUtilsLabelEXT(label_info);
|
||||
}
|
||||
|
||||
void HPPDebugMarkerExtDebugUtils::set_debug_name(vk::Device device, vk::ObjectType object_type, uint64_t object_handle, const char *name) const
|
||||
{
|
||||
vk::DebugMarkerObjectNameInfoEXT name_info{.objectType = vk::debugReportObjectType(object_type), .object = object_handle, .pObjectName = name};
|
||||
device.debugMarkerSetObjectNameEXT(name_info);
|
||||
}
|
||||
|
||||
void HPPDebugMarkerExtDebugUtils::set_debug_tag(
|
||||
vk::Device device, vk::ObjectType object_type, uint64_t object_handle, uint64_t tag_name, const void *tag_data, size_t tag_data_size) const
|
||||
{
|
||||
vk::DebugMarkerObjectTagInfoEXT tag_info{
|
||||
.objectType = vk::debugReportObjectType(object_type), .object = object_handle, .tagName = tag_name, .tagSize = tag_data_size, .pTag = tag_data};
|
||||
device.debugMarkerSetObjectTagEXT(tag_info);
|
||||
}
|
||||
|
||||
void HPPDebugMarkerExtDebugUtils::cmd_begin_label(vk::CommandBuffer command_buffer, const char *name, glm::vec4 const color) const
|
||||
{
|
||||
vk::DebugMarkerMarkerInfoEXT marker_info{.pMarkerName = name, .color = reinterpret_cast<std::array<float, 4> const &>(*&color[0])};
|
||||
command_buffer.debugMarkerBeginEXT(marker_info);
|
||||
}
|
||||
|
||||
void HPPDebugMarkerExtDebugUtils::cmd_end_label(vk::CommandBuffer command_buffer) const
|
||||
{
|
||||
command_buffer.debugMarkerEndEXT();
|
||||
}
|
||||
|
||||
void HPPDebugMarkerExtDebugUtils::cmd_insert_label(vk::CommandBuffer command_buffer, const char *name, glm::vec4 const color) const
|
||||
{
|
||||
vk::DebugMarkerMarkerInfoEXT marker_info{.pMarkerName = name, .color = reinterpret_cast<std::array<float, 4> const &>(*&color[0])};
|
||||
command_buffer.debugMarkerInsertEXT(marker_info);
|
||||
}
|
||||
|
||||
HPPScopedDebugLabel::HPPScopedDebugLabel(const HPPDebugUtils &debug_utils,
|
||||
vk::CommandBuffer command_buffer,
|
||||
std::string const &name,
|
||||
glm::vec4 const color) :
|
||||
debug_utils{&debug_utils}, command_buffer{VK_NULL_HANDLE}
|
||||
{
|
||||
if (!name.empty())
|
||||
{
|
||||
assert(command_buffer);
|
||||
this->command_buffer = command_buffer;
|
||||
|
||||
debug_utils.cmd_begin_label(command_buffer, name.c_str(), color);
|
||||
}
|
||||
}
|
||||
|
||||
HPPScopedDebugLabel::HPPScopedDebugLabel(const vkb::core::CommandBufferCpp &command_buffer,
|
||||
std::string const &name,
|
||||
glm::vec4 const color) :
|
||||
HPPScopedDebugLabel{command_buffer.get_device().get_debug_utils(), command_buffer.get_handle(), name, color}
|
||||
{
|
||||
}
|
||||
|
||||
HPPScopedDebugLabel::~HPPScopedDebugLabel()
|
||||
{
|
||||
if (command_buffer)
|
||||
{
|
||||
debug_utils->cmd_end_label(command_buffer);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace core
|
||||
} // namespace vkb
|
||||
@@ -0,0 +1,152 @@
|
||||
/* Copyright (c) 2022-2025, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* 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 "common/vk_common.h"
|
||||
#include <glm/glm.hpp>
|
||||
#include <vulkan/vulkan.hpp>
|
||||
|
||||
namespace vkb
|
||||
{
|
||||
namespace core
|
||||
{
|
||||
template <vkb::BindingType bindingType>
|
||||
class CommandBuffer;
|
||||
using CommandBufferCpp = CommandBuffer<vkb::BindingType::Cpp>;
|
||||
|
||||
/**
|
||||
* @brief An interface over platform-specific debug extensions.
|
||||
*/
|
||||
class HPPDebugUtils
|
||||
{
|
||||
public:
|
||||
virtual ~HPPDebugUtils() = default;
|
||||
|
||||
/**
|
||||
* @brief Sets the debug name for a Vulkan object.
|
||||
*/
|
||||
virtual void set_debug_name(vk::Device device, vk::ObjectType object_type, uint64_t object_handle, const char *name) const = 0;
|
||||
|
||||
/**
|
||||
* @brief Tags the given Vulkan object with some data.
|
||||
*/
|
||||
virtual void set_debug_tag(
|
||||
vk::Device device, vk::ObjectType object_type, uint64_t object_handle, uint64_t tag_name, const void *tag_data, size_t tag_data_size) const = 0;
|
||||
|
||||
/**
|
||||
* @brief Inserts a command to begin a new debug label/marker scope.
|
||||
*/
|
||||
virtual void cmd_begin_label(vk::CommandBuffer command_buffer, const char *name, glm::vec4 const color = {}) const = 0;
|
||||
|
||||
/**
|
||||
* @brief Inserts a command to end the current debug label/marker scope.
|
||||
*/
|
||||
virtual void cmd_end_label(vk::CommandBuffer command_buffer) const = 0;
|
||||
|
||||
/**
|
||||
* @brief Inserts a (non-scoped) debug label/marker in the command buffer.
|
||||
*/
|
||||
virtual void cmd_insert_label(vk::CommandBuffer command_buffer, const char *name, glm::vec4 const color = {}) const = 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief HPPDebugUtils implemented on top of VK_EXT_debug_utils.
|
||||
*/
|
||||
class HPPDebugUtilsExtDebugUtils final : public vkb::core::HPPDebugUtils
|
||||
{
|
||||
public:
|
||||
~HPPDebugUtilsExtDebugUtils() override = default;
|
||||
|
||||
void set_debug_name(vk::Device device, vk::ObjectType object_type, uint64_t object_handle, const char *name) const override;
|
||||
|
||||
void set_debug_tag(
|
||||
vk::Device device, vk::ObjectType object_type, uint64_t object_handle, uint64_t tag_name, const void *tag_data, size_t tag_data_size) const override;
|
||||
|
||||
void cmd_begin_label(vk::CommandBuffer command_buffer, const char *name, glm::vec4 const color) const override;
|
||||
|
||||
void cmd_end_label(vk::CommandBuffer command_buffer) const override;
|
||||
|
||||
void cmd_insert_label(vk::CommandBuffer command_buffer, const char *name, glm::vec4 const color) const override;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief HPPDebugUtils implemented on top of VK_EXT_debug_marker.
|
||||
*/
|
||||
class HPPDebugMarkerExtDebugUtils final : public vkb::core::HPPDebugUtils
|
||||
{
|
||||
public:
|
||||
~HPPDebugMarkerExtDebugUtils() override = default;
|
||||
|
||||
void set_debug_name(vk::Device device, vk::ObjectType object_type, uint64_t object_handle, const char *name) const override;
|
||||
|
||||
void set_debug_tag(
|
||||
vk::Device device, vk::ObjectType object_type, uint64_t object_handle, uint64_t tag_name, const void *tag_data, size_t tag_data_size) const override;
|
||||
|
||||
void cmd_begin_label(vk::CommandBuffer command_buffer, const char *name, glm::vec4 const color) const override;
|
||||
|
||||
void cmd_end_label(vk::CommandBuffer command_buffer) const override;
|
||||
|
||||
void cmd_insert_label(vk::CommandBuffer command_buffer, const char *name, glm::vec4 const color) const override;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief No-op HPPDebugUtils.
|
||||
*/
|
||||
class HPPDummyDebugUtils final : public vkb::core::HPPDebugUtils
|
||||
{
|
||||
public:
|
||||
~HPPDummyDebugUtils() override = default;
|
||||
|
||||
inline void set_debug_name(vk::Device, vk::ObjectType, uint64_t, const char *) const override
|
||||
{}
|
||||
|
||||
inline void set_debug_tag(vk::Device, vk::ObjectType, uint64_t, uint64_t, const void *, size_t) const override
|
||||
{}
|
||||
|
||||
inline void cmd_begin_label(vk::CommandBuffer, const char *, glm::vec4 const) const override
|
||||
{}
|
||||
|
||||
inline void cmd_end_label(vk::CommandBuffer) const override
|
||||
{}
|
||||
|
||||
inline void cmd_insert_label(vk::CommandBuffer, const char *, glm::vec4 const) const override
|
||||
{}
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A RAII debug label.
|
||||
* If any of EXT_debug_utils or EXT_debug_marker is available, this:
|
||||
* - Begins a debug label / marker on construction
|
||||
* - Ends it on destruction
|
||||
*/
|
||||
class HPPScopedDebugLabel final
|
||||
{
|
||||
public:
|
||||
HPPScopedDebugLabel(const vkb::core::HPPDebugUtils &debug_utils, vk::CommandBuffer command_buffer, std::string const &name, glm::vec4 const color = {});
|
||||
|
||||
HPPScopedDebugLabel(const vkb::core::CommandBufferCpp &command_buffer, std::string const &name, glm::vec4 const color = {});
|
||||
|
||||
~HPPScopedDebugLabel();
|
||||
|
||||
private:
|
||||
const vkb::core::HPPDebugUtils *debug_utils;
|
||||
vk::CommandBuffer command_buffer;
|
||||
};
|
||||
|
||||
} // namespace core
|
||||
} // namespace vkb
|
||||
@@ -0,0 +1,48 @@
|
||||
/* Copyright (c) 2023-2025, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* 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 "descriptor_pool.h"
|
||||
#include <core/hpp_descriptor_set_layout.h>
|
||||
|
||||
namespace vkb
|
||||
{
|
||||
namespace core
|
||||
{
|
||||
template <vkb::BindingType bindingType>
|
||||
class Device;
|
||||
using DeviceCpp = Device<vkb::BindingType::Cpp>;
|
||||
using DeviceC = Device<vkb::BindingType::C>;
|
||||
|
||||
/**
|
||||
* @brief facade class around vkb::DescriptorPool, providing a vulkan.hpp-based interface
|
||||
*
|
||||
* See vkb::DescriptorPool for documentation
|
||||
*/
|
||||
class HPPDescriptorPool : private vkb::DescriptorPool
|
||||
{
|
||||
public:
|
||||
using vkb::DescriptorPool::reset;
|
||||
|
||||
HPPDescriptorPool(vkb::core::DeviceCpp &device, const vkb::core::HPPDescriptorSetLayout &descriptor_set_layout, uint32_t pool_size = MAX_SETS_PER_POOL) :
|
||||
vkb::DescriptorPool(
|
||||
reinterpret_cast<vkb::core::DeviceC &>(device), reinterpret_cast<vkb::DescriptorSetLayout const &>(descriptor_set_layout), pool_size)
|
||||
{}
|
||||
};
|
||||
} // namespace core
|
||||
} // namespace vkb
|
||||
@@ -0,0 +1,72 @@
|
||||
/* Copyright (c) 2023-2025, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* 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 "descriptor_set.h"
|
||||
#include <core/hpp_descriptor_pool.h>
|
||||
#include <core/hpp_descriptor_set_layout.h>
|
||||
|
||||
namespace vkb
|
||||
{
|
||||
namespace core
|
||||
{
|
||||
/**
|
||||
* @brief facade class around vkb::DescriptorSet, providing a vulkan.hpp-based interface
|
||||
*
|
||||
* See vkb::DescriptorSet for documentation
|
||||
*/
|
||||
class HPPDescriptorSet : private vkb::DescriptorSet
|
||||
{
|
||||
public:
|
||||
using vkb::DescriptorSet::apply_writes;
|
||||
using vkb::DescriptorSet::update;
|
||||
|
||||
HPPDescriptorSet(vkb::core::DeviceCpp &device,
|
||||
const vkb::core::HPPDescriptorSetLayout &descriptor_set_layout,
|
||||
vkb::core::HPPDescriptorPool &descriptor_pool,
|
||||
const BindingMap<vk::DescriptorBufferInfo> &buffer_infos = {},
|
||||
const BindingMap<vk::DescriptorImageInfo> &image_infos = {}) :
|
||||
vkb::DescriptorSet(reinterpret_cast<vkb::core::DeviceC &>(device),
|
||||
reinterpret_cast<vkb::DescriptorSetLayout const &>(descriptor_set_layout),
|
||||
reinterpret_cast<vkb::DescriptorPool &>(descriptor_pool),
|
||||
reinterpret_cast<BindingMap<VkDescriptorBufferInfo> const &>(buffer_infos),
|
||||
reinterpret_cast<BindingMap<VkDescriptorImageInfo> const &>(image_infos))
|
||||
{}
|
||||
|
||||
BindingMap<vk::DescriptorBufferInfo> &get_buffer_infos()
|
||||
{
|
||||
return reinterpret_cast<BindingMap<vk::DescriptorBufferInfo> &>(vkb::DescriptorSet::get_buffer_infos());
|
||||
}
|
||||
|
||||
vk::DescriptorSet get_handle() const
|
||||
{
|
||||
return static_cast<vk::DescriptorSet>(vkb::DescriptorSet::get_handle());
|
||||
}
|
||||
|
||||
BindingMap<vk::DescriptorImageInfo> &get_image_infos()
|
||||
{
|
||||
return reinterpret_cast<BindingMap<vk::DescriptorImageInfo> &>(vkb::DescriptorSet::get_image_infos());
|
||||
}
|
||||
|
||||
const vkb::core::HPPDescriptorSetLayout &get_layout() const
|
||||
{
|
||||
return reinterpret_cast<vkb::core::HPPDescriptorSetLayout const &>(vkb::DescriptorSet::get_layout());
|
||||
}
|
||||
};
|
||||
} // namespace core
|
||||
} // namespace vkb
|
||||
@@ -0,0 +1,73 @@
|
||||
/* Copyright (c) 2023-2025, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* 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 "core/descriptor_set_layout.h"
|
||||
#include <vulkan/vulkan.hpp>
|
||||
|
||||
namespace vkb
|
||||
{
|
||||
namespace core
|
||||
{
|
||||
template <vkb::BindingType bindingType>
|
||||
class Device;
|
||||
using DeviceCpp = Device<vkb::BindingType::Cpp>;
|
||||
using DeviceC = Device<vkb::BindingType::C>;
|
||||
|
||||
class HPPShaderModule;
|
||||
struct HPPShaderResource;
|
||||
|
||||
/**
|
||||
* @brief facade class around vkb::DescriptorSetLayout, providing a vulkan.hpp-based interface
|
||||
*
|
||||
* See vkb::DescriptorSetLayout for documentation
|
||||
*/
|
||||
class HPPDescriptorSetLayout : private vkb::DescriptorSetLayout
|
||||
{
|
||||
public:
|
||||
using vkb::DescriptorSetLayout::get_index;
|
||||
|
||||
public:
|
||||
HPPDescriptorSetLayout(vkb::core::DeviceCpp &device,
|
||||
const uint32_t set_index,
|
||||
const std::vector<vkb::core::HPPShaderModule *> &shader_modules,
|
||||
const std::vector<vkb::core::HPPShaderResource> &resource_set) :
|
||||
vkb::DescriptorSetLayout(reinterpret_cast<vkb::core::DeviceC &>(device),
|
||||
set_index,
|
||||
reinterpret_cast<std::vector<vkb::ShaderModule *> const &>(shader_modules),
|
||||
reinterpret_cast<std::vector<vkb::ShaderResource> const &>(resource_set))
|
||||
{}
|
||||
|
||||
vk::DescriptorSetLayout get_handle() const
|
||||
{
|
||||
return static_cast<vk::DescriptorSetLayout>(vkb::DescriptorSetLayout::get_handle());
|
||||
}
|
||||
|
||||
std::unique_ptr<vk::DescriptorSetLayoutBinding> get_layout_binding(const uint32_t binding_index) const
|
||||
{
|
||||
return std::unique_ptr<vk::DescriptorSetLayoutBinding>(
|
||||
reinterpret_cast<vk::DescriptorSetLayoutBinding *>(vkb::DescriptorSetLayout::get_layout_binding(binding_index).release()));
|
||||
}
|
||||
|
||||
vk::DescriptorBindingFlagsEXT get_layout_binding_flag(const uint32_t binding_index) const
|
||||
{
|
||||
return static_cast<vk::DescriptorBindingFlagsEXT>(vkb::DescriptorSetLayout::get_layout_binding_flag(binding_index));
|
||||
}
|
||||
};
|
||||
} // namespace core
|
||||
} // namespace vkb
|
||||
@@ -0,0 +1,60 @@
|
||||
/* Copyright (c) 2023-2025, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* 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 "core/framebuffer.h"
|
||||
#include <vulkan/vulkan.hpp>
|
||||
|
||||
namespace vkb
|
||||
{
|
||||
namespace rendering
|
||||
{
|
||||
class HPPRenderTarget;
|
||||
}
|
||||
|
||||
namespace core
|
||||
{
|
||||
class HPPDevice;
|
||||
class HPPRenderPass;
|
||||
|
||||
/**
|
||||
* @brief facade class around vkb::Framebuffer, providing a vulkan.hpp-based interface
|
||||
*
|
||||
* See vkb::Framebuffer for documentation
|
||||
*/
|
||||
class HPPFramebuffer : private vkb::Framebuffer
|
||||
{
|
||||
public:
|
||||
HPPFramebuffer(vkb::core::DeviceCpp &device, const vkb::rendering::HPPRenderTarget &render_target, const vkb::core::HPPRenderPass &render_pass) :
|
||||
vkb::Framebuffer(reinterpret_cast<vkb::core::DeviceC &>(device),
|
||||
reinterpret_cast<vkb::RenderTarget const &>(render_target),
|
||||
reinterpret_cast<vkb::RenderPass const &>(render_pass))
|
||||
{}
|
||||
|
||||
const vk::Extent2D &get_extent() const
|
||||
{
|
||||
return reinterpret_cast<vk::Extent2D const &>(vkb::Framebuffer::get_extent());
|
||||
}
|
||||
|
||||
vk::Framebuffer get_handle() const
|
||||
{
|
||||
return static_cast<vk::Framebuffer>(vkb::Framebuffer::get_handle());
|
||||
}
|
||||
};
|
||||
} // namespace core
|
||||
} // namespace vkb
|
||||
@@ -0,0 +1,166 @@
|
||||
/* Copyright (c) 2021-2025, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* 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 "builder_base.h"
|
||||
#include "core/allocated.h"
|
||||
#include "core/vulkan_resource.h"
|
||||
#include <unordered_set>
|
||||
|
||||
namespace vkb
|
||||
{
|
||||
namespace core
|
||||
{
|
||||
class HPPDevice;
|
||||
class HPPImageView;
|
||||
class HPPImage;
|
||||
using HPPImagePtr = std::unique_ptr<HPPImage>;
|
||||
|
||||
struct HPPImageBuilder : public vkb::allocated::BuilderBaseCpp<HPPImageBuilder, vk::ImageCreateInfo>
|
||||
{
|
||||
private:
|
||||
using Parent = vkb::allocated::BuilderBaseCpp<HPPImageBuilder, vk::ImageCreateInfo>;
|
||||
|
||||
public:
|
||||
HPPImageBuilder(vk::Extent3D const &extent) : // Better reasonable defaults than vk::ImageCreateInfo default ctor
|
||||
Parent(vk::ImageCreateInfo{.imageType = vk::ImageType::e2D, .format = vk::Format::eR8G8B8A8Unorm, .extent = extent, .mipLevels = 1, .arrayLayers = 1})
|
||||
{
|
||||
}
|
||||
|
||||
HPPImageBuilder(vk::Extent2D const &extent) :
|
||||
HPPImageBuilder(vk::Extent3D{extent.width, extent.height, 1})
|
||||
{
|
||||
}
|
||||
|
||||
HPPImageBuilder(uint32_t width, uint32_t height = 1, uint32_t depth = 1) :
|
||||
HPPImageBuilder(vk::Extent3D{width, height, depth})
|
||||
{
|
||||
}
|
||||
|
||||
HPPImageBuilder &with_format(vk::Format format)
|
||||
{
|
||||
create_info.format = format;
|
||||
return *this;
|
||||
}
|
||||
|
||||
HPPImageBuilder &with_image_type(vk::ImageType type)
|
||||
{
|
||||
create_info.imageType = type;
|
||||
return *this;
|
||||
}
|
||||
|
||||
HPPImageBuilder &with_array_layers(uint32_t layers)
|
||||
{
|
||||
create_info.arrayLayers = layers;
|
||||
return *this;
|
||||
}
|
||||
|
||||
HPPImageBuilder &with_mip_levels(uint32_t levels)
|
||||
{
|
||||
create_info.mipLevels = levels;
|
||||
return *this;
|
||||
}
|
||||
|
||||
HPPImageBuilder &with_sample_count(vk::SampleCountFlagBits sample_count)
|
||||
{
|
||||
create_info.samples = sample_count;
|
||||
return *this;
|
||||
}
|
||||
|
||||
HPPImageBuilder &with_tiling(vk::ImageTiling tiling)
|
||||
{
|
||||
create_info.tiling = tiling;
|
||||
return *this;
|
||||
}
|
||||
|
||||
HPPImageBuilder &with_usage(vk::ImageUsageFlags usage)
|
||||
{
|
||||
create_info.usage = usage;
|
||||
return *this;
|
||||
}
|
||||
|
||||
HPPImageBuilder &with_flags(vk::ImageCreateFlags flags)
|
||||
{
|
||||
create_info.flags = flags;
|
||||
return *this;
|
||||
}
|
||||
|
||||
HPPImage build(vkb::core::DeviceCpp &device) const;
|
||||
HPPImagePtr build_unique(vkb::core::DeviceCpp &device) const;
|
||||
};
|
||||
|
||||
class HPPImage : public vkb::allocated::AllocatedCpp<vk::Image>
|
||||
{
|
||||
public:
|
||||
HPPImage(vkb::core::DeviceCpp &device,
|
||||
vk::Image handle,
|
||||
const vk::Extent3D &extent,
|
||||
vk::Format format,
|
||||
vk::ImageUsageFlags image_usage,
|
||||
vk::SampleCountFlagBits sample_count = vk::SampleCountFlagBits::e1);
|
||||
|
||||
//[[deprecated("Use the HPPImageBuilder ctor instead")]]
|
||||
HPPImage(vkb::core::DeviceCpp &device,
|
||||
const vk::Extent3D &extent,
|
||||
vk::Format format,
|
||||
vk::ImageUsageFlags image_usage,
|
||||
VmaMemoryUsage memory_usage = VMA_MEMORY_USAGE_AUTO,
|
||||
vk::SampleCountFlagBits sample_count = vk::SampleCountFlagBits::e1,
|
||||
uint32_t mip_levels = 1,
|
||||
uint32_t array_layers = 1,
|
||||
vk::ImageTiling tiling = vk::ImageTiling::eOptimal,
|
||||
vk::ImageCreateFlags flags = {},
|
||||
uint32_t num_queue_families = 0,
|
||||
const uint32_t *queue_families = nullptr);
|
||||
|
||||
HPPImage(vkb::core::DeviceCpp &device,
|
||||
HPPImageBuilder const &builder);
|
||||
|
||||
HPPImage(const HPPImage &) = delete;
|
||||
|
||||
HPPImage(HPPImage &&other) noexcept;
|
||||
|
||||
~HPPImage();
|
||||
|
||||
HPPImage &operator=(const HPPImage &) = delete;
|
||||
|
||||
HPPImage &operator=(HPPImage &&) = delete;
|
||||
|
||||
/**
|
||||
* @brief Maps vulkan memory to an host visible address
|
||||
* @return Pointer to host visible memory
|
||||
*/
|
||||
uint8_t *map();
|
||||
|
||||
vk::ImageType get_type() const;
|
||||
const vk::Extent3D &get_extent() const;
|
||||
vk::Format get_format() const;
|
||||
vk::SampleCountFlagBits get_sample_count() const;
|
||||
vk::ImageUsageFlags get_usage() const;
|
||||
vk::ImageTiling get_tiling() const;
|
||||
vk::ImageSubresource get_subresource() const;
|
||||
uint32_t get_array_layer_count() const;
|
||||
std::unordered_set<vkb::core::HPPImageView *> &get_views();
|
||||
|
||||
private:
|
||||
vk::ImageCreateInfo create_info;
|
||||
vk::ImageSubresource subresource;
|
||||
std::unordered_set<vkb::core::HPPImageView *> views; /// HPPImage views referring to this image
|
||||
};
|
||||
} // namespace core
|
||||
} // namespace vkb
|
||||
@@ -0,0 +1,184 @@
|
||||
/* Copyright (c) 2022-2025, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* 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 "core/device.h"
|
||||
#include "core/hpp_image.h"
|
||||
#include "core/hpp_image_view.h"
|
||||
|
||||
namespace vkb
|
||||
{
|
||||
namespace
|
||||
{
|
||||
inline vk::ImageType find_image_type(vk::Extent3D const &extent)
|
||||
{
|
||||
uint32_t dim_num = !!extent.width + !!extent.height + (1 < extent.depth);
|
||||
switch (dim_num)
|
||||
{
|
||||
case 1:
|
||||
return vk::ImageType::e1D;
|
||||
case 2:
|
||||
return vk::ImageType::e2D;
|
||||
case 3:
|
||||
return vk::ImageType::e3D;
|
||||
default:
|
||||
throw std::runtime_error("No image type found.");
|
||||
return vk::ImageType();
|
||||
}
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace core
|
||||
{
|
||||
|
||||
HPPImage HPPImageBuilder::build(vkb::core::DeviceCpp &device) const
|
||||
{
|
||||
return HPPImage(device, *this);
|
||||
}
|
||||
|
||||
HPPImagePtr HPPImageBuilder::build_unique(vkb::core::DeviceCpp &device) const
|
||||
{
|
||||
return std::make_unique<HPPImage>(device, *this);
|
||||
}
|
||||
|
||||
HPPImage::HPPImage(vkb::core::DeviceCpp &device,
|
||||
const vk::Extent3D &extent,
|
||||
vk::Format format,
|
||||
vk::ImageUsageFlags image_usage,
|
||||
VmaMemoryUsage memory_usage,
|
||||
vk::SampleCountFlagBits sample_count,
|
||||
const uint32_t mip_levels,
|
||||
const uint32_t array_layers,
|
||||
vk::ImageTiling tiling,
|
||||
vk::ImageCreateFlags flags,
|
||||
uint32_t num_queue_families,
|
||||
const uint32_t *queue_families) :
|
||||
HPPImage{device,
|
||||
HPPImageBuilder{extent}
|
||||
.with_format(format)
|
||||
.with_mip_levels(mip_levels)
|
||||
.with_array_layers(array_layers)
|
||||
.with_sample_count(sample_count)
|
||||
.with_tiling(tiling)
|
||||
.with_flags(flags)
|
||||
.with_usage(image_usage)
|
||||
.with_queue_families(num_queue_families, queue_families)}
|
||||
{}
|
||||
|
||||
HPPImage::HPPImage(vkb::core::DeviceCpp &device, HPPImageBuilder const &builder) :
|
||||
vkb::allocated::AllocatedCpp<vk::Image>{builder.get_allocation_create_info(), nullptr, &device}, create_info{builder.get_create_info()}
|
||||
{
|
||||
get_handle() = create_image(create_info);
|
||||
subresource.arrayLayer = create_info.arrayLayers;
|
||||
subresource.mipLevel = create_info.mipLevels;
|
||||
if (!builder.get_debug_name().empty())
|
||||
{
|
||||
set_debug_name(builder.get_debug_name());
|
||||
}
|
||||
}
|
||||
|
||||
HPPImage::HPPImage(vkb::core::DeviceCpp &device,
|
||||
vk::Image handle,
|
||||
const vk::Extent3D &extent,
|
||||
vk::Format format,
|
||||
vk::ImageUsageFlags image_usage,
|
||||
vk::SampleCountFlagBits sample_count) :
|
||||
vkb::allocated::AllocatedCpp<vk::Image>{handle, &device}
|
||||
{
|
||||
create_info.samples = sample_count;
|
||||
create_info.format = format;
|
||||
create_info.extent = extent;
|
||||
create_info.imageType = find_image_type(extent);
|
||||
create_info.arrayLayers = 1;
|
||||
create_info.mipLevels = 1;
|
||||
subresource.mipLevel = 1;
|
||||
subresource.arrayLayer = 1;
|
||||
}
|
||||
|
||||
HPPImage::HPPImage(HPPImage &&other) noexcept :
|
||||
vkb::allocated::AllocatedCpp<vk::Image>{std::move(other)},
|
||||
create_info(std::exchange(other.create_info, {})),
|
||||
subresource(std::exchange(other.subresource, {})),
|
||||
views(std::exchange(other.views, {}))
|
||||
{
|
||||
// Update image views references to this image to avoid dangling pointers
|
||||
for (auto &view : views)
|
||||
{
|
||||
view->set_image(*this);
|
||||
}
|
||||
}
|
||||
|
||||
HPPImage::~HPPImage()
|
||||
{
|
||||
destroy_image(get_handle());
|
||||
}
|
||||
|
||||
uint8_t *HPPImage::map()
|
||||
{
|
||||
if (create_info.tiling != vk::ImageTiling::eLinear)
|
||||
{
|
||||
LOGW("Mapping image memory that is not linear");
|
||||
}
|
||||
return vkb::allocated::AllocatedCpp<vk::Image>::map();
|
||||
}
|
||||
|
||||
vk::ImageType HPPImage::get_type() const
|
||||
{
|
||||
return create_info.imageType;
|
||||
}
|
||||
|
||||
const vk::Extent3D &HPPImage::get_extent() const
|
||||
{
|
||||
return create_info.extent;
|
||||
}
|
||||
|
||||
vk::Format HPPImage::get_format() const
|
||||
{
|
||||
return create_info.format;
|
||||
}
|
||||
|
||||
vk::SampleCountFlagBits HPPImage::get_sample_count() const
|
||||
{
|
||||
return create_info.samples;
|
||||
}
|
||||
|
||||
vk::ImageUsageFlags HPPImage::get_usage() const
|
||||
{
|
||||
return create_info.usage;
|
||||
}
|
||||
|
||||
vk::ImageTiling HPPImage::get_tiling() const
|
||||
{
|
||||
return create_info.tiling;
|
||||
}
|
||||
|
||||
vk::ImageSubresource HPPImage::get_subresource() const
|
||||
{
|
||||
return subresource;
|
||||
}
|
||||
|
||||
uint32_t HPPImage::get_array_layer_count() const
|
||||
{
|
||||
return create_info.arrayLayers;
|
||||
}
|
||||
|
||||
std::unordered_set<vkb::core::HPPImageView *> &HPPImage::get_views()
|
||||
{
|
||||
return views;
|
||||
}
|
||||
|
||||
} // namespace core
|
||||
} // namespace vkb
|
||||
@@ -0,0 +1,105 @@
|
||||
/* Copyright (c) 2023-2025, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* 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 "core/hpp_image_view.h"
|
||||
#include "common/hpp_vk_common.h"
|
||||
#include "core/device.h"
|
||||
#include "core/hpp_image.h"
|
||||
#include <vulkan/vulkan_format_traits.hpp>
|
||||
|
||||
namespace vkb
|
||||
{
|
||||
namespace core
|
||||
{
|
||||
HPPImageView::HPPImageView(vkb::core::HPPImage &img,
|
||||
vk::ImageViewType view_type,
|
||||
vk::Format format,
|
||||
uint32_t mip_level,
|
||||
uint32_t array_layer,
|
||||
uint32_t n_mip_levels,
|
||||
uint32_t n_array_layers) :
|
||||
VulkanResource{nullptr, &img.get_device()}, image{&img}, format{format}
|
||||
{
|
||||
if (format == vk::Format::eUndefined)
|
||||
{
|
||||
this->format = format = image->get_format();
|
||||
}
|
||||
|
||||
subresource_range = vk::ImageSubresourceRange{.aspectMask = (std::string(vk::componentName(format, 0)) == "D") ? vk::ImageAspectFlagBits::eDepth : vk::ImageAspectFlagBits::eColor,
|
||||
.baseMipLevel = mip_level,
|
||||
.levelCount = n_mip_levels == 0 ? image->get_subresource().mipLevel : n_mip_levels,
|
||||
.baseArrayLayer = array_layer,
|
||||
.layerCount = n_array_layers == 0 ? image->get_subresource().arrayLayer : n_array_layers};
|
||||
|
||||
vk::ImageViewCreateInfo image_view_create_info{
|
||||
.image = image->get_handle(), .viewType = view_type, .format = format, .subresourceRange = subresource_range};
|
||||
|
||||
set_handle(get_device().get_handle().createImageView(image_view_create_info));
|
||||
|
||||
// Register this image view to its image
|
||||
// in order to be notified when it gets moved
|
||||
image->get_views().emplace(this);
|
||||
}
|
||||
|
||||
HPPImageView::HPPImageView(HPPImageView &&other) :
|
||||
VulkanResource{std::move(other)}, image{other.image}, format{other.format}, subresource_range{other.subresource_range}
|
||||
{
|
||||
// Remove old view from image set and add this new one
|
||||
auto &views = image->get_views();
|
||||
views.erase(&other);
|
||||
views.emplace(this);
|
||||
|
||||
other.set_handle(nullptr);
|
||||
}
|
||||
|
||||
HPPImageView::~HPPImageView()
|
||||
{
|
||||
if (get_handle())
|
||||
{
|
||||
get_device().get_handle().destroyImageView(get_handle());
|
||||
}
|
||||
}
|
||||
|
||||
vk::Format HPPImageView::get_format() const
|
||||
{
|
||||
return format;
|
||||
}
|
||||
|
||||
const vkb::core::HPPImage &HPPImageView::get_image() const
|
||||
{
|
||||
assert(image && "vkb::core::HPPImage view is referring an invalid image");
|
||||
return *image;
|
||||
}
|
||||
|
||||
void HPPImageView::set_image(vkb::core::HPPImage &img)
|
||||
{
|
||||
image = &img;
|
||||
}
|
||||
|
||||
vk::ImageSubresourceLayers HPPImageView::get_subresource_layers() const
|
||||
{
|
||||
return vk::ImageSubresourceLayers{
|
||||
subresource_range.aspectMask, subresource_range.baseMipLevel, subresource_range.baseArrayLayer, subresource_range.layerCount};
|
||||
}
|
||||
|
||||
vk::ImageSubresourceRange HPPImageView::get_subresource_range() const
|
||||
{
|
||||
return subresource_range;
|
||||
}
|
||||
|
||||
} // namespace core
|
||||
} // namespace vkb
|
||||
@@ -0,0 +1,59 @@
|
||||
/* Copyright (c) 2023-2025, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* 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 "core/vulkan_resource.h"
|
||||
|
||||
namespace vkb
|
||||
{
|
||||
namespace core
|
||||
{
|
||||
class HPPImage;
|
||||
|
||||
class HPPImageView : public vkb::core::VulkanResourceCpp<vk::ImageView>
|
||||
{
|
||||
public:
|
||||
HPPImageView(vkb::core::HPPImage &image,
|
||||
vk::ImageViewType view_type,
|
||||
vk::Format format = vk::Format::eUndefined,
|
||||
uint32_t base_mip_level = 0,
|
||||
uint32_t base_array_layer = 0,
|
||||
uint32_t n_mip_levels = 0,
|
||||
uint32_t n_array_layers = 0);
|
||||
|
||||
HPPImageView(HPPImageView &) = delete;
|
||||
HPPImageView(HPPImageView &&other);
|
||||
|
||||
~HPPImageView() override;
|
||||
|
||||
HPPImageView &operator=(const HPPImageView &) = delete;
|
||||
HPPImageView &operator=(HPPImageView &&) = delete;
|
||||
|
||||
vk::Format get_format() const;
|
||||
vkb::core::HPPImage const &get_image() const;
|
||||
void set_image(vkb::core::HPPImage &image);
|
||||
vk::ImageSubresourceLayers get_subresource_layers() const;
|
||||
vk::ImageSubresourceRange get_subresource_range() const;
|
||||
|
||||
private:
|
||||
vkb::core::HPPImage *image = nullptr;
|
||||
vk::Format format;
|
||||
vk::ImageSubresourceRange subresource_range;
|
||||
};
|
||||
} // namespace core
|
||||
} // namespace vkb
|
||||
@@ -0,0 +1,160 @@
|
||||
/* Copyright (c) 2022-2025, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* 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 <core/hpp_physical_device.h>
|
||||
|
||||
#include <core/util/logging.hpp>
|
||||
|
||||
namespace vkb
|
||||
{
|
||||
namespace core
|
||||
{
|
||||
HPPPhysicalDevice::HPPPhysicalDevice(vkb::core::InstanceCpp &instance, vk::PhysicalDevice physical_device) :
|
||||
instance{instance},
|
||||
handle{physical_device}
|
||||
{
|
||||
features = physical_device.getFeatures();
|
||||
properties = physical_device.getProperties();
|
||||
memory_properties = physical_device.getMemoryProperties();
|
||||
|
||||
LOGI("Found GPU: {}", properties.deviceName.data());
|
||||
|
||||
queue_family_properties = physical_device.getQueueFamilyProperties();
|
||||
|
||||
device_extensions = physical_device.enumerateDeviceExtensionProperties();
|
||||
|
||||
// Display supported extensions
|
||||
if (device_extensions.size() > 0)
|
||||
{
|
||||
LOGD("HPPDevice supports the following extensions:");
|
||||
for (auto &extension : device_extensions)
|
||||
{
|
||||
LOGD(" \t{}", extension.extensionName.data());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DriverVersion HPPPhysicalDevice::get_driver_version() const
|
||||
{
|
||||
DriverVersion version;
|
||||
|
||||
vk::PhysicalDeviceProperties const &properties = get_properties();
|
||||
switch (properties.vendorID)
|
||||
{
|
||||
case 0x10DE:
|
||||
// Nvidia
|
||||
version.major = (properties.driverVersion >> 22) & 0x3ff;
|
||||
version.minor = (properties.driverVersion >> 14) & 0x0ff;
|
||||
version.patch = (properties.driverVersion >> 6) & 0x0ff;
|
||||
// Ignoring optional tertiary info in lower 6 bits
|
||||
break;
|
||||
case 0x8086:
|
||||
version.major = (properties.driverVersion >> 14) & 0x3ffff;
|
||||
version.minor = properties.driverVersion & 0x3ffff;
|
||||
break;
|
||||
default:
|
||||
version.major = VK_VERSION_MAJOR(properties.driverVersion);
|
||||
version.minor = VK_VERSION_MINOR(properties.driverVersion);
|
||||
version.patch = VK_VERSION_PATCH(properties.driverVersion);
|
||||
break;
|
||||
}
|
||||
|
||||
return version;
|
||||
}
|
||||
|
||||
void *HPPPhysicalDevice::get_extension_feature_chain() const
|
||||
{
|
||||
return last_requested_extension_feature;
|
||||
}
|
||||
|
||||
bool HPPPhysicalDevice::is_extension_supported(const std::string &requested_extension) const
|
||||
{
|
||||
return std::ranges::find_if(device_extensions,
|
||||
[requested_extension](auto &device_extension) { return std::strcmp(device_extension.extensionName, requested_extension.c_str()) == 0; }) != device_extensions.end();
|
||||
}
|
||||
|
||||
const vk::PhysicalDeviceFeatures &HPPPhysicalDevice::get_features() const
|
||||
{
|
||||
return features;
|
||||
}
|
||||
|
||||
vk::PhysicalDevice HPPPhysicalDevice::get_handle() const
|
||||
{
|
||||
return handle;
|
||||
}
|
||||
|
||||
vkb::core::InstanceCpp &HPPPhysicalDevice::get_instance() const
|
||||
{
|
||||
return instance;
|
||||
}
|
||||
|
||||
const vk::PhysicalDeviceMemoryProperties &HPPPhysicalDevice::get_memory_properties() const
|
||||
{
|
||||
return memory_properties;
|
||||
}
|
||||
|
||||
uint32_t HPPPhysicalDevice::get_memory_type(uint32_t bits, vk::MemoryPropertyFlags properties, vk::Bool32 *memory_type_found) const
|
||||
{
|
||||
for (uint32_t i = 0; i < memory_properties.memoryTypeCount; i++)
|
||||
{
|
||||
if ((bits & 1) == 1)
|
||||
{
|
||||
if ((memory_properties.memoryTypes[i].propertyFlags & properties) == properties)
|
||||
{
|
||||
if (memory_type_found)
|
||||
{
|
||||
*memory_type_found = true;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
}
|
||||
bits >>= 1;
|
||||
}
|
||||
|
||||
if (memory_type_found)
|
||||
{
|
||||
*memory_type_found = false;
|
||||
return ~0;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw std::runtime_error("Could not find a matching memory type");
|
||||
}
|
||||
}
|
||||
|
||||
const vk::PhysicalDeviceProperties &HPPPhysicalDevice::get_properties() const
|
||||
{
|
||||
return properties;
|
||||
}
|
||||
|
||||
const std::vector<vk::QueueFamilyProperties> &HPPPhysicalDevice::get_queue_family_properties() const
|
||||
{
|
||||
return queue_family_properties;
|
||||
}
|
||||
|
||||
const vk::PhysicalDeviceFeatures &HPPPhysicalDevice::get_requested_features() const
|
||||
{
|
||||
return requested_features;
|
||||
}
|
||||
|
||||
vk::PhysicalDeviceFeatures &HPPPhysicalDevice::get_mutable_requested_features()
|
||||
{
|
||||
return requested_features;
|
||||
}
|
||||
|
||||
} // namespace core
|
||||
} // namespace vkb
|
||||
@@ -0,0 +1,250 @@
|
||||
/* Copyright (c) 2023-2025, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* 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 "core/instance.h"
|
||||
#include <map>
|
||||
#include <vulkan/vulkan.hpp>
|
||||
|
||||
namespace vkb
|
||||
{
|
||||
namespace core
|
||||
{
|
||||
struct DriverVersion
|
||||
{
|
||||
uint16_t major;
|
||||
uint16_t minor;
|
||||
uint16_t patch;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A wrapper class for vk::PhysicalDevice
|
||||
*
|
||||
* This class is responsible for handling gpu features, properties, and queue families for the device creation.
|
||||
*/
|
||||
class HPPPhysicalDevice
|
||||
{
|
||||
public:
|
||||
HPPPhysicalDevice(vkb::core::InstanceCpp &instance, vk::PhysicalDevice physical_device);
|
||||
|
||||
HPPPhysicalDevice(const HPPPhysicalDevice &) = delete;
|
||||
|
||||
HPPPhysicalDevice(HPPPhysicalDevice &&) = delete;
|
||||
|
||||
HPPPhysicalDevice &operator=(const HPPPhysicalDevice &) = delete;
|
||||
|
||||
HPPPhysicalDevice &operator=(HPPPhysicalDevice &&) = delete;
|
||||
|
||||
/**
|
||||
* @return The version of the driver
|
||||
*/
|
||||
DriverVersion get_driver_version() const;
|
||||
|
||||
/**
|
||||
* @brief Used at logical device creation to pass the extensions feature chain to vkCreateDevice
|
||||
* @returns A void pointer to the start of the extension linked list
|
||||
*/
|
||||
void *get_extension_feature_chain() const;
|
||||
|
||||
bool is_extension_supported(const std::string &requested_extension) const;
|
||||
|
||||
const vk::PhysicalDeviceFeatures &get_features() const;
|
||||
|
||||
vk::PhysicalDevice get_handle() const;
|
||||
|
||||
vkb::core::InstanceCpp &get_instance() const;
|
||||
|
||||
const vk::PhysicalDeviceMemoryProperties &get_memory_properties() const;
|
||||
|
||||
/**
|
||||
* @brief Checks that a given memory type is supported by the GPU
|
||||
* @param bits The memory requirement type bits
|
||||
* @param properties The memory property to search for
|
||||
* @param memory_type_found True if found, false if not found
|
||||
* @returns The memory type index of the found memory type
|
||||
*/
|
||||
uint32_t get_memory_type(uint32_t bits, vk::MemoryPropertyFlags properties, vk::Bool32 *memory_type_found = nullptr) const;
|
||||
|
||||
const vk::PhysicalDeviceProperties &get_properties() const;
|
||||
|
||||
const std::vector<vk::QueueFamilyProperties> &get_queue_family_properties() const;
|
||||
|
||||
const vk::PhysicalDeviceFeatures &get_requested_features() const;
|
||||
|
||||
vk::PhysicalDeviceFeatures &get_mutable_requested_features();
|
||||
|
||||
/**
|
||||
* @brief Get an extension features struct
|
||||
*
|
||||
* Gets the actual extension features struct with the supported flags set.
|
||||
* The flags you're interested in can be set in a corresponding struct in the structure chain
|
||||
* by calling PhysicalDevice::add_extension_features()
|
||||
* @returns The extension feature struct
|
||||
*/
|
||||
template <typename HPPStructureType>
|
||||
HPPStructureType get_extension_features() const
|
||||
{
|
||||
// We cannot request extension features if the physical device properties 2 instance extension isn't enabled
|
||||
if (!instance.is_enabled(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME))
|
||||
{
|
||||
throw std::runtime_error("Couldn't request feature from device as " + std::string(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME) +
|
||||
" isn't enabled!");
|
||||
}
|
||||
|
||||
// Get the extension feature
|
||||
return handle.getFeatures2KHR<vk::PhysicalDeviceFeatures2KHR, HPPStructureType>().template get<HPPStructureType>();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Add an extension features struct to the structure chain used for device creation
|
||||
*
|
||||
* To have the features enabled, this function must be called before the logical device
|
||||
* is created. To do this request sample specific features inside
|
||||
* VulkanSample::request_gpu_features(vkb::HPPPhysicalDevice &gpu).
|
||||
*
|
||||
* If the feature extension requires you to ask for certain features to be enabled, you can
|
||||
* modify the struct returned by this function, it will propagate the changes to the logical
|
||||
* device.
|
||||
* @returns A reference to the extension feature struct in the structure chain
|
||||
*/
|
||||
template <typename HPPStructureType>
|
||||
HPPStructureType &add_extension_features()
|
||||
{
|
||||
// We cannot request extension features if the physical device properties 2 instance extension isn't enabled
|
||||
if (!instance.is_enabled(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME))
|
||||
{
|
||||
throw std::runtime_error("Couldn't request feature from device as " + std::string(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME) +
|
||||
" isn't enabled!");
|
||||
}
|
||||
|
||||
// Add an (empty) extension features into the map of extension features
|
||||
auto [it, added] = extension_features.insert({HPPStructureType::structureType, std::make_shared<HPPStructureType>()});
|
||||
if (added)
|
||||
{
|
||||
// if it was actually added, also add it to the structure chain
|
||||
if (last_requested_extension_feature)
|
||||
{
|
||||
static_cast<HPPStructureType *>(it->second.get())->pNext = last_requested_extension_feature;
|
||||
}
|
||||
last_requested_extension_feature = it->second.get();
|
||||
}
|
||||
|
||||
return *static_cast<HPPStructureType *>(it->second.get());
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Request an optional features flag
|
||||
*
|
||||
* Calls get_extension_features to get the support of the requested flag. If it's supported,
|
||||
* add_extension_features is called, otherwise a log message is generated.
|
||||
*
|
||||
* @returns true if the requested feature is supported, otherwise false
|
||||
*/
|
||||
template <typename Feature>
|
||||
vk::Bool32 request_optional_feature(vk::Bool32 Feature::*flag, std::string const &featureName, std::string const &flagName)
|
||||
{
|
||||
vk::Bool32 supported = get_extension_features<Feature>().*flag;
|
||||
if (supported)
|
||||
{
|
||||
add_extension_features<Feature>().*flag = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
LOGI("Requested optional feature <{}::{}> is not supported", featureName, flagName);
|
||||
}
|
||||
return supported;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Request a required features flag
|
||||
*
|
||||
* Calls get_extension_features to get the support of the requested flag. If it's supported,
|
||||
* add_extension_features is called, otherwise a runtime_error is thrown.
|
||||
*/
|
||||
template <typename Feature>
|
||||
void request_required_feature(vk::Bool32 Feature::*flag, std::string const &featureName, std::string const &flagName)
|
||||
{
|
||||
if (get_extension_features<Feature>().*flag)
|
||||
{
|
||||
add_extension_features<Feature>().*flag = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw std::runtime_error(std::string("Requested required feature <") + featureName + "::" + flagName + "> is not supported");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets whether or not the first graphics queue should have higher priority than other queues.
|
||||
* Very specific feature which is used by async compute samples.
|
||||
* @param enable If true, present queue will have prio 1.0 and other queues have prio 0.5.
|
||||
* Default state is false, where all queues have 0.5 priority.
|
||||
*/
|
||||
void set_high_priority_graphics_queue_enable(bool enable)
|
||||
{
|
||||
high_priority_graphics_queue = enable;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns high priority graphics queue state.
|
||||
* @return High priority state.
|
||||
*/
|
||||
bool has_high_priority_graphics_queue() const
|
||||
{
|
||||
return high_priority_graphics_queue;
|
||||
}
|
||||
|
||||
private:
|
||||
// Handle to the Vulkan instance
|
||||
vkb::core::InstanceCpp &instance;
|
||||
|
||||
// Handle to the Vulkan physical device
|
||||
vk::PhysicalDevice handle{nullptr};
|
||||
|
||||
// The features that this GPU supports
|
||||
vk::PhysicalDeviceFeatures features;
|
||||
|
||||
// The extensions that this GPU supports
|
||||
std::vector<vk::ExtensionProperties> device_extensions;
|
||||
|
||||
// The GPU properties
|
||||
vk::PhysicalDeviceProperties properties;
|
||||
|
||||
// The GPU memory properties
|
||||
vk::PhysicalDeviceMemoryProperties memory_properties;
|
||||
|
||||
// The GPU queue family properties
|
||||
std::vector<vk::QueueFamilyProperties> queue_family_properties;
|
||||
|
||||
// The features that will be requested to be enabled in the logical device
|
||||
vk::PhysicalDeviceFeatures requested_features;
|
||||
|
||||
// The extension feature pointer
|
||||
void *last_requested_extension_feature{nullptr};
|
||||
|
||||
// Holds the extension feature structures, we use a map to retain an order of requested structures
|
||||
std::map<vk::StructureType, std::shared_ptr<void>> extension_features;
|
||||
|
||||
bool high_priority_graphics_queue{false};
|
||||
};
|
||||
|
||||
#define HPP_REQUEST_OPTIONAL_FEATURE(gpu, Feature, flag) gpu.request_optional_feature<Feature>(&Feature::flag, #Feature, #flag)
|
||||
#define HPP_REQUEST_REQUIRED_FEATURE(gpu, Feature, flag) gpu.request_required_feature<Feature>(&Feature::flag, #Feature, #flag)
|
||||
|
||||
} // namespace core
|
||||
} // namespace vkb
|
||||
@@ -0,0 +1,75 @@
|
||||
/* Copyright (c) 2023-2025, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* 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 "core/pipeline.h"
|
||||
|
||||
namespace vkb
|
||||
{
|
||||
namespace rendering
|
||||
{
|
||||
class HPPPipelineState;
|
||||
}
|
||||
|
||||
namespace core
|
||||
{
|
||||
/**
|
||||
* @brief facade class around vkb::Pipeline, providing a vulkan.hpp-based interface
|
||||
*
|
||||
* See vkb::Pipeline for documentation
|
||||
*/
|
||||
class HPPPipeline : private vkb::Pipeline
|
||||
{
|
||||
public:
|
||||
vk::Pipeline get_handle() const
|
||||
{
|
||||
return static_cast<vk::Pipeline>(vkb::Pipeline::get_handle());
|
||||
}
|
||||
};
|
||||
|
||||
class HPPComputePipeline : private vkb::ComputePipeline
|
||||
{
|
||||
public:
|
||||
HPPComputePipeline(vkb::core::DeviceCpp &device, vk::PipelineCache pipeline_cache, vkb::rendering::HPPPipelineState &pipeline_state) :
|
||||
vkb::ComputePipeline(reinterpret_cast<vkb::core::DeviceC &>(device),
|
||||
static_cast<VkPipelineCache>(pipeline_cache),
|
||||
reinterpret_cast<vkb::PipelineState &>(pipeline_state))
|
||||
{}
|
||||
|
||||
vk::Pipeline get_handle() const
|
||||
{
|
||||
return static_cast<vk::Pipeline>(vkb::ComputePipeline::get_handle());
|
||||
}
|
||||
};
|
||||
|
||||
class HPPGraphicsPipeline : private vkb::GraphicsPipeline
|
||||
{
|
||||
public:
|
||||
HPPGraphicsPipeline(vkb::core::DeviceCpp &device, vk::PipelineCache pipeline_cache, vkb::rendering::HPPPipelineState &pipeline_state) :
|
||||
vkb::GraphicsPipeline(reinterpret_cast<vkb::core::DeviceC &>(device),
|
||||
static_cast<VkPipelineCache>(pipeline_cache),
|
||||
reinterpret_cast<vkb::PipelineState &>(pipeline_state))
|
||||
{}
|
||||
|
||||
vk::Pipeline get_handle() const
|
||||
{
|
||||
return static_cast<vk::Pipeline>(vkb::GraphicsPipeline::get_handle());
|
||||
}
|
||||
};
|
||||
} // namespace core
|
||||
} // namespace vkb
|
||||
@@ -0,0 +1,197 @@
|
||||
/* Copyright (c) 2023-2025, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* 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 "hpp_pipeline_layout.h"
|
||||
#include "core/device.h"
|
||||
|
||||
#include <core/hpp_shader_module.h>
|
||||
|
||||
namespace vkb
|
||||
{
|
||||
namespace core
|
||||
{
|
||||
HPPPipelineLayout::HPPPipelineLayout(vkb::core::DeviceCpp &device, const std::vector<vkb::core::HPPShaderModule *> &shader_modules) :
|
||||
device{device},
|
||||
shader_modules{shader_modules}
|
||||
{
|
||||
// Collect and combine all the shader resources from each of the shader modules
|
||||
// Collate them all into a map that is indexed by the name of the resource
|
||||
for (auto *shader_module : shader_modules)
|
||||
{
|
||||
for (const auto &shader_resource : shader_module->get_resources())
|
||||
{
|
||||
std::string key = shader_resource.name;
|
||||
|
||||
// Since 'Input' and 'Output' resources can have the same name, we modify the key string
|
||||
if (shader_resource.type == vkb::core::HPPShaderResourceType::Input || shader_resource.type == vkb::core::HPPShaderResourceType::Output)
|
||||
{
|
||||
key = std::to_string(static_cast<uint32_t>(shader_resource.stages)) + "_" + key;
|
||||
}
|
||||
|
||||
auto it = shader_resources.find(key);
|
||||
|
||||
if (it != shader_resources.end())
|
||||
{
|
||||
// Append stage flags if resource already exists
|
||||
it->second.stages |= shader_resource.stages;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Create a new entry in the map
|
||||
shader_resources.emplace(key, shader_resource);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Sift through the map of name indexed shader resources
|
||||
// Separate them into their respective sets
|
||||
for (auto &it : shader_resources)
|
||||
{
|
||||
auto &shader_resource = it.second;
|
||||
|
||||
// Find binding by set index in the map.
|
||||
auto it2 = shader_sets.find(shader_resource.set);
|
||||
|
||||
if (it2 != shader_sets.end())
|
||||
{
|
||||
// Add resource to the found set index
|
||||
it2->second.push_back(shader_resource);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Create a new set index and with the first resource
|
||||
shader_sets.emplace(shader_resource.set, std::vector<vkb::core::HPPShaderResource>{shader_resource});
|
||||
}
|
||||
}
|
||||
|
||||
// Create a descriptor set layout for each shader set in the shader modules
|
||||
for (auto &shader_set_it : shader_sets)
|
||||
{
|
||||
descriptor_set_layouts.emplace_back(
|
||||
&device.get_resource_cache().request_descriptor_set_layout(shader_set_it.first, shader_modules, shader_set_it.second));
|
||||
}
|
||||
|
||||
// Collect all the descriptor set layout handles, maintaining set order
|
||||
std::vector<vk::DescriptorSetLayout> descriptor_set_layout_handles;
|
||||
for (auto descriptor_set_layout : descriptor_set_layouts)
|
||||
{
|
||||
descriptor_set_layout_handles.push_back(descriptor_set_layout ? descriptor_set_layout->get_handle() : nullptr);
|
||||
}
|
||||
|
||||
// Collect all the push constant shader resources
|
||||
std::vector<vk::PushConstantRange> push_constant_ranges;
|
||||
for (auto &push_constant_resource : get_resources(vkb::core::HPPShaderResourceType::PushConstant))
|
||||
{
|
||||
push_constant_ranges.push_back({push_constant_resource.stages, push_constant_resource.offset, push_constant_resource.size});
|
||||
}
|
||||
|
||||
vk::PipelineLayoutCreateInfo create_info{.setLayoutCount = static_cast<uint32_t>(descriptor_set_layout_handles.size()),
|
||||
.pSetLayouts = descriptor_set_layout_handles.data(),
|
||||
.pushConstantRangeCount = static_cast<uint32_t>(push_constant_ranges.size()),
|
||||
.pPushConstantRanges = push_constant_ranges.data()};
|
||||
|
||||
// Create the Vulkan pipeline layout handle
|
||||
handle = device.get_handle().createPipelineLayout(create_info);
|
||||
}
|
||||
|
||||
HPPPipelineLayout::HPPPipelineLayout(HPPPipelineLayout &&other) :
|
||||
device{other.device},
|
||||
handle{other.handle},
|
||||
shader_modules{std::move(other.shader_modules)},
|
||||
shader_resources{std::move(other.shader_resources)},
|
||||
shader_sets{std::move(other.shader_sets)},
|
||||
descriptor_set_layouts{std::move(other.descriptor_set_layouts)}
|
||||
{
|
||||
other.handle = nullptr;
|
||||
}
|
||||
|
||||
HPPPipelineLayout::~HPPPipelineLayout()
|
||||
{
|
||||
// Destroy pipeline layout
|
||||
if (handle)
|
||||
{
|
||||
device.get_handle().destroyPipelineLayout(handle);
|
||||
}
|
||||
}
|
||||
|
||||
vkb::core::HPPDescriptorSetLayout const &HPPPipelineLayout::get_descriptor_set_layout(const uint32_t set_index) const
|
||||
{
|
||||
auto it = std::ranges::find_if(descriptor_set_layouts,
|
||||
[&set_index](auto const *descriptor_set_layout) { return descriptor_set_layout->get_index() == set_index; });
|
||||
if (it == descriptor_set_layouts.end())
|
||||
{
|
||||
throw std::runtime_error("Couldn't find descriptor set layout at set index " + to_string(set_index));
|
||||
}
|
||||
return **it;
|
||||
}
|
||||
|
||||
vk::PipelineLayout HPPPipelineLayout::get_handle() const
|
||||
{
|
||||
return handle;
|
||||
}
|
||||
|
||||
vk::ShaderStageFlags HPPPipelineLayout::get_push_constant_range_stage(uint32_t size, uint32_t offset) const
|
||||
{
|
||||
vk::ShaderStageFlags stages;
|
||||
|
||||
for (auto &push_constant_resource : get_resources(vkb::core::HPPShaderResourceType::PushConstant))
|
||||
{
|
||||
if (push_constant_resource.offset <= offset && offset + size <= push_constant_resource.offset + push_constant_resource.size)
|
||||
{
|
||||
stages |= push_constant_resource.stages;
|
||||
}
|
||||
}
|
||||
return stages;
|
||||
}
|
||||
|
||||
std::vector<vkb::core::HPPShaderResource> HPPPipelineLayout::get_resources(const vkb::core::HPPShaderResourceType &type, vk::ShaderStageFlagBits stage) const
|
||||
{
|
||||
std::vector<vkb::core::HPPShaderResource> found_resources;
|
||||
|
||||
for (auto &it : shader_resources)
|
||||
{
|
||||
auto &shader_resource = it.second;
|
||||
|
||||
if (shader_resource.type == type || type == vkb::core::HPPShaderResourceType::All)
|
||||
{
|
||||
if (shader_resource.stages == stage || stage == vk::ShaderStageFlagBits::eAll)
|
||||
{
|
||||
found_resources.push_back(shader_resource);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return found_resources;
|
||||
}
|
||||
|
||||
const std::vector<vkb::core::HPPShaderModule *> &HPPPipelineLayout::get_shader_modules() const
|
||||
{
|
||||
return shader_modules;
|
||||
}
|
||||
|
||||
const std::unordered_map<uint32_t, std::vector<vkb::core::HPPShaderResource>> &HPPPipelineLayout::get_shader_sets() const
|
||||
{
|
||||
return shader_sets;
|
||||
}
|
||||
|
||||
bool HPPPipelineLayout::has_descriptor_set_layout(const uint32_t set_index) const
|
||||
{
|
||||
return set_index < descriptor_set_layouts.size();
|
||||
}
|
||||
|
||||
} // namespace core
|
||||
} // namespace vkb
|
||||
@@ -0,0 +1,64 @@
|
||||
/* Copyright (c) 2023-2025, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* 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 <core/hpp_shader_module.h>
|
||||
#include <vector>
|
||||
#include <vulkan/vulkan.hpp>
|
||||
|
||||
namespace vkb
|
||||
{
|
||||
namespace core
|
||||
{
|
||||
class HPPDevice;
|
||||
class HPPDescriptorSetLayout;
|
||||
|
||||
/**
|
||||
* @brief A wrapper class for vk::HPPPipelineLayout
|
||||
*
|
||||
*/
|
||||
class HPPPipelineLayout
|
||||
{
|
||||
public:
|
||||
HPPPipelineLayout(vkb::core::DeviceCpp &device, const std::vector<vkb::core::HPPShaderModule *> &shader_modules);
|
||||
HPPPipelineLayout(const HPPPipelineLayout &) = delete;
|
||||
HPPPipelineLayout(HPPPipelineLayout &&other);
|
||||
~HPPPipelineLayout();
|
||||
|
||||
HPPPipelineLayout &operator=(const HPPPipelineLayout &) = delete;
|
||||
HPPPipelineLayout &operator=(HPPPipelineLayout &&) = delete;
|
||||
|
||||
vkb::core::HPPDescriptorSetLayout const &get_descriptor_set_layout(const uint32_t set_index) const;
|
||||
vk::PipelineLayout get_handle() const;
|
||||
vk::ShaderStageFlags get_push_constant_range_stage(uint32_t size, uint32_t offset = 0) const;
|
||||
std::vector<vkb::core::HPPShaderResource> get_resources(const vkb::core::HPPShaderResourceType &type = vkb::core::HPPShaderResourceType::All,
|
||||
vk::ShaderStageFlagBits stage = vk::ShaderStageFlagBits::eAll) const;
|
||||
const std::vector<vkb::core::HPPShaderModule *> &get_shader_modules() const;
|
||||
const std::unordered_map<uint32_t, std::vector<vkb::core::HPPShaderResource>> &get_shader_sets() const;
|
||||
bool has_descriptor_set_layout(const uint32_t set_index) const;
|
||||
|
||||
private:
|
||||
vkb::core::DeviceCpp &device;
|
||||
vk::PipelineLayout handle;
|
||||
std::vector<vkb::core::HPPShaderModule *> shader_modules; // The shader modules that this pipeline layout uses
|
||||
std::unordered_map<std::string, vkb::core::HPPShaderResource> shader_resources; // The shader resources that this pipeline layout uses, indexed by their name
|
||||
std::unordered_map<uint32_t, std::vector<vkb::core::HPPShaderResource>> shader_sets; // A map of each set and the resources it owns used by the pipeline layout
|
||||
std::vector<vkb::core::HPPDescriptorSetLayout *> descriptor_set_layouts; // The different descriptor set layouts for this pipeline layout
|
||||
};
|
||||
} // namespace core
|
||||
} // namespace vkb
|
||||
@@ -0,0 +1,40 @@
|
||||
/* Copyright (c) 2023, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* 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 "core/query_pool.h"
|
||||
|
||||
namespace vkb
|
||||
{
|
||||
namespace core
|
||||
{
|
||||
/**
|
||||
* @brief facade class around vkb::QueryPool, providing a vulkan.hpp-based interface
|
||||
*
|
||||
* See vkb::QueryPool for documentation
|
||||
*/
|
||||
class HPPQueryPool : private vkb::QueryPool
|
||||
{
|
||||
public:
|
||||
vk::QueryPool get_handle() const
|
||||
{
|
||||
return static_cast<vk::QueryPool>(vkb::QueryPool::get_handle());
|
||||
}
|
||||
};
|
||||
} // namespace core
|
||||
} // namespace vkb
|
||||
@@ -0,0 +1,88 @@
|
||||
/* Copyright (c) 2022-2025, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* 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 "core/hpp_queue.h"
|
||||
#include "core/command_buffer.h"
|
||||
#include "core/device.h"
|
||||
|
||||
namespace vkb
|
||||
{
|
||||
namespace core
|
||||
{
|
||||
HPPQueue::HPPQueue(vkb::core::DeviceCpp &device, uint32_t family_index, vk::QueueFamilyProperties const &properties, vk::Bool32 can_present, uint32_t index) :
|
||||
device{device}, family_index{family_index}, index{index}, can_present{can_present}, properties{properties}
|
||||
{
|
||||
handle = device.get_handle().getQueue(family_index, index);
|
||||
}
|
||||
|
||||
HPPQueue::HPPQueue(HPPQueue &&other) :
|
||||
device(other.device),
|
||||
handle(std::exchange(other.handle, {})),
|
||||
family_index(std::exchange(other.family_index, {})),
|
||||
index(std::exchange(other.index, 0)),
|
||||
can_present(std::exchange(other.can_present, false)),
|
||||
properties(std::exchange(other.properties, {}))
|
||||
{}
|
||||
|
||||
const vkb::core::DeviceCpp &HPPQueue::get_device() const
|
||||
{
|
||||
return device;
|
||||
}
|
||||
|
||||
vk::Queue HPPQueue::get_handle() const
|
||||
{
|
||||
return handle;
|
||||
}
|
||||
|
||||
uint32_t HPPQueue::get_family_index() const
|
||||
{
|
||||
return family_index;
|
||||
}
|
||||
|
||||
uint32_t HPPQueue::get_index() const
|
||||
{
|
||||
return index;
|
||||
}
|
||||
|
||||
const vk::QueueFamilyProperties &HPPQueue::get_properties() const
|
||||
{
|
||||
return properties;
|
||||
}
|
||||
|
||||
vk::Bool32 HPPQueue::support_present() const
|
||||
{
|
||||
return can_present;
|
||||
}
|
||||
|
||||
void HPPQueue::submit(const vkb::core::CommandBufferCpp &command_buffer, vk::Fence fence) const
|
||||
{
|
||||
vk::CommandBuffer commandBuffer = command_buffer.get_handle();
|
||||
vk::SubmitInfo submit_info{.commandBufferCount = 1, .pCommandBuffers = &commandBuffer};
|
||||
handle.submit(submit_info, fence);
|
||||
}
|
||||
|
||||
vk::Result HPPQueue::present(const vk::PresentInfoKHR &present_info) const
|
||||
{
|
||||
if (!can_present)
|
||||
{
|
||||
return vk::Result::eErrorIncompatibleDisplayKHR;
|
||||
}
|
||||
|
||||
return handle.presentKHR(present_info);
|
||||
}
|
||||
} // namespace core
|
||||
} // namespace vkb
|
||||
@@ -0,0 +1,82 @@
|
||||
/* Copyright (c) 2021-2025, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* 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 "common/vk_common.h"
|
||||
#include <vulkan/vulkan.hpp>
|
||||
|
||||
namespace vkb
|
||||
{
|
||||
namespace core
|
||||
{
|
||||
template <vkb::BindingType bindingType>
|
||||
class Device;
|
||||
using DeviceCpp = Device<vkb::BindingType::Cpp>;
|
||||
|
||||
template <vkb::BindingType bindingType>
|
||||
class CommandBuffer;
|
||||
using CommandBufferCpp = CommandBuffer<vkb::BindingType::Cpp>;
|
||||
|
||||
/**
|
||||
* @brief A wrapper class for vk::Queue
|
||||
*
|
||||
*/
|
||||
class HPPQueue
|
||||
{
|
||||
public:
|
||||
HPPQueue(vkb::core::DeviceCpp &device, uint32_t family_index, vk::QueueFamilyProperties const &properties, vk::Bool32 can_present, uint32_t index);
|
||||
|
||||
HPPQueue(const HPPQueue &) = default;
|
||||
|
||||
HPPQueue(HPPQueue &&other);
|
||||
|
||||
HPPQueue &operator=(const HPPQueue &) = delete;
|
||||
|
||||
HPPQueue &operator=(HPPQueue &&) = delete;
|
||||
|
||||
const vkb::core::DeviceCpp &get_device() const;
|
||||
|
||||
vk::Queue get_handle() const;
|
||||
|
||||
uint32_t get_family_index() const;
|
||||
|
||||
uint32_t get_index() const;
|
||||
|
||||
const vk::QueueFamilyProperties &get_properties() const;
|
||||
|
||||
vk::Bool32 support_present() const;
|
||||
|
||||
void submit(const vkb::core::CommandBufferCpp &command_buffer, vk::Fence fence) const;
|
||||
|
||||
vk::Result present(const vk::PresentInfoKHR &present_infos) const;
|
||||
|
||||
private:
|
||||
vkb::core::DeviceCpp &device;
|
||||
|
||||
vk::Queue handle;
|
||||
|
||||
uint32_t family_index{0};
|
||||
|
||||
uint32_t index{0};
|
||||
|
||||
vk::Bool32 can_present = false;
|
||||
|
||||
vk::QueueFamilyProperties properties{};
|
||||
};
|
||||
} // namespace core
|
||||
} // namespace vkb
|
||||
@@ -0,0 +1,82 @@
|
||||
/* Copyright (c) 2023-2025, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* 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 "core/render_pass.h"
|
||||
#include <vulkan/vulkan.hpp>
|
||||
|
||||
namespace vkb
|
||||
{
|
||||
namespace common
|
||||
{
|
||||
struct HPPLoadStoreInfo;
|
||||
|
||||
}
|
||||
namespace rendering
|
||||
{
|
||||
struct HPPAttachment;
|
||||
}
|
||||
|
||||
namespace core
|
||||
{
|
||||
/**
|
||||
* @brief facade class around vkb::RenderPass, providing a vulkan.hpp-based interface
|
||||
*
|
||||
* See vkb::RenderPass for documentation
|
||||
*/
|
||||
|
||||
struct HPPSubpassInfo
|
||||
{
|
||||
std::vector<uint32_t> input_attachments;
|
||||
std::vector<uint32_t> output_attachments;
|
||||
std::vector<uint32_t> color_resolve_attachments;
|
||||
bool disable_depth_stencil_attachment;
|
||||
uint32_t depth_stencil_resolve_attachment;
|
||||
vk::ResolveModeFlagBits depth_stencil_resolve_mode;
|
||||
std::string debug_name;
|
||||
};
|
||||
|
||||
class HPPRenderPass : private vkb::RenderPass
|
||||
{
|
||||
public:
|
||||
using vkb::RenderPass::get_color_output_count;
|
||||
|
||||
public:
|
||||
HPPRenderPass(vkb::core::DeviceCpp &device,
|
||||
const std::vector<vkb::rendering::HPPAttachment> &attachments,
|
||||
const std::vector<vkb::common::HPPLoadStoreInfo> &load_store_infos,
|
||||
const std::vector<vkb::core::HPPSubpassInfo> &subpasses) :
|
||||
vkb::RenderPass(reinterpret_cast<vkb::core::DeviceC &>(device),
|
||||
reinterpret_cast<std::vector<vkb::Attachment> const &>(attachments),
|
||||
reinterpret_cast<std::vector<vkb::LoadStoreInfo> const &>(load_store_infos),
|
||||
reinterpret_cast<std::vector<vkb::SubpassInfo> const &>(subpasses))
|
||||
{}
|
||||
|
||||
vk::RenderPass get_handle() const
|
||||
{
|
||||
return static_cast<vk::RenderPass>(vkb::RenderPass::get_handle());
|
||||
}
|
||||
|
||||
vk::Extent2D get_render_area_granularity() const
|
||||
{
|
||||
VkExtent2D extent = vkb::RenderPass::get_render_area_granularity();
|
||||
return *reinterpret_cast<vk::Extent2D *>(&extent);
|
||||
}
|
||||
};
|
||||
} // namespace core
|
||||
} // namespace vkb
|
||||
@@ -0,0 +1,42 @@
|
||||
/* Copyright (c) 2023-2025, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* 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 "hpp_sampler.h"
|
||||
#include "core/device.h"
|
||||
|
||||
namespace vkb
|
||||
{
|
||||
namespace core
|
||||
{
|
||||
HPPSampler::HPPSampler(vkb::core::DeviceCpp &device, const vk::SamplerCreateInfo &info) :
|
||||
vkb::core::VulkanResourceCpp<vk::Sampler>{device.get_handle().createSampler(info), &device}
|
||||
{}
|
||||
|
||||
HPPSampler::HPPSampler(HPPSampler &&other) :
|
||||
VulkanResource(std::move(other))
|
||||
{}
|
||||
|
||||
HPPSampler::~HPPSampler()
|
||||
{
|
||||
if (get_handle())
|
||||
{
|
||||
get_device().get_handle().destroySampler(get_handle());
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace core
|
||||
} // namespace vkb
|
||||
@@ -0,0 +1,50 @@
|
||||
/* Copyright (c) 2023-2025, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* 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 "core/vulkan_resource.h"
|
||||
|
||||
namespace vkb
|
||||
{
|
||||
namespace core
|
||||
{
|
||||
/**
|
||||
* @brief Represents a Vulkan Sampler, using Vulkan-Hpp
|
||||
*/
|
||||
class HPPSampler : public vkb::core::VulkanResourceCpp<vk::Sampler>
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief Creates a Vulkan HPPSampler
|
||||
* @param device The device to use
|
||||
* @param info Creation details
|
||||
*/
|
||||
HPPSampler(vkb::core::DeviceCpp &device, const vk::SamplerCreateInfo &info);
|
||||
|
||||
HPPSampler(const HPPSampler &) = delete;
|
||||
|
||||
HPPSampler(HPPSampler &&sampler);
|
||||
|
||||
~HPPSampler();
|
||||
|
||||
HPPSampler &operator=(const HPPSampler &) = delete;
|
||||
|
||||
HPPSampler &operator=(HPPSampler &&) = delete;
|
||||
};
|
||||
} // namespace core
|
||||
} // namespace vkb
|
||||
@@ -0,0 +1,120 @@
|
||||
/* Copyright (c) 2023-2025, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* 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 "core/shader_module.h"
|
||||
#include <vulkan/vulkan.hpp>
|
||||
|
||||
namespace vkb
|
||||
{
|
||||
namespace core
|
||||
{
|
||||
template <vkb::BindingType bindingType>
|
||||
class Device;
|
||||
using DeviceCpp = Device<vkb::BindingType::Cpp>;
|
||||
using DeviceC = Device<vkb::BindingType::C>;
|
||||
|
||||
/**
|
||||
* @brief facade class around vkb::ShaderModule, providing a vulkan.hpp-based interface
|
||||
*
|
||||
* See vkb::ShaderModule for documentation
|
||||
*/
|
||||
|
||||
/// Types of shader resources
|
||||
enum class HPPShaderResourceType
|
||||
{
|
||||
Input,
|
||||
InputAttachment,
|
||||
Output,
|
||||
Image,
|
||||
ImageSampler,
|
||||
ImageStorage,
|
||||
Sampler,
|
||||
BufferUniform,
|
||||
BufferStorage,
|
||||
PushConstant,
|
||||
SpecializationConstant,
|
||||
All
|
||||
};
|
||||
|
||||
/// This determines the type and method of how descriptor set should be created and bound
|
||||
enum class HPPShaderResourceMode
|
||||
{
|
||||
Static,
|
||||
Dynamic,
|
||||
UpdateAfterBind
|
||||
};
|
||||
|
||||
/// Store shader resource data.
|
||||
/// Used by the shader module.
|
||||
struct HPPShaderResource
|
||||
{
|
||||
vk::ShaderStageFlags stages;
|
||||
HPPShaderResourceType type;
|
||||
HPPShaderResourceMode mode;
|
||||
uint32_t set;
|
||||
uint32_t binding;
|
||||
uint32_t location;
|
||||
uint32_t input_attachment_index;
|
||||
uint32_t vec_size;
|
||||
uint32_t columns;
|
||||
uint32_t array_size;
|
||||
uint32_t offset;
|
||||
uint32_t size;
|
||||
uint32_t constant_id;
|
||||
uint32_t qualifiers;
|
||||
std::string name;
|
||||
};
|
||||
|
||||
class HPPShaderSource : private vkb::ShaderSource
|
||||
{
|
||||
public:
|
||||
HPPShaderSource(const std::string &filename) :
|
||||
vkb::ShaderSource(filename)
|
||||
{}
|
||||
};
|
||||
|
||||
class HPPShaderVariant : private vkb::ShaderVariant
|
||||
{};
|
||||
|
||||
class HPPShaderModule : private vkb::ShaderModule
|
||||
{
|
||||
public:
|
||||
using vkb::ShaderModule::get_id;
|
||||
|
||||
public:
|
||||
HPPShaderModule(vkb::core::DeviceCpp &device,
|
||||
vk::ShaderStageFlagBits stage,
|
||||
const vkb::core::HPPShaderSource &glsl_source,
|
||||
const std::string &entry_point,
|
||||
const vkb::core::HPPShaderVariant &shader_variant) :
|
||||
vkb::ShaderModule(reinterpret_cast<vkb::core::DeviceC &>(device),
|
||||
static_cast<VkShaderStageFlagBits>(stage),
|
||||
reinterpret_cast<vkb::ShaderSource const &>(glsl_source),
|
||||
entry_point,
|
||||
reinterpret_cast<vkb::ShaderVariant const &>(shader_variant))
|
||||
{}
|
||||
|
||||
const std::vector<vkb::core::HPPShaderResource> &get_resources() const
|
||||
{
|
||||
return reinterpret_cast<std::vector<vkb::core::HPPShaderResource> const &>(vkb::ShaderModule::get_resources());
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace core
|
||||
} // namespace vkb
|
||||
@@ -0,0 +1,416 @@
|
||||
/* Copyright (c) 2022-2025, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* 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 "core/hpp_swapchain.h"
|
||||
#include "core/device.h"
|
||||
#include "core/hpp_physical_device.h"
|
||||
|
||||
namespace vkb
|
||||
{
|
||||
namespace
|
||||
{
|
||||
template <class T>
|
||||
constexpr const T &clamp(const T &v, const T &lo, const T &hi)
|
||||
{
|
||||
return (v < lo) ? lo : ((hi < v) ? hi : v);
|
||||
}
|
||||
|
||||
vk::Extent2D choose_extent(vk::Extent2D request_extent,
|
||||
const vk::Extent2D &min_image_extent,
|
||||
const vk::Extent2D &max_image_extent,
|
||||
const vk::Extent2D ¤t_extent)
|
||||
{
|
||||
if (current_extent.width == 0xFFFFFFFF)
|
||||
{
|
||||
return request_extent;
|
||||
}
|
||||
|
||||
if (request_extent.width < 1 || request_extent.height < 1)
|
||||
{
|
||||
LOGW("(HPPSwapchain) Image extent ({}, {}) not supported. Selecting ({}, {}).",
|
||||
request_extent.width,
|
||||
request_extent.height,
|
||||
current_extent.width,
|
||||
current_extent.height);
|
||||
return current_extent;
|
||||
}
|
||||
|
||||
request_extent.width = clamp(request_extent.width, min_image_extent.width, max_image_extent.width);
|
||||
request_extent.height = clamp(request_extent.height, min_image_extent.height, max_image_extent.height);
|
||||
|
||||
return request_extent;
|
||||
}
|
||||
|
||||
vk::PresentModeKHR choose_present_mode(vk::PresentModeKHR request_present_mode,
|
||||
const std::vector<vk::PresentModeKHR> &available_present_modes,
|
||||
const std::vector<vk::PresentModeKHR> &present_mode_priority_list)
|
||||
{
|
||||
// Try to find the requested present mode in the available present modes
|
||||
auto const present_mode_it = std::ranges::find(available_present_modes, request_present_mode);
|
||||
if (present_mode_it == available_present_modes.end())
|
||||
{
|
||||
// If the requested present mode isn't found, then try to find a mode from the priority list
|
||||
auto const chosen_present_mode_it =
|
||||
std::ranges::find_if(present_mode_priority_list,
|
||||
[&available_present_modes](vk::PresentModeKHR present_mode) { return std::ranges::find(available_present_modes, present_mode) != available_present_modes.end(); });
|
||||
|
||||
// If nothing found, always default to FIFO
|
||||
vk::PresentModeKHR const chosen_present_mode = (chosen_present_mode_it != present_mode_priority_list.end()) ? *chosen_present_mode_it : vk::PresentModeKHR::eFifo;
|
||||
|
||||
LOGW("(HPPSwapchain) Present mode '{}' not supported. Selecting '{}'.", vk::to_string(request_present_mode), vk::to_string(chosen_present_mode));
|
||||
return chosen_present_mode;
|
||||
}
|
||||
else
|
||||
{
|
||||
LOGI("(HPPSwapchain) Present mode selected: {}", to_string(request_present_mode));
|
||||
return request_present_mode;
|
||||
}
|
||||
}
|
||||
|
||||
vk::SurfaceFormatKHR choose_surface_format(const vk::SurfaceFormatKHR requested_surface_format,
|
||||
const std::vector<vk::SurfaceFormatKHR> &available_surface_formats,
|
||||
const std::vector<vk::SurfaceFormatKHR> &surface_format_priority_list)
|
||||
{
|
||||
// Try to find the requested surface format in the available surface formats
|
||||
auto const surface_format_it = std::ranges::find(available_surface_formats, requested_surface_format);
|
||||
|
||||
// If the requested surface format isn't found, then try to request a format from the priority list
|
||||
if (surface_format_it == available_surface_formats.end())
|
||||
{
|
||||
auto const chosen_surface_format_it =
|
||||
std::ranges::find_if(surface_format_priority_list,
|
||||
[&available_surface_formats](vk::SurfaceFormatKHR surface_format) { return std::ranges::find(available_surface_formats, surface_format) != available_surface_formats.end(); });
|
||||
|
||||
// If nothing found, default to the first available format
|
||||
vk::SurfaceFormatKHR const &chosen_surface_format = (chosen_surface_format_it != surface_format_priority_list.end()) ? *chosen_surface_format_it : available_surface_formats[0];
|
||||
|
||||
LOGW("(HPPSwapchain) Surface format ({}) not supported. Selecting ({}).",
|
||||
vk::to_string(requested_surface_format.format) + ", " + vk::to_string(requested_surface_format.colorSpace),
|
||||
vk::to_string(chosen_surface_format.format) + ", " + vk::to_string(chosen_surface_format.colorSpace));
|
||||
return chosen_surface_format;
|
||||
}
|
||||
else
|
||||
{
|
||||
LOGI("(HPPSwapchain) Surface format selected: {}",
|
||||
vk::to_string(requested_surface_format.format) + ", " + vk::to_string(requested_surface_format.colorSpace));
|
||||
return requested_surface_format;
|
||||
}
|
||||
}
|
||||
|
||||
vk::SurfaceTransformFlagBitsKHR choose_transform(vk::SurfaceTransformFlagBitsKHR request_transform,
|
||||
vk::SurfaceTransformFlagsKHR supported_transform,
|
||||
vk::SurfaceTransformFlagBitsKHR current_transform)
|
||||
{
|
||||
if (request_transform & supported_transform)
|
||||
{
|
||||
return request_transform;
|
||||
}
|
||||
|
||||
LOGW("(HPPSwapchain) Surface transform '{}' not supported. Selecting '{}'.", vk::to_string(request_transform), vk::to_string(current_transform));
|
||||
return current_transform;
|
||||
}
|
||||
|
||||
vk::CompositeAlphaFlagBitsKHR choose_composite_alpha(vk::CompositeAlphaFlagBitsKHR request_composite_alpha,
|
||||
vk::CompositeAlphaFlagsKHR supported_composite_alpha)
|
||||
{
|
||||
if (request_composite_alpha & supported_composite_alpha)
|
||||
{
|
||||
return request_composite_alpha;
|
||||
}
|
||||
|
||||
static const std::vector<vk::CompositeAlphaFlagBitsKHR> composite_alpha_priority_list = {vk::CompositeAlphaFlagBitsKHR::eOpaque,
|
||||
vk::CompositeAlphaFlagBitsKHR::ePreMultiplied,
|
||||
vk::CompositeAlphaFlagBitsKHR::ePostMultiplied,
|
||||
vk::CompositeAlphaFlagBitsKHR::eInherit};
|
||||
|
||||
auto const chosen_composite_alpha_it =
|
||||
std::find_if(composite_alpha_priority_list.begin(),
|
||||
composite_alpha_priority_list.end(),
|
||||
[&supported_composite_alpha](vk::CompositeAlphaFlagBitsKHR composite_alpha) { return composite_alpha & supported_composite_alpha; });
|
||||
if (chosen_composite_alpha_it == composite_alpha_priority_list.end())
|
||||
{
|
||||
throw std::runtime_error("No compatible composite alpha found.");
|
||||
}
|
||||
else
|
||||
{
|
||||
LOGW("(HPPSwapchain) Composite alpha '{}' not supported. Selecting '{}.", vk::to_string(request_composite_alpha), vk::to_string(*chosen_composite_alpha_it));
|
||||
return *chosen_composite_alpha_it;
|
||||
}
|
||||
}
|
||||
|
||||
bool validate_format_feature(vk::ImageUsageFlagBits image_usage, vk::FormatFeatureFlags supported_features)
|
||||
{
|
||||
return (image_usage != vk::ImageUsageFlagBits::eStorage) || (supported_features & vk::FormatFeatureFlagBits::eStorageImage);
|
||||
}
|
||||
|
||||
std::set<vk::ImageUsageFlagBits> choose_image_usage(const std::set<vk::ImageUsageFlagBits> &requested_image_usage_flags,
|
||||
vk::ImageUsageFlags supported_image_usage,
|
||||
vk::FormatFeatureFlags supported_features)
|
||||
{
|
||||
std::set<vk::ImageUsageFlagBits> validated_image_usage_flags;
|
||||
for (auto flag : requested_image_usage_flags)
|
||||
{
|
||||
if ((flag & supported_image_usage) && validate_format_feature(flag, supported_features))
|
||||
{
|
||||
validated_image_usage_flags.insert(flag);
|
||||
}
|
||||
else
|
||||
{
|
||||
LOGW("(HPPSwapchain) Image usage ({}) requested but not supported.", vk::to_string(flag));
|
||||
}
|
||||
}
|
||||
|
||||
if (validated_image_usage_flags.empty())
|
||||
{
|
||||
// Pick the first format from list of defaults, if supported
|
||||
static const std::vector<vk::ImageUsageFlagBits> image_usage_priority_list = {
|
||||
vk::ImageUsageFlagBits::eColorAttachment, vk::ImageUsageFlagBits::eStorage, vk::ImageUsageFlagBits::eSampled, vk::ImageUsageFlagBits::eTransferDst};
|
||||
|
||||
auto const priority_list_it =
|
||||
std::ranges::find_if(image_usage_priority_list,
|
||||
[&supported_image_usage, &supported_features](auto const image_usage) { return ((image_usage & supported_image_usage) && validate_format_feature(image_usage, supported_features)); });
|
||||
if (priority_list_it != image_usage_priority_list.end())
|
||||
{
|
||||
validated_image_usage_flags.insert(*priority_list_it);
|
||||
}
|
||||
}
|
||||
|
||||
if (validated_image_usage_flags.empty())
|
||||
{
|
||||
throw std::runtime_error("No compatible image usage found.");
|
||||
}
|
||||
else
|
||||
{
|
||||
// Log image usage flags used
|
||||
std::string usage_list;
|
||||
for (vk::ImageUsageFlagBits image_usage : validated_image_usage_flags)
|
||||
{
|
||||
usage_list += to_string(image_usage) + " ";
|
||||
}
|
||||
LOGI("(HPPSwapchain) Image usage flags: {}", usage_list);
|
||||
}
|
||||
|
||||
return validated_image_usage_flags;
|
||||
}
|
||||
|
||||
vk::ImageUsageFlags composite_image_flags(std::set<vk::ImageUsageFlagBits> &image_usage_flags)
|
||||
{
|
||||
vk::ImageUsageFlags image_usage;
|
||||
for (auto flag : image_usage_flags)
|
||||
{
|
||||
image_usage |= flag;
|
||||
}
|
||||
return image_usage;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace core
|
||||
{
|
||||
HPPSwapchain::HPPSwapchain(HPPSwapchain &old_swapchain, const vk::Extent2D &extent) :
|
||||
HPPSwapchain{old_swapchain.device,
|
||||
old_swapchain.surface,
|
||||
old_swapchain.properties.present_mode,
|
||||
old_swapchain.present_mode_priority_list,
|
||||
old_swapchain.surface_format_priority_list,
|
||||
extent,
|
||||
old_swapchain.properties.image_count,
|
||||
old_swapchain.properties.pre_transform,
|
||||
old_swapchain.image_usage_flags,
|
||||
old_swapchain.get_handle()}
|
||||
{}
|
||||
|
||||
HPPSwapchain::HPPSwapchain(HPPSwapchain &old_swapchain, const uint32_t image_count) :
|
||||
HPPSwapchain{old_swapchain.device,
|
||||
old_swapchain.surface,
|
||||
old_swapchain.properties.present_mode,
|
||||
old_swapchain.present_mode_priority_list,
|
||||
old_swapchain.surface_format_priority_list,
|
||||
old_swapchain.properties.extent,
|
||||
image_count,
|
||||
old_swapchain.properties.pre_transform,
|
||||
old_swapchain.image_usage_flags,
|
||||
old_swapchain.get_handle()}
|
||||
{}
|
||||
|
||||
HPPSwapchain::HPPSwapchain(HPPSwapchain &old_swapchain, const std::set<vk::ImageUsageFlagBits> &image_usage_flags) :
|
||||
HPPSwapchain{old_swapchain.device,
|
||||
old_swapchain.surface,
|
||||
old_swapchain.properties.present_mode,
|
||||
old_swapchain.present_mode_priority_list,
|
||||
old_swapchain.surface_format_priority_list,
|
||||
old_swapchain.properties.extent,
|
||||
old_swapchain.properties.image_count,
|
||||
old_swapchain.properties.pre_transform,
|
||||
image_usage_flags,
|
||||
old_swapchain.get_handle()}
|
||||
{}
|
||||
|
||||
HPPSwapchain::HPPSwapchain(HPPSwapchain &old_swapchain, const vk::Extent2D &extent, const vk::SurfaceTransformFlagBitsKHR transform) :
|
||||
HPPSwapchain{old_swapchain.device,
|
||||
old_swapchain.surface,
|
||||
old_swapchain.properties.present_mode,
|
||||
old_swapchain.present_mode_priority_list,
|
||||
old_swapchain.surface_format_priority_list,
|
||||
extent,
|
||||
old_swapchain.properties.image_count,
|
||||
transform,
|
||||
old_swapchain.image_usage_flags,
|
||||
old_swapchain.get_handle()}
|
||||
{}
|
||||
|
||||
HPPSwapchain::HPPSwapchain(vkb::core::DeviceCpp &device,
|
||||
vk::SurfaceKHR surface,
|
||||
const vk::PresentModeKHR present_mode,
|
||||
const std::vector<vk::PresentModeKHR> &present_mode_priority_list,
|
||||
const std::vector<vk::SurfaceFormatKHR> &surface_format_priority_list,
|
||||
const vk::Extent2D &extent,
|
||||
const uint32_t image_count,
|
||||
const vk::SurfaceTransformFlagBitsKHR transform,
|
||||
const std::set<vk::ImageUsageFlagBits> &image_usage_flags,
|
||||
vk::SwapchainKHR old_swapchain) :
|
||||
device{device},
|
||||
surface{surface}
|
||||
{
|
||||
this->present_mode_priority_list = present_mode_priority_list;
|
||||
this->surface_format_priority_list = surface_format_priority_list;
|
||||
|
||||
std::vector<vk::SurfaceFormatKHR> surface_formats = device.get_gpu().get_handle().getSurfaceFormatsKHR(surface);
|
||||
LOGI("Surface supports the following surface formats:");
|
||||
for (auto &surface_format : surface_formats)
|
||||
{
|
||||
LOGI(" \t{}", vk::to_string(surface_format.format) + ", " + vk::to_string(surface_format.colorSpace));
|
||||
}
|
||||
|
||||
std::vector<vk::PresentModeKHR> present_modes = device.get_gpu().get_handle().getSurfacePresentModesKHR(surface);
|
||||
LOGI("Surface supports the following present modes:");
|
||||
for (auto &present_mode : present_modes)
|
||||
{
|
||||
LOGI(" \t{}", to_string(present_mode));
|
||||
}
|
||||
|
||||
// Choose best properties based on surface capabilities
|
||||
vk::SurfaceCapabilitiesKHR const surface_capabilities = device.get_gpu().get_handle().getSurfaceCapabilitiesKHR(surface);
|
||||
|
||||
properties.old_swapchain = old_swapchain;
|
||||
properties.image_count = clamp(image_count,
|
||||
surface_capabilities.minImageCount,
|
||||
surface_capabilities.maxImageCount ? surface_capabilities.maxImageCount : std::numeric_limits<uint32_t>::max());
|
||||
properties.extent = choose_extent(extent, surface_capabilities.minImageExtent, surface_capabilities.maxImageExtent, surface_capabilities.currentExtent);
|
||||
properties.surface_format = choose_surface_format(properties.surface_format, surface_formats, surface_format_priority_list);
|
||||
properties.array_layers = 1;
|
||||
|
||||
vk::FormatProperties const format_properties = device.get_gpu().get_handle().getFormatProperties(properties.surface_format.format);
|
||||
this->image_usage_flags = choose_image_usage(image_usage_flags, surface_capabilities.supportedUsageFlags, format_properties.optimalTilingFeatures);
|
||||
|
||||
properties.image_usage = composite_image_flags(this->image_usage_flags);
|
||||
properties.pre_transform = choose_transform(transform, surface_capabilities.supportedTransforms, surface_capabilities.currentTransform);
|
||||
properties.composite_alpha = choose_composite_alpha(vk::CompositeAlphaFlagBitsKHR::eInherit, surface_capabilities.supportedCompositeAlpha);
|
||||
properties.present_mode = choose_present_mode(present_mode, present_modes, present_mode_priority_list);
|
||||
|
||||
vk::SwapchainCreateInfoKHR const create_info{.surface = surface,
|
||||
.minImageCount = properties.image_count,
|
||||
.imageFormat = properties.surface_format.format,
|
||||
.imageColorSpace = properties.surface_format.colorSpace,
|
||||
.imageExtent = properties.extent,
|
||||
.imageArrayLayers = properties.array_layers,
|
||||
.imageUsage = properties.image_usage,
|
||||
.preTransform = properties.pre_transform,
|
||||
.compositeAlpha = properties.composite_alpha,
|
||||
.presentMode = properties.present_mode,
|
||||
.oldSwapchain = properties.old_swapchain};
|
||||
|
||||
handle = device.get_handle().createSwapchainKHR(create_info);
|
||||
|
||||
images = device.get_handle().getSwapchainImagesKHR(handle);
|
||||
}
|
||||
|
||||
HPPSwapchain::~HPPSwapchain()
|
||||
{
|
||||
if (handle)
|
||||
{
|
||||
device.get_handle().destroySwapchainKHR(handle);
|
||||
}
|
||||
}
|
||||
|
||||
HPPSwapchain::HPPSwapchain(HPPSwapchain &&other) :
|
||||
device{other.device},
|
||||
surface{std::exchange(other.surface, nullptr)},
|
||||
handle{std::exchange(other.handle, nullptr)},
|
||||
images{std::exchange(other.images, {})},
|
||||
properties{std::exchange(other.properties, {})},
|
||||
present_mode_priority_list{std::exchange(other.present_mode_priority_list, {})},
|
||||
surface_format_priority_list{std::exchange(other.surface_format_priority_list, {})},
|
||||
image_usage_flags{std::move(other.image_usage_flags)}
|
||||
{}
|
||||
|
||||
bool HPPSwapchain::is_valid() const
|
||||
{
|
||||
return !!handle;
|
||||
}
|
||||
|
||||
vkb::core::DeviceCpp const &HPPSwapchain::get_device() const
|
||||
{
|
||||
return device;
|
||||
}
|
||||
|
||||
vk::SwapchainKHR HPPSwapchain::get_handle() const
|
||||
{
|
||||
return handle;
|
||||
}
|
||||
|
||||
std::pair<vk::Result, uint32_t> HPPSwapchain::acquire_next_image(vk::Semaphore image_acquired_semaphore, vk::Fence fence) const
|
||||
{
|
||||
vk::ResultValue<uint32_t> rv = device.get_handle().acquireNextImageKHR(handle, std::numeric_limits<uint64_t>::max(), image_acquired_semaphore, fence);
|
||||
return std::make_pair(rv.result, rv.value);
|
||||
}
|
||||
|
||||
const vk::Extent2D &HPPSwapchain::get_extent() const
|
||||
{
|
||||
return properties.extent;
|
||||
}
|
||||
|
||||
vk::Format HPPSwapchain::get_format() const
|
||||
{
|
||||
return properties.surface_format.format;
|
||||
}
|
||||
|
||||
const std::vector<vk::Image> &HPPSwapchain::get_images() const
|
||||
{
|
||||
return images;
|
||||
}
|
||||
|
||||
vk::SurfaceTransformFlagBitsKHR HPPSwapchain::get_transform() const
|
||||
{
|
||||
return properties.pre_transform;
|
||||
}
|
||||
|
||||
vk::SurfaceKHR HPPSwapchain::get_surface() const
|
||||
{
|
||||
return surface;
|
||||
}
|
||||
|
||||
vk::ImageUsageFlags HPPSwapchain::get_usage() const
|
||||
{
|
||||
return properties.image_usage;
|
||||
}
|
||||
|
||||
vk::PresentModeKHR HPPSwapchain::get_present_mode() const
|
||||
{
|
||||
return properties.present_mode;
|
||||
}
|
||||
} // namespace core
|
||||
} // namespace vkb
|
||||
@@ -0,0 +1,139 @@
|
||||
/* Copyright (c) 2021-2025, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* 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 "common/vk_common.h"
|
||||
#include <set>
|
||||
#include <vulkan/vulkan.hpp>
|
||||
|
||||
namespace vkb
|
||||
{
|
||||
namespace core
|
||||
{
|
||||
template <vkb::BindingType bindingType>
|
||||
class Device;
|
||||
using DeviceCpp = Device<vkb::BindingType::Cpp>;
|
||||
|
||||
struct HPPSwapchainProperties
|
||||
{
|
||||
vk::SwapchainKHR old_swapchain;
|
||||
uint32_t image_count{3};
|
||||
vk::Extent2D extent;
|
||||
vk::SurfaceFormatKHR surface_format;
|
||||
uint32_t array_layers;
|
||||
vk::ImageUsageFlags image_usage;
|
||||
vk::SurfaceTransformFlagBitsKHR pre_transform;
|
||||
vk::CompositeAlphaFlagBitsKHR composite_alpha;
|
||||
vk::PresentModeKHR present_mode;
|
||||
};
|
||||
|
||||
class HPPSwapchain
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief Constructor to create a swapchain by changing the extent
|
||||
* only and preserving the configuration from the old swapchain.
|
||||
*/
|
||||
HPPSwapchain(HPPSwapchain &old_swapchain, const vk::Extent2D &extent);
|
||||
|
||||
/**
|
||||
* @brief Constructor to create a swapchain by changing the image count
|
||||
* only and preserving the configuration from the old swapchain.
|
||||
*/
|
||||
HPPSwapchain(HPPSwapchain &old_swapchain, const uint32_t image_count);
|
||||
|
||||
/**
|
||||
* @brief Constructor to create a swapchain by changing the image usage
|
||||
* only and preserving the configuration from the old swapchain.
|
||||
*/
|
||||
HPPSwapchain(HPPSwapchain &old_swapchain, const std::set<vk::ImageUsageFlagBits> &image_usage_flags);
|
||||
|
||||
/**
|
||||
* @brief Constructor to create a swapchain by changing the extent
|
||||
* and transform only and preserving the configuration from the old swapchain.
|
||||
*/
|
||||
HPPSwapchain(HPPSwapchain &swapchain, const vk::Extent2D &extent, const vk::SurfaceTransformFlagBitsKHR transform);
|
||||
|
||||
/**
|
||||
* @brief Constructor to create a swapchain.
|
||||
*/
|
||||
HPPSwapchain(vkb::core::DeviceCpp &device,
|
||||
vk::SurfaceKHR surface,
|
||||
const vk::PresentModeKHR present_mode,
|
||||
const std::vector<vk::PresentModeKHR> &present_mode_priority_list = {vk::PresentModeKHR::eFifo, vk::PresentModeKHR::eMailbox},
|
||||
const std::vector<vk::SurfaceFormatKHR> &surface_format_priority_list = {{vk::Format::eR8G8B8A8Srgb, vk::ColorSpaceKHR::eSrgbNonlinear},
|
||||
{vk::Format::eB8G8R8A8Srgb, vk::ColorSpaceKHR::eSrgbNonlinear}},
|
||||
const vk::Extent2D &extent = {},
|
||||
const uint32_t image_count = 3,
|
||||
const vk::SurfaceTransformFlagBitsKHR transform = vk::SurfaceTransformFlagBitsKHR::eIdentity,
|
||||
const std::set<vk::ImageUsageFlagBits> &image_usage_flags = {vk::ImageUsageFlagBits::eColorAttachment, vk::ImageUsageFlagBits::eTransferSrc},
|
||||
vk::SwapchainKHR old_swapchain = nullptr);
|
||||
|
||||
HPPSwapchain(const HPPSwapchain &) = delete;
|
||||
|
||||
HPPSwapchain(HPPSwapchain &&other);
|
||||
|
||||
~HPPSwapchain();
|
||||
|
||||
HPPSwapchain &operator=(const HPPSwapchain &) = delete;
|
||||
|
||||
HPPSwapchain &operator=(HPPSwapchain &&) = delete;
|
||||
|
||||
bool is_valid() const;
|
||||
|
||||
vkb::core::DeviceCpp const &get_device() const;
|
||||
|
||||
vk::SwapchainKHR get_handle() const;
|
||||
|
||||
std::pair<vk::Result, uint32_t> acquire_next_image(vk::Semaphore image_acquired_semaphore, vk::Fence fence = nullptr) const;
|
||||
|
||||
const vk::Extent2D &get_extent() const;
|
||||
|
||||
vk::Format get_format() const;
|
||||
|
||||
const std::vector<vk::Image> &get_images() const;
|
||||
|
||||
vk::SurfaceTransformFlagBitsKHR get_transform() const;
|
||||
|
||||
vk::SurfaceKHR get_surface() const;
|
||||
|
||||
vk::ImageUsageFlags get_usage() const;
|
||||
|
||||
vk::PresentModeKHR get_present_mode() const;
|
||||
|
||||
private:
|
||||
vkb::core::DeviceCpp &device;
|
||||
|
||||
vk::SurfaceKHR surface;
|
||||
|
||||
vk::SwapchainKHR handle;
|
||||
|
||||
std::vector<vk::Image> images;
|
||||
|
||||
HPPSwapchainProperties properties;
|
||||
|
||||
// A list of present modes in order of priority (vector[0] has high priority, vector[size-1] has low priority)
|
||||
std::vector<vk::PresentModeKHR> present_mode_priority_list;
|
||||
|
||||
// A list of surface formats in order of priority (vector[0] has high priority, vector[size-1] has low priority)
|
||||
std::vector<vk::SurfaceFormatKHR> surface_format_priority_list;
|
||||
|
||||
std::set<vk::ImageUsageFlagBits> image_usage_flags;
|
||||
};
|
||||
} // namespace core
|
||||
} // namespace vkb
|
||||
@@ -0,0 +1,192 @@
|
||||
/* Copyright (c) 2019-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 <unordered_set>
|
||||
|
||||
#include "builder_base.h"
|
||||
#include "common/helpers.h"
|
||||
#include "common/vk_common.h"
|
||||
#include "core/allocated.h"
|
||||
#include "core/vulkan_resource.h"
|
||||
|
||||
namespace vkb
|
||||
{
|
||||
namespace core
|
||||
{
|
||||
template <vkb::BindingType bindingType>
|
||||
class Device;
|
||||
using DeviceC = Device<vkb::BindingType::C>;
|
||||
|
||||
class Image;
|
||||
using ImagePtr = std::unique_ptr<Image>;
|
||||
|
||||
struct ImageBuilder : public vkb::allocated::BuilderBaseC<ImageBuilder, VkImageCreateInfo>
|
||||
{
|
||||
private:
|
||||
using Parent = vkb::allocated::BuilderBaseC<ImageBuilder, VkImageCreateInfo>;
|
||||
|
||||
public:
|
||||
ImageBuilder(VkExtent3D const &extent) :
|
||||
Parent(VkImageCreateInfo{VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, nullptr})
|
||||
{
|
||||
VkImageCreateInfo &create_info = get_create_info();
|
||||
create_info.extent = extent;
|
||||
create_info.arrayLayers = 1;
|
||||
create_info.mipLevels = 1;
|
||||
create_info.imageType = VK_IMAGE_TYPE_2D;
|
||||
create_info.format = VK_FORMAT_R8G8B8A8_UNORM;
|
||||
create_info.samples = VK_SAMPLE_COUNT_1_BIT;
|
||||
}
|
||||
|
||||
ImageBuilder(uint32_t width, uint32_t height = 1, uint32_t depth = 1) :
|
||||
ImageBuilder(VkExtent3D{width, height, depth})
|
||||
{
|
||||
}
|
||||
|
||||
ImageBuilder &with_format(VkFormat format)
|
||||
{
|
||||
get_create_info().format = format;
|
||||
return *this;
|
||||
}
|
||||
|
||||
ImageBuilder &with_usage(VkImageUsageFlags usage)
|
||||
{
|
||||
get_create_info().usage = usage;
|
||||
return *this;
|
||||
}
|
||||
|
||||
ImageBuilder &with_flags(VkImageCreateFlags flags)
|
||||
{
|
||||
get_create_info().flags = flags;
|
||||
return *this;
|
||||
}
|
||||
|
||||
ImageBuilder &with_image_type(VkImageType type)
|
||||
{
|
||||
get_create_info().imageType = type;
|
||||
return *this;
|
||||
}
|
||||
|
||||
ImageBuilder &with_array_layers(uint32_t layers)
|
||||
{
|
||||
get_create_info().arrayLayers = layers;
|
||||
return *this;
|
||||
}
|
||||
|
||||
ImageBuilder &with_mip_levels(uint32_t levels)
|
||||
{
|
||||
get_create_info().mipLevels = levels;
|
||||
return *this;
|
||||
}
|
||||
|
||||
ImageBuilder &with_sample_count(VkSampleCountFlagBits sample_count)
|
||||
{
|
||||
get_create_info().samples = sample_count;
|
||||
return *this;
|
||||
}
|
||||
|
||||
ImageBuilder &with_tiling(VkImageTiling tiling)
|
||||
{
|
||||
get_create_info().tiling = tiling;
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename ExtensionType>
|
||||
ImageBuilder &with_extension(ExtensionType &extension)
|
||||
{
|
||||
extension.pNext = create_info.pNext;
|
||||
|
||||
create_info.pNext = &extension;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
Image build(Device<vkb::BindingType::C> &device) const;
|
||||
ImagePtr build_unique(Device<vkb::BindingType::C> &device) const;
|
||||
};
|
||||
|
||||
class ImageView;
|
||||
|
||||
class Image : public vkb::allocated::AllocatedC<VkImage>
|
||||
{
|
||||
public:
|
||||
Image(vkb::core::DeviceC &device,
|
||||
VkImage handle,
|
||||
const VkExtent3D &extent,
|
||||
VkFormat format,
|
||||
VkImageUsageFlags image_usage,
|
||||
VkSampleCountFlagBits sample_count = VK_SAMPLE_COUNT_1_BIT);
|
||||
|
||||
// [[deprecated("Use the ImageBuilder ctor instead")]]
|
||||
Image(
|
||||
vkb::core::DeviceC &device,
|
||||
const VkExtent3D &extent,
|
||||
VkFormat format,
|
||||
VkImageUsageFlags image_usage,
|
||||
VmaMemoryUsage memory_usage = VMA_MEMORY_USAGE_AUTO,
|
||||
VkSampleCountFlagBits sample_count = VK_SAMPLE_COUNT_1_BIT,
|
||||
uint32_t mip_levels = 1,
|
||||
uint32_t array_layers = 1,
|
||||
VkImageTiling tiling = VK_IMAGE_TILING_OPTIMAL,
|
||||
VkImageCreateFlags flags = 0,
|
||||
uint32_t num_queue_families = 0,
|
||||
const uint32_t *queue_families = nullptr);
|
||||
|
||||
Image(vkb::core::DeviceC &device, ImageBuilder const &builder);
|
||||
|
||||
Image(const Image &) = delete;
|
||||
|
||||
Image(Image &&other) noexcept;
|
||||
|
||||
~Image() override;
|
||||
|
||||
Image &operator=(const Image &) = delete;
|
||||
|
||||
Image &operator=(Image &&) = delete;
|
||||
|
||||
VkImageType get_type() const;
|
||||
|
||||
const VkExtent3D &get_extent() const;
|
||||
|
||||
VkFormat get_format() const;
|
||||
|
||||
VkSampleCountFlagBits get_sample_count() const;
|
||||
|
||||
VkImageUsageFlags get_usage() const;
|
||||
|
||||
VkImageTiling get_tiling() const;
|
||||
|
||||
const VkImageSubresource &get_subresource() const;
|
||||
|
||||
uint32_t get_array_layer_count() const;
|
||||
|
||||
std::unordered_set<ImageView *> &get_views();
|
||||
|
||||
VkDeviceSize get_image_required_size() const;
|
||||
|
||||
VkImageCompressionPropertiesEXT get_applied_compression() const;
|
||||
|
||||
private:
|
||||
/// Image views referring to this image
|
||||
VkImageCreateInfo create_info{VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO};
|
||||
VkImageSubresource subresource{};
|
||||
std::unordered_set<ImageView *> views;
|
||||
};
|
||||
} // namespace core
|
||||
} // namespace vkb
|
||||
@@ -0,0 +1,210 @@
|
||||
/* Copyright (c) 2019-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 "image.h"
|
||||
|
||||
#include "device.h"
|
||||
#include "image_view.h"
|
||||
|
||||
namespace vkb
|
||||
{
|
||||
namespace
|
||||
{
|
||||
inline VkImageType find_image_type(VkExtent3D extent)
|
||||
{
|
||||
VkImageType result{};
|
||||
|
||||
uint32_t dim_num{0};
|
||||
|
||||
if (extent.width >= 1)
|
||||
{
|
||||
dim_num++;
|
||||
}
|
||||
|
||||
if (extent.height >= 1)
|
||||
{
|
||||
dim_num++;
|
||||
}
|
||||
|
||||
if (extent.depth > 1)
|
||||
{
|
||||
dim_num++;
|
||||
}
|
||||
|
||||
switch (dim_num)
|
||||
{
|
||||
case 1:
|
||||
result = VK_IMAGE_TYPE_1D;
|
||||
break;
|
||||
case 2:
|
||||
result = VK_IMAGE_TYPE_2D;
|
||||
break;
|
||||
case 3:
|
||||
result = VK_IMAGE_TYPE_3D;
|
||||
break;
|
||||
default:
|
||||
throw std::runtime_error("No image type found.");
|
||||
break;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
namespace core
|
||||
{
|
||||
|
||||
Image ImageBuilder::build(vkb::core::DeviceC &device) const
|
||||
{
|
||||
return Image(device, *this);
|
||||
}
|
||||
|
||||
ImagePtr ImageBuilder::build_unique(vkb::core::DeviceC &device) const
|
||||
{
|
||||
return std::make_unique<Image>(device, *this);
|
||||
}
|
||||
|
||||
Image::Image(vkb::core::DeviceC &device,
|
||||
const VkExtent3D &extent,
|
||||
VkFormat format,
|
||||
VkImageUsageFlags image_usage,
|
||||
VmaMemoryUsage memory_usage,
|
||||
VkSampleCountFlagBits sample_count,
|
||||
const uint32_t mip_levels,
|
||||
const uint32_t array_layers,
|
||||
VkImageTiling tiling,
|
||||
VkImageCreateFlags flags,
|
||||
uint32_t num_queue_families,
|
||||
const uint32_t *queue_families) :
|
||||
// Pass through to the ImageBuilder ctor
|
||||
Image(device,
|
||||
ImageBuilder(extent)
|
||||
.with_format(format)
|
||||
.with_image_type(find_image_type(extent))
|
||||
.with_usage(image_usage)
|
||||
.with_mip_levels(mip_levels)
|
||||
.with_array_layers(array_layers)
|
||||
.with_tiling(tiling)
|
||||
.with_flags(flags)
|
||||
.with_vma_usage(memory_usage)
|
||||
.with_sample_count(sample_count)
|
||||
.with_queue_families(num_queue_families, queue_families)
|
||||
.with_implicit_sharing_mode())
|
||||
{
|
||||
}
|
||||
|
||||
Image::Image(vkb::core::DeviceC &device, ImageBuilder const &builder) :
|
||||
vkb::allocated::AllocatedC<VkImage>{builder.get_allocation_create_info(), VK_NULL_HANDLE, &device}, create_info(builder.get_create_info())
|
||||
{
|
||||
set_handle(create_image(create_info));
|
||||
subresource.arrayLayer = create_info.arrayLayers;
|
||||
subresource.mipLevel = create_info.mipLevels;
|
||||
if (!builder.get_debug_name().empty())
|
||||
{
|
||||
set_debug_name(builder.get_debug_name());
|
||||
}
|
||||
}
|
||||
|
||||
Image::Image(
|
||||
vkb::core::DeviceC &device, VkImage handle, const VkExtent3D &extent, VkFormat format, VkImageUsageFlags image_usage, VkSampleCountFlagBits sample_count) :
|
||||
vkb::allocated::AllocatedC<VkImage>{handle, &device}
|
||||
{
|
||||
create_info.extent = extent;
|
||||
create_info.imageType = find_image_type(extent);
|
||||
create_info.format = format;
|
||||
create_info.samples = sample_count;
|
||||
create_info.usage = image_usage;
|
||||
subresource.arrayLayer = create_info.arrayLayers = 1;
|
||||
subresource.mipLevel = create_info.mipLevels = 1;
|
||||
}
|
||||
|
||||
Image::Image(Image &&other) noexcept
|
||||
:
|
||||
vkb::allocated::AllocatedC<VkImage>{std::move(other)}, create_info{std::exchange(other.create_info, {})}, subresource{std::exchange(other.subresource, {})}, views(std::exchange(other.views, {}))
|
||||
{
|
||||
// Update image views references to this image to avoid dangling pointers
|
||||
for (auto &view : views)
|
||||
{
|
||||
view->set_image(*this);
|
||||
}
|
||||
}
|
||||
|
||||
Image::~Image()
|
||||
{
|
||||
destroy_image(get_handle());
|
||||
}
|
||||
|
||||
VkImageType Image::get_type() const
|
||||
{
|
||||
return create_info.imageType;
|
||||
}
|
||||
|
||||
const VkExtent3D &Image::get_extent() const
|
||||
{
|
||||
return create_info.extent;
|
||||
}
|
||||
|
||||
VkFormat Image::get_format() const
|
||||
{
|
||||
return create_info.format;
|
||||
}
|
||||
|
||||
VkSampleCountFlagBits Image::get_sample_count() const
|
||||
{
|
||||
return create_info.samples;
|
||||
}
|
||||
|
||||
VkImageUsageFlags Image::get_usage() const
|
||||
{
|
||||
return create_info.usage;
|
||||
}
|
||||
|
||||
VkImageTiling Image::get_tiling() const
|
||||
{
|
||||
return create_info.tiling;
|
||||
}
|
||||
|
||||
const VkImageSubresource &Image::get_subresource() const
|
||||
{
|
||||
return subresource;
|
||||
}
|
||||
|
||||
uint32_t Image::get_array_layer_count() const
|
||||
{
|
||||
return create_info.arrayLayers;
|
||||
}
|
||||
|
||||
std::unordered_set<ImageView *> &Image::get_views()
|
||||
{
|
||||
return views;
|
||||
}
|
||||
|
||||
VkDeviceSize Image::get_image_required_size() const
|
||||
{
|
||||
VkMemoryRequirements memory_requirements;
|
||||
|
||||
vkGetImageMemoryRequirements(get_device().get_handle(), get_handle(), &memory_requirements);
|
||||
|
||||
return memory_requirements.size;
|
||||
}
|
||||
|
||||
VkImageCompressionPropertiesEXT Image::get_applied_compression() const
|
||||
{
|
||||
return query_applied_compression(get_device().get_handle(), get_handle());
|
||||
}
|
||||
} // namespace core
|
||||
} // namespace vkb
|
||||
@@ -0,0 +1,122 @@
|
||||
/* Copyright (c) 2019-2024, 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 "image_view.h"
|
||||
|
||||
#include "core/image.h"
|
||||
#include "device.h"
|
||||
|
||||
namespace vkb
|
||||
{
|
||||
namespace core
|
||||
{
|
||||
ImageView::ImageView(Image &img, VkImageViewType view_type, VkFormat format,
|
||||
uint32_t mip_level, uint32_t array_layer,
|
||||
uint32_t n_mip_levels, uint32_t n_array_layers) :
|
||||
VulkanResource{VK_NULL_HANDLE, &img.get_device()},
|
||||
image{&img},
|
||||
format{format}
|
||||
{
|
||||
if (format == VK_FORMAT_UNDEFINED)
|
||||
{
|
||||
this->format = format = image->get_format();
|
||||
}
|
||||
|
||||
subresource_range.baseMipLevel = mip_level;
|
||||
subresource_range.baseArrayLayer = array_layer;
|
||||
subresource_range.levelCount = n_mip_levels == 0 ? image->get_subresource().mipLevel : n_mip_levels;
|
||||
subresource_range.layerCount = n_array_layers == 0 ? image->get_subresource().arrayLayer : n_array_layers;
|
||||
|
||||
if (is_depth_format(format))
|
||||
{
|
||||
subresource_range.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT;
|
||||
}
|
||||
else
|
||||
{
|
||||
subresource_range.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||
}
|
||||
|
||||
VkImageViewCreateInfo view_info{VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO};
|
||||
view_info.image = image->get_handle();
|
||||
view_info.viewType = view_type;
|
||||
view_info.format = format;
|
||||
view_info.subresourceRange = subresource_range;
|
||||
|
||||
auto result = vkCreateImageView(get_device().get_handle(), &view_info, nullptr, &get_handle());
|
||||
|
||||
if (result != VK_SUCCESS)
|
||||
{
|
||||
throw VulkanException{result, "Cannot create ImageView"};
|
||||
}
|
||||
|
||||
// Register this image view to its image
|
||||
// in order to be notified when it gets moved
|
||||
image->get_views().emplace(this);
|
||||
}
|
||||
|
||||
ImageView::ImageView(ImageView &&other) :
|
||||
VulkanResource{std::move(other)},
|
||||
image{other.image},
|
||||
format{other.format},
|
||||
subresource_range{other.subresource_range}
|
||||
{
|
||||
// Remove old view from image set and add this new one
|
||||
auto &views = image->get_views();
|
||||
views.erase(&other);
|
||||
views.emplace(this);
|
||||
}
|
||||
|
||||
ImageView::~ImageView()
|
||||
{
|
||||
if (has_device())
|
||||
{
|
||||
vkDestroyImageView(get_device().get_handle(), get_handle(), nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
const Image &ImageView::get_image() const
|
||||
{
|
||||
assert(image && "Image view is referring an invalid image");
|
||||
return *image;
|
||||
}
|
||||
|
||||
void ImageView::set_image(Image &img)
|
||||
{
|
||||
image = &img;
|
||||
}
|
||||
|
||||
VkFormat ImageView::get_format() const
|
||||
{
|
||||
return format;
|
||||
}
|
||||
|
||||
VkImageSubresourceRange ImageView::get_subresource_range() const
|
||||
{
|
||||
return subresource_range;
|
||||
}
|
||||
|
||||
VkImageSubresourceLayers ImageView::get_subresource_layers() const
|
||||
{
|
||||
VkImageSubresourceLayers subresource{};
|
||||
subresource.aspectMask = subresource_range.aspectMask;
|
||||
subresource.baseArrayLayer = subresource_range.baseArrayLayer;
|
||||
subresource.layerCount = subresource_range.layerCount;
|
||||
subresource.mipLevel = subresource_range.baseMipLevel;
|
||||
return subresource;
|
||||
}
|
||||
} // namespace core
|
||||
} // namespace vkb
|
||||
@@ -0,0 +1,69 @@
|
||||
/* Copyright (c) 2019-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 "common/helpers.h"
|
||||
#include "common/vk_common.h"
|
||||
#include "core/vulkan_resource.h"
|
||||
|
||||
namespace vkb
|
||||
{
|
||||
namespace core
|
||||
{
|
||||
class Image;
|
||||
|
||||
class ImageView : public vkb::core::VulkanResourceC<VkImageView>
|
||||
{
|
||||
public:
|
||||
ImageView(Image &image, VkImageViewType view_type, VkFormat format = VK_FORMAT_UNDEFINED,
|
||||
uint32_t base_mip_level = 0, uint32_t base_array_layer = 0,
|
||||
uint32_t n_mip_levels = 0, uint32_t n_array_layers = 0);
|
||||
|
||||
ImageView(ImageView &) = delete;
|
||||
|
||||
ImageView(ImageView &&other);
|
||||
|
||||
~ImageView() override;
|
||||
|
||||
ImageView &operator=(const ImageView &) = delete;
|
||||
|
||||
ImageView &operator=(ImageView &&) = delete;
|
||||
|
||||
const Image &get_image() const;
|
||||
|
||||
/**
|
||||
* @brief Update the image this view is referring to
|
||||
* Used on image move
|
||||
*/
|
||||
void set_image(Image &image);
|
||||
|
||||
VkFormat get_format() const;
|
||||
|
||||
VkImageSubresourceRange get_subresource_range() const;
|
||||
|
||||
VkImageSubresourceLayers get_subresource_layers() const;
|
||||
|
||||
private:
|
||||
Image *image{};
|
||||
|
||||
VkFormat format{};
|
||||
|
||||
VkImageSubresourceRange subresource_range{};
|
||||
};
|
||||
} // namespace core
|
||||
} // namespace vkb
|
||||
@@ -0,0 +1,769 @@
|
||||
/* Copyright (c) 2018-2025, Arm Limited and Contributors
|
||||
* Copyright (c) 2022-2025, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* 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 "common/vk_common.h"
|
||||
#include <ranges>
|
||||
#include <vulkan/vulkan.hpp>
|
||||
|
||||
#if defined(VKB_DEBUG) || defined(VKB_VALIDATION_LAYERS)
|
||||
# define USE_VALIDATION_LAYERS
|
||||
#endif
|
||||
|
||||
#if defined(USE_VALIDATION_LAYERS) && \
|
||||
(defined(VKB_VALIDATION_LAYERS_GPU_ASSISTED) || defined(VKB_VALIDATION_LAYERS_BEST_PRACTICES) || defined(VKB_VALIDATION_LAYERS_SYNCHRONIZATION))
|
||||
# define USE_VALIDATION_LAYER_FEATURES
|
||||
#endif
|
||||
|
||||
namespace vkb
|
||||
{
|
||||
class PhysicalDevice;
|
||||
|
||||
namespace core
|
||||
{
|
||||
class HPPPhysicalDevice;
|
||||
|
||||
/**
|
||||
* @brief A wrapper class for InstanceType
|
||||
*
|
||||
* This class is responsible for initializing the dispatcher, enumerating over all available extensions and validation layers
|
||||
* enabling them if they exist, setting up debug messaging and querying all the physical devices existing on the machine.
|
||||
*/
|
||||
template <vkb::BindingType bindingType>
|
||||
class Instance
|
||||
{
|
||||
public:
|
||||
using InstanceType = typename std::conditional<bindingType == vkb::BindingType::Cpp, vk::Instance, VkInstance>::type;
|
||||
using SurfaceType = typename std::conditional<bindingType == vkb::BindingType::Cpp, vk::SurfaceKHR, VkSurfaceKHR>::type;
|
||||
|
||||
using PhysicalDeviceType = typename std::conditional<bindingType == vkb::BindingType::Cpp, vkb::core::HPPPhysicalDevice, vkb::PhysicalDevice>::type;
|
||||
|
||||
public:
|
||||
/**
|
||||
* @brief Can be set from the GPU selection plugin to explicitly select a GPU instead
|
||||
*/
|
||||
inline static uint32_t selected_gpu_index = ~0;
|
||||
|
||||
/**
|
||||
* @brief Initializes the connection to Vulkan
|
||||
* @param application_name The name of the application
|
||||
* @param requested_extensions The extensions requested to be enabled
|
||||
* @param requested_layers The validation layers to be enabled
|
||||
* @param requested_layer_settings The layer settings to be enabled
|
||||
* @param api_version The Vulkan API version that the instance will be using
|
||||
* @throws runtime_error if the required extensions and validation layers are not found
|
||||
*/
|
||||
Instance(std::string const &application_name,
|
||||
std::unordered_map<char const *, bool> const &requested_extensions = {},
|
||||
std::unordered_map<char const *, bool> const &requested_layers = {},
|
||||
const std::vector<vk::LayerSettingEXT> &requested_layer_settings = {},
|
||||
uint32_t api_version = VK_API_VERSION_1_1);
|
||||
Instance(std::string const &application_name,
|
||||
std::unordered_map<char const *, bool> const &requested_extensions,
|
||||
std::unordered_map<char const *, bool> const &requested_layers,
|
||||
std::vector<VkLayerSettingEXT> const &requested_layer_settings,
|
||||
uint32_t api_version = VK_API_VERSION_1_1);
|
||||
|
||||
/**
|
||||
* @brief Queries the GPUs of a InstanceType that is already created
|
||||
* @param instance A valid InstanceType
|
||||
* @param externally_enabled_extensions List of extensions that have been enabled, used for following checks e.g. during device creation
|
||||
* @param needsToInitializeDispatcher If the sample uses the C-bindings and some "non-standard" initialization, the dispatcher needs to be initialized
|
||||
*/
|
||||
Instance(vk::Instance instance, const std::vector<const char *> &externally_enabled_extensions = {}, bool needsToInitializeDispatcher = false);
|
||||
Instance(VkInstance instance, const std::vector<const char *> &externally_enabled_extensions = {});
|
||||
|
||||
Instance(Instance const &) = delete;
|
||||
Instance(Instance &&) = delete;
|
||||
|
||||
~Instance();
|
||||
|
||||
Instance &operator=(Instance const &) = delete;
|
||||
Instance &operator=(Instance &&) = delete;
|
||||
|
||||
const std::vector<const char *> &get_extensions();
|
||||
|
||||
/**
|
||||
* @brief Tries to find the first available discrete GPU
|
||||
* @returns A valid physical device
|
||||
*/
|
||||
PhysicalDeviceType &get_first_gpu();
|
||||
|
||||
InstanceType get_handle() const;
|
||||
|
||||
/**
|
||||
* @brief Tries to find the first available discrete GPU that can render to the given surface
|
||||
* @param surface to test against
|
||||
* @param headless_surface Is surface created with VK_EXT_headless_surface
|
||||
* @returns A valid physical device
|
||||
*/
|
||||
PhysicalDeviceType &get_suitable_gpu(SurfaceType surface, bool headless_surface);
|
||||
|
||||
/**
|
||||
* @brief Checks if the given extension is enabled in the InstanceType
|
||||
* @param extension An extension to check
|
||||
*/
|
||||
bool is_enabled(char const *extension) const;
|
||||
|
||||
private:
|
||||
/**
|
||||
* @brief Queries the instance for the physical devices on the machine
|
||||
*/
|
||||
void query_gpus();
|
||||
|
||||
private:
|
||||
std::vector<const char *> enabled_extensions; // The enabled extensions
|
||||
std::vector<std::unique_ptr<vkb::core::HPPPhysicalDevice>> gpus; // The physical devices found on the machine
|
||||
vk::Instance handle; // The Vulkan instance
|
||||
|
||||
#if defined(VKB_DEBUG) || defined(VKB_VALIDATION_LAYERS)
|
||||
vk::DebugReportCallbackEXT debug_report_callback; // The debug report callback
|
||||
vk::DebugUtilsMessengerEXT debug_utils_messenger; // Debug utils messenger callback for VK_EXT_Debug_Utils
|
||||
#endif
|
||||
};
|
||||
|
||||
using InstanceC = Instance<vkb::BindingType::C>;
|
||||
using InstanceCpp = Instance<vkb::BindingType::Cpp>;
|
||||
} // namespace core
|
||||
} // namespace vkb
|
||||
|
||||
#include "core/hpp_physical_device.h"
|
||||
|
||||
namespace vkb
|
||||
{
|
||||
namespace core
|
||||
{
|
||||
namespace
|
||||
{
|
||||
#ifdef USE_VALIDATION_LAYERS
|
||||
inline VKAPI_ATTR vk::Bool32 VKAPI_CALL debug_utils_messenger_callback(vk::DebugUtilsMessageSeverityFlagBitsEXT message_severity,
|
||||
vk::DebugUtilsMessageTypeFlagsEXT message_type,
|
||||
vk::DebugUtilsMessengerCallbackDataEXT const *callback_data,
|
||||
void *user_data)
|
||||
{
|
||||
// Log debug message
|
||||
if (message_severity & vk::DebugUtilsMessageSeverityFlagBitsEXT::eWarning)
|
||||
{
|
||||
LOGW("{} - {}: {}", callback_data->messageIdNumber, callback_data->pMessageIdName, callback_data->pMessage);
|
||||
}
|
||||
else if (message_severity & vk::DebugUtilsMessageSeverityFlagBitsEXT::eError)
|
||||
{
|
||||
LOGE("{} - {}: {}", callback_data->messageIdNumber, callback_data->pMessageIdName, callback_data->pMessage);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
inline VKAPI_ATTR vk::Bool32 VKAPI_CALL debug_callback(vk::DebugReportFlagsEXT flags,
|
||||
vk::DebugReportObjectTypeEXT /*type*/,
|
||||
uint64_t /*object*/,
|
||||
size_t /*location*/,
|
||||
int32_t /*message_code*/,
|
||||
char const *layer_prefix,
|
||||
char const *message,
|
||||
void * /*user_data*/)
|
||||
{
|
||||
if (flags & vk::DebugReportFlagBitsEXT::eError)
|
||||
{
|
||||
LOGE("{}: {}", layer_prefix, message);
|
||||
}
|
||||
else if (flags & vk::DebugReportFlagBitsEXT::eWarning)
|
||||
{
|
||||
LOGW("{}: {}", layer_prefix, message);
|
||||
}
|
||||
else if (flags & vk::DebugReportFlagBitsEXT::ePerformanceWarning)
|
||||
{
|
||||
LOGW("{}: {}", layer_prefix, message);
|
||||
}
|
||||
else
|
||||
{
|
||||
LOGI("{}: {}", layer_prefix, message);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
|
||||
inline bool enable_extension(char const *requested_extension,
|
||||
std::vector<vk::ExtensionProperties> const &available_extensions,
|
||||
std::vector<char const *> &enabled_extensions)
|
||||
{
|
||||
bool is_available = std::ranges::any_of(available_extensions,
|
||||
[&requested_extension](auto const &available_extension) { return strcmp(requested_extension, available_extension.extensionName) == 0; });
|
||||
if (is_available)
|
||||
{
|
||||
bool is_already_enabled = std::ranges::any_of(
|
||||
enabled_extensions, [&requested_extension](auto const &enabled_extension) { return strcmp(requested_extension, enabled_extension) == 0; });
|
||||
if (!is_already_enabled)
|
||||
{
|
||||
LOGI("Extension {} available, enabling it", requested_extension);
|
||||
enabled_extensions.emplace_back(requested_extension);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LOGI("Extension {} not available", requested_extension);
|
||||
}
|
||||
|
||||
return is_available;
|
||||
}
|
||||
|
||||
inline bool enable_layer(char const *requested_layer, std::vector<vk::LayerProperties> const &available_layers, std::vector<char const *> &enabled_layers)
|
||||
{
|
||||
bool is_available = std::ranges::any_of(
|
||||
available_layers, [&requested_layer](auto const &available_layer) { return strcmp(requested_layer, available_layer.layerName) == 0; });
|
||||
if (is_available)
|
||||
{
|
||||
bool is_already_enabled =
|
||||
std::ranges::any_of(enabled_layers, [&requested_layer](auto const &enabled_layer) { return strcmp(requested_layer, enabled_layer) == 0; });
|
||||
if (!is_already_enabled)
|
||||
{
|
||||
LOGI("Layer {} available, enabling it", requested_layer);
|
||||
enabled_layers.emplace_back(requested_layer);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LOGI("Layer {} not available", requested_layer);
|
||||
}
|
||||
|
||||
return is_available;
|
||||
}
|
||||
|
||||
inline bool enable_layer_setting(vk::LayerSettingEXT const &requested_layer_setting,
|
||||
std::vector<char const *> const &enabled_layers,
|
||||
std::vector<vk::LayerSettingEXT> &enabled_layer_settings)
|
||||
{
|
||||
// We are checking if the layer is available.
|
||||
// Vulkan does not provide a reflection API for layer settings. Layer settings are described in each layer JSON manifest.
|
||||
bool is_available = std::ranges::any_of(
|
||||
enabled_layers, [&requested_layer_setting](auto const &available_layer) { return strcmp(available_layer, requested_layer_setting.pLayerName) == 0; });
|
||||
#if defined(PLATFORM__MACOS)
|
||||
// On Apple platforms the MoltenVK layer is implicitly enabled and available, and cannot be explicitly added or checked via enabled_layers.
|
||||
is_available = is_available || strcmp(requested_layer_setting.pLayerName, "MoltenVK") == 0;
|
||||
#endif
|
||||
|
||||
if (!is_available)
|
||||
{
|
||||
LOGW("Layer: {} not found. Disabling layer setting: {}", requested_layer_setting.pLayerName, requested_layer_setting.pSettingName);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool is_already_enabled = std::ranges::any_of(enabled_layer_settings,
|
||||
[&requested_layer_setting](VkLayerSettingEXT const &enabled_layer_setting) {
|
||||
return (strcmp(requested_layer_setting.pLayerName, enabled_layer_setting.pLayerName) == 0) &&
|
||||
(strcmp(requested_layer_setting.pSettingName, enabled_layer_setting.pSettingName) == 0);
|
||||
});
|
||||
|
||||
if (is_already_enabled)
|
||||
{
|
||||
LOGW("Ignoring duplicated layer setting {} in layer {}.", requested_layer_setting.pSettingName, requested_layer_setting.pLayerName);
|
||||
return false;
|
||||
}
|
||||
|
||||
LOGI("Enabling layer setting {} in layer {}.", requested_layer_setting.pSettingName, requested_layer_setting.pLayerName);
|
||||
enabled_layer_settings.push_back(requested_layer_setting);
|
||||
return true;
|
||||
}
|
||||
|
||||
inline bool enable_layer_setting(VkLayerSettingEXT const &requested_layer_setting,
|
||||
std::vector<char const *> const &enabled_layers,
|
||||
std::vector<vk::LayerSettingEXT> &enabled_layer_settings)
|
||||
{
|
||||
return enable_layer_setting(reinterpret_cast<vk::LayerSettingEXT const &>(requested_layer_setting), enabled_layers, enabled_layer_settings);
|
||||
}
|
||||
|
||||
inline bool validate_layers(std::vector<char const *> const &required, std::vector<vk::LayerProperties> const &available)
|
||||
{
|
||||
for (auto layer : required)
|
||||
{
|
||||
bool found = false;
|
||||
for (auto &available_layer : available)
|
||||
{
|
||||
if (strcmp(available_layer.layerName, layer) == 0)
|
||||
{
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found)
|
||||
{
|
||||
LOGE("Validation Layer {} not found", layer);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
template <vkb::BindingType bindingType>
|
||||
inline Instance<bindingType>::Instance(std::string const &application_name,
|
||||
std::unordered_map<char const *, bool> const &requested_extensions,
|
||||
std::unordered_map<char const *, bool> const &requested_layers,
|
||||
std::vector<vk::LayerSettingEXT> const &requested_layer_settings,
|
||||
uint32_t api_version)
|
||||
{
|
||||
std::vector<vk::ExtensionProperties> available_instance_extensions = vk::enumerateInstanceExtensionProperties();
|
||||
|
||||
#ifdef USE_VALIDATION_LAYERS
|
||||
// Check if VK_EXT_debug_utils is supported, which supersedes VK_EXT_Debug_Report
|
||||
const bool has_debug_utils = enable_extension(VK_EXT_DEBUG_UTILS_EXTENSION_NAME, available_instance_extensions, enabled_extensions);
|
||||
bool has_debug_report = false;
|
||||
|
||||
if (!has_debug_utils)
|
||||
{
|
||||
has_debug_report = enable_extension(VK_EXT_DEBUG_REPORT_EXTENSION_NAME, available_instance_extensions, enabled_extensions);
|
||||
if (!has_debug_report)
|
||||
{
|
||||
LOGW("Neither of {} or {} are available; disabling debug reporting", VK_EXT_DEBUG_UTILS_EXTENSION_NAME, VK_EXT_DEBUG_REPORT_EXTENSION_NAME);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
#if (defined(VKB_ENABLE_PORTABILITY))
|
||||
enable_extension(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME, available_instance_extensions, enabled_extensions);
|
||||
bool portability_enumeration_available = enable_extension(VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME, available_instance_extensions, enabled_extensions);
|
||||
#endif
|
||||
|
||||
#ifdef USE_VALIDATION_LAYERS
|
||||
const char *validation_layer_name = "VK_LAYER_KHRONOS_validation";
|
||||
# ifdef USE_VALIDATION_LAYER_FEATURES
|
||||
bool validation_features = false;
|
||||
{
|
||||
std::vector<vk::ExtensionProperties> available_layer_instance_extensions = vk::enumerateInstanceExtensionProperties(std::string(validation_layer_name));
|
||||
validation_features = enable_extension(VK_EXT_LAYER_SETTINGS_EXTENSION_NAME, available_layer_instance_extensions, enabled_extensions);
|
||||
}
|
||||
# endif // USE_VALIDATION_LAYER_FEATURES
|
||||
#endif // USE_VALIDATION_LAYERS
|
||||
|
||||
// Specific surface extensions are obtained from Window::get_required_surface_extensions
|
||||
// They are already added to requested_extensions by VulkanSample::prepare
|
||||
|
||||
// Even for a headless surface a swapchain is still required
|
||||
enable_extension(VK_KHR_SURFACE_EXTENSION_NAME, available_instance_extensions, enabled_extensions);
|
||||
|
||||
// VK_KHR_get_physical_device_properties2 is a prerequisite of VK_KHR_performance_query
|
||||
// which will be used for stats gathering where available.
|
||||
enable_extension(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME, available_instance_extensions, enabled_extensions);
|
||||
|
||||
for (auto requested_extension : requested_extensions)
|
||||
{
|
||||
auto const &extension_name = requested_extension.first;
|
||||
auto extension_is_optional = requested_extension.second;
|
||||
if (!enable_extension(extension_name, available_instance_extensions, enabled_extensions))
|
||||
{
|
||||
if (extension_is_optional)
|
||||
{
|
||||
LOGW("Optional instance extension {} not available, some features may be disabled", extension_name);
|
||||
}
|
||||
else
|
||||
{
|
||||
LOGE("Required instance extension {} not available, cannot run", extension_name);
|
||||
throw std::runtime_error("Required instance extensions are missing.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<vk::LayerProperties> supported_layers = vk::enumerateInstanceLayerProperties();
|
||||
|
||||
std::vector<char const *> enabled_layers;
|
||||
|
||||
auto layer_error = false;
|
||||
for (auto const &requested_layer : requested_layers)
|
||||
{
|
||||
auto const &layer_name = requested_layer.first;
|
||||
auto layer_is_optional = requested_layer.second;
|
||||
if (!enable_layer(layer_name, supported_layers, enabled_layers))
|
||||
{
|
||||
if (layer_is_optional)
|
||||
{
|
||||
LOGW("Optional layer {} not available, some features may be disabled", layer_name);
|
||||
}
|
||||
else
|
||||
{
|
||||
LOGE("Required layer {} not available, cannot run", layer_name);
|
||||
throw std::runtime_error("Required layers are missing.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef USE_VALIDATION_LAYERS
|
||||
// NOTE: It's important to have the validation layer as the last one here!!!!
|
||||
// Otherwise, device creation fails !?!
|
||||
enable_layer(validation_layer_name, supported_layers, enabled_layers);
|
||||
#endif
|
||||
|
||||
vk::ApplicationInfo app_info{.pApplicationName = application_name.c_str(), .pEngineName = "Vulkan Samples", .apiVersion = api_version};
|
||||
|
||||
vk::InstanceCreateInfo instance_info{.pApplicationInfo = &app_info,
|
||||
.enabledLayerCount = static_cast<uint32_t>(enabled_layers.size()),
|
||||
.ppEnabledLayerNames = enabled_layers.data(),
|
||||
.enabledExtensionCount = static_cast<uint32_t>(enabled_extensions.size()),
|
||||
.ppEnabledExtensionNames = enabled_extensions.data()};
|
||||
|
||||
std::vector<vk::LayerSettingEXT> enabled_layer_settings;
|
||||
|
||||
for (auto const &layer_setting : requested_layer_settings)
|
||||
{
|
||||
enable_layer_setting(layer_setting, enabled_layers, enabled_layer_settings);
|
||||
}
|
||||
|
||||
#ifdef USE_VALIDATION_LAYERS
|
||||
vk::DebugUtilsMessengerCreateInfoEXT debug_utils_create_info;
|
||||
vk::DebugReportCallbackCreateInfoEXT debug_report_create_info;
|
||||
if (has_debug_utils)
|
||||
{
|
||||
debug_utils_create_info = vk::DebugUtilsMessengerCreateInfoEXT{
|
||||
.messageSeverity = vk::DebugUtilsMessageSeverityFlagBitsEXT::eError | vk::DebugUtilsMessageSeverityFlagBitsEXT::eWarning,
|
||||
.messageType = vk::DebugUtilsMessageTypeFlagBitsEXT::eValidation | vk::DebugUtilsMessageTypeFlagBitsEXT::ePerformance,
|
||||
.pfnUserCallback = debug_utils_messenger_callback};
|
||||
|
||||
instance_info.pNext = &debug_utils_create_info;
|
||||
}
|
||||
else if (has_debug_report)
|
||||
{
|
||||
debug_report_create_info = {.flags =
|
||||
vk::DebugReportFlagBitsEXT::eError | vk::DebugReportFlagBitsEXT::eWarning | vk::DebugReportFlagBitsEXT::ePerformanceWarning,
|
||||
.pfnCallback = debug_callback};
|
||||
|
||||
instance_info.pNext = &debug_report_create_info;
|
||||
}
|
||||
#endif
|
||||
|
||||
#if (defined(VKB_ENABLE_PORTABILITY))
|
||||
if (portability_enumeration_available)
|
||||
{
|
||||
instance_info.flags |= vk::InstanceCreateFlagBits::eEnumeratePortabilityKHR;
|
||||
}
|
||||
#endif
|
||||
|
||||
// Some of the specialized layers need to be enabled explicitly
|
||||
// The validation layer does not need to be enabled in code and it can also be configured using the vulkan configurator.
|
||||
#ifdef USE_VALIDATION_LAYER_FEATURES
|
||||
|
||||
# if defined(VKB_VALIDATION_LAYERS_GPU_ASSISTED)
|
||||
const vk::Bool32 setting_validate_gpuav = true;
|
||||
if (validation_features)
|
||||
{
|
||||
enable_layer_setting(vk::LayerSettingEXT(validation_layer_name, "gpuav_enable", vk::LayerSettingTypeEXT::eBool32, 1, &setting_validate_gpuav), enabled_layers, enabled_layer_settings);
|
||||
}
|
||||
# endif
|
||||
|
||||
# if defined(VKB_VALIDATION_LAYERS_BEST_PRACTICES)
|
||||
const vk::Bool32 setting_validate_best_practices = true;
|
||||
const vk::Bool32 setting_validate_best_practices_arm = true;
|
||||
const vk::Bool32 setting_validate_best_practices_amd = true;
|
||||
const vk::Bool32 setting_validate_best_practices_img = true;
|
||||
const vk::Bool32 setting_validate_best_practices_nvidia = true;
|
||||
if (validation_features)
|
||||
{
|
||||
enable_layer_setting(
|
||||
vk::LayerSettingEXT(validation_layer_name, "validate_best_practices", vk::LayerSettingTypeEXT::eBool32, 1, &setting_validate_best_practices),
|
||||
enabled_layers,
|
||||
enabled_layer_settings);
|
||||
enable_layer_setting(
|
||||
vk::LayerSettingEXT(validation_layer_name, "validate_best_practices_arm", vk::LayerSettingTypeEXT::eBool32, 1, &setting_validate_best_practices_arm),
|
||||
enabled_layers,
|
||||
enabled_layer_settings);
|
||||
enable_layer_setting(
|
||||
vk::LayerSettingEXT(validation_layer_name, "validate_best_practices_amd", vk::LayerSettingTypeEXT::eBool32, 1, &setting_validate_best_practices_amd),
|
||||
enabled_layers,
|
||||
enabled_layer_settings);
|
||||
enable_layer_setting(
|
||||
vk::LayerSettingEXT(validation_layer_name, "validate_best_practices_img", vk::LayerSettingTypeEXT::eBool32, 1, &setting_validate_best_practices_img),
|
||||
enabled_layers,
|
||||
enabled_layer_settings);
|
||||
enable_layer_setting(
|
||||
vk::LayerSettingEXT(
|
||||
validation_layer_name, "validate_best_practices_nvidia", vk::LayerSettingTypeEXT::eBool32, 1, &setting_validate_best_practices_nvidia),
|
||||
enabled_layers,
|
||||
enabled_layer_settings);
|
||||
}
|
||||
# endif
|
||||
|
||||
# if defined(VKB_VALIDATION_LAYERS_SYNCHRONIZATION)
|
||||
const vk::Bool32 setting_validate_sync = true;
|
||||
const vk::Bool32 setting_validate_sync_heuristics = true;
|
||||
if (validation_features)
|
||||
{
|
||||
enable_layer_setting(vk::LayerSettingEXT(validation_layer_name, "validate_sync", vk::LayerSettingTypeEXT::eBool32, 1, &setting_validate_sync),
|
||||
enabled_layers,
|
||||
enabled_layer_settings);
|
||||
enable_layer_setting(
|
||||
vk::LayerSettingEXT(
|
||||
validation_layer_name, "syncval_shader_accesses_heuristic", vk::LayerSettingTypeEXT::eBool32, 1, &setting_validate_sync_heuristics),
|
||||
enabled_layers,
|
||||
enabled_layer_settings);
|
||||
}
|
||||
# endif
|
||||
#endif
|
||||
|
||||
vk::LayerSettingsCreateInfoEXT layerSettingsCreateInfo;
|
||||
|
||||
// If layer settings are defined, then activate the sample's required layer settings during instance creation
|
||||
if (enabled_layer_settings.size() > 0)
|
||||
{
|
||||
layerSettingsCreateInfo.settingCount = static_cast<uint32_t>(enabled_layer_settings.size());
|
||||
layerSettingsCreateInfo.pSettings = enabled_layer_settings.data();
|
||||
layerSettingsCreateInfo.pNext = instance_info.pNext;
|
||||
instance_info.pNext = &layerSettingsCreateInfo;
|
||||
}
|
||||
|
||||
// Create the Vulkan instance
|
||||
handle = vk::createInstance(instance_info);
|
||||
|
||||
// initialize the Vulkan-Hpp default dispatcher on the instance
|
||||
VULKAN_HPP_DEFAULT_DISPATCHER.init(handle);
|
||||
|
||||
// Need to load volk for all the not-yet Vulkan-Hpp calls
|
||||
volkLoadInstance(handle);
|
||||
|
||||
#ifdef USE_VALIDATION_LAYERS
|
||||
if (has_debug_utils)
|
||||
{
|
||||
debug_utils_messenger = handle.createDebugUtilsMessengerEXT(debug_utils_create_info);
|
||||
}
|
||||
else if (has_debug_report)
|
||||
{
|
||||
debug_report_callback = handle.createDebugReportCallbackEXT(debug_report_create_info);
|
||||
}
|
||||
#endif
|
||||
|
||||
query_gpus();
|
||||
}
|
||||
|
||||
template <vkb::BindingType bindingType>
|
||||
inline Instance<bindingType>::Instance(std::string const &application_name,
|
||||
std::unordered_map<char const *, bool> const &requested_extensions,
|
||||
std::unordered_map<char const *, bool> const &requested_layers,
|
||||
std::vector<VkLayerSettingEXT> const &requested_layer_settings,
|
||||
uint32_t api_version) :
|
||||
Instance<bindingType>(application_name,
|
||||
requested_extensions,
|
||||
requested_layers,
|
||||
reinterpret_cast<std::vector<vk::LayerSettingEXT> const &>(requested_layer_settings),
|
||||
api_version)
|
||||
{}
|
||||
|
||||
template <vkb::BindingType bindingType>
|
||||
inline Instance<bindingType>::Instance(vk::Instance instance, const std::vector<const char *> &externally_enabled_extensions, bool needsToInitializeDispatcher) :
|
||||
handle{instance}
|
||||
{
|
||||
if (needsToInitializeDispatcher)
|
||||
{
|
||||
#if defined(_HPP_VULKAN_LIBRARY)
|
||||
static vk::detail::DynamicLoader dl(_HPP_VULKAN_LIBRARY);
|
||||
#else
|
||||
static vk::detail::DynamicLoader dl;
|
||||
#endif
|
||||
PFN_vkGetInstanceProcAddr vkGetInstanceProcAddr = dl.getProcAddress<PFN_vkGetInstanceProcAddr>("vkGetInstanceProcAddr");
|
||||
VULKAN_HPP_DEFAULT_DISPATCHER.init(vkGetInstanceProcAddr);
|
||||
VULKAN_HPP_DEFAULT_DISPATCHER.init(instance);
|
||||
}
|
||||
|
||||
// Some parts of the framework will check for certain extensions to be enabled
|
||||
// To make those work we need to copy over externally enabled extensions into this class
|
||||
for (auto extension : externally_enabled_extensions)
|
||||
{
|
||||
enabled_extensions.push_back(extension);
|
||||
}
|
||||
|
||||
if (handle)
|
||||
{
|
||||
query_gpus();
|
||||
}
|
||||
else
|
||||
{
|
||||
throw std::runtime_error("Instance not valid");
|
||||
}
|
||||
}
|
||||
|
||||
template <vkb::BindingType bindingType>
|
||||
inline Instance<bindingType>::Instance(VkInstance instance, const std::vector<const char *> &externally_enabled_extensions) :
|
||||
Instance<bindingType>(static_cast<vk::Instance>(instance), externally_enabled_extensions, true)
|
||||
{}
|
||||
|
||||
template <vkb::BindingType bindingType>
|
||||
inline Instance<bindingType>::~Instance()
|
||||
{
|
||||
#ifdef USE_VALIDATION_LAYERS
|
||||
if (debug_utils_messenger)
|
||||
{
|
||||
handle.destroyDebugUtilsMessengerEXT(debug_utils_messenger);
|
||||
}
|
||||
if (debug_report_callback)
|
||||
{
|
||||
handle.destroyDebugReportCallbackEXT(debug_report_callback);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (handle)
|
||||
{
|
||||
handle.destroy();
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(USE_VALIDATION_LAYERS)
|
||||
# undef USE_VALIDATION_LAYERS
|
||||
#endif
|
||||
|
||||
#if defined(USE_VALIDATION_LAYER_FEATURES)
|
||||
# undef USE_VALIDATION_LAYER_FEATURES
|
||||
#endif
|
||||
|
||||
template <vkb::BindingType bindingType>
|
||||
inline typename Instance<bindingType>::PhysicalDeviceType &Instance<bindingType>::get_first_gpu()
|
||||
{
|
||||
assert(!gpus.empty() && "No physical devices were found on the system.");
|
||||
|
||||
// Find a discrete GPU
|
||||
auto gpuIt = std::ranges::find_if(gpus, [](auto const &gpu) { return gpu->get_properties().deviceType == vk::PhysicalDeviceType::eDiscreteGpu; });
|
||||
if (gpuIt == gpus.end())
|
||||
{
|
||||
LOGW("Couldn't find a discrete physical device, picking default GPU");
|
||||
gpuIt = gpus.begin();
|
||||
}
|
||||
if constexpr (bindingType == BindingType::Cpp)
|
||||
{
|
||||
return **gpuIt;
|
||||
}
|
||||
else
|
||||
{
|
||||
return reinterpret_cast<vkb::PhysicalDevice &>(**gpuIt);
|
||||
}
|
||||
}
|
||||
|
||||
template <vkb::BindingType bindingType>
|
||||
inline typename Instance<bindingType>::InstanceType Instance<bindingType>::get_handle() const
|
||||
{
|
||||
if constexpr (bindingType == BindingType::Cpp)
|
||||
{
|
||||
return handle;
|
||||
}
|
||||
else
|
||||
{
|
||||
return static_cast<VkInstance>(handle);
|
||||
}
|
||||
return handle;
|
||||
}
|
||||
|
||||
template <vkb::BindingType bindingType>
|
||||
inline typename Instance<bindingType>::PhysicalDeviceType &Instance<bindingType>::get_suitable_gpu(SurfaceType surface, bool headless_surface)
|
||||
{
|
||||
assert(!gpus.empty() && "No physical devices were found on the system.");
|
||||
|
||||
// A GPU can be explicitly selected via the command line (see plugins/gpu_selection.cpp), this overrides the below GPU selection algorithm
|
||||
auto gpuIt = gpus.begin();
|
||||
if (selected_gpu_index != ~0)
|
||||
{
|
||||
LOGI("Explicitly selecting GPU {}", selected_gpu_index);
|
||||
if (selected_gpu_index > gpus.size() - 1)
|
||||
{
|
||||
throw std::runtime_error("Selected GPU index is not within no. of available GPUs");
|
||||
}
|
||||
gpuIt = gpus.begin() + selected_gpu_index;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (headless_surface)
|
||||
{
|
||||
LOGW("Using headless surface with multiple GPUs. Considered explicitly selecting the target GPU.")
|
||||
}
|
||||
|
||||
// Find a discrete GPU
|
||||
#if 0
|
||||
gpuIt = std::ranges::find_if(gpus,
|
||||
[&surface](auto const &gpu) {
|
||||
return (gpu->get_properties().deviceType == vk::PhysicalDeviceType::eDiscreteGpu) &&
|
||||
std::ranges::any_of(std::views::iota(size_t(0), gpu->get_queue_family_properties().size()),
|
||||
[&gpu, &surface](auto const &queue_idx) {
|
||||
return gpu->get_handle().getSurfaceSupportKHR(static_cast<uint32_t>(queue_idx), surface);
|
||||
});
|
||||
});
|
||||
#else
|
||||
gpuIt = std::ranges::find_if(gpus,
|
||||
[&surface](auto const &gpu) {
|
||||
auto gpu_supports_surface = [&gpu, &surface]() {
|
||||
for (uint32_t queue_idx = 0; queue_idx < gpu->get_queue_family_properties().size(); ++queue_idx)
|
||||
{
|
||||
if (gpu->get_handle().getSurfaceSupportKHR(queue_idx, surface))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
return (gpu->get_properties().deviceType == vk::PhysicalDeviceType::eDiscreteGpu) && gpu_supports_surface();
|
||||
});
|
||||
#endif
|
||||
if (gpuIt == gpus.end())
|
||||
{
|
||||
LOGW("Couldn't find a discrete physical device that supports the surface, picking default GPU");
|
||||
gpuIt = gpus.begin();
|
||||
}
|
||||
}
|
||||
|
||||
if constexpr (bindingType == BindingType::Cpp)
|
||||
{
|
||||
return **gpuIt;
|
||||
}
|
||||
else
|
||||
{
|
||||
return reinterpret_cast<vkb::PhysicalDevice &>(**gpuIt);
|
||||
}
|
||||
}
|
||||
|
||||
template <vkb::BindingType bindingType>
|
||||
inline bool Instance<bindingType>::is_enabled(char const *extension) const
|
||||
{
|
||||
return std::ranges::find_if(enabled_extensions, [extension](char const *enabled_extension) { return strcmp(extension, enabled_extension) == 0; }) !=
|
||||
enabled_extensions.end();
|
||||
}
|
||||
|
||||
template <vkb::BindingType bindingType>
|
||||
inline void Instance<bindingType>::query_gpus()
|
||||
{
|
||||
// Querying valid physical devices on the machine
|
||||
std::vector<vk::PhysicalDevice> physical_devices = handle.enumeratePhysicalDevices();
|
||||
if (physical_devices.empty())
|
||||
{
|
||||
throw std::runtime_error("Couldn't find a physical device that supports Vulkan.");
|
||||
}
|
||||
|
||||
// Create gpus wrapper objects from the vk::PhysicalDevice's
|
||||
for (auto &physical_device : physical_devices)
|
||||
{
|
||||
if constexpr (bindingType == BindingType::Cpp)
|
||||
{
|
||||
gpus.push_back(std::make_unique<vkb::core::HPPPhysicalDevice>(*this, physical_device));
|
||||
}
|
||||
else
|
||||
{
|
||||
gpus.push_back(std::make_unique<vkb::core::HPPPhysicalDevice>(*reinterpret_cast<vkb::core::InstanceCpp *>(this), physical_device));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <vkb::BindingType bindingType>
|
||||
inline std::vector<char const *> const &Instance<bindingType>::get_extensions()
|
||||
{
|
||||
return enabled_extensions;
|
||||
}
|
||||
} // namespace core
|
||||
} // namespace vkb
|
||||
@@ -0,0 +1,203 @@
|
||||
/* Copyright (c) 2020-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 "physical_device.h"
|
||||
|
||||
namespace vkb
|
||||
{
|
||||
PhysicalDevice::PhysicalDevice(vkb::core::InstanceC &instance, VkPhysicalDevice physical_device) :
|
||||
instance{instance},
|
||||
handle{physical_device}
|
||||
{
|
||||
vkGetPhysicalDeviceFeatures(physical_device, &features);
|
||||
vkGetPhysicalDeviceProperties(physical_device, &properties);
|
||||
vkGetPhysicalDeviceMemoryProperties(physical_device, &memory_properties);
|
||||
|
||||
LOGI("Found GPU: {}", properties.deviceName);
|
||||
|
||||
uint32_t queue_family_properties_count = 0;
|
||||
vkGetPhysicalDeviceQueueFamilyProperties(physical_device, &queue_family_properties_count, nullptr);
|
||||
queue_family_properties = std::vector<VkQueueFamilyProperties>(queue_family_properties_count);
|
||||
vkGetPhysicalDeviceQueueFamilyProperties(physical_device, &queue_family_properties_count, queue_family_properties.data());
|
||||
|
||||
uint32_t device_extension_count;
|
||||
VK_CHECK(vkEnumerateDeviceExtensionProperties(get_handle(), nullptr, &device_extension_count, nullptr));
|
||||
device_extensions = std::vector<VkExtensionProperties>(device_extension_count);
|
||||
VK_CHECK(vkEnumerateDeviceExtensionProperties(get_handle(), nullptr, &device_extension_count, device_extensions.data()));
|
||||
|
||||
// Display supported extensions
|
||||
if (device_extensions.size() > 0)
|
||||
{
|
||||
LOGD("Device supports the following extensions:");
|
||||
for (auto &extension : device_extensions)
|
||||
{
|
||||
LOGD(" \t{}", extension.extensionName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DriverVersion PhysicalDevice::get_driver_version() const
|
||||
{
|
||||
DriverVersion version;
|
||||
|
||||
VkPhysicalDeviceProperties const &properties = get_properties();
|
||||
switch (properties.vendorID)
|
||||
{
|
||||
case 0x10DE:
|
||||
// Nvidia
|
||||
version.major = (properties.driverVersion >> 22) & 0x3ff;
|
||||
version.minor = (properties.driverVersion >> 14) & 0x0ff;
|
||||
version.patch = (properties.driverVersion >> 6) & 0x0ff;
|
||||
// Ignoring optional tertiary info in lower 6 bits
|
||||
break;
|
||||
case 0x8086:
|
||||
version.major = (properties.driverVersion >> 14) & 0x3ffff;
|
||||
version.minor = properties.driverVersion & 0x3ffff;
|
||||
break;
|
||||
default:
|
||||
version.major = VK_VERSION_MAJOR(properties.driverVersion);
|
||||
version.minor = VK_VERSION_MINOR(properties.driverVersion);
|
||||
version.patch = VK_VERSION_PATCH(properties.driverVersion);
|
||||
break;
|
||||
}
|
||||
|
||||
return version;
|
||||
}
|
||||
|
||||
vkb::core::InstanceC &PhysicalDevice::get_instance() const
|
||||
{
|
||||
return instance;
|
||||
}
|
||||
|
||||
VkBool32 PhysicalDevice::is_present_supported(VkSurfaceKHR surface, uint32_t queue_family_index) const
|
||||
{
|
||||
VkBool32 present_supported{VK_FALSE};
|
||||
|
||||
if (surface != VK_NULL_HANDLE)
|
||||
{
|
||||
VK_CHECK(vkGetPhysicalDeviceSurfaceSupportKHR(handle, queue_family_index, surface, &present_supported));
|
||||
}
|
||||
|
||||
return present_supported;
|
||||
}
|
||||
|
||||
bool PhysicalDevice::is_extension_supported(const std::string &requested_extension) const
|
||||
{
|
||||
return std::ranges::find_if(device_extensions,
|
||||
[requested_extension](auto &device_extension) {
|
||||
return std::strcmp(device_extension.extensionName, requested_extension.c_str()) == 0;
|
||||
}) != device_extensions.end();
|
||||
}
|
||||
|
||||
const VkFormatProperties PhysicalDevice::get_format_properties(VkFormat format) const
|
||||
{
|
||||
VkFormatProperties format_properties;
|
||||
|
||||
vkGetPhysicalDeviceFormatProperties(handle, format, &format_properties);
|
||||
|
||||
return format_properties;
|
||||
}
|
||||
|
||||
VkPhysicalDevice PhysicalDevice::get_handle() const
|
||||
{
|
||||
return handle;
|
||||
}
|
||||
|
||||
const VkPhysicalDeviceFeatures &PhysicalDevice::get_features() const
|
||||
{
|
||||
return features;
|
||||
}
|
||||
|
||||
const VkPhysicalDeviceProperties &PhysicalDevice::get_properties() const
|
||||
{
|
||||
return properties;
|
||||
}
|
||||
|
||||
const VkPhysicalDeviceMemoryProperties &PhysicalDevice::get_memory_properties() const
|
||||
{
|
||||
return memory_properties;
|
||||
}
|
||||
|
||||
uint32_t PhysicalDevice::get_memory_type(uint32_t bits, VkMemoryPropertyFlags properties, VkBool32 *memory_type_found) const
|
||||
{
|
||||
for (uint32_t i = 0; i < memory_properties.memoryTypeCount; i++)
|
||||
{
|
||||
if ((bits & 1) == 1)
|
||||
{
|
||||
if ((memory_properties.memoryTypes[i].propertyFlags & properties) == properties)
|
||||
{
|
||||
if (memory_type_found)
|
||||
{
|
||||
*memory_type_found = true;
|
||||
}
|
||||
return i;
|
||||
}
|
||||
}
|
||||
bits >>= 1;
|
||||
}
|
||||
|
||||
if (memory_type_found)
|
||||
{
|
||||
*memory_type_found = false;
|
||||
return ~0;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw std::runtime_error("Could not find a matching memory type");
|
||||
}
|
||||
}
|
||||
|
||||
const std::vector<VkQueueFamilyProperties> &PhysicalDevice::get_queue_family_properties() const
|
||||
{
|
||||
return queue_family_properties;
|
||||
}
|
||||
|
||||
uint32_t PhysicalDevice::get_queue_family_performance_query_passes(
|
||||
const VkQueryPoolPerformanceCreateInfoKHR *perf_query_create_info) const
|
||||
{
|
||||
uint32_t passes_needed;
|
||||
vkGetPhysicalDeviceQueueFamilyPerformanceQueryPassesKHR(get_handle(), perf_query_create_info,
|
||||
&passes_needed);
|
||||
return passes_needed;
|
||||
}
|
||||
|
||||
void PhysicalDevice::enumerate_queue_family_performance_query_counters(
|
||||
uint32_t queue_family_index,
|
||||
uint32_t *count,
|
||||
VkPerformanceCounterKHR *counters,
|
||||
VkPerformanceCounterDescriptionKHR *descriptions) const
|
||||
{
|
||||
VK_CHECK(vkEnumeratePhysicalDeviceQueueFamilyPerformanceQueryCountersKHR(
|
||||
get_handle(), queue_family_index, count, counters, descriptions));
|
||||
}
|
||||
|
||||
const VkPhysicalDeviceFeatures PhysicalDevice::get_requested_features() const
|
||||
{
|
||||
return requested_features;
|
||||
}
|
||||
|
||||
VkPhysicalDeviceFeatures &PhysicalDevice::get_mutable_requested_features()
|
||||
{
|
||||
return requested_features;
|
||||
}
|
||||
|
||||
void *PhysicalDevice::get_extension_feature_chain() const
|
||||
{
|
||||
return last_requested_extension_feature;
|
||||
}
|
||||
|
||||
} // namespace vkb
|
||||
@@ -0,0 +1,255 @@
|
||||
/* Copyright (c) 2020-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 "core/instance.h"
|
||||
|
||||
namespace vkb
|
||||
{
|
||||
struct DriverVersion
|
||||
{
|
||||
uint16_t major;
|
||||
uint16_t minor;
|
||||
uint16_t patch;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief A wrapper class for VkPhysicalDevice
|
||||
*
|
||||
* This class is responsible for handling gpu features, properties, and queue families for the device creation.
|
||||
*/
|
||||
class PhysicalDevice
|
||||
{
|
||||
public:
|
||||
PhysicalDevice(vkb::core::InstanceC &instance, VkPhysicalDevice physical_device);
|
||||
|
||||
PhysicalDevice(const PhysicalDevice &) = delete;
|
||||
|
||||
PhysicalDevice(PhysicalDevice &&) = delete;
|
||||
|
||||
PhysicalDevice &operator=(const PhysicalDevice &) = delete;
|
||||
|
||||
PhysicalDevice &operator=(PhysicalDevice &&) = delete;
|
||||
|
||||
DriverVersion get_driver_version() const;
|
||||
|
||||
vkb::core::InstanceC &get_instance() const;
|
||||
|
||||
VkBool32 is_present_supported(VkSurfaceKHR surface, uint32_t queue_family_index) const;
|
||||
|
||||
bool is_extension_supported(const std::string &extension) const;
|
||||
|
||||
const VkFormatProperties get_format_properties(VkFormat format) const;
|
||||
|
||||
VkPhysicalDevice get_handle() const;
|
||||
|
||||
const VkPhysicalDeviceFeatures &get_features() const;
|
||||
|
||||
const VkPhysicalDeviceProperties &get_properties() const;
|
||||
|
||||
const VkPhysicalDeviceMemoryProperties &get_memory_properties() const;
|
||||
|
||||
uint32_t get_memory_type(uint32_t bits, VkMemoryPropertyFlags properties, VkBool32 *memory_type_found = nullptr) const;
|
||||
|
||||
const std::vector<VkQueueFamilyProperties> &get_queue_family_properties() const;
|
||||
|
||||
uint32_t get_queue_family_performance_query_passes(
|
||||
const VkQueryPoolPerformanceCreateInfoKHR *perf_query_create_info) const;
|
||||
|
||||
void enumerate_queue_family_performance_query_counters(
|
||||
uint32_t queue_family_index,
|
||||
uint32_t *count,
|
||||
VkPerformanceCounterKHR *counters,
|
||||
VkPerformanceCounterDescriptionKHR *descriptions) const;
|
||||
|
||||
const VkPhysicalDeviceFeatures get_requested_features() const;
|
||||
|
||||
VkPhysicalDeviceFeatures &get_mutable_requested_features();
|
||||
|
||||
/**
|
||||
* @brief Used at logical device creation to pass the extensions feature chain to vkCreateDevice
|
||||
* @returns A void pointer to the start of the extension linked list
|
||||
*/
|
||||
void *get_extension_feature_chain() const;
|
||||
|
||||
/**
|
||||
* @brief Get an extension features struct
|
||||
*
|
||||
* Gets the actual extension features struct with the supported flags set.
|
||||
* The flags you're interested in can be set in a corresponding struct in the structure chain
|
||||
* by calling PhysicalDevice::add_extension_features()
|
||||
* @param type The VkStructureType for the extension you are requesting
|
||||
* @returns The extension feature struct
|
||||
*/
|
||||
template <typename T>
|
||||
T get_extension_features(VkStructureType type)
|
||||
{
|
||||
// We cannot request extension features if the physical device properties 2 instance extension isn't enabled
|
||||
if (!instance.is_enabled(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME))
|
||||
{
|
||||
throw std::runtime_error("Couldn't request feature from device as " + std::string(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME) +
|
||||
" isn't enabled!");
|
||||
}
|
||||
|
||||
// Get the extension features
|
||||
T features{type};
|
||||
VkPhysicalDeviceFeatures2KHR physical_device_features{VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2_KHR};
|
||||
physical_device_features.pNext = &features;
|
||||
vkGetPhysicalDeviceFeatures2KHR(handle, &physical_device_features);
|
||||
|
||||
return features;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Add an extension features struct to the structure chain used for device creation
|
||||
*
|
||||
* To have the features enabled, this function must be called before the logical device
|
||||
* is created. To do this request sample specific features inside
|
||||
* VulkanSample::request_gpu_features(vkb::PhysicalDevice &gpu).
|
||||
*
|
||||
* If the feature extension requires you to ask for certain features to be enabled, you can
|
||||
* modify the struct returned by this function, it will propagate the changes to the logical
|
||||
* device.
|
||||
* @param type The VkStructureType for the extension you are requesting
|
||||
* @returns A reference to extension feature struct in the structure chain
|
||||
*/
|
||||
template <typename T>
|
||||
T &add_extension_features(VkStructureType type)
|
||||
{
|
||||
// We cannot request extension features if the physical device properties 2 instance extension isn't enabled
|
||||
if (!instance.is_enabled(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME))
|
||||
{
|
||||
throw std::runtime_error("Couldn't request feature from device as " + std::string(VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME) +
|
||||
" isn't enabled!");
|
||||
}
|
||||
|
||||
// Add an (empty) extension features into the map of extension features
|
||||
auto [it, added] = extension_features.insert({type, std::make_shared<T>(T{type})});
|
||||
if (added)
|
||||
{
|
||||
// if it was actually added, also add it to the structure chain
|
||||
if (last_requested_extension_feature)
|
||||
{
|
||||
static_cast<T *>(it->second.get())->pNext = last_requested_extension_feature;
|
||||
}
|
||||
last_requested_extension_feature = it->second.get();
|
||||
}
|
||||
|
||||
return *static_cast<T *>(it->second.get());
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Request an optional features flag
|
||||
*
|
||||
* Calls get_extension_features to get the support of the requested flag. If it's supported,
|
||||
* add_extension_features is called, otherwise a log message is generated.
|
||||
*
|
||||
* @returns true if the requested feature is supported, otherwise false
|
||||
*/
|
||||
template <typename Feature>
|
||||
VkBool32 request_optional_feature(VkStructureType type, VkBool32 Feature::*flag, std::string const &featureName, std::string const &flagName)
|
||||
{
|
||||
VkBool32 supported = get_extension_features<Feature>(type).*flag;
|
||||
if (supported)
|
||||
{
|
||||
add_extension_features<Feature>(type).*flag = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
LOGI("Requested optional feature <{}::{}> is not supported", featureName, flagName);
|
||||
}
|
||||
return supported;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Request a required features flag
|
||||
*
|
||||
* Calls get_extension_features to get the support of the requested flag. If it's supported,
|
||||
* add_extension_features is called, otherwise a runtime_error is thrown.
|
||||
*/
|
||||
template <typename Feature>
|
||||
void request_required_feature(VkStructureType type, VkBool32 Feature::*flag, std::string const &featureName, std::string const &flagName)
|
||||
{
|
||||
if (get_extension_features<Feature>(type).*flag)
|
||||
{
|
||||
add_extension_features<Feature>(type).*flag = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw std::runtime_error(std::string("Requested required feature <") + featureName + "::" + flagName + "> is not supported");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets whether or not the first graphics queue should have higher priority than other queues.
|
||||
* Very specific feature which is used by async compute samples.
|
||||
* @param enable If true, present queue will have prio 1.0 and other queues have prio 0.5.
|
||||
* Default state is false, where all queues have 0.5 priority.
|
||||
*/
|
||||
void set_high_priority_graphics_queue_enable(bool enable)
|
||||
{
|
||||
high_priority_graphics_queue = enable;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns high priority graphics queue state.
|
||||
* @return High priority state.
|
||||
*/
|
||||
bool has_high_priority_graphics_queue() const
|
||||
{
|
||||
return high_priority_graphics_queue;
|
||||
}
|
||||
|
||||
private:
|
||||
// Handle to the Vulkan instance
|
||||
vkb::core::InstanceC &instance;
|
||||
|
||||
// Handle to the Vulkan physical device
|
||||
VkPhysicalDevice handle{VK_NULL_HANDLE};
|
||||
|
||||
// The features that this GPU supports
|
||||
VkPhysicalDeviceFeatures features{};
|
||||
|
||||
// The extensions that this GPU supports
|
||||
std::vector<VkExtensionProperties> device_extensions;
|
||||
|
||||
// The GPU properties
|
||||
VkPhysicalDeviceProperties properties;
|
||||
|
||||
// The GPU memory properties
|
||||
VkPhysicalDeviceMemoryProperties memory_properties;
|
||||
|
||||
// The GPU queue family properties
|
||||
std::vector<VkQueueFamilyProperties> queue_family_properties;
|
||||
|
||||
// The features that will be requested to be enabled in the logical device
|
||||
VkPhysicalDeviceFeatures requested_features{};
|
||||
|
||||
// The extension feature pointer
|
||||
void *last_requested_extension_feature{nullptr};
|
||||
|
||||
// Holds the extension feature structures, we use a map to retain an order of requested structures
|
||||
std::map<VkStructureType, std::shared_ptr<void>> extension_features;
|
||||
|
||||
bool high_priority_graphics_queue{};
|
||||
};
|
||||
|
||||
#define REQUEST_OPTIONAL_FEATURE(gpu, Feature, type, flag) gpu.request_optional_feature<Feature>(type, &Feature::flag, #Feature, #flag)
|
||||
#define REQUEST_REQUIRED_FEATURE(gpu, Feature, type, flag) gpu.request_required_feature<Feature>(type, &Feature::flag, #Feature, #flag)
|
||||
|
||||
} // namespace vkb
|
||||
@@ -0,0 +1,307 @@
|
||||
/* Copyright (c) 2019-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 "pipeline.h"
|
||||
|
||||
#include "debug.h"
|
||||
#include "device.h"
|
||||
#include "pipeline_layout.h"
|
||||
#include "shader_module.h"
|
||||
|
||||
namespace vkb
|
||||
{
|
||||
Pipeline::Pipeline(vkb::core::DeviceC &device) :
|
||||
device{device}
|
||||
{}
|
||||
|
||||
Pipeline::Pipeline(Pipeline &&other) :
|
||||
device{other.device},
|
||||
handle{other.handle},
|
||||
state{other.state}
|
||||
{
|
||||
other.handle = VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
Pipeline::~Pipeline()
|
||||
{
|
||||
// Destroy pipeline
|
||||
if (handle != VK_NULL_HANDLE)
|
||||
{
|
||||
vkDestroyPipeline(device.get_handle(), handle, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
VkPipeline Pipeline::get_handle() const
|
||||
{
|
||||
return handle;
|
||||
}
|
||||
|
||||
const PipelineState &Pipeline::get_state() const
|
||||
{
|
||||
return state;
|
||||
}
|
||||
|
||||
ComputePipeline::ComputePipeline(vkb::core::DeviceC &device,
|
||||
VkPipelineCache pipeline_cache,
|
||||
PipelineState &pipeline_state) :
|
||||
Pipeline{device}
|
||||
{
|
||||
const ShaderModule *shader_module = pipeline_state.get_pipeline_layout().get_shader_modules().front();
|
||||
|
||||
if (shader_module->get_stage() != VK_SHADER_STAGE_COMPUTE_BIT)
|
||||
{
|
||||
throw VulkanException{VK_ERROR_INVALID_SHADER_NV, "Shader module stage is not compute"};
|
||||
}
|
||||
|
||||
VkPipelineShaderStageCreateInfo stage{VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO};
|
||||
|
||||
stage.stage = shader_module->get_stage();
|
||||
stage.pName = shader_module->get_entry_point().c_str();
|
||||
|
||||
// Create the Vulkan handle
|
||||
VkShaderModuleCreateInfo vk_create_info{VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO};
|
||||
|
||||
vk_create_info.codeSize = shader_module->get_binary().size() * sizeof(uint32_t);
|
||||
vk_create_info.pCode = shader_module->get_binary().data();
|
||||
|
||||
VkResult result = vkCreateShaderModule(device.get_handle(), &vk_create_info, nullptr, &stage.module);
|
||||
if (result != VK_SUCCESS)
|
||||
{
|
||||
throw VulkanException{result};
|
||||
}
|
||||
|
||||
device.get_debug_utils().set_debug_name(device.get_handle(),
|
||||
VK_OBJECT_TYPE_SHADER_MODULE, reinterpret_cast<uint64_t>(stage.module),
|
||||
shader_module->get_debug_name().c_str());
|
||||
|
||||
// Create specialization info from tracked state.
|
||||
std::vector<uint8_t> data{};
|
||||
std::vector<VkSpecializationMapEntry> map_entries{};
|
||||
|
||||
const auto specialization_constant_state = pipeline_state.get_specialization_constant_state().get_specialization_constant_state();
|
||||
|
||||
for (const auto specialization_constant : specialization_constant_state)
|
||||
{
|
||||
map_entries.push_back({specialization_constant.first, to_u32(data.size()), specialization_constant.second.size()});
|
||||
data.insert(data.end(), specialization_constant.second.begin(), specialization_constant.second.end());
|
||||
}
|
||||
|
||||
VkSpecializationInfo specialization_info{};
|
||||
specialization_info.mapEntryCount = to_u32(map_entries.size());
|
||||
specialization_info.pMapEntries = map_entries.data();
|
||||
specialization_info.dataSize = data.size();
|
||||
specialization_info.pData = data.data();
|
||||
|
||||
stage.pSpecializationInfo = &specialization_info;
|
||||
|
||||
VkComputePipelineCreateInfo create_info{VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO};
|
||||
|
||||
create_info.layout = pipeline_state.get_pipeline_layout().get_handle();
|
||||
create_info.stage = stage;
|
||||
|
||||
result = vkCreateComputePipelines(device.get_handle(), pipeline_cache, 1, &create_info, nullptr, &handle);
|
||||
|
||||
if (result != VK_SUCCESS)
|
||||
{
|
||||
throw VulkanException{result, "Cannot create ComputePipelines"};
|
||||
}
|
||||
|
||||
vkDestroyShaderModule(device.get_handle(), stage.module, nullptr);
|
||||
}
|
||||
|
||||
GraphicsPipeline::GraphicsPipeline(vkb::core::DeviceC &device,
|
||||
VkPipelineCache pipeline_cache,
|
||||
PipelineState &pipeline_state) :
|
||||
Pipeline{device}
|
||||
{
|
||||
std::vector<VkShaderModule> shader_modules;
|
||||
|
||||
std::vector<VkPipelineShaderStageCreateInfo> stage_create_infos;
|
||||
|
||||
// Create specialization info from tracked state. This is shared by all shaders.
|
||||
std::vector<uint8_t> data{};
|
||||
std::vector<VkSpecializationMapEntry> map_entries{};
|
||||
|
||||
const auto specialization_constant_state = pipeline_state.get_specialization_constant_state().get_specialization_constant_state();
|
||||
|
||||
for (const auto specialization_constant : specialization_constant_state)
|
||||
{
|
||||
map_entries.push_back({specialization_constant.first, to_u32(data.size()), specialization_constant.second.size()});
|
||||
data.insert(data.end(), specialization_constant.second.begin(), specialization_constant.second.end());
|
||||
}
|
||||
|
||||
VkSpecializationInfo specialization_info{};
|
||||
specialization_info.mapEntryCount = to_u32(map_entries.size());
|
||||
specialization_info.pMapEntries = map_entries.data();
|
||||
specialization_info.dataSize = data.size();
|
||||
specialization_info.pData = data.data();
|
||||
|
||||
for (const ShaderModule *shader_module : pipeline_state.get_pipeline_layout().get_shader_modules())
|
||||
{
|
||||
VkPipelineShaderStageCreateInfo stage_create_info{VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO};
|
||||
|
||||
stage_create_info.stage = shader_module->get_stage();
|
||||
stage_create_info.pName = shader_module->get_entry_point().c_str();
|
||||
|
||||
// Create the Vulkan handle
|
||||
VkShaderModuleCreateInfo vk_create_info{VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO};
|
||||
|
||||
vk_create_info.codeSize = shader_module->get_binary().size() * sizeof(uint32_t);
|
||||
vk_create_info.pCode = shader_module->get_binary().data();
|
||||
|
||||
VkResult result = vkCreateShaderModule(device.get_handle(), &vk_create_info, nullptr, &stage_create_info.module);
|
||||
if (result != VK_SUCCESS)
|
||||
{
|
||||
throw VulkanException{result};
|
||||
}
|
||||
|
||||
device.get_debug_utils().set_debug_name(device.get_handle(),
|
||||
VK_OBJECT_TYPE_SHADER_MODULE, reinterpret_cast<uint64_t>(stage_create_info.module),
|
||||
shader_module->get_debug_name().c_str());
|
||||
|
||||
stage_create_info.pSpecializationInfo = &specialization_info;
|
||||
|
||||
stage_create_infos.push_back(stage_create_info);
|
||||
shader_modules.push_back(stage_create_info.module);
|
||||
}
|
||||
|
||||
VkGraphicsPipelineCreateInfo create_info{VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO};
|
||||
|
||||
create_info.stageCount = to_u32(stage_create_infos.size());
|
||||
create_info.pStages = stage_create_infos.data();
|
||||
|
||||
VkPipelineVertexInputStateCreateInfo vertex_input_state{VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO};
|
||||
|
||||
vertex_input_state.pVertexAttributeDescriptions = pipeline_state.get_vertex_input_state().attributes.data();
|
||||
vertex_input_state.vertexAttributeDescriptionCount = to_u32(pipeline_state.get_vertex_input_state().attributes.size());
|
||||
|
||||
vertex_input_state.pVertexBindingDescriptions = pipeline_state.get_vertex_input_state().bindings.data();
|
||||
vertex_input_state.vertexBindingDescriptionCount = to_u32(pipeline_state.get_vertex_input_state().bindings.size());
|
||||
|
||||
VkPipelineInputAssemblyStateCreateInfo input_assembly_state{VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO};
|
||||
|
||||
input_assembly_state.topology = pipeline_state.get_input_assembly_state().topology;
|
||||
input_assembly_state.primitiveRestartEnable = pipeline_state.get_input_assembly_state().primitive_restart_enable;
|
||||
|
||||
VkPipelineViewportStateCreateInfo viewport_state{VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO};
|
||||
|
||||
viewport_state.viewportCount = pipeline_state.get_viewport_state().viewport_count;
|
||||
viewport_state.scissorCount = pipeline_state.get_viewport_state().scissor_count;
|
||||
|
||||
VkPipelineRasterizationStateCreateInfo rasterization_state{VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO};
|
||||
|
||||
rasterization_state.depthClampEnable = pipeline_state.get_rasterization_state().depth_clamp_enable;
|
||||
rasterization_state.rasterizerDiscardEnable = pipeline_state.get_rasterization_state().rasterizer_discard_enable;
|
||||
rasterization_state.polygonMode = pipeline_state.get_rasterization_state().polygon_mode;
|
||||
rasterization_state.cullMode = pipeline_state.get_rasterization_state().cull_mode;
|
||||
rasterization_state.frontFace = pipeline_state.get_rasterization_state().front_face;
|
||||
rasterization_state.depthBiasEnable = pipeline_state.get_rasterization_state().depth_bias_enable;
|
||||
rasterization_state.depthBiasClamp = 1.0f;
|
||||
rasterization_state.depthBiasSlopeFactor = 1.0f;
|
||||
rasterization_state.lineWidth = 1.0f;
|
||||
|
||||
VkPipelineMultisampleStateCreateInfo multisample_state{VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO};
|
||||
|
||||
multisample_state.sampleShadingEnable = pipeline_state.get_multisample_state().sample_shading_enable;
|
||||
multisample_state.rasterizationSamples = pipeline_state.get_multisample_state().rasterization_samples;
|
||||
multisample_state.minSampleShading = pipeline_state.get_multisample_state().min_sample_shading;
|
||||
multisample_state.alphaToCoverageEnable = pipeline_state.get_multisample_state().alpha_to_coverage_enable;
|
||||
multisample_state.alphaToOneEnable = pipeline_state.get_multisample_state().alpha_to_one_enable;
|
||||
|
||||
if (pipeline_state.get_multisample_state().sample_mask)
|
||||
{
|
||||
multisample_state.pSampleMask = &pipeline_state.get_multisample_state().sample_mask;
|
||||
}
|
||||
|
||||
VkPipelineDepthStencilStateCreateInfo depth_stencil_state{VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO};
|
||||
|
||||
depth_stencil_state.depthTestEnable = pipeline_state.get_depth_stencil_state().depth_test_enable;
|
||||
depth_stencil_state.depthWriteEnable = pipeline_state.get_depth_stencil_state().depth_write_enable;
|
||||
depth_stencil_state.depthCompareOp = pipeline_state.get_depth_stencil_state().depth_compare_op;
|
||||
depth_stencil_state.depthBoundsTestEnable = pipeline_state.get_depth_stencil_state().depth_bounds_test_enable;
|
||||
depth_stencil_state.stencilTestEnable = pipeline_state.get_depth_stencil_state().stencil_test_enable;
|
||||
depth_stencil_state.front.failOp = pipeline_state.get_depth_stencil_state().front.fail_op;
|
||||
depth_stencil_state.front.passOp = pipeline_state.get_depth_stencil_state().front.pass_op;
|
||||
depth_stencil_state.front.depthFailOp = pipeline_state.get_depth_stencil_state().front.depth_fail_op;
|
||||
depth_stencil_state.front.compareOp = pipeline_state.get_depth_stencil_state().front.compare_op;
|
||||
depth_stencil_state.front.compareMask = ~0U;
|
||||
depth_stencil_state.front.writeMask = ~0U;
|
||||
depth_stencil_state.front.reference = ~0U;
|
||||
depth_stencil_state.back.failOp = pipeline_state.get_depth_stencil_state().back.fail_op;
|
||||
depth_stencil_state.back.passOp = pipeline_state.get_depth_stencil_state().back.pass_op;
|
||||
depth_stencil_state.back.depthFailOp = pipeline_state.get_depth_stencil_state().back.depth_fail_op;
|
||||
depth_stencil_state.back.compareOp = pipeline_state.get_depth_stencil_state().back.compare_op;
|
||||
depth_stencil_state.back.compareMask = ~0U;
|
||||
depth_stencil_state.back.writeMask = ~0U;
|
||||
depth_stencil_state.back.reference = ~0U;
|
||||
|
||||
VkPipelineColorBlendStateCreateInfo color_blend_state{VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO};
|
||||
|
||||
color_blend_state.logicOpEnable = pipeline_state.get_color_blend_state().logic_op_enable;
|
||||
color_blend_state.logicOp = pipeline_state.get_color_blend_state().logic_op;
|
||||
color_blend_state.attachmentCount = to_u32(pipeline_state.get_color_blend_state().attachments.size());
|
||||
color_blend_state.pAttachments = reinterpret_cast<const VkPipelineColorBlendAttachmentState *>(pipeline_state.get_color_blend_state().attachments.data());
|
||||
color_blend_state.blendConstants[0] = 1.0f;
|
||||
color_blend_state.blendConstants[1] = 1.0f;
|
||||
color_blend_state.blendConstants[2] = 1.0f;
|
||||
color_blend_state.blendConstants[3] = 1.0f;
|
||||
|
||||
std::array<VkDynamicState, 9> dynamic_states{
|
||||
VK_DYNAMIC_STATE_VIEWPORT,
|
||||
VK_DYNAMIC_STATE_SCISSOR,
|
||||
VK_DYNAMIC_STATE_LINE_WIDTH,
|
||||
VK_DYNAMIC_STATE_DEPTH_BIAS,
|
||||
VK_DYNAMIC_STATE_BLEND_CONSTANTS,
|
||||
VK_DYNAMIC_STATE_DEPTH_BOUNDS,
|
||||
VK_DYNAMIC_STATE_STENCIL_COMPARE_MASK,
|
||||
VK_DYNAMIC_STATE_STENCIL_WRITE_MASK,
|
||||
VK_DYNAMIC_STATE_STENCIL_REFERENCE,
|
||||
};
|
||||
|
||||
VkPipelineDynamicStateCreateInfo dynamic_state{VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO};
|
||||
|
||||
dynamic_state.pDynamicStates = dynamic_states.data();
|
||||
dynamic_state.dynamicStateCount = to_u32(dynamic_states.size());
|
||||
|
||||
create_info.pVertexInputState = &vertex_input_state;
|
||||
create_info.pInputAssemblyState = &input_assembly_state;
|
||||
create_info.pViewportState = &viewport_state;
|
||||
create_info.pRasterizationState = &rasterization_state;
|
||||
create_info.pMultisampleState = &multisample_state;
|
||||
create_info.pDepthStencilState = &depth_stencil_state;
|
||||
create_info.pColorBlendState = &color_blend_state;
|
||||
create_info.pDynamicState = &dynamic_state;
|
||||
|
||||
create_info.layout = pipeline_state.get_pipeline_layout().get_handle();
|
||||
create_info.renderPass = pipeline_state.get_render_pass()->get_handle();
|
||||
create_info.subpass = pipeline_state.get_subpass_index();
|
||||
|
||||
auto result = vkCreateGraphicsPipelines(device.get_handle(), pipeline_cache, 1, &create_info, nullptr, &handle);
|
||||
|
||||
if (result != VK_SUCCESS)
|
||||
{
|
||||
throw VulkanException{result, "Cannot create GraphicsPipelines"};
|
||||
}
|
||||
|
||||
for (auto shader_module : shader_modules)
|
||||
{
|
||||
vkDestroyShaderModule(device.get_handle(), shader_module, nullptr);
|
||||
}
|
||||
|
||||
state = pipeline_state;
|
||||
}
|
||||
} // namespace vkb
|
||||
@@ -0,0 +1,76 @@
|
||||
/* Copyright (c) 2019-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 "common/helpers.h"
|
||||
#include "common/vk_common.h"
|
||||
#include "rendering/pipeline_state.h"
|
||||
|
||||
namespace vkb
|
||||
{
|
||||
class Pipeline
|
||||
{
|
||||
public:
|
||||
Pipeline(vkb::core::DeviceC &device);
|
||||
|
||||
Pipeline(const Pipeline &) = delete;
|
||||
|
||||
Pipeline(Pipeline &&other);
|
||||
|
||||
virtual ~Pipeline();
|
||||
|
||||
Pipeline &operator=(const Pipeline &) = delete;
|
||||
|
||||
Pipeline &operator=(Pipeline &&) = delete;
|
||||
|
||||
VkPipeline get_handle() const;
|
||||
|
||||
const PipelineState &get_state() const;
|
||||
|
||||
protected:
|
||||
vkb::core::DeviceC &device;
|
||||
|
||||
VkPipeline handle = VK_NULL_HANDLE;
|
||||
|
||||
PipelineState state;
|
||||
};
|
||||
|
||||
class ComputePipeline : public Pipeline
|
||||
{
|
||||
public:
|
||||
ComputePipeline(ComputePipeline &&) = default;
|
||||
|
||||
virtual ~ComputePipeline() = default;
|
||||
|
||||
ComputePipeline(vkb::core::DeviceC &device,
|
||||
VkPipelineCache pipeline_cache,
|
||||
PipelineState &pipeline_state);
|
||||
};
|
||||
|
||||
class GraphicsPipeline : public Pipeline
|
||||
{
|
||||
public:
|
||||
GraphicsPipeline(GraphicsPipeline &&) = default;
|
||||
|
||||
virtual ~GraphicsPipeline() = default;
|
||||
|
||||
GraphicsPipeline(vkb::core::DeviceC &device,
|
||||
VkPipelineCache pipeline_cache,
|
||||
PipelineState &pipeline_state);
|
||||
};
|
||||
} // namespace vkb
|
||||
@@ -0,0 +1,210 @@
|
||||
/* Copyright (c) 2019-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 "pipeline_layout.h"
|
||||
|
||||
#include "descriptor_set_layout.h"
|
||||
#include "device.h"
|
||||
#include "pipeline.h"
|
||||
#include "resource_cache.h"
|
||||
#include "shader_module.h"
|
||||
|
||||
namespace vkb
|
||||
{
|
||||
PipelineLayout::PipelineLayout(vkb::core::DeviceC &device, const std::vector<ShaderModule *> &shader_modules) :
|
||||
device{device},
|
||||
shader_modules{shader_modules}
|
||||
{
|
||||
// Collect and combine all the shader resources from each of the shader modules
|
||||
// Collate them all into a map that is indexed by the name of the resource
|
||||
for (auto *shader_module : shader_modules)
|
||||
{
|
||||
for (const auto &shader_resource : shader_module->get_resources())
|
||||
{
|
||||
std::string key = shader_resource.name;
|
||||
|
||||
// Since 'Input' and 'Output' resources can have the same name, we modify the key string
|
||||
if (shader_resource.type == ShaderResourceType::Input || shader_resource.type == ShaderResourceType::Output)
|
||||
{
|
||||
key = std::to_string(shader_resource.stages) + "_" + key;
|
||||
}
|
||||
|
||||
auto it = shader_resources.find(key);
|
||||
|
||||
if (it != shader_resources.end())
|
||||
{
|
||||
// Append stage flags if resource already exists
|
||||
it->second.stages |= shader_resource.stages;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Create a new entry in the map
|
||||
shader_resources.emplace(key, shader_resource);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Sift through the map of name indexed shader resources
|
||||
// Separate them into their respective sets
|
||||
for (auto &it : shader_resources)
|
||||
{
|
||||
auto &shader_resource = it.second;
|
||||
|
||||
// Find binding by set index in the map.
|
||||
auto it2 = shader_sets.find(shader_resource.set);
|
||||
|
||||
if (it2 != shader_sets.end())
|
||||
{
|
||||
// Add resource to the found set index
|
||||
it2->second.push_back(shader_resource);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Create a new set index and with the first resource
|
||||
shader_sets.emplace(shader_resource.set, std::vector<ShaderResource>{shader_resource});
|
||||
}
|
||||
}
|
||||
|
||||
// Create a descriptor set layout for each shader set in the shader modules
|
||||
for (auto &shader_set_it : shader_sets)
|
||||
{
|
||||
descriptor_set_layouts.emplace_back(&device.get_resource_cache().request_descriptor_set_layout(shader_set_it.first, shader_modules, shader_set_it.second));
|
||||
}
|
||||
|
||||
// Collect all the descriptor set layout handles, maintaining set order
|
||||
std::vector<VkDescriptorSetLayout> descriptor_set_layout_handles;
|
||||
for (uint32_t i = 0; i < descriptor_set_layouts.size(); ++i)
|
||||
{
|
||||
if (descriptor_set_layouts[i])
|
||||
{
|
||||
descriptor_set_layout_handles.push_back(descriptor_set_layouts[i]->get_handle());
|
||||
}
|
||||
else
|
||||
{
|
||||
descriptor_set_layout_handles.push_back(VK_NULL_HANDLE);
|
||||
}
|
||||
}
|
||||
|
||||
// Collect all the push constant shader resources
|
||||
std::vector<VkPushConstantRange> push_constant_ranges;
|
||||
for (auto &push_constant_resource : get_resources(ShaderResourceType::PushConstant))
|
||||
{
|
||||
push_constant_ranges.push_back({push_constant_resource.stages, push_constant_resource.offset, push_constant_resource.size});
|
||||
}
|
||||
|
||||
VkPipelineLayoutCreateInfo create_info{VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO};
|
||||
|
||||
create_info.setLayoutCount = to_u32(descriptor_set_layout_handles.size());
|
||||
create_info.pSetLayouts = descriptor_set_layout_handles.data();
|
||||
create_info.pushConstantRangeCount = to_u32(push_constant_ranges.size());
|
||||
create_info.pPushConstantRanges = push_constant_ranges.data();
|
||||
|
||||
// Create the Vulkan pipeline layout handle
|
||||
auto result = vkCreatePipelineLayout(device.get_handle(), &create_info, nullptr, &handle);
|
||||
|
||||
if (result != VK_SUCCESS)
|
||||
{
|
||||
throw VulkanException{result, "Cannot create PipelineLayout"};
|
||||
}
|
||||
}
|
||||
|
||||
PipelineLayout::PipelineLayout(PipelineLayout &&other) :
|
||||
device{other.device},
|
||||
handle{other.handle},
|
||||
shader_modules{std::move(other.shader_modules)},
|
||||
shader_resources{std::move(other.shader_resources)},
|
||||
shader_sets{std::move(other.shader_sets)},
|
||||
descriptor_set_layouts{std::move(other.descriptor_set_layouts)}
|
||||
{
|
||||
other.handle = VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
PipelineLayout::~PipelineLayout()
|
||||
{
|
||||
// Destroy pipeline layout
|
||||
if (handle != VK_NULL_HANDLE)
|
||||
{
|
||||
vkDestroyPipelineLayout(device.get_handle(), handle, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
VkPipelineLayout PipelineLayout::get_handle() const
|
||||
{
|
||||
return handle;
|
||||
}
|
||||
|
||||
const std::vector<ShaderModule *> &PipelineLayout::get_shader_modules() const
|
||||
{
|
||||
return shader_modules;
|
||||
}
|
||||
|
||||
const std::vector<ShaderResource> PipelineLayout::get_resources(const ShaderResourceType &type, VkShaderStageFlagBits stage) const
|
||||
{
|
||||
std::vector<ShaderResource> found_resources;
|
||||
|
||||
for (auto &it : shader_resources)
|
||||
{
|
||||
auto &shader_resource = it.second;
|
||||
|
||||
if (shader_resource.type == type || type == ShaderResourceType::All)
|
||||
{
|
||||
if (shader_resource.stages == stage || stage == VK_SHADER_STAGE_ALL)
|
||||
{
|
||||
found_resources.push_back(shader_resource);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return found_resources;
|
||||
}
|
||||
|
||||
const std::unordered_map<uint32_t, std::vector<ShaderResource>> &PipelineLayout::get_shader_sets() const
|
||||
{
|
||||
return shader_sets;
|
||||
}
|
||||
|
||||
bool PipelineLayout::has_descriptor_set_layout(const uint32_t set_index) const
|
||||
{
|
||||
return set_index < descriptor_set_layouts.size();
|
||||
}
|
||||
|
||||
DescriptorSetLayout &PipelineLayout::get_descriptor_set_layout(const uint32_t set_index) const
|
||||
{
|
||||
for (auto &descriptor_set_layout : descriptor_set_layouts)
|
||||
{
|
||||
if (descriptor_set_layout->get_index() == set_index)
|
||||
{
|
||||
return *descriptor_set_layout;
|
||||
}
|
||||
}
|
||||
throw std::runtime_error("Couldn't find descriptor set layout at set index " + to_string(set_index));
|
||||
}
|
||||
|
||||
VkShaderStageFlags PipelineLayout::get_push_constant_range_stage(uint32_t size, uint32_t offset) const
|
||||
{
|
||||
VkShaderStageFlags stages = 0;
|
||||
|
||||
for (auto &push_constant_resource : get_resources(ShaderResourceType::PushConstant))
|
||||
{
|
||||
if (offset >= push_constant_resource.offset && offset + size <= push_constant_resource.offset + push_constant_resource.size)
|
||||
{
|
||||
stages |= push_constant_resource.stages;
|
||||
}
|
||||
}
|
||||
return stages;
|
||||
}
|
||||
} // namespace vkb
|
||||
@@ -0,0 +1,76 @@
|
||||
/* Copyright (c) 2019-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 "common/helpers.h"
|
||||
#include "common/vk_common.h"
|
||||
#include "core/descriptor_set_layout.h"
|
||||
#include "core/shader_module.h"
|
||||
|
||||
namespace vkb
|
||||
{
|
||||
class ShaderModule;
|
||||
class DescriptorSetLayout;
|
||||
|
||||
class PipelineLayout
|
||||
{
|
||||
public:
|
||||
PipelineLayout(vkb::core::DeviceC &device, const std::vector<ShaderModule *> &shader_modules);
|
||||
|
||||
PipelineLayout(const PipelineLayout &) = delete;
|
||||
|
||||
PipelineLayout(PipelineLayout &&other);
|
||||
|
||||
~PipelineLayout();
|
||||
|
||||
PipelineLayout &operator=(const PipelineLayout &) = delete;
|
||||
|
||||
PipelineLayout &operator=(PipelineLayout &&) = delete;
|
||||
|
||||
VkPipelineLayout get_handle() const;
|
||||
|
||||
const std::vector<ShaderModule *> &get_shader_modules() const;
|
||||
|
||||
const std::vector<ShaderResource> get_resources(const ShaderResourceType &type = ShaderResourceType::All, VkShaderStageFlagBits stage = VK_SHADER_STAGE_ALL) const;
|
||||
|
||||
const std::unordered_map<uint32_t, std::vector<ShaderResource>> &get_shader_sets() const;
|
||||
|
||||
bool has_descriptor_set_layout(const uint32_t set_index) const;
|
||||
|
||||
DescriptorSetLayout &get_descriptor_set_layout(const uint32_t set_index) const;
|
||||
|
||||
VkShaderStageFlags get_push_constant_range_stage(uint32_t size, uint32_t offset = 0) const;
|
||||
|
||||
private:
|
||||
vkb::core::DeviceC &device;
|
||||
|
||||
VkPipelineLayout handle{VK_NULL_HANDLE};
|
||||
|
||||
// The shader modules that this pipeline layout uses
|
||||
std::vector<ShaderModule *> shader_modules;
|
||||
|
||||
// The shader resources that this pipeline layout uses, indexed by their name
|
||||
std::unordered_map<std::string, ShaderResource> shader_resources;
|
||||
|
||||
// A map of each set and the resources it owns used by the pipeline layout
|
||||
std::unordered_map<uint32_t, std::vector<ShaderResource>> shader_sets;
|
||||
|
||||
// The different descriptor set layouts for this pipeline layout
|
||||
std::vector<DescriptorSetLayout *> descriptor_set_layouts;
|
||||
};
|
||||
} // namespace vkb
|
||||
@@ -0,0 +1,67 @@
|
||||
/* Copyright (c) 2020-2025, Broadcom Inc. 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 "query_pool.h"
|
||||
|
||||
#include "device.h"
|
||||
|
||||
namespace vkb
|
||||
{
|
||||
QueryPool::QueryPool(vkb::core::DeviceC &d, const VkQueryPoolCreateInfo &info) :
|
||||
device{d}
|
||||
{
|
||||
VK_CHECK(vkCreateQueryPool(device.get_handle(), &info, nullptr, &handle));
|
||||
}
|
||||
|
||||
QueryPool::QueryPool(QueryPool &&other) :
|
||||
device{other.device},
|
||||
handle{other.handle}
|
||||
{
|
||||
other.handle = VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
QueryPool::~QueryPool()
|
||||
{
|
||||
if (handle != VK_NULL_HANDLE)
|
||||
{
|
||||
vkDestroyQueryPool(device.get_handle(), handle, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
VkQueryPool QueryPool::get_handle() const
|
||||
{
|
||||
assert(handle != VK_NULL_HANDLE && "QueryPool handle is invalid");
|
||||
return handle;
|
||||
}
|
||||
|
||||
void QueryPool::host_reset(uint32_t first_query, uint32_t query_count)
|
||||
{
|
||||
assert(device.is_extension_enabled("VK_EXT_host_query_reset") &&
|
||||
"VK_EXT_host_query_reset needs to be enabled to call QueryPool::host_reset");
|
||||
|
||||
vkResetQueryPoolEXT(device.get_handle(), get_handle(), first_query, query_count);
|
||||
}
|
||||
|
||||
VkResult QueryPool::get_results(uint32_t first_query, uint32_t num_queries,
|
||||
size_t result_bytes, void *results, VkDeviceSize stride,
|
||||
VkQueryResultFlags flags)
|
||||
{
|
||||
return vkGetQueryPoolResults(device.get_handle(), get_handle(), first_query, num_queries,
|
||||
result_bytes, results, stride, flags);
|
||||
}
|
||||
|
||||
} // namespace vkb
|
||||
@@ -0,0 +1,85 @@
|
||||
/* Copyright (c) 2020-2025, Broadcom Inc. 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 "common/helpers.h"
|
||||
#include "common/vk_common.h"
|
||||
|
||||
namespace vkb
|
||||
{
|
||||
namespace core
|
||||
{
|
||||
template <vkb::BindingType bindingType>
|
||||
class Device;
|
||||
using DeviceC = Device<vkb::BindingType::C>;
|
||||
} // namespace core
|
||||
|
||||
/**
|
||||
* @brief Represents a Vulkan Query Pool
|
||||
*/
|
||||
class QueryPool
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief Creates a Vulkan Query Pool
|
||||
* @param d The device to use
|
||||
* @param info Creation details
|
||||
*/
|
||||
QueryPool(vkb::core::DeviceC &d, const VkQueryPoolCreateInfo &info);
|
||||
|
||||
QueryPool(const QueryPool &) = delete;
|
||||
|
||||
QueryPool(QueryPool &&pool);
|
||||
|
||||
~QueryPool();
|
||||
|
||||
QueryPool &operator=(const QueryPool &) = delete;
|
||||
|
||||
QueryPool &operator=(QueryPool &&) = delete;
|
||||
|
||||
/**
|
||||
* @return The vulkan query pool handle
|
||||
*/
|
||||
VkQueryPool get_handle() const;
|
||||
|
||||
/**
|
||||
* @brief Reset a range of queries in the query pool. Only call if VK_EXT_host_query_reset is enabled.
|
||||
* @param first_query The first query to reset
|
||||
* @param query_count The number of queries to reset
|
||||
*/
|
||||
void host_reset(uint32_t first_query, uint32_t query_count);
|
||||
|
||||
/**
|
||||
* @brief Get query pool results
|
||||
* @param first_query The initial query index
|
||||
* @param num_queries The number of queries to read
|
||||
* @param result_bytes The number of bytes in the results array
|
||||
* @param results Array of bytes result_bytes long
|
||||
* @param stride The stride in bytes between results for individual queries
|
||||
* @param flags A bitmask of VkQueryResultFlagBits
|
||||
*/
|
||||
VkResult get_results(uint32_t first_query, uint32_t num_queries,
|
||||
size_t result_bytes, void *results, VkDeviceSize stride,
|
||||
VkQueryResultFlags flags);
|
||||
|
||||
private:
|
||||
vkb::core::DeviceC &device;
|
||||
|
||||
VkQueryPool handle{VK_NULL_HANDLE};
|
||||
};
|
||||
} // namespace vkb
|
||||
@@ -0,0 +1,104 @@
|
||||
/* Copyright (c) 2019-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 "queue.h"
|
||||
|
||||
#include "command_buffer.h"
|
||||
#include "device.h"
|
||||
|
||||
namespace vkb
|
||||
{
|
||||
Queue::Queue(vkb::core::DeviceC &device, uint32_t family_index, VkQueueFamilyProperties properties, VkBool32 can_present, uint32_t index) :
|
||||
device{device}, family_index{family_index}, index{index}, can_present{can_present}, properties{properties}
|
||||
{
|
||||
vkGetDeviceQueue(device.get_handle(), family_index, index, &handle);
|
||||
}
|
||||
|
||||
Queue::Queue(Queue &&other) :
|
||||
device{other.device},
|
||||
handle{other.handle},
|
||||
family_index{other.family_index},
|
||||
index{other.index},
|
||||
can_present{other.can_present},
|
||||
properties{other.properties}
|
||||
{
|
||||
other.handle = VK_NULL_HANDLE;
|
||||
other.family_index = {};
|
||||
other.properties = {};
|
||||
other.can_present = VK_FALSE;
|
||||
other.index = 0;
|
||||
}
|
||||
|
||||
const vkb::core::DeviceC &Queue::get_device() const
|
||||
{
|
||||
return device;
|
||||
}
|
||||
|
||||
VkQueue Queue::get_handle() const
|
||||
{
|
||||
return handle;
|
||||
}
|
||||
|
||||
uint32_t Queue::get_family_index() const
|
||||
{
|
||||
return family_index;
|
||||
}
|
||||
|
||||
uint32_t Queue::get_index() const
|
||||
{
|
||||
return index;
|
||||
}
|
||||
|
||||
const VkQueueFamilyProperties &Queue::get_properties() const
|
||||
{
|
||||
return properties;
|
||||
}
|
||||
|
||||
VkBool32 Queue::support_present() const
|
||||
{
|
||||
return can_present;
|
||||
}
|
||||
|
||||
VkResult Queue::submit(const std::vector<VkSubmitInfo> &submit_infos, VkFence fence) const
|
||||
{
|
||||
return vkQueueSubmit(handle, to_u32(submit_infos.size()), submit_infos.data(), fence);
|
||||
}
|
||||
|
||||
VkResult Queue::submit(const vkb::core::CommandBufferC &command_buffer, VkFence fence) const
|
||||
{
|
||||
VkSubmitInfo submit_info{VK_STRUCTURE_TYPE_SUBMIT_INFO};
|
||||
submit_info.commandBufferCount = 1;
|
||||
submit_info.pCommandBuffers = &command_buffer.get_handle();
|
||||
|
||||
return submit({submit_info}, fence);
|
||||
}
|
||||
|
||||
VkResult Queue::present(const VkPresentInfoKHR &present_info) const
|
||||
{
|
||||
if (!can_present)
|
||||
{
|
||||
return VK_ERROR_INCOMPATIBLE_DISPLAY_KHR;
|
||||
}
|
||||
|
||||
return vkQueuePresentKHR(handle, &present_info);
|
||||
} // namespace vkb
|
||||
|
||||
VkResult Queue::wait_idle() const
|
||||
{
|
||||
return vkQueueWaitIdle(handle);
|
||||
}
|
||||
} // namespace vkb
|
||||
@@ -0,0 +1,80 @@
|
||||
/* Copyright (c) 2019-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 "common/helpers.h"
|
||||
#include "common/vk_common.h"
|
||||
#include "core/swapchain.h"
|
||||
|
||||
namespace vkb
|
||||
{
|
||||
|
||||
namespace core
|
||||
{
|
||||
template <vkb::BindingType bindingType>
|
||||
class CommandBuffer;
|
||||
using CommandBufferC = CommandBuffer<vkb::BindingType::C>;
|
||||
} // namespace core
|
||||
|
||||
class Queue
|
||||
{
|
||||
public:
|
||||
Queue(vkb::core::DeviceC &device, uint32_t family_index, VkQueueFamilyProperties properties, VkBool32 can_present, uint32_t index);
|
||||
|
||||
Queue(const Queue &) = default;
|
||||
|
||||
Queue(Queue &&other);
|
||||
|
||||
Queue &operator=(const Queue &) = delete;
|
||||
|
||||
Queue &operator=(Queue &&) = delete;
|
||||
|
||||
const vkb::core::DeviceC &get_device() const;
|
||||
|
||||
VkQueue get_handle() const;
|
||||
|
||||
uint32_t get_family_index() const;
|
||||
|
||||
uint32_t get_index() const;
|
||||
|
||||
const VkQueueFamilyProperties &get_properties() const;
|
||||
|
||||
VkBool32 support_present() const;
|
||||
|
||||
VkResult submit(const std::vector<VkSubmitInfo> &submit_infos, VkFence fence) const;
|
||||
|
||||
VkResult submit(const vkb::core::CommandBufferC &command_buffer, VkFence fence) const;
|
||||
|
||||
VkResult present(const VkPresentInfoKHR &present_infos) const;
|
||||
|
||||
VkResult wait_idle() const;
|
||||
|
||||
private:
|
||||
vkb::core::DeviceC &device;
|
||||
|
||||
VkQueue handle{VK_NULL_HANDLE};
|
||||
|
||||
uint32_t family_index{0};
|
||||
|
||||
uint32_t index{0};
|
||||
|
||||
VkBool32 can_present{VK_FALSE};
|
||||
|
||||
VkQueueFamilyProperties properties{};
|
||||
};
|
||||
} // namespace vkb
|
||||
@@ -0,0 +1,559 @@
|
||||
/* Copyright (c) 2019-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 "render_pass.h"
|
||||
|
||||
#include <numeric>
|
||||
#include <span>
|
||||
|
||||
#include "device.h"
|
||||
#include "rendering/render_target.h"
|
||||
|
||||
namespace vkb
|
||||
{
|
||||
namespace
|
||||
{
|
||||
inline void set_structure_type(VkAttachmentDescription &attachment)
|
||||
{
|
||||
// VkAttachmentDescription has no sType field
|
||||
}
|
||||
|
||||
inline void set_structure_type(VkAttachmentDescription2KHR &attachment)
|
||||
{
|
||||
attachment.sType = VK_STRUCTURE_TYPE_ATTACHMENT_DESCRIPTION_2_KHR;
|
||||
}
|
||||
|
||||
inline void set_structure_type(VkAttachmentReference &reference)
|
||||
{
|
||||
// VkAttachmentReference has no sType field
|
||||
}
|
||||
|
||||
inline void set_structure_type(VkAttachmentReference2KHR &reference)
|
||||
{
|
||||
reference.sType = VK_STRUCTURE_TYPE_ATTACHMENT_REFERENCE_2_KHR;
|
||||
}
|
||||
|
||||
inline void set_structure_type(VkRenderPassCreateInfo &create_info)
|
||||
{
|
||||
create_info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
|
||||
}
|
||||
|
||||
inline void set_structure_type(VkRenderPassCreateInfo2KHR &create_info)
|
||||
{
|
||||
create_info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO_2_KHR;
|
||||
}
|
||||
|
||||
inline void set_structure_type(VkSubpassDescription &description)
|
||||
{
|
||||
// VkSubpassDescription has no sType field
|
||||
}
|
||||
|
||||
inline void set_structure_type(VkSubpassDescription2KHR &description)
|
||||
{
|
||||
description.sType = VK_STRUCTURE_TYPE_SUBPASS_DESCRIPTION_2_KHR;
|
||||
}
|
||||
|
||||
inline void set_pointer_next(VkSubpassDescription &subpass_description, VkSubpassDescriptionDepthStencilResolveKHR &depth_resolve, VkAttachmentReference &depth_resolve_attachment)
|
||||
{
|
||||
// VkSubpassDescription cannot have pNext point to a VkSubpassDescriptionDepthStencilResolveKHR containing a VkAttachmentReference
|
||||
}
|
||||
|
||||
inline void set_pointer_next(VkSubpassDescription2KHR &subpass_description, VkSubpassDescriptionDepthStencilResolveKHR &depth_resolve, VkAttachmentReference2KHR &depth_resolve_attachment)
|
||||
{
|
||||
depth_resolve.pDepthStencilResolveAttachment = &depth_resolve_attachment;
|
||||
subpass_description.pNext = &depth_resolve;
|
||||
}
|
||||
|
||||
inline const VkAttachmentReference2KHR *get_depth_resolve_reference(const VkSubpassDescription &subpass_description)
|
||||
{
|
||||
// VkSubpassDescription cannot have pNext point to a VkSubpassDescriptionDepthStencilResolveKHR containing a VkAttachmentReference2KHR
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
inline const VkAttachmentReference2KHR *get_depth_resolve_reference(const VkSubpassDescription2KHR &subpass_description)
|
||||
{
|
||||
auto description_depth_resolve = static_cast<const VkSubpassDescriptionDepthStencilResolveKHR *>(subpass_description.pNext);
|
||||
|
||||
const VkAttachmentReference2KHR *depth_resolve_attachment = nullptr;
|
||||
if (description_depth_resolve)
|
||||
{
|
||||
depth_resolve_attachment = description_depth_resolve->pDepthStencilResolveAttachment;
|
||||
}
|
||||
|
||||
return depth_resolve_attachment;
|
||||
}
|
||||
|
||||
inline VkResult create_vk_renderpass(VkDevice device, VkRenderPassCreateInfo &create_info, VkRenderPass *handle)
|
||||
{
|
||||
return vkCreateRenderPass(device, &create_info, nullptr, handle);
|
||||
}
|
||||
|
||||
inline VkResult create_vk_renderpass(VkDevice device, VkRenderPassCreateInfo2KHR &create_info, VkRenderPass *handle)
|
||||
{
|
||||
return vkCreateRenderPass2KHR(device, &create_info, nullptr, handle);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
template <typename T>
|
||||
std::vector<T> get_attachment_descriptions(const std::vector<Attachment> &attachments, const std::vector<LoadStoreInfo> &load_store_infos)
|
||||
{
|
||||
std::vector<T> attachment_descriptions;
|
||||
|
||||
for (size_t i = 0U; i < attachments.size(); ++i)
|
||||
{
|
||||
T attachment{};
|
||||
set_structure_type(attachment);
|
||||
|
||||
attachment.format = attachments[i].format;
|
||||
attachment.samples = attachments[i].samples;
|
||||
attachment.initialLayout = attachments[i].initial_layout;
|
||||
attachment.finalLayout =
|
||||
vkb::is_depth_format(attachment.format) ? VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL : VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;
|
||||
|
||||
if (i < load_store_infos.size())
|
||||
{
|
||||
attachment.loadOp = load_store_infos[i].load_op;
|
||||
attachment.storeOp = load_store_infos[i].store_op;
|
||||
attachment.stencilLoadOp = load_store_infos[i].load_op;
|
||||
attachment.stencilStoreOp = load_store_infos[i].store_op;
|
||||
}
|
||||
|
||||
attachment_descriptions.push_back(std::move(attachment));
|
||||
}
|
||||
|
||||
return attachment_descriptions;
|
||||
}
|
||||
|
||||
template <typename T_SubpassDescription, typename T_AttachmentDescription, typename T_AttachmentReference>
|
||||
void set_attachment_layouts(std::vector<T_SubpassDescription> &subpass_descriptions, std::vector<T_AttachmentDescription> &attachment_descriptions)
|
||||
{
|
||||
// Make the initial layout same as in the first subpass using that attachment
|
||||
for (auto &subpass : subpass_descriptions)
|
||||
{
|
||||
for (size_t k = 0U; k < subpass.colorAttachmentCount; ++k)
|
||||
{
|
||||
auto &reference = subpass.pColorAttachments[k];
|
||||
// Set it only if not defined yet
|
||||
if (attachment_descriptions[reference.attachment].initialLayout == VK_IMAGE_LAYOUT_UNDEFINED)
|
||||
{
|
||||
attachment_descriptions[reference.attachment].initialLayout = reference.layout;
|
||||
}
|
||||
}
|
||||
|
||||
for (size_t k = 0U; k < subpass.inputAttachmentCount; ++k)
|
||||
{
|
||||
auto &reference = subpass.pInputAttachments[k];
|
||||
// Set it only if not defined yet
|
||||
if (attachment_descriptions[reference.attachment].initialLayout == VK_IMAGE_LAYOUT_UNDEFINED)
|
||||
{
|
||||
attachment_descriptions[reference.attachment].initialLayout = reference.layout;
|
||||
}
|
||||
}
|
||||
|
||||
if (subpass.pDepthStencilAttachment)
|
||||
{
|
||||
auto &reference = *subpass.pDepthStencilAttachment;
|
||||
// Set it only if not defined yet
|
||||
if (attachment_descriptions[reference.attachment].initialLayout == VK_IMAGE_LAYOUT_UNDEFINED)
|
||||
{
|
||||
attachment_descriptions[reference.attachment].initialLayout = reference.layout;
|
||||
}
|
||||
}
|
||||
|
||||
if (subpass.pResolveAttachments)
|
||||
{
|
||||
for (size_t k = 0U; k < subpass.colorAttachmentCount; ++k)
|
||||
{
|
||||
auto &reference = subpass.pResolveAttachments[k];
|
||||
// Set it only if not defined yet
|
||||
if (attachment_descriptions[reference.attachment].initialLayout == VK_IMAGE_LAYOUT_UNDEFINED)
|
||||
{
|
||||
attachment_descriptions[reference.attachment].initialLayout = reference.layout;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (const auto depth_resolve = get_depth_resolve_reference(subpass))
|
||||
{
|
||||
// Set it only if not defined yet
|
||||
if (attachment_descriptions[depth_resolve->attachment].initialLayout == VK_IMAGE_LAYOUT_UNDEFINED)
|
||||
{
|
||||
attachment_descriptions[depth_resolve->attachment].initialLayout = depth_resolve->layout;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Make the final layout same as the last subpass layout
|
||||
{
|
||||
auto &subpass = subpass_descriptions.back();
|
||||
|
||||
for (size_t k = 0U; k < subpass.colorAttachmentCount; ++k)
|
||||
{
|
||||
const auto &reference = subpass.pColorAttachments[k];
|
||||
|
||||
attachment_descriptions[reference.attachment].finalLayout = reference.layout;
|
||||
}
|
||||
|
||||
for (size_t k = 0U; k < subpass.inputAttachmentCount; ++k)
|
||||
{
|
||||
const auto &reference = subpass.pInputAttachments[k];
|
||||
|
||||
attachment_descriptions[reference.attachment].finalLayout = reference.layout;
|
||||
|
||||
// Do not use depth attachment if used as input
|
||||
if (vkb::is_depth_format(attachment_descriptions[reference.attachment].format))
|
||||
{
|
||||
subpass.pDepthStencilAttachment = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
if (subpass.pDepthStencilAttachment)
|
||||
{
|
||||
const auto &reference = *subpass.pDepthStencilAttachment;
|
||||
|
||||
attachment_descriptions[reference.attachment].finalLayout = reference.layout;
|
||||
}
|
||||
|
||||
if (subpass.pResolveAttachments)
|
||||
{
|
||||
for (size_t k = 0U; k < subpass.colorAttachmentCount; ++k)
|
||||
{
|
||||
const auto &reference = subpass.pResolveAttachments[k];
|
||||
|
||||
attachment_descriptions[reference.attachment].finalLayout = reference.layout;
|
||||
}
|
||||
}
|
||||
|
||||
if (const auto depth_resolve = get_depth_resolve_reference(subpass))
|
||||
{
|
||||
attachment_descriptions[depth_resolve->attachment].finalLayout = depth_resolve->layout;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Assuming there is only one depth attachment
|
||||
*/
|
||||
template <typename T_SubpassDescription, typename T_AttachmentDescription>
|
||||
bool is_depth_a_dependency(std::vector<T_SubpassDescription> &subpass_descriptions, std::vector<T_AttachmentDescription> &attachment_descriptions)
|
||||
{
|
||||
// More than 1 subpass uses depth
|
||||
if (std::ranges::count_if(subpass_descriptions,
|
||||
[](auto const &subpass) {
|
||||
return subpass.pDepthStencilAttachment != nullptr;
|
||||
}) > 1)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// Otherwise check if any uses depth as an input
|
||||
return std::ranges::any_of(
|
||||
subpass_descriptions,
|
||||
[&attachment_descriptions](auto const &subpass) {
|
||||
return std::ranges::any_of(
|
||||
std::span{subpass.pInputAttachments, subpass.inputAttachmentCount},
|
||||
[&attachment_descriptions](auto const &reference) {
|
||||
return vkb::is_depth_format(attachment_descriptions[reference.attachment].format);
|
||||
});
|
||||
});
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
std::vector<T> get_subpass_dependencies(const size_t subpass_count, bool depth_stencil_dependency)
|
||||
{
|
||||
std::vector<T> dependencies{};
|
||||
|
||||
if (subpass_count > 1)
|
||||
{
|
||||
for (uint32_t subpass_id = 0; subpass_id < to_u32(subpass_count - 1); ++subpass_id)
|
||||
{
|
||||
T color_dep{};
|
||||
color_dep.srcSubpass = subpass_id;
|
||||
color_dep.dstSubpass = subpass_id + 1;
|
||||
color_dep.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
|
||||
color_dep.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
|
||||
color_dep.srcAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
|
||||
color_dep.dstAccessMask = VK_ACCESS_INPUT_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
|
||||
color_dep.dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT;
|
||||
dependencies.push_back(color_dep);
|
||||
|
||||
if (depth_stencil_dependency)
|
||||
{
|
||||
T depth_dep{};
|
||||
depth_dep.srcSubpass = subpass_id;
|
||||
depth_dep.dstSubpass = subpass_id + 1;
|
||||
depth_dep.srcStageMask = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT;
|
||||
depth_dep.dstStageMask = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
|
||||
depth_dep.srcAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
|
||||
depth_dep.dstAccessMask = VK_ACCESS_INPUT_ATTACHMENT_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
|
||||
depth_dep.dependencyFlags = VK_DEPENDENCY_BY_REGION_BIT;
|
||||
dependencies.push_back(depth_dep);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return dependencies;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T get_attachment_reference(const uint32_t attachment, const VkImageLayout layout)
|
||||
{
|
||||
T reference{};
|
||||
set_structure_type(reference);
|
||||
|
||||
reference.attachment = attachment;
|
||||
reference.layout = layout;
|
||||
|
||||
return reference;
|
||||
}
|
||||
|
||||
template <typename T_SubpassDescription, typename T_AttachmentDescription, typename T_AttachmentReference, typename T_SubpassDependency, typename T_RenderPassCreateInfo>
|
||||
void RenderPass::create_renderpass(const std::vector<Attachment> &attachments, const std::vector<LoadStoreInfo> &load_store_infos, const std::vector<SubpassInfo> &subpasses)
|
||||
{
|
||||
if (attachments.size() != load_store_infos.size())
|
||||
{
|
||||
LOGW("Render Pass creation: size of attachment list and load/store info list does not match: {} vs {}", attachments.size(), load_store_infos.size());
|
||||
}
|
||||
|
||||
auto attachment_descriptions = get_attachment_descriptions<T_AttachmentDescription>(attachments, load_store_infos);
|
||||
|
||||
// Store attachments for every subpass
|
||||
std::vector<std::vector<T_AttachmentReference>> input_attachments{subpass_count};
|
||||
std::vector<std::vector<T_AttachmentReference>> color_attachments{subpass_count};
|
||||
std::vector<std::vector<T_AttachmentReference>> depth_stencil_attachments{subpass_count};
|
||||
std::vector<std::vector<T_AttachmentReference>> color_resolve_attachments{subpass_count};
|
||||
std::vector<std::vector<T_AttachmentReference>> depth_resolve_attachments{subpass_count};
|
||||
|
||||
std::string new_debug_name{};
|
||||
const bool needs_debug_name = get_debug_name().empty();
|
||||
if (needs_debug_name)
|
||||
{
|
||||
new_debug_name = fmt::format("RP with {} subpasses:\n", subpasses.size());
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < subpasses.size(); ++i)
|
||||
{
|
||||
auto &subpass = subpasses[i];
|
||||
|
||||
if (needs_debug_name)
|
||||
{
|
||||
new_debug_name += fmt::format("\t[{}]: {}\n", i, subpass.debug_name);
|
||||
}
|
||||
|
||||
// Fill color attachments references
|
||||
for (auto o_attachment : subpass.output_attachments)
|
||||
{
|
||||
auto initial_layout = attachments[o_attachment].initial_layout == VK_IMAGE_LAYOUT_UNDEFINED ? VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL : attachments[o_attachment].initial_layout;
|
||||
auto &description = attachment_descriptions[o_attachment];
|
||||
if (!is_depth_format(description.format))
|
||||
{
|
||||
color_attachments[i].push_back(get_attachment_reference<T_AttachmentReference>(o_attachment, initial_layout));
|
||||
}
|
||||
}
|
||||
|
||||
// Fill input attachments references
|
||||
for (auto i_attachment : subpass.input_attachments)
|
||||
{
|
||||
auto initial_layout = vkb::is_depth_format(attachments[i_attachment].format) ? VK_IMAGE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL : VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;
|
||||
input_attachments[i].push_back(get_attachment_reference<T_AttachmentReference>(i_attachment, initial_layout));
|
||||
}
|
||||
|
||||
for (auto r_attachment : subpass.color_resolve_attachments)
|
||||
{
|
||||
auto initial_layout = attachments[r_attachment].initial_layout == VK_IMAGE_LAYOUT_UNDEFINED ? VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL : attachments[r_attachment].initial_layout;
|
||||
color_resolve_attachments[i].push_back(get_attachment_reference<T_AttachmentReference>(r_attachment, initial_layout));
|
||||
}
|
||||
|
||||
if (!subpass.disable_depth_stencil_attachment)
|
||||
{
|
||||
// Assumption: depth stencil attachment appears in the list before any depth stencil resolve attachment
|
||||
auto it = find_if(attachments.begin(), attachments.end(), [](const Attachment attachment) { return is_depth_format(attachment.format); });
|
||||
if (it != attachments.end())
|
||||
{
|
||||
auto i_depth_stencil = vkb::to_u32(std::distance(attachments.begin(), it));
|
||||
auto initial_layout = it->initial_layout == VK_IMAGE_LAYOUT_UNDEFINED ? VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL : it->initial_layout;
|
||||
depth_stencil_attachments[i].push_back(get_attachment_reference<T_AttachmentReference>(i_depth_stencil, initial_layout));
|
||||
|
||||
if (subpass.depth_stencil_resolve_mode != VK_RESOLVE_MODE_NONE)
|
||||
{
|
||||
auto i_depth_stencil_resolve = subpass.depth_stencil_resolve_attachment;
|
||||
initial_layout = attachments[i_depth_stencil_resolve].initial_layout == VK_IMAGE_LAYOUT_UNDEFINED ? VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL : attachments[i_depth_stencil_resolve].initial_layout;
|
||||
depth_resolve_attachments[i].push_back(get_attachment_reference<T_AttachmentReference>(i_depth_stencil_resolve, initial_layout));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<T_SubpassDescription> subpass_descriptions;
|
||||
subpass_descriptions.reserve(subpass_count);
|
||||
VkSubpassDescriptionDepthStencilResolveKHR depth_resolve{};
|
||||
for (size_t i = 0; i < subpasses.size(); ++i)
|
||||
{
|
||||
auto &subpass = subpasses[i];
|
||||
|
||||
T_SubpassDescription subpass_description{};
|
||||
set_structure_type(subpass_description);
|
||||
subpass_description.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
|
||||
|
||||
subpass_description.pInputAttachments = input_attachments[i].empty() ? nullptr : input_attachments[i].data();
|
||||
subpass_description.inputAttachmentCount = to_u32(input_attachments[i].size());
|
||||
|
||||
subpass_description.pColorAttachments = color_attachments[i].empty() ? nullptr : color_attachments[i].data();
|
||||
subpass_description.colorAttachmentCount = to_u32(color_attachments[i].size());
|
||||
|
||||
subpass_description.pResolveAttachments = color_resolve_attachments[i].empty() ? nullptr : color_resolve_attachments[i].data();
|
||||
|
||||
subpass_description.pDepthStencilAttachment = nullptr;
|
||||
if (!depth_stencil_attachments[i].empty())
|
||||
{
|
||||
subpass_description.pDepthStencilAttachment = depth_stencil_attachments[i].data();
|
||||
|
||||
if (!depth_resolve_attachments[i].empty())
|
||||
{
|
||||
// If the pNext list of VkSubpassDescription2 includes a VkSubpassDescriptionDepthStencilResolve structure,
|
||||
// then that structure describes multisample resolve operations for the depth/stencil attachment in a subpass
|
||||
depth_resolve.sType = VK_STRUCTURE_TYPE_SUBPASS_DESCRIPTION_DEPTH_STENCIL_RESOLVE_KHR;
|
||||
depth_resolve.depthResolveMode = subpass.depth_stencil_resolve_mode;
|
||||
set_pointer_next(subpass_description, depth_resolve, depth_resolve_attachments[i][0]);
|
||||
|
||||
auto &reference = depth_resolve_attachments[i][0];
|
||||
// Set it only if not defined yet
|
||||
if (attachment_descriptions[reference.attachment].initialLayout == VK_IMAGE_LAYOUT_UNDEFINED)
|
||||
{
|
||||
attachment_descriptions[reference.attachment].initialLayout = reference.layout;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
subpass_descriptions.push_back(subpass_description);
|
||||
}
|
||||
|
||||
// Default subpass
|
||||
if (subpasses.empty())
|
||||
{
|
||||
T_SubpassDescription subpass_description{};
|
||||
set_structure_type(subpass_description);
|
||||
subpass_description.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
|
||||
uint32_t default_depth_stencil_attachment{VK_ATTACHMENT_UNUSED};
|
||||
|
||||
for (uint32_t k = 0U; k < to_u32(attachment_descriptions.size()); ++k)
|
||||
{
|
||||
if (vkb::is_depth_format(attachments[k].format))
|
||||
{
|
||||
if (default_depth_stencil_attachment == VK_ATTACHMENT_UNUSED)
|
||||
{
|
||||
default_depth_stencil_attachment = k;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
color_attachments[0].push_back(get_attachment_reference<T_AttachmentReference>(k, VK_IMAGE_LAYOUT_GENERAL));
|
||||
}
|
||||
|
||||
subpass_description.pColorAttachments = color_attachments[0].data();
|
||||
|
||||
if (default_depth_stencil_attachment != VK_ATTACHMENT_UNUSED)
|
||||
{
|
||||
depth_stencil_attachments[0].push_back(get_attachment_reference<T_AttachmentReference>(default_depth_stencil_attachment, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL));
|
||||
|
||||
subpass_description.pDepthStencilAttachment = depth_stencil_attachments[0].data();
|
||||
}
|
||||
|
||||
subpass_descriptions.push_back(subpass_description);
|
||||
}
|
||||
|
||||
set_attachment_layouts<T_SubpassDescription, T_AttachmentDescription, T_AttachmentReference>(subpass_descriptions, attachment_descriptions);
|
||||
|
||||
color_output_count.reserve(subpass_count);
|
||||
for (size_t i = 0; i < subpass_count; i++)
|
||||
{
|
||||
color_output_count.push_back(to_u32(color_attachments[i].size()));
|
||||
}
|
||||
|
||||
const auto &subpass_dependencies = get_subpass_dependencies<T_SubpassDependency>(subpass_count, is_depth_a_dependency(subpass_descriptions, attachment_descriptions));
|
||||
|
||||
T_RenderPassCreateInfo create_info{};
|
||||
set_structure_type(create_info);
|
||||
create_info.attachmentCount = to_u32(attachment_descriptions.size());
|
||||
create_info.pAttachments = attachment_descriptions.data();
|
||||
create_info.subpassCount = to_u32(subpass_descriptions.size());
|
||||
create_info.pSubpasses = subpass_descriptions.data();
|
||||
create_info.dependencyCount = to_u32(subpass_dependencies.size());
|
||||
create_info.pDependencies = subpass_dependencies.data();
|
||||
|
||||
auto result = create_vk_renderpass(get_device().get_handle(), create_info, &get_handle());
|
||||
|
||||
if (result != VK_SUCCESS)
|
||||
{
|
||||
throw VulkanException{result, "Cannot create RenderPass"};
|
||||
}
|
||||
|
||||
if (needs_debug_name)
|
||||
{
|
||||
set_debug_name(new_debug_name);
|
||||
}
|
||||
}
|
||||
|
||||
RenderPass::RenderPass(vkb::core::DeviceC &device,
|
||||
const std::vector<Attachment> &attachments,
|
||||
const std::vector<LoadStoreInfo> &load_store_infos,
|
||||
const std::vector<SubpassInfo> &subpasses) :
|
||||
VulkanResource{VK_NULL_HANDLE, &device}, subpass_count{std::max<size_t>(1, subpasses.size())}, // At least 1 subpass
|
||||
color_output_count{}
|
||||
{
|
||||
if (device.is_extension_enabled(VK_KHR_CREATE_RENDERPASS_2_EXTENSION_NAME))
|
||||
{
|
||||
create_renderpass<VkSubpassDescription2KHR, VkAttachmentDescription2KHR, VkAttachmentReference2KHR, VkSubpassDependency2KHR, VkRenderPassCreateInfo2KHR>(
|
||||
attachments, load_store_infos, subpasses);
|
||||
}
|
||||
else
|
||||
{
|
||||
create_renderpass<VkSubpassDescription, VkAttachmentDescription, VkAttachmentReference, VkSubpassDependency, VkRenderPassCreateInfo>(
|
||||
attachments, load_store_infos, subpasses);
|
||||
}
|
||||
}
|
||||
|
||||
RenderPass::RenderPass(RenderPass &&other) :
|
||||
VulkanResource{std::move(other)},
|
||||
subpass_count{other.subpass_count},
|
||||
color_output_count{other.color_output_count}
|
||||
{}
|
||||
|
||||
RenderPass::~RenderPass()
|
||||
{
|
||||
// Destroy render pass
|
||||
if (has_device())
|
||||
{
|
||||
vkDestroyRenderPass(get_device().get_handle(), get_handle(), nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
const uint32_t RenderPass::get_color_output_count(uint32_t subpass_index) const
|
||||
{
|
||||
return color_output_count[subpass_index];
|
||||
}
|
||||
|
||||
VkExtent2D RenderPass::get_render_area_granularity() const
|
||||
{
|
||||
VkExtent2D render_area_granularity = {};
|
||||
vkGetRenderAreaGranularity(get_device().get_handle(), get_handle(), &render_area_granularity);
|
||||
|
||||
return render_area_granularity;
|
||||
}
|
||||
} // namespace vkb
|
||||
@@ -0,0 +1,80 @@
|
||||
/* Copyright (c) 2019-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 "core/vulkan_resource.h"
|
||||
|
||||
namespace vkb
|
||||
{
|
||||
namespace core
|
||||
{
|
||||
template <vkb::BindingType bindingType>
|
||||
class Device;
|
||||
using DeviceC = Device<vkb::BindingType::C>;
|
||||
} // namespace core
|
||||
|
||||
struct Attachment;
|
||||
|
||||
struct SubpassInfo
|
||||
{
|
||||
std::vector<uint32_t> input_attachments;
|
||||
|
||||
std::vector<uint32_t> output_attachments;
|
||||
|
||||
std::vector<uint32_t> color_resolve_attachments;
|
||||
|
||||
bool disable_depth_stencil_attachment;
|
||||
|
||||
uint32_t depth_stencil_resolve_attachment;
|
||||
|
||||
VkResolveModeFlagBits depth_stencil_resolve_mode;
|
||||
|
||||
std::string debug_name;
|
||||
};
|
||||
|
||||
class RenderPass : public vkb::core::VulkanResourceC<VkRenderPass>
|
||||
{
|
||||
public:
|
||||
RenderPass(vkb::core::DeviceC &device,
|
||||
const std::vector<Attachment> &attachments,
|
||||
const std::vector<LoadStoreInfo> &load_store_infos,
|
||||
const std::vector<SubpassInfo> &subpasses);
|
||||
|
||||
RenderPass(const RenderPass &) = delete;
|
||||
|
||||
RenderPass(RenderPass &&other);
|
||||
|
||||
~RenderPass();
|
||||
|
||||
RenderPass &operator=(const RenderPass &) = delete;
|
||||
|
||||
RenderPass &operator=(RenderPass &&) = delete;
|
||||
|
||||
const uint32_t get_color_output_count(uint32_t subpass_index) const;
|
||||
|
||||
VkExtent2D get_render_area_granularity() const;
|
||||
|
||||
private:
|
||||
size_t subpass_count;
|
||||
|
||||
template <typename T_SubpassDescription, typename T_AttachmentDescription, typename T_AttachmentReference, typename T_SubpassDependency, typename T_RenderPassCreateInfo>
|
||||
void create_renderpass(const std::vector<Attachment> &attachments, const std::vector<LoadStoreInfo> &load_store_infos, const std::vector<SubpassInfo> &subpasses);
|
||||
|
||||
std::vector<uint32_t> color_output_count;
|
||||
};
|
||||
} // namespace vkb
|
||||
@@ -0,0 +1,105 @@
|
||||
/* Copyright (c) 2021-2023, 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 "sampled_image.h"
|
||||
|
||||
#include "rendering/render_target.h"
|
||||
|
||||
namespace vkb
|
||||
{
|
||||
namespace core
|
||||
{
|
||||
SampledImage::SampledImage(const core::ImageView &image_view, Sampler *sampler) :
|
||||
image_view{&image_view},
|
||||
target_attachment{0},
|
||||
render_target{nullptr},
|
||||
sampler{sampler},
|
||||
isDepthResolve{false}
|
||||
{}
|
||||
|
||||
SampledImage::SampledImage(uint32_t target_attachment, RenderTarget *render_target, Sampler *sampler, bool isDepthResolve) :
|
||||
image_view{nullptr},
|
||||
target_attachment{target_attachment},
|
||||
render_target{render_target},
|
||||
sampler{sampler},
|
||||
isDepthResolve{isDepthResolve}
|
||||
{}
|
||||
|
||||
SampledImage::SampledImage(const SampledImage &to_copy) :
|
||||
image_view{to_copy.image_view},
|
||||
target_attachment{to_copy.target_attachment},
|
||||
render_target{to_copy.render_target},
|
||||
sampler{to_copy.sampler},
|
||||
isDepthResolve{false}
|
||||
{}
|
||||
|
||||
SampledImage &SampledImage::operator=(const SampledImage &to_copy)
|
||||
{
|
||||
image_view = to_copy.image_view;
|
||||
target_attachment = to_copy.target_attachment;
|
||||
render_target = to_copy.render_target;
|
||||
sampler = to_copy.sampler;
|
||||
isDepthResolve = to_copy.isDepthResolve;
|
||||
return *this;
|
||||
}
|
||||
|
||||
SampledImage::SampledImage(SampledImage &&to_move) :
|
||||
image_view{std::move(to_move.image_view)},
|
||||
target_attachment{std::move(to_move.target_attachment)},
|
||||
render_target{std::move(to_move.render_target)},
|
||||
sampler{std::move(to_move.sampler)},
|
||||
isDepthResolve{std::move(to_move.isDepthResolve)}
|
||||
{}
|
||||
|
||||
SampledImage &SampledImage::operator=(SampledImage &&to_move)
|
||||
{
|
||||
image_view = std::move(to_move.image_view);
|
||||
target_attachment = std::move(to_move.target_attachment);
|
||||
render_target = std::move(to_move.render_target);
|
||||
sampler = std::move(to_move.sampler);
|
||||
isDepthResolve = std::move(to_move.isDepthResolve);
|
||||
return *this;
|
||||
}
|
||||
|
||||
const core::ImageView &SampledImage::get_image_view(const RenderTarget &default_target) const
|
||||
{
|
||||
if (image_view != nullptr)
|
||||
{
|
||||
return *image_view;
|
||||
}
|
||||
else
|
||||
{
|
||||
const auto &target = render_target ? *render_target : default_target;
|
||||
assert(target_attachment < target.get_views().size());
|
||||
return target.get_views()[target_attachment];
|
||||
}
|
||||
}
|
||||
|
||||
const uint32_t *SampledImage::get_target_attachment() const
|
||||
{
|
||||
if (image_view != nullptr)
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
else
|
||||
{
|
||||
return &target_attachment;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace core
|
||||
} // namespace vkb
|
||||
@@ -0,0 +1,143 @@
|
||||
/* Copyright (c) 2021, 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 "core/image.h"
|
||||
#include "core/sampler.h"
|
||||
#include <memory>
|
||||
|
||||
namespace vkb
|
||||
{
|
||||
class RenderTarget;
|
||||
|
||||
namespace core
|
||||
{
|
||||
/**
|
||||
* @brief A reference to a vkb::core::ImageView, plus an optional sampler for it
|
||||
* - either coming from a vkb::RenderTarget or from a user-created Image.
|
||||
*/
|
||||
class SampledImage
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief Constructs a SampledImage referencing the given image and with the given sampler.
|
||||
* @remarks If the sampler is null, a default sampler will be used.
|
||||
*/
|
||||
SampledImage(const ImageView &image_view, Sampler *sampler = nullptr);
|
||||
|
||||
/**
|
||||
* @brief Constructs a SampledImage referencing a certain attachment of a render target.
|
||||
* @remarks If the render target is null, the default is assumed.
|
||||
* If the sampler is null, a default sampler is used.
|
||||
*/
|
||||
SampledImage(uint32_t target_attachment, RenderTarget *render_target = nullptr, Sampler *sampler = nullptr, bool isDepthResolve = false);
|
||||
|
||||
SampledImage(const SampledImage &to_copy);
|
||||
SampledImage &operator=(const SampledImage &to_copy);
|
||||
|
||||
SampledImage(SampledImage &&to_move);
|
||||
SampledImage &operator=(SampledImage &&to_move);
|
||||
|
||||
~SampledImage() = default;
|
||||
|
||||
/**
|
||||
* @brief Replaces the current image view with the given one.
|
||||
*/
|
||||
inline void set_image_view(const ImageView &new_view)
|
||||
{
|
||||
image_view = &new_view;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Replaces the image view with an attachment of the PostProcessingPipeline's render target.
|
||||
*/
|
||||
inline void set_image_view(uint32_t new_attachment)
|
||||
{
|
||||
image_view = nullptr;
|
||||
target_attachment = new_attachment;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief If this view refers to a render target attachment, returns a pointer to its index;
|
||||
* otherwise, returns `null`.
|
||||
* @remarks The lifetime of the returned pointer matches that of this `SampledImage`.
|
||||
*/
|
||||
const uint32_t *get_target_attachment() const;
|
||||
|
||||
/**
|
||||
* @brief Returns either the ImageView, if set, or the image view for the set target attachment.
|
||||
* If the view has no render target associated with it, default_target is used.
|
||||
*/
|
||||
const ImageView &get_image_view(const vkb::RenderTarget &default_target) const;
|
||||
|
||||
/**
|
||||
* @brief Returns the currently-set sampler, if any.
|
||||
*/
|
||||
inline Sampler *get_sampler() const
|
||||
{
|
||||
return sampler;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets the sampler for this SampledImage.
|
||||
*/
|
||||
inline void set_sampler(Sampler *new_sampler)
|
||||
{
|
||||
sampler = new_sampler;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns the RenderTarget, if set.
|
||||
*/
|
||||
inline RenderTarget *get_render_target() const
|
||||
{
|
||||
return render_target;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Returns either the RenderTarget, if set, or - if not - the given fallback render target.
|
||||
*/
|
||||
inline RenderTarget &get_render_target(RenderTarget &fallback) const
|
||||
{
|
||||
return render_target ? *render_target : fallback;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Sets the sampler for this SampledImage.
|
||||
* Setting it to null will make it use the default instead.
|
||||
*/
|
||||
inline void set_render_target(RenderTarget *new_render_target)
|
||||
{
|
||||
render_target = std::move(new_render_target);
|
||||
}
|
||||
|
||||
inline bool is_depth_resolve() const
|
||||
{
|
||||
return isDepthResolve;
|
||||
}
|
||||
|
||||
private:
|
||||
const ImageView *image_view;
|
||||
uint32_t target_attachment;
|
||||
RenderTarget * render_target;
|
||||
Sampler * sampler;
|
||||
bool isDepthResolve;
|
||||
};
|
||||
|
||||
} // namespace core
|
||||
} // namespace vkb
|
||||
@@ -0,0 +1,55 @@
|
||||
/* Copyright (c) 2019-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 "common/helpers.h"
|
||||
#include "common/vk_common.h"
|
||||
#include "core/vulkan_resource.h"
|
||||
|
||||
namespace vkb
|
||||
{
|
||||
namespace core
|
||||
{
|
||||
template <vkb::BindingType bindingType>
|
||||
class Device;
|
||||
using DeviceC = Device<vkb::BindingType::C>;
|
||||
/**
|
||||
* @brief Represents a Vulkan Sampler
|
||||
*/
|
||||
class Sampler : public VulkanResourceC<VkSampler>
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief Creates a Vulkan Sampler
|
||||
* @param d The device to use
|
||||
* @param info Creation details
|
||||
*/
|
||||
Sampler(vkb::core::DeviceC &d, const VkSamplerCreateInfo &info);
|
||||
|
||||
Sampler(const Sampler &) = delete;
|
||||
|
||||
Sampler(Sampler &&sampler);
|
||||
|
||||
~Sampler();
|
||||
|
||||
Sampler &operator=(const Sampler &) = delete;
|
||||
|
||||
Sampler &operator=(Sampler &&) = delete;
|
||||
};
|
||||
} // namespace core
|
||||
} // namespace vkb
|
||||
@@ -0,0 +1,46 @@
|
||||
/* Copyright (c) 2019-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 "sampler.h"
|
||||
|
||||
#include "device.h"
|
||||
|
||||
namespace vkb
|
||||
{
|
||||
namespace core
|
||||
{
|
||||
Sampler::Sampler(vkb::core::DeviceC &d, const VkSamplerCreateInfo &info) :
|
||||
VulkanResource{VK_NULL_HANDLE, &d}
|
||||
{
|
||||
VK_CHECK(vkCreateSampler(get_device().get_handle(), &info, nullptr, &get_handle()));
|
||||
}
|
||||
|
||||
Sampler::Sampler(Sampler &&other) :
|
||||
VulkanResource{std::move(other)}
|
||||
{
|
||||
}
|
||||
|
||||
Sampler::~Sampler()
|
||||
{
|
||||
if (has_device())
|
||||
{
|
||||
vkDestroySampler(get_device().get_handle(), get_handle(), nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace core
|
||||
} // namespace vkb
|
||||
@@ -0,0 +1,181 @@
|
||||
/* Copyright (c) 2019-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_module.h"
|
||||
|
||||
#include "core/util/logging.hpp"
|
||||
#include "device.h"
|
||||
#include "filesystem/legacy.h"
|
||||
#include "spirv_reflection.h"
|
||||
|
||||
namespace vkb
|
||||
{
|
||||
ShaderModule::ShaderModule(vkb::core::DeviceC &device,
|
||||
VkShaderStageFlagBits stage,
|
||||
const ShaderSource &shader_source,
|
||||
const std::string &entry_point,
|
||||
const ShaderVariant &shader_variant) :
|
||||
device{device}, stage{stage}, entry_point{entry_point}
|
||||
{
|
||||
debug_name = fmt::format("{} [variant {:X}] [entrypoint {}]", shader_source.get_filename(), shader_variant.get_id(), entry_point);
|
||||
|
||||
// Shaders in binary SPIR-V format can be loaded directly
|
||||
spirv = vkb::fs::read_shader_binary_u32(shader_source.get_filename());
|
||||
|
||||
// Reflection is used to dynamically create descriptor bindings
|
||||
|
||||
SPIRVReflection spirv_reflection;
|
||||
// Reflect all shader resources
|
||||
if (!spirv_reflection.reflect_shader_resources(stage, spirv, resources, shader_variant))
|
||||
{
|
||||
throw VulkanException{VK_ERROR_INITIALIZATION_FAILED};
|
||||
}
|
||||
|
||||
// Generate a unique id, determined by source and variant
|
||||
std::hash<std::string> hasher{};
|
||||
id = hasher(std::string{reinterpret_cast<const char *>(spirv.data()),
|
||||
reinterpret_cast<const char *>(spirv.data() + spirv.size())});
|
||||
}
|
||||
|
||||
ShaderModule::ShaderModule(ShaderModule &&other) :
|
||||
device{other.device},
|
||||
id{other.id},
|
||||
stage{other.stage},
|
||||
entry_point{other.entry_point},
|
||||
debug_name{other.debug_name},
|
||||
spirv{other.spirv},
|
||||
resources{other.resources}
|
||||
{
|
||||
other.stage = {};
|
||||
}
|
||||
|
||||
size_t ShaderModule::get_id() const
|
||||
{
|
||||
return id;
|
||||
}
|
||||
|
||||
VkShaderStageFlagBits ShaderModule::get_stage() const
|
||||
{
|
||||
return stage;
|
||||
}
|
||||
|
||||
const std::string &ShaderModule::get_entry_point() const
|
||||
{
|
||||
return entry_point;
|
||||
}
|
||||
|
||||
const std::vector<ShaderResource> &ShaderModule::get_resources() const
|
||||
{
|
||||
return resources;
|
||||
}
|
||||
|
||||
const std::vector<uint32_t> &ShaderModule::get_binary() const
|
||||
{
|
||||
return spirv;
|
||||
}
|
||||
|
||||
void ShaderModule::set_resource_mode(const std::string &resource_name, const ShaderResourceMode &resource_mode)
|
||||
{
|
||||
auto it = std::ranges::find_if(resources, [&resource_name](const ShaderResource &resource) { return resource.name == resource_name; });
|
||||
|
||||
if (it != resources.end())
|
||||
{
|
||||
if (resource_mode == ShaderResourceMode::Dynamic)
|
||||
{
|
||||
if (it->type == ShaderResourceType::BufferUniform || it->type == ShaderResourceType::BufferStorage)
|
||||
{
|
||||
it->mode = resource_mode;
|
||||
}
|
||||
else
|
||||
{
|
||||
LOGW("Resource `{}` does not support dynamic.", resource_name);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
it->mode = resource_mode;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LOGW("Resource `{}` not found for shader.", resource_name);
|
||||
}
|
||||
}
|
||||
|
||||
size_t ShaderVariant::get_id() const
|
||||
{
|
||||
return id;
|
||||
}
|
||||
|
||||
void ShaderVariant::add_runtime_array_size(const std::string &runtime_array_name, size_t size)
|
||||
{
|
||||
if (runtime_array_sizes.find(runtime_array_name) == runtime_array_sizes.end())
|
||||
{
|
||||
runtime_array_sizes.insert({runtime_array_name, size});
|
||||
}
|
||||
else
|
||||
{
|
||||
runtime_array_sizes[runtime_array_name] = size;
|
||||
}
|
||||
}
|
||||
|
||||
void ShaderVariant::set_runtime_array_sizes(const std::unordered_map<std::string, size_t> &sizes)
|
||||
{
|
||||
this->runtime_array_sizes = sizes;
|
||||
}
|
||||
|
||||
const std::unordered_map<std::string, size_t> &ShaderVariant::get_runtime_array_sizes() const
|
||||
{
|
||||
return runtime_array_sizes;
|
||||
}
|
||||
|
||||
void ShaderVariant::clear()
|
||||
{
|
||||
runtime_array_sizes.clear();
|
||||
id = 0;
|
||||
}
|
||||
|
||||
ShaderSource::ShaderSource(const std::string &filename) :
|
||||
filename{filename},
|
||||
source{fs::read_text_file(filename)}
|
||||
{
|
||||
std::hash<std::string> hasher{};
|
||||
id = hasher(std::string{this->source.cbegin(), this->source.cend()});
|
||||
}
|
||||
|
||||
size_t ShaderSource::get_id() const
|
||||
{
|
||||
return id;
|
||||
}
|
||||
|
||||
const std::string &ShaderSource::get_filename() const
|
||||
{
|
||||
return filename;
|
||||
}
|
||||
|
||||
void ShaderSource::set_source(const std::string &source_)
|
||||
{
|
||||
source = source_;
|
||||
std::hash<std::string> hasher{};
|
||||
id = hasher(std::string{this->source.cbegin(), this->source.cend()});
|
||||
}
|
||||
|
||||
const std::string &ShaderSource::get_source() const
|
||||
{
|
||||
return source;
|
||||
}
|
||||
} // namespace vkb
|
||||
@@ -0,0 +1,234 @@
|
||||
/* Copyright (c) 2019-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 "common/helpers.h"
|
||||
#include "common/vk_common.h"
|
||||
|
||||
#if defined(VK_USE_PLATFORM_XLIB_KHR)
|
||||
# undef None
|
||||
#endif
|
||||
|
||||
namespace vkb
|
||||
{
|
||||
namespace core
|
||||
{
|
||||
template <vkb::BindingType bindingType>
|
||||
class Device;
|
||||
using DeviceC = Device<vkb::BindingType::C>;
|
||||
} // namespace core
|
||||
|
||||
/// Types of shader resources
|
||||
enum class ShaderResourceType
|
||||
{
|
||||
Input,
|
||||
InputAttachment,
|
||||
Output,
|
||||
Image,
|
||||
ImageSampler,
|
||||
ImageStorage,
|
||||
Sampler,
|
||||
BufferUniform,
|
||||
BufferStorage,
|
||||
PushConstant,
|
||||
SpecializationConstant,
|
||||
All
|
||||
};
|
||||
|
||||
/// This determines the type and method of how descriptor set should be created and bound
|
||||
enum class ShaderResourceMode
|
||||
{
|
||||
Static,
|
||||
Dynamic,
|
||||
UpdateAfterBind
|
||||
};
|
||||
|
||||
/// A bitmask of qualifiers applied to a resource
|
||||
struct ShaderResourceQualifiers
|
||||
{
|
||||
enum : uint32_t
|
||||
{
|
||||
None = 0,
|
||||
NonReadable = 1,
|
||||
NonWritable = 2,
|
||||
};
|
||||
};
|
||||
|
||||
/// Store shader resource data.
|
||||
/// Used by the shader module.
|
||||
struct ShaderResource
|
||||
{
|
||||
VkShaderStageFlags stages;
|
||||
|
||||
ShaderResourceType type;
|
||||
|
||||
ShaderResourceMode mode;
|
||||
|
||||
uint32_t set;
|
||||
|
||||
uint32_t binding;
|
||||
|
||||
uint32_t location;
|
||||
|
||||
uint32_t input_attachment_index;
|
||||
|
||||
uint32_t vec_size;
|
||||
|
||||
uint32_t columns;
|
||||
|
||||
uint32_t array_size;
|
||||
|
||||
uint32_t offset;
|
||||
|
||||
uint32_t size;
|
||||
|
||||
uint32_t constant_id;
|
||||
|
||||
uint32_t qualifiers;
|
||||
|
||||
std::string name;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Adds support for C style preprocessor macros to glsl shaders
|
||||
* enabling you to define or undefine certain symbols
|
||||
*/
|
||||
class ShaderVariant
|
||||
{
|
||||
public:
|
||||
ShaderVariant() = default;
|
||||
|
||||
size_t get_id() const;
|
||||
|
||||
/**
|
||||
* @brief Specifies the size of a named runtime array for automatic reflection. If already specified, overrides the size.
|
||||
* @param runtime_array_name String under which the runtime array is named in the shader
|
||||
* @param size Integer specifying the wanted size of the runtime array (in number of elements, not size in bytes), used for automatic allocation of buffers.
|
||||
* See get_declared_struct_size_runtime_array() in spirv_cross.h
|
||||
*/
|
||||
void add_runtime_array_size(const std::string &runtime_array_name, size_t size);
|
||||
|
||||
void set_runtime_array_sizes(const std::unordered_map<std::string, size_t> &sizes);
|
||||
|
||||
const std::unordered_map<std::string, size_t> &get_runtime_array_sizes() const;
|
||||
|
||||
void clear();
|
||||
|
||||
private:
|
||||
size_t id;
|
||||
|
||||
std::unordered_map<std::string, size_t> runtime_array_sizes;
|
||||
};
|
||||
|
||||
class ShaderSource
|
||||
{
|
||||
public:
|
||||
ShaderSource() = default;
|
||||
|
||||
ShaderSource(const std::string &filename);
|
||||
|
||||
size_t get_id() const;
|
||||
|
||||
const std::string &get_filename() const;
|
||||
|
||||
void set_source(const std::string &source);
|
||||
|
||||
const std::string &get_source() const;
|
||||
|
||||
private:
|
||||
size_t id;
|
||||
|
||||
std::string filename;
|
||||
|
||||
std::string source;
|
||||
};
|
||||
|
||||
/**
|
||||
* @brief Contains shader code, with an entry point, for a specific shader stage.
|
||||
* It is needed by a PipelineLayout to create a Pipeline.
|
||||
* ShaderModule can do auto-pairing between shader code and textures.
|
||||
* The low level code can change bindings, just keeping the name of the texture.
|
||||
* Variants for each texture are also generated, such as HAS_BASE_COLOR_TEX.
|
||||
* It works similarly for attribute locations. A current limitation is that only set 0
|
||||
* is considered. Uniform buffers are currently hardcoded as well.
|
||||
*/
|
||||
class ShaderModule
|
||||
{
|
||||
public:
|
||||
ShaderModule(vkb::core::DeviceC &device,
|
||||
VkShaderStageFlagBits stage,
|
||||
const ShaderSource &shader_source,
|
||||
const std::string &entry_point,
|
||||
const ShaderVariant &shader_variant);
|
||||
|
||||
ShaderModule(const ShaderModule &) = delete;
|
||||
|
||||
ShaderModule(ShaderModule &&other);
|
||||
|
||||
ShaderModule &operator=(const ShaderModule &) = delete;
|
||||
|
||||
ShaderModule &operator=(ShaderModule &&) = delete;
|
||||
|
||||
size_t get_id() const;
|
||||
|
||||
VkShaderStageFlagBits get_stage() const;
|
||||
|
||||
const std::string &get_entry_point() const;
|
||||
|
||||
const std::vector<ShaderResource> &get_resources() const;
|
||||
|
||||
const std::vector<uint32_t> &get_binary() const;
|
||||
|
||||
inline const std::string &get_debug_name() const
|
||||
{
|
||||
return debug_name;
|
||||
}
|
||||
|
||||
inline void set_debug_name(const std::string &name)
|
||||
{
|
||||
debug_name = name;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Flags a resource to use a different method of being bound to the shader
|
||||
* @param resource_name The name of the shader resource
|
||||
* @param resource_mode The mode of how the shader resource will be bound
|
||||
*/
|
||||
void set_resource_mode(const std::string &resource_name, const ShaderResourceMode &resource_mode);
|
||||
|
||||
private:
|
||||
vkb::core::DeviceC &device;
|
||||
|
||||
/// Shader unique id
|
||||
size_t id;
|
||||
|
||||
/// Stage of the shader (vertex, fragment, etc)
|
||||
VkShaderStageFlagBits stage{};
|
||||
|
||||
/// Name of the main function
|
||||
std::string entry_point;
|
||||
|
||||
/// Human-readable name for the shader
|
||||
std::string debug_name;
|
||||
|
||||
/// Compiled source
|
||||
std::vector<uint32_t> spirv;
|
||||
|
||||
std::vector<ShaderResource> resources;
|
||||
};
|
||||
} // namespace vkb
|
||||
@@ -0,0 +1,645 @@
|
||||
/* Copyright (c) 2019-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 "core/swapchain.h"
|
||||
|
||||
#include "core/util/logging.hpp"
|
||||
#include "device.h"
|
||||
#include "image.h"
|
||||
|
||||
namespace vkb
|
||||
{
|
||||
namespace
|
||||
{
|
||||
inline uint32_t choose_image_count(
|
||||
uint32_t request_image_count,
|
||||
uint32_t min_image_count,
|
||||
uint32_t max_image_count)
|
||||
{
|
||||
if (max_image_count != 0)
|
||||
{
|
||||
request_image_count = std::min(request_image_count, max_image_count);
|
||||
}
|
||||
|
||||
request_image_count = std::max(request_image_count, min_image_count);
|
||||
|
||||
return request_image_count;
|
||||
}
|
||||
|
||||
inline uint32_t choose_image_array_layers(
|
||||
uint32_t request_image_array_layers,
|
||||
uint32_t max_image_array_layers)
|
||||
{
|
||||
request_image_array_layers = std::min(request_image_array_layers, max_image_array_layers);
|
||||
request_image_array_layers = std::max(request_image_array_layers, 1u);
|
||||
|
||||
return request_image_array_layers;
|
||||
}
|
||||
|
||||
inline VkExtent2D choose_extent(
|
||||
VkExtent2D request_extent,
|
||||
const VkExtent2D &min_image_extent,
|
||||
const VkExtent2D &max_image_extent,
|
||||
const VkExtent2D ¤t_extent)
|
||||
{
|
||||
if (current_extent.width == 0xFFFFFFFF)
|
||||
{
|
||||
return request_extent;
|
||||
}
|
||||
|
||||
if (request_extent.width < 1 || request_extent.height < 1)
|
||||
{
|
||||
LOGW("(Swapchain) Image extent ({}, {}) not supported. Selecting ({}, {}).", request_extent.width, request_extent.height, current_extent.width, current_extent.height);
|
||||
return current_extent;
|
||||
}
|
||||
|
||||
request_extent.width = std::max(request_extent.width, min_image_extent.width);
|
||||
request_extent.width = std::min(request_extent.width, max_image_extent.width);
|
||||
|
||||
request_extent.height = std::max(request_extent.height, min_image_extent.height);
|
||||
request_extent.height = std::min(request_extent.height, max_image_extent.height);
|
||||
|
||||
return request_extent;
|
||||
}
|
||||
|
||||
inline VkPresentModeKHR choose_present_mode(
|
||||
VkPresentModeKHR request_present_mode,
|
||||
const std::vector<VkPresentModeKHR> &available_present_modes,
|
||||
const std::vector<VkPresentModeKHR> &present_mode_priority_list)
|
||||
{
|
||||
auto present_mode_it = std::ranges::find(available_present_modes, request_present_mode);
|
||||
|
||||
if (present_mode_it == available_present_modes.end())
|
||||
{
|
||||
// If nothing found, always default to FIFO
|
||||
VkPresentModeKHR chosen_present_mode = VK_PRESENT_MODE_FIFO_KHR;
|
||||
|
||||
for (auto &present_mode : present_mode_priority_list)
|
||||
{
|
||||
if (std::ranges::find(available_present_modes, present_mode) != available_present_modes.end())
|
||||
{
|
||||
chosen_present_mode = present_mode;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
LOGW("(Swapchain) Present mode '{}' not supported. Selecting '{}'.", to_string(request_present_mode), to_string(chosen_present_mode));
|
||||
return chosen_present_mode;
|
||||
}
|
||||
else
|
||||
{
|
||||
LOGI("(Swapchain) Present mode selected: {}", to_string(request_present_mode));
|
||||
return *present_mode_it;
|
||||
}
|
||||
}
|
||||
|
||||
inline VkSurfaceFormatKHR choose_surface_format(
|
||||
const VkSurfaceFormatKHR requested_surface_format,
|
||||
const std::vector<VkSurfaceFormatKHR> &available_surface_formats,
|
||||
const std::vector<VkSurfaceFormatKHR> &surface_format_priority_list)
|
||||
{
|
||||
// Try to find the requested surface format in the supported surface formats
|
||||
auto surface_format_it = std::ranges::find_if(
|
||||
available_surface_formats,
|
||||
[&requested_surface_format](const VkSurfaceFormatKHR &surface) {
|
||||
if (surface.format == requested_surface_format.format &&
|
||||
surface.colorSpace == requested_surface_format.colorSpace)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
// If the requested surface format isn't found, then try to request a format from the priority list
|
||||
if (surface_format_it == available_surface_formats.end())
|
||||
{
|
||||
for (auto &surface_format : surface_format_priority_list)
|
||||
{
|
||||
surface_format_it = std::ranges::find_if(
|
||||
available_surface_formats,
|
||||
[&surface_format](const VkSurfaceFormatKHR &surface) {
|
||||
if (surface.format == surface_format.format &&
|
||||
surface.colorSpace == surface_format.colorSpace)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
});
|
||||
if (surface_format_it != available_surface_formats.end())
|
||||
{
|
||||
LOGW("(Swapchain) Surface format ({}) not supported. Selecting ({}).", to_string(requested_surface_format), to_string(*surface_format_it));
|
||||
return *surface_format_it;
|
||||
}
|
||||
}
|
||||
|
||||
// If nothing found, default to the first supported surface format
|
||||
surface_format_it = available_surface_formats.begin();
|
||||
LOGW("(Swapchain) Surface format ({}) not supported. Selecting ({}).", to_string(requested_surface_format), to_string(*surface_format_it));
|
||||
}
|
||||
else
|
||||
{
|
||||
LOGI("(Swapchain) Surface format selected: {}", to_string(requested_surface_format));
|
||||
}
|
||||
|
||||
return *surface_format_it;
|
||||
}
|
||||
|
||||
inline VkSurfaceTransformFlagBitsKHR choose_transform(
|
||||
VkSurfaceTransformFlagBitsKHR request_transform,
|
||||
VkSurfaceTransformFlagsKHR supported_transform,
|
||||
VkSurfaceTransformFlagBitsKHR current_transform)
|
||||
{
|
||||
if (request_transform & supported_transform)
|
||||
{
|
||||
return request_transform;
|
||||
}
|
||||
|
||||
LOGW("(Swapchain) Surface transform '{}' not supported. Selecting '{}'.", to_string(request_transform), to_string(current_transform));
|
||||
|
||||
return current_transform;
|
||||
}
|
||||
|
||||
inline VkCompositeAlphaFlagBitsKHR choose_composite_alpha(VkCompositeAlphaFlagBitsKHR request_composite_alpha, VkCompositeAlphaFlagsKHR supported_composite_alpha)
|
||||
{
|
||||
if (request_composite_alpha & supported_composite_alpha)
|
||||
{
|
||||
return request_composite_alpha;
|
||||
}
|
||||
|
||||
static const std::vector<VkCompositeAlphaFlagBitsKHR> composite_alpha_flags = {
|
||||
VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR,
|
||||
VK_COMPOSITE_ALPHA_PRE_MULTIPLIED_BIT_KHR,
|
||||
VK_COMPOSITE_ALPHA_POST_MULTIPLIED_BIT_KHR,
|
||||
VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR};
|
||||
|
||||
for (VkCompositeAlphaFlagBitsKHR composite_alpha : composite_alpha_flags)
|
||||
{
|
||||
if (composite_alpha & supported_composite_alpha)
|
||||
{
|
||||
LOGW("(Swapchain) Composite alpha '{}' not supported. Selecting '{}.", to_string(request_composite_alpha), to_string(composite_alpha));
|
||||
return composite_alpha;
|
||||
}
|
||||
}
|
||||
|
||||
throw std::runtime_error("No compatible composite alpha found.");
|
||||
}
|
||||
|
||||
inline bool validate_format_feature(VkImageUsageFlagBits image_usage, VkFormatFeatureFlags supported_features)
|
||||
{
|
||||
switch (image_usage)
|
||||
{
|
||||
case VK_IMAGE_USAGE_STORAGE_BIT:
|
||||
return VK_FORMAT_FEATURE_STORAGE_IMAGE_BIT & supported_features;
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
inline std::set<VkImageUsageFlagBits> choose_image_usage(const std::set<VkImageUsageFlagBits> &requested_image_usage_flags, VkImageUsageFlags supported_image_usage, VkFormatFeatureFlags supported_features)
|
||||
{
|
||||
std::set<VkImageUsageFlagBits> validated_image_usage_flags;
|
||||
for (auto flag : requested_image_usage_flags)
|
||||
{
|
||||
if ((flag & supported_image_usage) && validate_format_feature(flag, supported_features))
|
||||
{
|
||||
validated_image_usage_flags.insert(flag);
|
||||
}
|
||||
else
|
||||
{
|
||||
LOGW("(Swapchain) Image usage ({}) requested but not supported.", to_string(flag));
|
||||
}
|
||||
}
|
||||
|
||||
if (validated_image_usage_flags.empty())
|
||||
{
|
||||
// Pick the first format from list of defaults, if supported
|
||||
static const std::vector<VkImageUsageFlagBits> image_usage_flags = {
|
||||
VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT,
|
||||
VK_IMAGE_USAGE_STORAGE_BIT,
|
||||
VK_IMAGE_USAGE_SAMPLED_BIT,
|
||||
VK_IMAGE_USAGE_TRANSFER_DST_BIT};
|
||||
|
||||
for (VkImageUsageFlagBits image_usage : image_usage_flags)
|
||||
{
|
||||
if ((image_usage & supported_image_usage) && validate_format_feature(image_usage, supported_features))
|
||||
{
|
||||
validated_image_usage_flags.insert(image_usage);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!validated_image_usage_flags.empty())
|
||||
{
|
||||
// Log image usage flags used
|
||||
std::string usage_list;
|
||||
for (VkImageUsageFlagBits image_usage : validated_image_usage_flags)
|
||||
{
|
||||
usage_list += to_string(image_usage) + " ";
|
||||
}
|
||||
LOGI("(Swapchain) Image usage flags: {}", usage_list);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw std::runtime_error("No compatible image usage found.");
|
||||
}
|
||||
|
||||
return validated_image_usage_flags;
|
||||
}
|
||||
|
||||
inline VkImageUsageFlags composite_image_flags(std::set<VkImageUsageFlagBits> &image_usage_flags)
|
||||
{
|
||||
VkImageUsageFlags image_usage{};
|
||||
for (auto flag : image_usage_flags)
|
||||
{
|
||||
image_usage |= flag;
|
||||
}
|
||||
return image_usage;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
Swapchain::Swapchain(Swapchain &old_swapchain, const VkExtent2D &extent) :
|
||||
Swapchain{old_swapchain,
|
||||
old_swapchain.device,
|
||||
old_swapchain.surface,
|
||||
old_swapchain.properties.present_mode,
|
||||
old_swapchain.present_mode_priority_list,
|
||||
old_swapchain.surface_format_priority_list,
|
||||
extent,
|
||||
old_swapchain.properties.image_count,
|
||||
old_swapchain.properties.pre_transform,
|
||||
old_swapchain.image_usage_flags,
|
||||
old_swapchain.requested_compression,
|
||||
old_swapchain.requested_compression_fixed_rate}
|
||||
{}
|
||||
|
||||
Swapchain::Swapchain(Swapchain &old_swapchain, const uint32_t image_count) :
|
||||
Swapchain{old_swapchain,
|
||||
old_swapchain.device,
|
||||
old_swapchain.surface,
|
||||
old_swapchain.properties.present_mode,
|
||||
old_swapchain.present_mode_priority_list,
|
||||
old_swapchain.surface_format_priority_list,
|
||||
old_swapchain.properties.extent,
|
||||
image_count,
|
||||
old_swapchain.properties.pre_transform,
|
||||
old_swapchain.image_usage_flags,
|
||||
old_swapchain.requested_compression,
|
||||
old_swapchain.requested_compression_fixed_rate}
|
||||
{}
|
||||
|
||||
Swapchain::Swapchain(Swapchain &old_swapchain, const std::set<VkImageUsageFlagBits> &image_usage_flags) :
|
||||
Swapchain{old_swapchain,
|
||||
old_swapchain.device,
|
||||
old_swapchain.surface,
|
||||
old_swapchain.properties.present_mode,
|
||||
old_swapchain.present_mode_priority_list,
|
||||
old_swapchain.surface_format_priority_list,
|
||||
old_swapchain.properties.extent,
|
||||
old_swapchain.properties.image_count,
|
||||
old_swapchain.properties.pre_transform,
|
||||
image_usage_flags,
|
||||
old_swapchain.requested_compression,
|
||||
old_swapchain.requested_compression_fixed_rate}
|
||||
{}
|
||||
|
||||
Swapchain::Swapchain(Swapchain &old_swapchain, const VkExtent2D &extent, const VkSurfaceTransformFlagBitsKHR transform) :
|
||||
Swapchain{old_swapchain,
|
||||
old_swapchain.device,
|
||||
old_swapchain.surface,
|
||||
old_swapchain.properties.present_mode,
|
||||
old_swapchain.present_mode_priority_list,
|
||||
old_swapchain.surface_format_priority_list,
|
||||
extent,
|
||||
old_swapchain.properties.image_count,
|
||||
transform,
|
||||
old_swapchain.image_usage_flags,
|
||||
old_swapchain.requested_compression,
|
||||
old_swapchain.requested_compression_fixed_rate}
|
||||
{}
|
||||
|
||||
Swapchain::Swapchain(Swapchain &old_swapchain, const VkImageCompressionFlagsEXT requested_compression, const VkImageCompressionFixedRateFlagsEXT requested_compression_fixed_rate) :
|
||||
Swapchain{old_swapchain,
|
||||
old_swapchain.device,
|
||||
old_swapchain.surface,
|
||||
old_swapchain.properties.present_mode,
|
||||
old_swapchain.present_mode_priority_list,
|
||||
old_swapchain.surface_format_priority_list,
|
||||
old_swapchain.properties.extent,
|
||||
old_swapchain.properties.image_count,
|
||||
old_swapchain.properties.pre_transform,
|
||||
old_swapchain.image_usage_flags,
|
||||
requested_compression,
|
||||
requested_compression_fixed_rate}
|
||||
{}
|
||||
|
||||
Swapchain::Swapchain(vkb::core::DeviceC &device,
|
||||
VkSurfaceKHR surface,
|
||||
const VkPresentModeKHR present_mode,
|
||||
std::vector<VkPresentModeKHR> const &present_mode_priority_list,
|
||||
const std::vector<VkSurfaceFormatKHR> &surface_format_priority_list,
|
||||
const VkExtent2D &extent,
|
||||
const uint32_t image_count,
|
||||
const VkSurfaceTransformFlagBitsKHR transform,
|
||||
const std::set<VkImageUsageFlagBits> &image_usage_flags,
|
||||
const VkImageCompressionFlagsEXT requested_compression,
|
||||
const VkImageCompressionFixedRateFlagsEXT requested_compression_fixed_rate) :
|
||||
Swapchain{*this, device, surface, present_mode, present_mode_priority_list, surface_format_priority_list, extent, image_count, transform, image_usage_flags}
|
||||
{
|
||||
}
|
||||
|
||||
Swapchain::Swapchain(Swapchain &old_swapchain,
|
||||
vkb::core::DeviceC &device,
|
||||
VkSurfaceKHR surface,
|
||||
const VkPresentModeKHR present_mode,
|
||||
std::vector<VkPresentModeKHR> const &present_mode_priority_list,
|
||||
const std::vector<VkSurfaceFormatKHR> &surface_format_priority_list,
|
||||
const VkExtent2D &extent,
|
||||
const uint32_t image_count,
|
||||
const VkSurfaceTransformFlagBitsKHR transform,
|
||||
const std::set<VkImageUsageFlagBits> &image_usage_flags,
|
||||
const VkImageCompressionFlagsEXT requested_compression,
|
||||
const VkImageCompressionFixedRateFlagsEXT requested_compression_fixed_rate) :
|
||||
device{device},
|
||||
surface{surface},
|
||||
requested_compression{requested_compression},
|
||||
requested_compression_fixed_rate{requested_compression_fixed_rate}
|
||||
{
|
||||
this->present_mode_priority_list = present_mode_priority_list;
|
||||
this->surface_format_priority_list = surface_format_priority_list;
|
||||
|
||||
VkSurfaceCapabilitiesKHR surface_capabilities{};
|
||||
vkGetPhysicalDeviceSurfaceCapabilitiesKHR(this->device.get_gpu().get_handle(), surface, &surface_capabilities);
|
||||
|
||||
uint32_t surface_format_count{0U};
|
||||
VK_CHECK(vkGetPhysicalDeviceSurfaceFormatsKHR(this->device.get_gpu().get_handle(), surface, &surface_format_count, nullptr));
|
||||
|
||||
std::vector<VkSurfaceFormatKHR> surface_formats(surface_format_count);
|
||||
VK_CHECK(vkGetPhysicalDeviceSurfaceFormatsKHR(this->device.get_gpu().get_handle(), surface, &surface_format_count, surface_formats.data()));
|
||||
|
||||
LOGI("Surface supports the following surface formats:");
|
||||
for (auto &surface_format : surface_formats)
|
||||
{
|
||||
LOGI(" \t{}", to_string(surface_format));
|
||||
}
|
||||
|
||||
uint32_t present_mode_count{0U};
|
||||
VK_CHECK(vkGetPhysicalDeviceSurfacePresentModesKHR(this->device.get_gpu().get_handle(), surface, &present_mode_count, nullptr));
|
||||
|
||||
std::vector<VkPresentModeKHR> present_modes(present_mode_count);
|
||||
VK_CHECK(vkGetPhysicalDeviceSurfacePresentModesKHR(this->device.get_gpu().get_handle(), surface, &present_mode_count, present_modes.data()));
|
||||
|
||||
LOGI("Surface supports the following present modes:");
|
||||
for (auto &pm : present_modes)
|
||||
{
|
||||
LOGI(" \t{}", to_string(pm));
|
||||
}
|
||||
|
||||
// Choose best properties based on surface capabilities
|
||||
properties.old_swapchain = old_swapchain.get_handle();
|
||||
properties.image_count = choose_image_count(image_count, surface_capabilities.minImageCount, surface_capabilities.maxImageCount);
|
||||
properties.extent = choose_extent(extent, surface_capabilities.minImageExtent, surface_capabilities.maxImageExtent, surface_capabilities.currentExtent);
|
||||
properties.surface_format = choose_surface_format(properties.surface_format, surface_formats, surface_format_priority_list);
|
||||
properties.array_layers = choose_image_array_layers(1U, surface_capabilities.maxImageArrayLayers);
|
||||
|
||||
VkFormatProperties format_properties;
|
||||
vkGetPhysicalDeviceFormatProperties(this->device.get_gpu().get_handle(), properties.surface_format.format, &format_properties);
|
||||
this->image_usage_flags = choose_image_usage(image_usage_flags, surface_capabilities.supportedUsageFlags, format_properties.optimalTilingFeatures);
|
||||
|
||||
properties.image_usage = composite_image_flags(this->image_usage_flags);
|
||||
properties.pre_transform = choose_transform(transform, surface_capabilities.supportedTransforms, surface_capabilities.currentTransform);
|
||||
properties.composite_alpha = choose_composite_alpha(VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR, surface_capabilities.supportedCompositeAlpha);
|
||||
properties.present_mode = choose_present_mode(present_mode, present_modes, present_mode_priority_list);
|
||||
|
||||
VkSwapchainCreateInfoKHR create_info{VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR};
|
||||
create_info.minImageCount = properties.image_count;
|
||||
create_info.imageExtent = properties.extent;
|
||||
create_info.presentMode = properties.present_mode;
|
||||
create_info.imageFormat = properties.surface_format.format;
|
||||
create_info.imageColorSpace = properties.surface_format.colorSpace;
|
||||
create_info.imageArrayLayers = properties.array_layers;
|
||||
create_info.imageUsage = properties.image_usage;
|
||||
create_info.preTransform = properties.pre_transform;
|
||||
create_info.compositeAlpha = properties.composite_alpha;
|
||||
create_info.oldSwapchain = properties.old_swapchain;
|
||||
create_info.surface = surface;
|
||||
|
||||
auto fixed_rate_flags = requested_compression_fixed_rate;
|
||||
VkImageCompressionControlEXT compression_control{VK_STRUCTURE_TYPE_IMAGE_COMPRESSION_CONTROL_EXT};
|
||||
compression_control.flags = requested_compression;
|
||||
if (device.is_extension_enabled(VK_EXT_IMAGE_COMPRESSION_CONTROL_SWAPCHAIN_EXTENSION_NAME))
|
||||
{
|
||||
create_info.pNext = &compression_control;
|
||||
|
||||
if (VK_IMAGE_COMPRESSION_FIXED_RATE_EXPLICIT_EXT == requested_compression)
|
||||
{
|
||||
// Do not support compression for multi-planar formats
|
||||
compression_control.compressionControlPlaneCount = 1;
|
||||
compression_control.pFixedRateFlags = &fixed_rate_flags;
|
||||
}
|
||||
else if (VK_IMAGE_COMPRESSION_DISABLED_EXT == requested_compression)
|
||||
{
|
||||
LOGW("(Swapchain) Disabling default (lossless) compression, which can negatively impact performance")
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (VK_IMAGE_COMPRESSION_DEFAULT_EXT != requested_compression)
|
||||
{
|
||||
LOGW("(Swapchain) Compression cannot be controlled because VK_EXT_image_compression_control_swapchain is not enabled")
|
||||
|
||||
this->requested_compression = VK_IMAGE_COMPRESSION_DEFAULT_EXT;
|
||||
this->requested_compression_fixed_rate = VK_IMAGE_COMPRESSION_FIXED_RATE_NONE_EXT;
|
||||
}
|
||||
}
|
||||
|
||||
VkResult result = vkCreateSwapchainKHR(device.get_handle(), &create_info, nullptr, &handle);
|
||||
|
||||
if (result != VK_SUCCESS)
|
||||
{
|
||||
throw VulkanException{result, "Cannot create Swapchain"};
|
||||
}
|
||||
|
||||
uint32_t image_available{0u};
|
||||
VK_CHECK(vkGetSwapchainImagesKHR(device.get_handle(), handle, &image_available, nullptr));
|
||||
|
||||
images.resize(image_available);
|
||||
|
||||
VK_CHECK(vkGetSwapchainImagesKHR(device.get_handle(), handle, &image_available, images.data()));
|
||||
|
||||
if (device.is_extension_enabled(VK_EXT_IMAGE_COMPRESSION_CONTROL_SWAPCHAIN_EXTENSION_NAME) &&
|
||||
VK_IMAGE_COMPRESSION_FIXED_RATE_EXPLICIT_EXT == requested_compression)
|
||||
{
|
||||
// Check if fixed-rate compression was applied
|
||||
const auto applied_compression_fixed_rate = vkb::query_applied_compression(device.get_handle(), images[0]).imageCompressionFixedRateFlags;
|
||||
|
||||
if (applied_compression_fixed_rate != requested_compression_fixed_rate)
|
||||
{
|
||||
LOGW("(Swapchain) Requested fixed-rate compression ({}) was not applied, instead images use {}",
|
||||
image_compression_fixed_rate_flags_to_string(requested_compression_fixed_rate),
|
||||
image_compression_fixed_rate_flags_to_string(applied_compression_fixed_rate));
|
||||
|
||||
this->requested_compression_fixed_rate = applied_compression_fixed_rate;
|
||||
|
||||
if (VK_IMAGE_COMPRESSION_FIXED_RATE_NONE_EXT == applied_compression_fixed_rate)
|
||||
{
|
||||
this->requested_compression = VK_IMAGE_COMPRESSION_DEFAULT_EXT;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LOGI("(Swapchain) Applied fixed-rate compression: {}",
|
||||
image_compression_fixed_rate_flags_to_string(applied_compression_fixed_rate));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Swapchain::~Swapchain()
|
||||
{
|
||||
if (handle != VK_NULL_HANDLE)
|
||||
{
|
||||
vkDestroySwapchainKHR(device.get_handle(), handle, nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
Swapchain::Swapchain(Swapchain &&other) :
|
||||
device{other.device},
|
||||
surface{std::exchange(other.surface, VK_NULL_HANDLE)},
|
||||
handle{std::exchange(other.handle, VK_NULL_HANDLE)},
|
||||
images{std::exchange(other.images, {})},
|
||||
properties{std::exchange(other.properties, {})},
|
||||
present_mode_priority_list{std::exchange(other.present_mode_priority_list, {})},
|
||||
surface_format_priority_list{std::exchange(other.surface_format_priority_list, {})},
|
||||
image_usage_flags{std::move(other.image_usage_flags)}
|
||||
{
|
||||
}
|
||||
|
||||
bool Swapchain::is_valid() const
|
||||
{
|
||||
return handle != VK_NULL_HANDLE;
|
||||
}
|
||||
|
||||
vkb::core::DeviceC &Swapchain::get_device()
|
||||
{
|
||||
return device;
|
||||
}
|
||||
|
||||
VkSwapchainKHR Swapchain::get_handle() const
|
||||
{
|
||||
return handle;
|
||||
}
|
||||
|
||||
VkResult Swapchain::acquire_next_image(uint32_t &image_index, VkSemaphore image_acquired_semaphore, VkFence fence) const
|
||||
{
|
||||
return vkAcquireNextImageKHR(device.get_handle(), handle, std::numeric_limits<uint64_t>::max(), image_acquired_semaphore, fence, &image_index);
|
||||
}
|
||||
|
||||
const VkExtent2D &Swapchain::get_extent() const
|
||||
{
|
||||
return properties.extent;
|
||||
}
|
||||
|
||||
VkFormat Swapchain::get_format() const
|
||||
{
|
||||
return properties.surface_format.format;
|
||||
}
|
||||
|
||||
VkSurfaceFormatKHR Swapchain::get_surface_format() const
|
||||
{
|
||||
return properties.surface_format;
|
||||
}
|
||||
|
||||
const std::vector<VkImage> &Swapchain::get_images() const
|
||||
{
|
||||
return images;
|
||||
}
|
||||
|
||||
VkSurfaceTransformFlagBitsKHR Swapchain::get_transform() const
|
||||
{
|
||||
return properties.pre_transform;
|
||||
}
|
||||
|
||||
VkSurfaceKHR Swapchain::get_surface() const
|
||||
{
|
||||
return surface;
|
||||
}
|
||||
|
||||
VkImageUsageFlags Swapchain::get_usage() const
|
||||
{
|
||||
return properties.image_usage;
|
||||
}
|
||||
|
||||
VkPresentModeKHR Swapchain::get_present_mode() const
|
||||
{
|
||||
return properties.present_mode;
|
||||
}
|
||||
|
||||
VkImageCompressionFlagsEXT Swapchain::get_applied_compression() const
|
||||
{
|
||||
return vkb::query_applied_compression(device.get_handle(), get_images()[0]).imageCompressionFlags;
|
||||
}
|
||||
|
||||
std::vector<Swapchain::SurfaceFormatCompression> Swapchain::query_supported_fixed_rate_compression(vkb::core::DeviceC &device, const VkSurfaceKHR &surface)
|
||||
{
|
||||
std::vector<SurfaceFormatCompression> surface_format_compression_list;
|
||||
|
||||
if (device.is_extension_enabled(VK_EXT_IMAGE_COMPRESSION_CONTROL_SWAPCHAIN_EXTENSION_NAME))
|
||||
{
|
||||
if (device.get_gpu().get_instance().is_enabled(VK_KHR_GET_SURFACE_CAPABILITIES_2_EXTENSION_NAME))
|
||||
{
|
||||
VkPhysicalDeviceSurfaceInfo2KHR surface_info{VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SURFACE_INFO_2_KHR};
|
||||
surface_info.surface = surface;
|
||||
|
||||
uint32_t surface_format_count{0U};
|
||||
|
||||
VK_CHECK(vkGetPhysicalDeviceSurfaceFormats2KHR(device.get_gpu().get_handle(), &surface_info, &surface_format_count, nullptr));
|
||||
|
||||
std::vector<VkSurfaceFormat2KHR> surface_formats;
|
||||
surface_formats.resize(surface_format_count, {VK_STRUCTURE_TYPE_SURFACE_FORMAT_2_KHR});
|
||||
|
||||
std::vector<VkImageCompressionPropertiesEXT> compression_properties;
|
||||
compression_properties.resize(surface_format_count, {VK_STRUCTURE_TYPE_IMAGE_COMPRESSION_PROPERTIES_EXT});
|
||||
|
||||
for (uint32_t i = 0; i < surface_format_count; i++)
|
||||
{
|
||||
surface_formats[i].pNext = &compression_properties[i];
|
||||
}
|
||||
|
||||
VK_CHECK(vkGetPhysicalDeviceSurfaceFormats2KHR(device.get_gpu().get_handle(), &surface_info, &surface_format_count, surface_formats.data()));
|
||||
|
||||
surface_format_compression_list.reserve(surface_format_count);
|
||||
for (uint32_t i = 0; i < surface_format_count; i++)
|
||||
{
|
||||
surface_format_compression_list.push_back({surface_formats[i], compression_properties[i]});
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LOGW("(Swapchain) To query fixed-rate compression support, instance extension VK_KHR_get_surface_capabilities2 must be enabled")
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LOGW("(Swapchain) To query fixed-rate compression support, device extension VK_EXT_image_compression_control_swapchain must be enabled")
|
||||
}
|
||||
|
||||
return surface_format_compression_list;
|
||||
}
|
||||
} // namespace vkb
|
||||
@@ -0,0 +1,194 @@
|
||||
/* Copyright (c) 2019-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 "common/helpers.h"
|
||||
#include "common/vk_common.h"
|
||||
|
||||
namespace vkb
|
||||
{
|
||||
namespace core
|
||||
{
|
||||
template <vkb::BindingType bindingType>
|
||||
class Device;
|
||||
using DeviceC = Device<vkb::BindingType::C>;
|
||||
} // namespace core
|
||||
|
||||
enum ImageFormat
|
||||
{
|
||||
sRGB,
|
||||
UNORM
|
||||
};
|
||||
|
||||
struct SwapchainProperties
|
||||
{
|
||||
VkSwapchainKHR old_swapchain;
|
||||
uint32_t image_count{3};
|
||||
VkExtent2D extent{};
|
||||
VkSurfaceFormatKHR surface_format{};
|
||||
uint32_t array_layers;
|
||||
VkImageUsageFlags image_usage;
|
||||
VkSurfaceTransformFlagBitsKHR pre_transform;
|
||||
VkCompositeAlphaFlagBitsKHR composite_alpha;
|
||||
VkPresentModeKHR present_mode;
|
||||
};
|
||||
|
||||
class Swapchain
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* @brief Constructor to create a swapchain by changing the extent
|
||||
* only and preserving the configuration from the old swapchain.
|
||||
*/
|
||||
Swapchain(Swapchain &old_swapchain, const VkExtent2D &extent);
|
||||
|
||||
/**
|
||||
* @brief Constructor to create a swapchain by changing the image count
|
||||
* only and preserving the configuration from the old swapchain.
|
||||
*/
|
||||
Swapchain(Swapchain &old_swapchain, const uint32_t image_count);
|
||||
|
||||
/**
|
||||
* @brief Constructor to create a swapchain by changing the image usage
|
||||
* only and preserving the configuration from the old swapchain.
|
||||
*/
|
||||
Swapchain(Swapchain &old_swapchain, const std::set<VkImageUsageFlagBits> &image_usage_flags);
|
||||
|
||||
/**
|
||||
* @brief Constructor to create a swapchain by changing the extent
|
||||
* and transform only and preserving the configuration from the old swapchain.
|
||||
*/
|
||||
Swapchain(Swapchain &swapchain, const VkExtent2D &extent, const VkSurfaceTransformFlagBitsKHR transform);
|
||||
|
||||
/**
|
||||
* @brief Constructor to create a swapchain by changing the compression settings
|
||||
* only and preserving the configuration from the old swapchain.
|
||||
*/
|
||||
Swapchain(Swapchain &swapchain, const VkImageCompressionFlagsEXT requested_compression, const VkImageCompressionFixedRateFlagsEXT requested_compression_fixed_rate);
|
||||
|
||||
/**
|
||||
* @brief Constructor to create a swapchain.
|
||||
*/
|
||||
Swapchain(vkb::core::DeviceC &device,
|
||||
VkSurfaceKHR surface,
|
||||
const VkPresentModeKHR present_mode,
|
||||
const std::vector<VkPresentModeKHR> &present_mode_priority_list = {VK_PRESENT_MODE_FIFO_KHR,
|
||||
VK_PRESENT_MODE_MAILBOX_KHR},
|
||||
const std::vector<VkSurfaceFormatKHR> &surface_format_priority_list = {{VK_FORMAT_R8G8B8A8_SRGB, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR},
|
||||
{VK_FORMAT_B8G8R8A8_SRGB, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR}},
|
||||
const VkExtent2D &extent = {},
|
||||
const uint32_t image_count = 3,
|
||||
const VkSurfaceTransformFlagBitsKHR transform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR,
|
||||
const std::set<VkImageUsageFlagBits> &image_usage_flags = {VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, VK_IMAGE_USAGE_TRANSFER_SRC_BIT},
|
||||
const VkImageCompressionFlagsEXT requested_compression = VK_IMAGE_COMPRESSION_DEFAULT_EXT,
|
||||
const VkImageCompressionFixedRateFlagsEXT requested_compression_fixed_rate = VK_IMAGE_COMPRESSION_FIXED_RATE_NONE_EXT);
|
||||
|
||||
/**
|
||||
* @brief Constructor to create a swapchain from the old swapchain
|
||||
* by configuring all parameters.
|
||||
*/
|
||||
Swapchain(Swapchain &old_swapchain,
|
||||
vkb::core::DeviceC &device,
|
||||
VkSurfaceKHR surface,
|
||||
const VkPresentModeKHR present_mode,
|
||||
const std::vector<VkPresentModeKHR> &present_mode_priority_list = {VK_PRESENT_MODE_FIFO_KHR, VK_PRESENT_MODE_MAILBOX_KHR},
|
||||
const std::vector<VkSurfaceFormatKHR> &surface_format_priority_list = {{VK_FORMAT_R8G8B8A8_SRGB, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR},
|
||||
{VK_FORMAT_B8G8R8A8_SRGB, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR}},
|
||||
const VkExtent2D &extent = {},
|
||||
const uint32_t image_count = 3,
|
||||
const VkSurfaceTransformFlagBitsKHR transform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR,
|
||||
const std::set<VkImageUsageFlagBits> &image_usage_flags = {VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, VK_IMAGE_USAGE_TRANSFER_SRC_BIT},
|
||||
const VkImageCompressionFlagsEXT requested_compression = VK_IMAGE_COMPRESSION_DEFAULT_EXT,
|
||||
const VkImageCompressionFixedRateFlagsEXT requested_compression_fixed_rate = VK_IMAGE_COMPRESSION_FIXED_RATE_NONE_EXT);
|
||||
|
||||
Swapchain(const Swapchain &) = delete;
|
||||
|
||||
Swapchain(Swapchain &&other);
|
||||
|
||||
~Swapchain();
|
||||
|
||||
Swapchain &operator=(const Swapchain &) = delete;
|
||||
|
||||
Swapchain &operator=(Swapchain &&) = delete;
|
||||
|
||||
bool is_valid() const;
|
||||
|
||||
vkb::core::DeviceC &get_device();
|
||||
|
||||
VkSwapchainKHR get_handle() const;
|
||||
|
||||
VkResult acquire_next_image(uint32_t &image_index, VkSemaphore image_acquired_semaphore, VkFence fence = VK_NULL_HANDLE) const;
|
||||
|
||||
const VkExtent2D &get_extent() const;
|
||||
|
||||
VkFormat get_format() const;
|
||||
|
||||
VkSurfaceFormatKHR get_surface_format() const;
|
||||
|
||||
const std::vector<VkImage> &get_images() const;
|
||||
|
||||
VkSurfaceTransformFlagBitsKHR get_transform() const;
|
||||
|
||||
VkSurfaceKHR get_surface() const;
|
||||
|
||||
VkImageUsageFlags get_usage() const;
|
||||
|
||||
VkPresentModeKHR get_present_mode() const;
|
||||
|
||||
VkImageCompressionFlagsEXT get_applied_compression() const;
|
||||
|
||||
/**
|
||||
* Helper functions for compression controls
|
||||
*/
|
||||
|
||||
struct SurfaceFormatCompression
|
||||
{
|
||||
VkSurfaceFormat2KHR surface_format{VK_STRUCTURE_TYPE_SURFACE_FORMAT_2_KHR};
|
||||
VkImageCompressionPropertiesEXT compression_properties{VK_STRUCTURE_TYPE_IMAGE_COMPRESSION_PROPERTIES_EXT};
|
||||
};
|
||||
|
||||
static std::vector<SurfaceFormatCompression> query_supported_fixed_rate_compression(vkb::core::DeviceC &device, const VkSurfaceKHR &surface);
|
||||
|
||||
private:
|
||||
vkb::core::DeviceC &device;
|
||||
|
||||
VkSurfaceKHR surface{VK_NULL_HANDLE};
|
||||
|
||||
VkSwapchainKHR handle{VK_NULL_HANDLE};
|
||||
|
||||
std::vector<VkImage> images;
|
||||
|
||||
SwapchainProperties properties;
|
||||
|
||||
// A list of present modes in order of priority (vector[0] has high priority, vector[size-1] has low priority)
|
||||
std::vector<VkPresentModeKHR> present_mode_priority_list = {
|
||||
VK_PRESENT_MODE_FIFO_KHR,
|
||||
VK_PRESENT_MODE_MAILBOX_KHR};
|
||||
|
||||
// A list of surface formats in order of priority (vector[0] has high priority, vector[size-1] has low priority)
|
||||
std::vector<VkSurfaceFormatKHR> surface_format_priority_list = {
|
||||
{VK_FORMAT_R8G8B8A8_SRGB, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR},
|
||||
{VK_FORMAT_B8G8R8A8_SRGB, VK_COLOR_SPACE_SRGB_NONLINEAR_KHR}};
|
||||
|
||||
std::set<VkImageUsageFlagBits> image_usage_flags;
|
||||
|
||||
VkImageCompressionFlagsEXT requested_compression{VK_IMAGE_COMPRESSION_DEFAULT_EXT};
|
||||
|
||||
VkImageCompressionFixedRateFlagsEXT requested_compression_fixed_rate{VK_IMAGE_COMPRESSION_FIXED_RATE_NONE_EXT};
|
||||
};
|
||||
} // namespace vkb
|
||||
@@ -0,0 +1,235 @@
|
||||
/* Copyright (c) 2021-2025, Arm Limited and Contributors
|
||||
* Copyright (c) 2024-2025, NVIDIA CORPORATION. All rights reserved.
|
||||
*
|
||||
* 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 "common/vk_common.h"
|
||||
#include "core/debug.h"
|
||||
#include "vulkan_type_mapping.h"
|
||||
|
||||
#include <utility>
|
||||
#include <vulkan/vulkan.hpp>
|
||||
|
||||
namespace vkb
|
||||
{
|
||||
namespace core
|
||||
{
|
||||
template <vkb::BindingType bindingType>
|
||||
class Device;
|
||||
using DeviceC = Device<vkb::BindingType::C>;
|
||||
using DeviceCpp = Device<vkb::BindingType::Cpp>;
|
||||
|
||||
/// Inherit this for any Vulkan object with a handle of type `Handle`.
|
||||
///
|
||||
/// This allows the derived class to store a Vulkan handle, and also a pointer to the parent vkb::Device.
|
||||
/// It also allows to set a debug name for any Vulkan object.
|
||||
template <vkb::BindingType bindingType, typename Handle>
|
||||
class VulkanResource
|
||||
{
|
||||
public:
|
||||
// we always want to store a vk::Handle as a resource, so we have to figure out that type, depending on the BindingType!
|
||||
using ResourceType = typename vkb::VulkanTypeMapping<bindingType, Handle>::Type;
|
||||
|
||||
public:
|
||||
using ObjectType = typename std::conditional<bindingType == vkb::BindingType::Cpp, vk::ObjectType, VkObjectType>::type;
|
||||
|
||||
VulkanResource(Handle handle = nullptr, Device<bindingType> *device_ = nullptr);
|
||||
|
||||
VulkanResource(const VulkanResource &) = delete;
|
||||
VulkanResource &operator=(const VulkanResource &) = delete;
|
||||
|
||||
VulkanResource(VulkanResource &&other);
|
||||
VulkanResource &operator=(VulkanResource &&other);
|
||||
|
||||
virtual ~VulkanResource() = default;
|
||||
|
||||
const std::string &get_debug_name() const;
|
||||
vkb::core::Device<bindingType> &get_device();
|
||||
vkb::core::Device<bindingType> const &get_device() const;
|
||||
Handle &get_handle();
|
||||
const Handle &get_handle() const;
|
||||
uint64_t get_handle_u64() const;
|
||||
ObjectType get_object_type() const;
|
||||
ResourceType const &get_resource() const;
|
||||
bool has_device() const;
|
||||
bool has_handle() const;
|
||||
void set_debug_name(const std::string &name);
|
||||
void set_handle(Handle hdl);
|
||||
|
||||
private:
|
||||
std::string debug_name;
|
||||
vkb::core::DeviceCpp *device;
|
||||
ResourceType handle;
|
||||
};
|
||||
|
||||
template <typename Handle>
|
||||
using VulkanResourceC = VulkanResource<vkb::BindingType::C, Handle>;
|
||||
template <typename Handle>
|
||||
using VulkanResourceCpp = VulkanResource<vkb::BindingType::Cpp, Handle>;
|
||||
|
||||
template <vkb::BindingType bindingType, typename Handle>
|
||||
inline VulkanResource<bindingType, Handle>::VulkanResource(Handle handle_, Device<bindingType> *device_) :
|
||||
handle{handle_}
|
||||
{
|
||||
if constexpr (bindingType == vkb::BindingType::Cpp)
|
||||
{
|
||||
device = device_;
|
||||
}
|
||||
else
|
||||
{
|
||||
device = reinterpret_cast<vkb::core::DeviceCpp *>(device_);
|
||||
}
|
||||
}
|
||||
|
||||
template <vkb::BindingType bindingType, typename Handle>
|
||||
inline VulkanResource<bindingType, Handle>::VulkanResource(VulkanResource &&other) :
|
||||
handle(std::exchange(other.handle, {})),
|
||||
device(std::exchange(other.device, {})),
|
||||
debug_name(std::exchange(other.debug_name, {}))
|
||||
{}
|
||||
|
||||
template <vkb::BindingType bindingType, typename Handle>
|
||||
inline VulkanResource<bindingType, Handle> &VulkanResource<bindingType, Handle>::operator=(VulkanResource &&other)
|
||||
{
|
||||
handle = std::exchange(other.handle, {});
|
||||
device = std::exchange(other.device, {});
|
||||
debug_name = std::exchange(other.debug_name, {});
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <vkb::BindingType bindingType, typename Handle>
|
||||
inline const std::string &VulkanResource<bindingType, Handle>::get_debug_name() const
|
||||
{
|
||||
return debug_name;
|
||||
}
|
||||
|
||||
template <vkb::BindingType bindingType, typename Handle>
|
||||
inline Device<bindingType> &VulkanResource<bindingType, Handle>::get_device()
|
||||
{
|
||||
assert(device && "VKBDevice handle not set");
|
||||
if constexpr (bindingType == vkb::BindingType::Cpp)
|
||||
{
|
||||
return *device;
|
||||
}
|
||||
else
|
||||
{
|
||||
return *reinterpret_cast<vkb::core::DeviceC *>(device);
|
||||
}
|
||||
}
|
||||
|
||||
template <vkb::BindingType bindingType, typename Handle>
|
||||
inline Device<bindingType> const &VulkanResource<bindingType, Handle>::get_device() const
|
||||
{
|
||||
assert(device && "VKBDevice handle not set");
|
||||
if constexpr (bindingType == vkb::BindingType::Cpp)
|
||||
{
|
||||
return *device;
|
||||
}
|
||||
else
|
||||
{
|
||||
return *reinterpret_cast<vkb::core::DeviceC const *>(device);
|
||||
}
|
||||
}
|
||||
|
||||
template <vkb::BindingType bindingType, typename Handle>
|
||||
inline Handle &VulkanResource<bindingType, Handle>::get_handle()
|
||||
{
|
||||
if constexpr (bindingType == vkb::BindingType::Cpp)
|
||||
{
|
||||
return handle;
|
||||
}
|
||||
else
|
||||
{
|
||||
return *reinterpret_cast<typename ResourceType::NativeType *>(&handle);
|
||||
}
|
||||
}
|
||||
|
||||
template <vkb::BindingType bindingType, typename Handle>
|
||||
inline const Handle &VulkanResource<bindingType, Handle>::get_handle() const
|
||||
{
|
||||
if constexpr (bindingType == vkb::BindingType::Cpp)
|
||||
{
|
||||
return handle;
|
||||
}
|
||||
else
|
||||
{
|
||||
return *reinterpret_cast<typename ResourceType::NativeType const *>(&handle);
|
||||
}
|
||||
}
|
||||
|
||||
template <vkb::BindingType bindingType, typename Handle>
|
||||
inline uint64_t VulkanResource<bindingType, Handle>::get_handle_u64() const
|
||||
{
|
||||
// See https://github.com/KhronosGroup/Vulkan-Docs/issues/368 .
|
||||
// Dispatchable and non-dispatchable handle types are *not* necessarily binary-compatible!
|
||||
// Non-dispatchable handles _might_ be only 32-bit long. This is because, on 32-bit machines, they might be a typedef to a 32-bit pointer.
|
||||
using UintHandle = typename std::conditional<sizeof(ResourceType) == sizeof(uint32_t), uint32_t, uint64_t>::type;
|
||||
|
||||
return static_cast<uint64_t>(*reinterpret_cast<UintHandle const *>(&handle));
|
||||
}
|
||||
|
||||
template <vkb::BindingType bindingType, typename Handle>
|
||||
inline typename VulkanResource<bindingType, Handle>::ObjectType VulkanResource<bindingType, Handle>::get_object_type() const
|
||||
{
|
||||
if constexpr (bindingType == vkb::BindingType::Cpp)
|
||||
{
|
||||
return ResourceType::objectType;
|
||||
}
|
||||
else
|
||||
{
|
||||
return static_cast<VkObjectType>(ResourceType::objectType);
|
||||
}
|
||||
}
|
||||
|
||||
template <vkb::BindingType bindingType, typename Handle>
|
||||
inline typename VulkanResource<bindingType, Handle>::ResourceType const &VulkanResource<bindingType, Handle>::get_resource() const
|
||||
{
|
||||
return handle;
|
||||
}
|
||||
|
||||
template <vkb::BindingType bindingType, typename Handle>
|
||||
inline bool VulkanResource<bindingType, Handle>::has_device() const
|
||||
{
|
||||
return device != nullptr;
|
||||
}
|
||||
|
||||
template <vkb::BindingType bindingType, typename Handle>
|
||||
inline bool VulkanResource<bindingType, Handle>::has_handle() const
|
||||
{
|
||||
return handle != nullptr;
|
||||
}
|
||||
|
||||
template <vkb::BindingType bindingType, typename Handle>
|
||||
inline void VulkanResource<bindingType, Handle>::set_debug_name(const std::string &name)
|
||||
{
|
||||
debug_name = name;
|
||||
|
||||
if (device && !debug_name.empty())
|
||||
{
|
||||
get_device().get_debug_utils().set_debug_name(get_device().get_handle(), get_object_type(), get_handle_u64(), debug_name.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
template <vkb::BindingType bindingType, typename Handle>
|
||||
inline void VulkanResource<bindingType, Handle>::set_handle(Handle hdl)
|
||||
{
|
||||
handle = hdl;
|
||||
}
|
||||
|
||||
} // namespace core
|
||||
} // namespace vkb
|
||||
@@ -0,0 +1,41 @@
|
||||
/* Copyright (c) 2019-2023, 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 "debug_info.h"
|
||||
|
||||
namespace vkb
|
||||
{
|
||||
const std::vector<std::unique_ptr<field::Base>> &DebugInfo::get_fields() const
|
||||
{
|
||||
return fields;
|
||||
}
|
||||
|
||||
float DebugInfo::get_longest_label() const
|
||||
{
|
||||
float column_width = 0.0f;
|
||||
for (auto &field : fields)
|
||||
{
|
||||
const std::string &label = field->label;
|
||||
|
||||
if (label.size() > column_width)
|
||||
{
|
||||
column_width = static_cast<float>(label.size());
|
||||
}
|
||||
}
|
||||
return column_width;
|
||||
}
|
||||
} // namespace vkb
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user