Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found
Select Git revision
  • 119-graphicspipeline-refactoring
  • 129-projekte-und-assets-auslagern
  • 132-denoising-module
  • 143-ar-vr-support-via-openxr
  • 43-multi-threading
  • 91-compute-first-network
  • 95-arm64-raspberry-pi-4-support
  • develop
  • master
  • optimizations
  • 0.1.0
  • 0.2.0
12 results

Target

Select target project
  • vulkan2021/vkcv-framework
1 result
Select Git revision
Show changes
Commits on Source (19)
Showing
with 387 additions and 27 deletions
......@@ -118,12 +118,14 @@ message(STATUS "Framework:")
message(" - Includes: [ ${vkcv_includes} ]")
message(" - Libraries: [ ${vkcv_libraries} ]")
message(" - Flags: [ ${vkcv_flags} ]")
message(" - Headers: [ ${vkcv_headers} ]")
# set the compiler flags for the framework
set(CMAKE_CXX_FLAGS ${vkcv_flags})
# create VkCV framework as library using all source files
add_library(vkcv ${vkcv_build_attribute} ${vkcv_sources})
set_target_properties(vkcv PROPERTIES PUBLIC_HEADER "${vkcv_headers}")
if(MSVC)
#enable multicore compilation on visual studio
......@@ -171,4 +173,7 @@ if (vkcv_parent_scope)
set(vkcv_includes ${vkcv_includes} PARENT_SCOPE)
set(vkcv_libraries ${vkcv_libraries} PARENT_SCOPE)
endif()
\ No newline at end of file
endif()
install(TARGETS vkcv PUBLIC_HEADER DESTINATION
${CMAKE_INSTALL_INCLUDEDIR}/vkcv)
......@@ -70,7 +70,7 @@ targets:
- [particle_simulation](projects/particle_simulation/README.md)
- [path_tracer](projects/path_tracer/README.md)
- [ray_tracer](projects/ray_tracer/README.md)
- [rtx_ambient_occlusion](projects/rtx_ambient_occlusion/README.md)
- [rt_ambient_occlusion](projects/rt_ambient_occlusion/README.md)
- [sph](projects/sph/README.md)
- [voxelization](projects/voxelization/README.md)
......
......@@ -54,3 +54,6 @@ if (vkcv_definitions)
endif ()
list(JOIN vkcv_flags " " vkcv_flags)
# add custom function to filter headers from source file lists
include(${vkcv_config_ext}/FilterHeaders.cmake)
......@@ -51,6 +51,9 @@ set(vkcv_sources
${vkcv_source}/vkcv/ImageManager.hpp
${vkcv_source}/vkcv/ImageManager.cpp
${vkcv_source}/vkcv/AccelerationStructureManager.hpp
${vkcv_source}/vkcv/AccelerationStructureManager.cpp
${vkcv_include}/vkcv/PipelineConfig.hpp
${vkcv_source}/vkcv/PipelineConfig.cpp
......@@ -73,6 +76,9 @@ set(vkcv_sources
${vkcv_source}/vkcv/GraphicsPipelineManager.hpp
${vkcv_source}/vkcv/GraphicsPipelineManager.cpp
${vkcv_source}/vkcv/RayTracingPipelineManager.hpp
${vkcv_source}/vkcv/RayTracingPipelineManager.cpp
${vkcv_include}/vkcv/QueueManager.hpp
${vkcv_source}/vkcv/QueueManager.cpp
......@@ -139,9 +145,17 @@ set(vkcv_sources
${vkcv_include}/vkcv/VertexData.hpp
${vkcv_source}/vkcv/VertexData.cpp
${vkcv_include}/vkcv/GeometryData.hpp
${vkcv_source}/vkcv/GeometryData.cpp
${vkcv_include}/vkcv/Result.hpp
${vkcv_include}/vkcv/RayTracingPipelineConfig.hpp
${vkcv_source}/vkcv/RayTracingPipelineConfig.cpp
)
filter_headers(vkcv_sources ${vkcv_include} vkcv_headers)
if (BUILD_CLANG_FORMAT)
message(STATUS "Clang-Format: ON")
......
function(filter_headers sources include_path headers)
set(header_list "")
foreach(src_path IN ITEMS ${${sources}})
string(FIND ${src_path} ${include_path} src_include_dir)
if (${src_include_dir} MATCHES 0)
list(APPEND header_list ${src_path})
endif()
endforeach()
set(${headers} "${header_list}" PARENT_SCOPE)
endfunction()
......@@ -54,6 +54,19 @@ namespace vkcv {
[[nodiscard]] BufferMemoryType getMemoryType() const {
return m_core->getBufferMemoryType(m_handle);
}
/**
* @brief Returns the stride of elements in the buffer.
*
* Beware that this returned value is only the correct
* stride for this buffer if it is used tightly packed
* storing elements of type T.
*
* @return The likely stride of the #Buffer using type T
*/
[[nodiscard]] size_t getStride() const {
return sizeof(T);
}
/**
* @brief Returns the count of elements in the buffer.
......@@ -168,5 +181,10 @@ namespace vkcv {
return Buffer<T>(&core,
core.createBuffer(type, typeGuard<T>(), count, memoryType, readable));
}
template <typename T>
VertexBufferBinding vertexBufferBinding(const Buffer<T>& buffer) {
return vertexBufferBinding(buffer.getHandle(), buffer.getStride());
}
} // namespace vkcv
......@@ -17,6 +17,9 @@ namespace vkcv {
STORAGE,
STAGING,
INDIRECT,
SHADER_BINDING,
ACCELERATION_STRUCTURE_INPUT,
ACCELERATION_STRUCTURE_STORAGE,
UNKNOWN
};
......
......@@ -58,6 +58,13 @@ namespace vkcv {
* @return Vulkan device
*/
[[nodiscard]] const vk::Device &getDevice() const;
/**
* @brief Returns a dynamic dispatch loader of the context.
*
* @return Dynamic dispatch loader
*/
[[nodiscard]] const vk::DispatchLoaderDynamic &getDispatchLoaderDynamic() const;
/**
* @brief Returns the feature manager of the context.
......@@ -113,6 +120,7 @@ namespace vkcv {
vk::Instance m_Instance;
vk::PhysicalDevice m_PhysicalDevice;
vk::Device m_Device;
vk::DispatchLoaderDynamic m_DispatchDynamic;
FeatureManager m_FeatureManager;
QueueManager m_QueueManager;
vma::Allocator m_Allocator;
......
......@@ -19,11 +19,13 @@
#include "Drawcall.hpp"
#include "Event.hpp"
#include "EventFunctionTypes.hpp"
#include "GeometryData.hpp"
#include "GraphicsPipelineConfig.hpp"
#include "Handles.hpp"
#include "ImageConfig.hpp"
#include "PassConfig.hpp"
#include "PushConstants.hpp"
#include "RayTracingPipelineConfig.hpp"
#include "Result.hpp"
#include "SamplerTypes.hpp"
#include "Window.hpp"
......@@ -37,11 +39,13 @@ namespace vkcv {
class PassManager;
class GraphicsPipelineManager;
class ComputePipelineManager;
class RayTracingPipelineManager;
class DescriptorSetLayoutManager;
class DescriptorSetManager;
class BufferManager;
class SamplerManager;
class ImageManager;
class AccelerationStructureManager;
class CommandStreamManager;
class WindowManager;
class SwapchainManager;
......@@ -71,11 +75,13 @@ namespace vkcv {
std::unique_ptr<PassManager> m_PassManager;
std::unique_ptr<GraphicsPipelineManager> m_GraphicsPipelineManager;
std::unique_ptr<ComputePipelineManager> m_ComputePipelineManager;
std::unique_ptr<RayTracingPipelineManager> m_RayTracingPipelineManager;
std::unique_ptr<DescriptorSetLayoutManager> m_DescriptorSetLayoutManager;
std::unique_ptr<DescriptorSetManager> m_DescriptorSetManager;
std::unique_ptr<BufferManager> m_BufferManager;
std::unique_ptr<SamplerManager> m_SamplerManager;
std::unique_ptr<ImageManager> m_ImageManager;
std::unique_ptr<AccelerationStructureManager> m_AccelerationStructureManager;
std::unique_ptr<CommandStreamManager> m_CommandStreamManager;
std::unique_ptr<WindowManager> m_WindowManager;
std::unique_ptr<SwapchainManager> m_SwapchainManager;
......@@ -180,6 +186,17 @@ namespace vkcv {
*/
[[nodiscard]] ComputePipelineHandle
createComputePipeline(const ComputePipelineConfig &config);
/**
* Creates a basic vulkan ray tracing pipeline using @p shader program and returns it using
* the @p handle. Fixed Functions for pipeline are set with standard values.
*
* @param config a pipeline config object from the pipeline config class
* layout
* @return True if pipeline creation was successful, False if not
*/
[[nodiscard]] RayTracingPipelineHandle
createRayTracingPipeline(const RayTracingPipelineConfig &config);
/**
* Creates a basic vulkan render pass using @p config from the render pass config class and
......@@ -260,6 +277,15 @@ namespace vkcv {
* @return Size of the buffer
*/
[[nodiscard]] size_t getBufferSize(const BufferHandle &buffer) const;
/**
* @brief Returns the device address of a buffer represented
* by a given buffer handle.
*
* @param[in] buffer Buffer handle
* @return Device address of the buffer
*/
[[nodiscard]] vk::DeviceAddress getBufferDeviceAddress(const BufferHandle &buffer) const;
/**
* @brief Fills a buffer represented by a given buffer
......@@ -553,29 +579,23 @@ namespace vkcv {
const WindowHandle &windowHandle);
/**
* Records the rtx ray generation to the @p cmdStreamHandle.
* Currently only supports @p closestHit, @p rayGen and @c miss shaderstages @c.
* Records the ray generation via ray tracing pipeline to the @p cmdStreamHandle.
*
* @param cmdStreamHandle The command stream handle which receives relevant commands for
* drawing.
* @param rtxPipeline The raytracing pipeline from the RTXModule.
* @param rtxPipelineLayout The raytracing pipeline layout from the RTXModule.
* @param rgenRegion The shader binding table region for ray generation shaders.
* @param rmissRegion The shader binding table region for ray miss shaders.
* @param rchitRegion The shader binding table region for ray closest hit shaders.
* @param rcallRegion The shader binding table region for callable shaders.
* @param rayTracingPipeline The raytracing pipeline
* @param dispatchSize How many work groups are dispatched
* @param descriptorSetUsages The descriptor set usages.
* @param pushConstants The push constants.
* @param windowHandle The window handle defining in which window to render.
*/
void recordRayGenerationToCmdStream(
CommandStreamHandle cmdStreamHandle, vk::Pipeline rtxPipeline,
vk::PipelineLayout rtxPipelineLayout, vk::StridedDeviceAddressRegionKHR rgenRegion,
vk::StridedDeviceAddressRegionKHR rmissRegion,
vk::StridedDeviceAddressRegionKHR rchitRegion,
vk::StridedDeviceAddressRegionKHR rcallRegion,
const std::vector<DescriptorSetUsage> &descriptorSetUsages,
const PushConstants &pushConstants, const WindowHandle &windowHandle);
void recordRayGenerationToCmdStream(const CommandStreamHandle &cmdStreamHandle,
const RayTracingPipelineHandle &rayTracingPipeline,
const DispatchSize &dispatchSize,
const std::vector<DescriptorSetUsage>
&descriptorSetUsages,
const PushConstants &pushConstants,
const vkcv::WindowHandle &windowHandle);
/**
* @brief Record a compute shader dispatch into a command stream
......@@ -940,5 +960,57 @@ namespace vkcv {
* @return Vulkan device memory
*/
[[nodiscard]] vk::DeviceMemory getVulkanDeviceMemory(const ImageHandle &handle) const;
/**
* @brief Creates an acceleration structure handle built with a given list of geometry data.
*
* @param[in] geometryData List of geometry data
* @return Acceleration structure handle
*/
AccelerationStructureHandle createAccelerationStructure(
const std::vector<GeometryData> &geometryData,
const BufferHandle &transformBuffer = {},
bool compaction = false);
/**
* @brief Creates an acceleration structure handle built with a given list of
* other bottom-level acceleration structures.
*
* @param[in] handles List of acceleration structure handles
* @return Acceleration structure handle
*/
AccelerationStructureHandle createAccelerationStructure(
const std::vector<AccelerationStructureHandle> &handles);
/**
* @brief the underlying vulkan handle for an acceleration structure
* by its given acceleration structure handle.
*
* @param[in] handle Acceleration structure handle
* @return Vulkan acceleration structure
*/
[[nodiscard]] vk::AccelerationStructureKHR getVulkanAccelerationStructure(
const AccelerationStructureHandle &handle) const;
/**
* @brief Return the underlying vulkan handle for an acceleration
* structure by its given acceleration structure handle.
*
* @param[in] handle Acceleration structure handle
* @return Vulkan buffer
*/
[[nodiscard]] vk::Buffer getVulkanBuffer(
const vkcv::AccelerationStructureHandle &handle) const;
/**
* @brief Returns the device address of an acceleration structure represented
* by a given acceleration structure handle.
*
* @param[in] handle Acceleration structure handle
* @return Device address of the acceleration structure
*/
[[nodiscard]] vk::DeviceAddress getAccelerationStructureDeviceAddress(
const vkcv::AccelerationStructureHandle &handle) const;
};
} // namespace vkcv
......@@ -61,7 +61,7 @@ namespace vkcv {
*/
struct AccelerationDescriptorWrite {
uint32_t binding;
std::vector<vk::AccelerationStructureKHR> structures;
std::vector<AccelerationStructureHandle> structures;
};
/**
......@@ -154,12 +154,12 @@ namespace vkcv {
* of a descriptor set.
*
* @param[in] binding Binding index
* @param[in] structures Acceleration structures
* @param[in] structures Acceleration structure handles
* @return Instance of descriptor writes
*/
DescriptorWrites &
writeAcceleration(uint32_t binding,
const std::vector<vk::AccelerationStructureKHR> &structures);
const std::vector<AccelerationStructureHandle> &structures);
/**
* @brief Returns the list of stored write entries for sampled images.
......
#pragma once
/**
* @authors Tobias Frisch
* @file vkcv/GeometryData.hpp
* @brief Types to configure geometry data for acceleration structure building.
*/
#include "VertexData.hpp"
namespace vkcv {
/**
* @brief Enum class to specify the format of vertex in geometry.
*/
enum class GeometryVertexType {
POSITION_FLOAT3,
UNDEFINED
};
/**
* @brief Class to store the details of geometry data for acceleration structure building
*/
class GeometryData {
private:
VertexBufferBinding m_vertexBinding;
size_t m_maxVertexIndex;
GeometryVertexType m_vertexType;
BufferHandle m_indices;
IndexBitCount m_indexBitCount;
size_t m_count;
public:
/**
* @brief Default constructor of invalid geometry data.
*/
GeometryData();
/**
* @brief Constructor of geometry data by providing an vertex buffer binding,
* the used stride for vertex elements and its geometry vertex type.
*
* @param[in] binding Geometry buffer binding
* @param[in] stride Vertex element stride
* @param[in] geometryVertexType Geometry vertex type
*/
explicit GeometryData(const VertexBufferBinding &binding,
size_t maxVertexIndex = 0,
GeometryVertexType geometryVertexType =
GeometryVertexType::POSITION_FLOAT3);
GeometryData(const GeometryData &other) = default;
GeometryData(GeometryData &&other) noexcept = default;
~GeometryData() = default;
GeometryData &operator=(const GeometryData &other) = default;
GeometryData &operator=(GeometryData &&other) noexcept = default;
/**
* @brief Return whether the geometry is valid to use.
*
* @return True if the geometry data is valid, otherwise false.
*/
[[nodiscard]] bool isValid() const;
/**
* @brief Return the used vertex buffer binding of the geometry data.
*
* @return Vertex buffer binding
*/
[[nodiscard]] const VertexBufferBinding &getVertexBufferBinding() const;
/**
* @brief Return the stride of vertex elements of the geometry data.
*
* @return Vertex stride
*/
[[nodiscard]] uint32_t getVertexStride() const;
/**
* @brief Return the maximal index from vertex elements of the geometry data.
*
* @return Maximal vertex index
*/
[[nodiscard]] size_t getMaxVertexIndex() const;
/**
* @brief Return the geometry vertex type of the geometry data.
*
* @return Geometry vertex type
*/
[[nodiscard]] GeometryVertexType getGeometryVertexType() const;
/**
* @brief Set the optional index buffer and its used index bit count.
*
* @param[in] indices Index buffer handle
* @param[in] indexBitCount Index bit count
*/
void setIndexBuffer(const BufferHandle &indices,
IndexBitCount indexBitCount = IndexBitCount::Bit16);
/**
* @brief Return the handle from the used index buffer of the vertex
* data.
*
* @return Index buffer handle
*/
[[nodiscard]] const BufferHandle &getIndexBuffer() const;
/**
* @brief Return the index bit count of the indices used in the
* vertex data.
*
* @return Index bit count
*/
[[nodiscard]] IndexBitCount getIndexBitCount() const;
/**
* @brief Set the count of elements to use by the vertex data.
*
* @param count Count of vertex elements
*/
void setCount(size_t count);
/**
* @brief Return the count of elements in use by the vertex data.
*
* @return Count of vertex elements
*/
[[nodiscard]] size_t getCount() const;
};
} // namespace vkcv
......@@ -80,7 +80,8 @@ namespace vkcv {
public:
GraphicsPipelineConfig();
GraphicsPipelineConfig(const ShaderProgram &program, const PassHandle &pass,
GraphicsPipelineConfig(const ShaderProgram &program,
const PassHandle &pass,
const VertexLayout &vertexLayout,
const std::vector<DescriptorSetLayoutHandle> &layouts);
......
......@@ -130,6 +130,16 @@ namespace vkcv {
private:
using Handle::Handle;
};
/**
* @brief Handle class for ray tracing pipelines.
*/
class RayTracingPipelineHandle : public Handle {
friend class RayTracingPipelineManager;
private:
using Handle::Handle;
};
/**
* @brief Handle class for descriptor set layouts.
......@@ -188,6 +198,16 @@ namespace vkcv {
static ImageHandle
createSwapchainImageHandle(const HandleDestroyFunction &destroy = nullptr);
};
/**
* @brief Handle class for acceleration structures.
*/
class AccelerationStructureHandle : public Handle {
friend class AccelerationStructureManager;
private:
using Handle::Handle;
};
/**
* @brief Handle class for windows.
......
#pragma once
/**
* @authors Tobias Frisch
* @file vkcv/RayTracingPipelineConfig.hpp
* @brief Ray tracing pipeline config struct to hand over required information to pipeline creation.
*/
#include "PipelineConfig.hpp"
namespace vkcv {
/**
* @brief Class to configure a ray tracing pipeline before its creation.
*/
class RayTracingPipelineConfig : public PipelineConfig {
public:
RayTracingPipelineConfig();
RayTracingPipelineConfig(const ShaderProgram &program,
const std::vector<DescriptorSetLayoutHandle> &layouts);
RayTracingPipelineConfig(const RayTracingPipelineConfig &other) = default;
RayTracingPipelineConfig(RayTracingPipelineConfig &&other) = default;
~RayTracingPipelineConfig() = default;
RayTracingPipelineConfig &operator=(const RayTracingPipelineConfig &other) = default;
RayTracingPipelineConfig &operator=(RayTracingPipelineConfig &&other) = default;
};
}
......@@ -15,11 +15,23 @@ namespace vkcv {
* @brief Structure to store details about a vertex buffer binding.
*/
struct VertexBufferBinding {
BufferHandle buffer;
size_t offset;
BufferHandle m_buffer;
size_t m_stride;
size_t m_offset;
};
VertexBufferBinding vertexBufferBinding(const BufferHandle &buffer, size_t offset = 0);
/**
* Create a vertex buffer binding using a given buffer handle and
* its stride in bytes.
*
* @param[in] buffer Vertex buffer
* @param[in] stride Stride in bytes
* @param[in] offset (Optional) Offset in bytes
* @return Vertex buffer binding
*/
VertexBufferBinding vertexBufferBinding(const BufferHandle &buffer,
size_t stride,
size_t offset = 0);
typedef std::vector<VertexBufferBinding> VertexBufferBindings;
......
......@@ -13,7 +13,6 @@ add_subdirectory(material)
add_subdirectory(meshlet)
add_subdirectory(scene)
add_subdirectory(shader_compiler)
add_subdirectory(testing)
add_subdirectory(upscaling)
message(STATUS "Modules:")
......
......@@ -13,6 +13,8 @@ set(vkcv_algorithm_sources
${vkcv_algorithm_source}/vkcv/algorithm/SinglePassDownsampler.cpp
)
filter_headers(vkcv_algorithm_sources ${vkcv_algorithm_include} vkcv_algorithm_headers)
# Setup some path variables to load libraries
set(vkcv_algorithm_lib lib)
set(vkcv_algorithm_lib_path ${PROJECT_SOURCE_DIR}/${vkcv_algorithm_lib})
......@@ -22,6 +24,7 @@ include(config/FidelityFX_SPD.cmake)
# adding source files to the project
add_library(vkcv_algorithm ${vkcv_build_attribute} ${vkcv_algorithm_sources})
set_target_properties(vkcv_algorithm PROPERTIES PUBLIC_HEADER "${vkcv_algorithm_headers}")
# link the required libraries to the module
target_link_libraries(vkcv_algorithm ${vkcv_algorithm_libraries} vkcv vkcv_shader_compiler vkcv_camera vkcv_asset_loader)
......@@ -39,3 +42,6 @@ if (vkcv_parent_scope)
set(vkcv_modules_includes ${vkcv_modules_includes} PARENT_SCOPE)
set(vkcv_modules_libraries ${vkcv_modules_libraries} PARENT_SCOPE)
endif()
install(TARGETS vkcv_algorithm PUBLIC_HEADER DESTINATION
${CMAKE_INSTALL_INCLUDEDIR}/vkcv/algorithm)
......@@ -14,8 +14,11 @@ set(vkcv_asset_loader_sources
${vkcv_asset_loader_source}/vkcv/asset/asset_loader.cpp
)
filter_headers(vkcv_asset_loader_sources ${vkcv_asset_loader_include} vkcv_asset_loader_headers)
# adding source files to the module
add_library(vkcv_asset_loader ${vkcv_build_attribute} ${vkcv_asset_loader_sources})
set_target_properties(vkcv_asset_loader PROPERTIES PUBLIC_HEADER "${vkcv_asset_loader_headers}")
# Setup some path variables to load libraries
set(vkcv_asset_loader_lib lib)
......@@ -48,3 +51,6 @@ if (vkcv_parent_scope)
set(vkcv_modules_includes ${vkcv_modules_includes} PARENT_SCOPE)
set(vkcv_modules_libraries ${vkcv_modules_libraries} PARENT_SCOPE)
endif()
install(TARGETS vkcv_asset_loader PUBLIC_HEADER DESTINATION
${CMAKE_INSTALL_INCLUDEDIR}/vkcv/asset)
......@@ -885,7 +885,11 @@ namespace vkcv::asset {
break;
}
bindings.push_back(vkcv::vertexBufferBinding(buffer, attribute->offset));
bindings.push_back(vkcv::vertexBufferBinding(
buffer,
attribute->stride,
attribute->offset
));
}
return bindings;
......
......@@ -11,17 +11,24 @@ set(vkcv_camera_include ${PROJECT_SOURCE_DIR}/include)
set(vkcv_camera_sources
${vkcv_camera_include}/vkcv/camera/Camera.hpp
${vkcv_camera_source}/vkcv/camera/Camera.cpp
${vkcv_camera_include}/vkcv/camera/CameraManager.hpp
${vkcv_camera_source}/vkcv/camera/CameraManager.cpp
${vkcv_camera_include}/vkcv/camera/CameraController.hpp
${vkcv_camera_include}/vkcv/camera/PilotCameraController.hpp
${vkcv_camera_source}/vkcv/camera/PilotCameraController.cpp
${vkcv_camera_include}/vkcv/camera/TrackballCameraController.hpp
${vkcv_camera_source}/vkcv/camera/TrackballCameraController.cpp
)
filter_headers(vkcv_camera_sources ${vkcv_camera_include} vkcv_camera_headers)
# adding source files to the project
add_library(vkcv_camera ${vkcv_build_attribute} ${vkcv_camera_sources})
set_target_properties(vkcv_camera PROPERTIES PUBLIC_HEADER "${vkcv_camera_headers}")
# Setup some path variables to load libraries
set(vkcv_camera_lib lib)
......@@ -51,3 +58,6 @@ if (vkcv_parent_scope)
set(vkcv_modules_includes ${vkcv_modules_includes} PARENT_SCOPE)
set(vkcv_modules_libraries ${vkcv_modules_libraries} PARENT_SCOPE)
endif()
install(TARGETS vkcv_camera PUBLIC_HEADER DESTINATION
${CMAKE_INSTALL_INCLUDEDIR}/vkcv/camera)