init
This commit is contained in:
@@ -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
|
||||
Reference in New Issue
Block a user