This commit is contained in:
xsl
2025-09-04 10:54:47 +08:00
commit 6bc8f61b18
1808 changed files with 208268 additions and 0 deletions
+35
View File
@@ -0,0 +1,35 @@
/* Copyright (c) 2018-2020, Arm Limited and Contributors
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 the "License";
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "error.h"
#include "helpers.h"
namespace vkb
{
VulkanException::VulkanException(const VkResult result, const std::string &msg) :
result{result},
std::runtime_error{msg}
{
error_message = std::string(std::runtime_error::what()) + std::string{" : "} + to_string(result);
}
const char *VulkanException::what() const noexcept
{
return error_message.c_str();
}
} // namespace vkb
+75
View File
@@ -0,0 +1,75 @@
/* Copyright (c) 2018-2024, Arm Limited and Contributors
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 the "License";
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#include <cassert>
#include <stdexcept>
#include <string>
#include "core/util/error.hpp"
#include "common/strings.h"
#include "core/util/logging.hpp"
#include "vk_common.h"
namespace vkb
{
/**
* @brief Vulkan exception structure
*/
class VulkanException : public std::runtime_error
{
public:
/**
* @brief Vulkan exception constructor
*/
VulkanException(VkResult result, const std::string &msg = "Vulkan error");
/**
* @brief Returns the Vulkan error code as string
* @return String message of exception
*/
const char *what() const noexcept override;
VkResult result;
private:
std::string error_message;
};
} // namespace vkb
/// @brief Helper macro to test the result of Vulkan calls which can return an error.
#define VK_CHECK(x) \
do \
{ \
VkResult err = x; \
if (err) \
{ \
throw std::runtime_error("Detected Vulkan error: " + vkb::to_string(err)); \
} \
} while (0)
#define ASSERT_VK_HANDLE(handle) \
do \
{ \
if ((handle) == VK_NULL_HANDLE) \
{ \
LOGE("Handle is NULL"); \
abort(); \
} \
} while (0)
+28
View File
@@ -0,0 +1,28 @@
/* Copyright (c) 2019, Arm Limited and Contributors
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 the "License";
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#ifndef GLM_FORCE_RADIANS
# define GLM_FORCE_RADIANS
#endif
#ifndef GLM_FORCE_DEPTH_ZERO_TO_ONE
# define GLM_FORCE_DEPTH_ZERO_TO_ONE
#endif
#include <glm/glm.hpp>
#include <glm/gtx/rotate_vector.hpp>
#include <glm/gtx/transform.hpp>
+215
View File
@@ -0,0 +1,215 @@
/* Copyright (c) 2018-2024, Arm Limited and Contributors
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 the "License";
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#include <algorithm>
#include <array>
#include <cstdio>
#include <cstdlib>
#include <fstream>
#include <functional>
#include <iterator>
#include <map>
#include <memory>
#include <queue>
#include <set>
#include <sstream>
#include <string>
#include <unordered_map>
#include <unordered_set>
#include <vector>
#include "common/error.h"
#include "common/glm_common.h"
#include <glm/gtx/hash.hpp>
namespace vkb
{
template <typename T>
inline void read(std::istringstream &is, T &value)
{
is.read(reinterpret_cast<char *>(&value), sizeof(T));
}
inline void read(std::istringstream &is, std::string &value)
{
std::size_t size;
read(is, size);
value.resize(size);
is.read(const_cast<char *>(value.data()), size);
}
template <class T>
inline void read(std::istringstream &is, std::set<T> &value)
{
std::size_t size;
read(is, size);
for (uint32_t i = 0; i < size; i++)
{
T item;
is.read(reinterpret_cast<char *>(&item), sizeof(T));
value.insert(std::move(item));
}
}
template <class T>
inline void read(std::istringstream &is, std::vector<T> &value)
{
std::size_t size;
read(is, size);
value.resize(size);
is.read(reinterpret_cast<char *>(value.data()), value.size() * sizeof(T));
}
template <class T, class S>
inline void read(std::istringstream &is, std::map<T, S> &value)
{
std::size_t size;
read(is, size);
for (uint32_t i = 0; i < size; i++)
{
std::pair<T, S> item;
read(is, item.first);
read(is, item.second);
value.insert(std::move(item));
}
}
template <class T, uint32_t N>
inline void read(std::istringstream &is, std::array<T, N> &value)
{
is.read(reinterpret_cast<char *>(value.data()), N * sizeof(T));
}
template <typename T, typename... Args>
inline void read(std::istringstream &is, T &first_arg, Args &...args)
{
read(is, first_arg);
read(is, args...);
}
template <typename T>
inline void write(std::ostringstream &os, const T &value)
{
os.write(reinterpret_cast<const char *>(&value), sizeof(T));
}
inline void write(std::ostringstream &os, const std::string &value)
{
write(os, value.size());
os.write(value.data(), value.size());
}
template <class T>
inline void write(std::ostringstream &os, const std::set<T> &value)
{
write(os, value.size());
for (const T &item : value)
{
os.write(reinterpret_cast<const char *>(&item), sizeof(T));
}
}
template <class T>
inline void write(std::ostringstream &os, const std::vector<T> &value)
{
write(os, value.size());
os.write(reinterpret_cast<const char *>(value.data()), value.size() * sizeof(T));
}
template <class T, class S>
inline void write(std::ostringstream &os, const std::map<T, S> &value)
{
write(os, value.size());
for (const std::pair<T, S> &item : value)
{
write(os, item.first);
write(os, item.second);
}
}
template <class T, uint32_t N>
inline void write(std::ostringstream &os, const std::array<T, N> &value)
{
os.write(reinterpret_cast<const char *>(value.data()), N * sizeof(T));
}
template <typename T, typename... Args>
inline void write(std::ostringstream &os, const T &first_arg, const Args &...args)
{
write(os, first_arg);
write(os, args...);
}
/**
* @brief Helper function to combine a given hash
* with a generated hash for the input param.
*/
template <class T>
inline void hash_combine(size_t &seed, const T &v)
{
std::hash<T> hasher;
glm::detail::hash_combine(seed, hasher(v));
}
/**
* @brief Helper function to convert a data type
* to string using output stream operator.
* @param value The object to be converted to string
* @return String version of the given object
*/
template <class T>
inline std::string to_string(const T &value)
{
std::stringstream ss;
ss << std::fixed << value;
return ss.str();
}
/**
* @brief Helper function to check size_t is correctly converted to uint32_t
* @param value Value of type size_t to convert
* @return An uint32_t representation of the same value
*/
template <class T>
uint32_t to_u32(T value)
{
static_assert(std::is_arithmetic<T>::value, "T must be numeric");
if (static_cast<uintmax_t>(value) > static_cast<uintmax_t>(std::numeric_limits<uint32_t>::max()))
{
throw std::runtime_error("to_u32() failed, value is too big to be converted to uint32_t");
}
return static_cast<uint32_t>(value);
}
template <typename T>
inline std::vector<uint8_t> to_bytes(const T &value)
{
return std::vector<uint8_t>{reinterpret_cast<const uint8_t *>(&value),
reinterpret_cast<const uint8_t *>(&value) + sizeof(T)};
}
} // namespace vkb
+41
View File
@@ -0,0 +1,41 @@
/* Copyright (c) 2022-2024, NVIDIA CORPORATION. All rights reserved.
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 the "License";
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#include <common/error.h>
#include <vulkan/vulkan.hpp>
namespace vkb
{
namespace common
{
/**
* @brief facade class around vkb::VulkanException, providing a vulkan.hpp-based interface
*
* See vkb::VulkanException for documentation
*/
class HPPVulkanException : public vkb::VulkanException
{
public:
HPPVulkanException(vk::Result result, std::string const &msg = "Vulkan error") :
vkb::VulkanException(static_cast<VkResult>(result), msg)
{}
};
} // namespace common
} // namespace vkb
+414
View File
@@ -0,0 +1,414 @@
/* Copyright (c) 2023-2025, NVIDIA CORPORATION. All rights reserved.
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 the "License";
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#include "common/hpp_vk_common.h"
#include "core/hpp_descriptor_set.h"
#include "core/hpp_image_view.h"
#include "core/hpp_render_pass.h"
#include "core/hpp_shader_module.h"
#include "hpp_resource_record.h"
#include "rendering/hpp_render_target.h"
#include "resource_caching.h"
#include <vulkan/vulkan_hash.hpp>
namespace std
{
template <typename Key, typename Value>
struct hash<std::map<Key, Value>>
{
size_t operator()(std::map<Key, Value> const &bindings) const
{
size_t result = 0;
vkb::hash_combine(result, bindings.size());
for (auto const &binding : bindings)
{
vkb::hash_combine(result, binding.first);
vkb::hash_combine(result, binding.second);
}
return result;
}
};
template <typename T>
struct hash<std::vector<T>>
{
size_t operator()(std::vector<T> const &values) const
{
size_t result = 0;
vkb::hash_combine(result, values.size());
for (auto const &value : values)
{
vkb::hash_combine(result, value);
}
return result;
}
};
template <>
struct hash<vkb::common::HPPLoadStoreInfo>
{
size_t operator()(vkb::common::HPPLoadStoreInfo const &lsi) const
{
size_t result = 0;
vkb::hash_combine(result, lsi.load_op);
vkb::hash_combine(result, lsi.store_op);
return result;
}
};
template <vkb::BindingType bindingType, typename T>
struct hash<vkb::core::VulkanResource<bindingType, T>>
{
size_t operator()(const vkb::core::VulkanResource<bindingType, T> &vulkan_resource) const
{
return std::hash<T>()(vulkan_resource.get_handle());
}
};
template <>
struct hash<vkb::core::HPPDescriptorPool>
{
size_t operator()(const vkb::core::HPPDescriptorPool &descriptor_pool) const
{
return std::hash<vkb::DescriptorPool>()(reinterpret_cast<vkb::DescriptorPool const &>(descriptor_pool));
}
};
template <>
struct hash<vkb::core::HPPDescriptorSet>
{
size_t operator()(vkb::core::HPPDescriptorSet &descriptor_set) const
{
size_t result = 0;
vkb::hash_combine(result, descriptor_set.get_layout());
// descriptor_pool ?
vkb::hash_combine(result, descriptor_set.get_buffer_infos());
vkb::hash_combine(result, descriptor_set.get_image_infos());
vkb::hash_combine(result, descriptor_set.get_handle());
// write_descriptor_sets ?
return result;
}
};
template <>
struct hash<vkb::core::HPPDescriptorSetLayout>
{
size_t operator()(const vkb::core::HPPDescriptorSetLayout &descriptor_set_layout) const
{
return std::hash<vkb::DescriptorSetLayout>()(reinterpret_cast<vkb::DescriptorSetLayout const &>(descriptor_set_layout));
}
};
template <>
struct hash<vkb::core::HPPImage>
{
size_t operator()(const vkb::core::HPPImage &image) const
{
size_t result = 0;
vkb::hash_combine(result, image.get_memory());
vkb::hash_combine(result, image.get_type());
vkb::hash_combine(result, image.get_extent());
vkb::hash_combine(result, image.get_format());
vkb::hash_combine(result, image.get_usage());
vkb::hash_combine(result, image.get_sample_count());
vkb::hash_combine(result, image.get_tiling());
vkb::hash_combine(result, image.get_subresource());
vkb::hash_combine(result, image.get_array_layer_count());
return result;
}
};
template <>
struct hash<vkb::core::HPPImageView>
{
size_t operator()(const vkb::core::HPPImageView &image_view) const
{
size_t result = std::hash<vkb::core::VulkanResourceCpp<vk::ImageView>>()(image_view);
vkb::hash_combine(result, image_view.get_image());
vkb::hash_combine(result, image_view.get_format());
vkb::hash_combine(result, image_view.get_subresource_range());
return result;
}
};
template <>
struct hash<vkb::core::HPPRenderPass>
{
size_t operator()(const vkb::core::HPPRenderPass &render_pass) const
{
return std::hash<vkb::RenderPass>()(reinterpret_cast<vkb::RenderPass const &>(render_pass));
}
};
template <>
struct hash<vkb::core::HPPShaderModule>
{
size_t operator()(const vkb::core::HPPShaderModule &shader_module) const
{
return std::hash<vkb::ShaderModule>()(reinterpret_cast<vkb::ShaderModule const &>(shader_module));
}
};
template <>
struct hash<vkb::core::HPPShaderResource>
{
size_t operator()(vkb::core::HPPShaderResource const &shader_resource) const
{
size_t result = 0;
vkb::hash_combine(result, shader_resource.stages);
vkb::hash_combine(result, shader_resource.type);
vkb::hash_combine(result, shader_resource.mode);
vkb::hash_combine(result, shader_resource.set);
vkb::hash_combine(result, shader_resource.binding);
vkb::hash_combine(result, shader_resource.location);
vkb::hash_combine(result, shader_resource.input_attachment_index);
vkb::hash_combine(result, shader_resource.vec_size);
vkb::hash_combine(result, shader_resource.columns);
vkb::hash_combine(result, shader_resource.array_size);
vkb::hash_combine(result, shader_resource.offset);
vkb::hash_combine(result, shader_resource.size);
vkb::hash_combine(result, shader_resource.constant_id);
vkb::hash_combine(result, shader_resource.qualifiers);
vkb::hash_combine(result, shader_resource.name);
return result;
}
};
template <>
struct hash<vkb::core::HPPShaderSource>
{
size_t operator()(const vkb::core::HPPShaderSource &shader_source) const
{
return std::hash<vkb::ShaderSource>()(reinterpret_cast<vkb::ShaderSource const &>(shader_source));
}
};
template <>
struct hash<vkb::core::HPPShaderVariant>
{
size_t operator()(const vkb::core::HPPShaderVariant &shader_variant) const
{
return std::hash<vkb::ShaderVariant>()(reinterpret_cast<vkb::ShaderVariant const &>(shader_variant));
}
};
template <>
struct hash<vkb::core::HPPSubpassInfo>
{
size_t operator()(vkb::core::HPPSubpassInfo const &subpass_info) const
{
size_t result = 0;
vkb::hash_combine(result, subpass_info.input_attachments);
vkb::hash_combine(result, subpass_info.output_attachments);
vkb::hash_combine(result, subpass_info.color_resolve_attachments);
vkb::hash_combine(result, subpass_info.disable_depth_stencil_attachment);
vkb::hash_combine(result, subpass_info.depth_stencil_resolve_attachment);
vkb::hash_combine(result, subpass_info.depth_stencil_resolve_mode);
vkb::hash_combine(result, subpass_info.debug_name);
return result;
}
};
template <>
struct hash<vkb::rendering::HPPAttachment>
{
size_t operator()(const vkb::rendering::HPPAttachment &attachment) const
{
size_t result = 0;
vkb::hash_combine(result, attachment.format);
vkb::hash_combine(result, attachment.samples);
vkb::hash_combine(result, attachment.usage);
vkb::hash_combine(result, attachment.initial_layout);
return result;
}
};
template <>
struct hash<vkb::rendering::HPPPipelineState>
{
size_t operator()(const vkb::rendering::HPPPipelineState &pipeline_state) const
{
return std::hash<vkb::PipelineState>()(reinterpret_cast<vkb::PipelineState const &>(pipeline_state));
}
};
template <>
struct hash<vkb::rendering::HPPRenderTarget>
{
size_t operator()(const vkb::rendering::HPPRenderTarget &render_target) const
{
size_t result = 0;
vkb::hash_combine(result, render_target.get_extent());
for (auto const &view : render_target.get_views())
{
vkb::hash_combine(result, view);
}
for (auto const &attachment : render_target.get_attachments())
{
vkb::hash_combine(result, attachment);
}
for (auto const &input : render_target.get_input_attachments())
{
vkb::hash_combine(result, input);
}
for (auto const &output : render_target.get_output_attachments())
{
vkb::hash_combine(result, output);
}
return result;
}
};
} // namespace std
namespace vkb
{
namespace common
{
/**
* @brief facade helper functions and structs around the functions and structs in common/resource_caching, providing a vulkan.hpp-based interface
*/
namespace
{
template <class T, class... A>
struct HPPRecordHelper
{
size_t record(HPPResourceRecord & /*recorder*/, A &.../*args*/)
{
return 0;
}
void index(HPPResourceRecord & /*recorder*/, size_t /*index*/, T & /*resource*/)
{}
};
template <class... A>
struct HPPRecordHelper<vkb::core::HPPShaderModule, A...>
{
size_t record(HPPResourceRecord &recorder, A &...args)
{
return recorder.register_shader_module(args...);
}
void index(HPPResourceRecord &recorder, size_t index, vkb::core::HPPShaderModule &shader_module)
{
recorder.set_shader_module(index, shader_module);
}
};
template <class... A>
struct HPPRecordHelper<vkb::core::HPPPipelineLayout, A...>
{
size_t record(HPPResourceRecord &recorder, A &...args)
{
return recorder.register_pipeline_layout(args...);
}
void index(HPPResourceRecord &recorder, size_t index, vkb::core::HPPPipelineLayout &pipeline_layout)
{
recorder.set_pipeline_layout(index, pipeline_layout);
}
};
template <class... A>
struct HPPRecordHelper<vkb::core::HPPRenderPass, A...>
{
size_t record(HPPResourceRecord &recorder, A &...args)
{
return recorder.register_render_pass(args...);
}
void index(HPPResourceRecord &recorder, size_t index, vkb::core::HPPRenderPass &render_pass)
{
recorder.set_render_pass(index, render_pass);
}
};
template <class... A>
struct HPPRecordHelper<vkb::core::HPPGraphicsPipeline, A...>
{
size_t record(HPPResourceRecord &recorder, A &...args)
{
return recorder.register_graphics_pipeline(args...);
}
void index(HPPResourceRecord &recorder, size_t index, vkb::core::HPPGraphicsPipeline &graphics_pipeline)
{
recorder.set_graphics_pipeline(index, graphics_pipeline);
}
};
} // namespace
template <class T, class... A>
T &request_resource(vkb::core::DeviceCpp &device, vkb::HPPResourceRecord *recorder, std::unordered_map<size_t, T> &resources, A &...args)
{
HPPRecordHelper<T, A...> record_helper;
size_t hash{0U};
hash_param(hash, args...);
auto res_it = resources.find(hash);
if (res_it != resources.end())
{
return res_it->second;
}
// If we do not have it already, create and cache it
const char *res_type = typeid(T).name();
size_t res_id = resources.size();
LOGD("Building #{} cache object ({})", res_id, res_type);
// Only error handle in release
#ifndef DEBUG
try
{
#endif
T resource(device, args...);
auto res_ins_it = resources.emplace(hash, std::move(resource));
if (!res_ins_it.second)
{
throw std::runtime_error{std::string{"Insertion error for #"} + std::to_string(res_id) + "cache object (" + res_type + ")"};
}
res_it = res_ins_it.first;
if (recorder)
{
size_t index = record_helper.record(*recorder, args...);
record_helper.index(*recorder, index, res_it->second);
}
#ifndef DEBUG
}
catch (const std::exception &e)
{
LOGE("Creation error for #{} cache object ({})", res_id, res_type);
throw e;
}
#endif
return res_it->second;
}
} // namespace common
} // namespace vkb
+36
View File
@@ -0,0 +1,36 @@
/* Copyright (c) 2021-2022, NVIDIA CORPORATION. All rights reserved.
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 the "License";
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#include <common/strings.h>
#include <vulkan/vulkan.hpp>
namespace vkb
{
namespace common
{
/**
* @brief facade helper functions around the functions in common/strings.h, providing a vulkan.hpp-based interface
*/
std::string to_string(vk::Extent2D const &extent)
{
return vkb::to_string(static_cast<VkExtent2D const &>(extent));
}
} // namespace common
} // namespace vkb
+42
View File
@@ -0,0 +1,42 @@
/* Copyright (c) 2021-2024, NVIDIA CORPORATION. All rights reserved.
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 the "License";
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#include <common/utils.h>
#include <rendering/hpp_render_context.h>
#include <scene_graph/hpp_scene.h>
/**
* @brief facade helper functions around the functions in common/utils.h, providing a vulkan.hpp-based interface
*/
namespace vkb
{
namespace common
{
inline sg::Node &add_free_camera(vkb::scene_graph::HPPScene &scene, const std::string &node_name, vk::Extent2D const &extent)
{
return vkb::add_free_camera(reinterpret_cast<vkb::sg::Scene &>(scene), node_name, static_cast<VkExtent2D>(extent));
}
inline void screenshot(vkb::rendering::HPPRenderContext &render_context, const std::string &filename)
{
vkb::screenshot(reinterpret_cast<vkb::RenderContext &>(render_context), filename);
}
} // namespace common
} // namespace vkb
+453
View File
@@ -0,0 +1,453 @@
/* Copyright (c) 2021-2025, NVIDIA CORPORATION. All rights reserved.
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 the "License";
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#include "common/vk_common.h"
#include "core/util/logging.hpp"
#include "vulkan/vulkan.hpp"
#include "vulkan/vulkan_format_traits.hpp"
namespace vkb
{
namespace common
{
/**
* @brief facade helper functions and structs around the functions and structs in common/vk_common, providing a vulkan.hpp-based interface
*/
struct HPPBufferMemoryBarrier
{
vk::PipelineStageFlags src_stage_mask = vk::PipelineStageFlagBits::eBottomOfPipe;
vk::PipelineStageFlags dst_stage_mask = vk::PipelineStageFlagBits::eTopOfPipe;
vk::AccessFlags src_access_mask = {};
vk::AccessFlags dst_access_mask = {};
};
struct HPPImageMemoryBarrier
{
vk::PipelineStageFlags src_stage_mask = vk::PipelineStageFlagBits::eBottomOfPipe;
vk::PipelineStageFlags dst_stage_mask = vk::PipelineStageFlagBits::eTopOfPipe;
vk::AccessFlags src_access_mask;
vk::AccessFlags dst_access_mask;
vk::ImageLayout old_layout = vk::ImageLayout::eUndefined;
vk::ImageLayout new_layout = vk::ImageLayout::eUndefined;
uint32_t src_queue_family = VK_QUEUE_FAMILY_IGNORED;
uint32_t dst_queue_family = VK_QUEUE_FAMILY_IGNORED;
};
struct HPPLoadStoreInfo
{
vk::AttachmentLoadOp load_op = vk::AttachmentLoadOp::eClear;
vk::AttachmentStoreOp store_op = vk::AttachmentStoreOp::eStore;
};
inline int32_t get_bits_per_pixel(vk::Format format)
{
return vkb::get_bits_per_pixel(static_cast<VkFormat>(format));
}
inline vk::Format get_suitable_depth_format(vk::PhysicalDevice physical_device,
bool depth_only = false,
const std::vector<vk::Format> &depth_format_priority_list = {
vk::Format::eD32Sfloat, vk::Format::eD24UnormS8Uint, vk::Format::eD16Unorm})
{
return static_cast<vk::Format>(
vkb::get_suitable_depth_format(physical_device, depth_only, reinterpret_cast<std::vector<VkFormat> const &>(depth_format_priority_list)));
}
inline bool is_buffer_descriptor_type(vk::DescriptorType descriptor_type)
{
return vkb::is_buffer_descriptor_type(static_cast<VkDescriptorType>(descriptor_type));
}
inline bool is_depth_only_format(vk::Format format)
{
assert(vkb::is_depth_only_format(static_cast<VkFormat>(format)) ==
((vk::componentCount(format) == 1) && (std::string(vk::componentName(format, 0)) == "D")));
return vkb::is_depth_only_format(static_cast<VkFormat>(format));
}
inline bool is_depth_stencil_format(vk::Format format)
{
assert(vkb::is_depth_stencil_format(static_cast<VkFormat>(format)) ==
((vk::componentCount(format) == 2) && (std::string(vk::componentName(format, 0)) == "D") &&
(std::string(vk::componentName(format, 1)) == "S")));
return vkb::is_depth_stencil_format(static_cast<VkFormat>(format));
}
inline bool is_depth_format(vk::Format format)
{
assert(vkb::is_depth_format(static_cast<VkFormat>(format)) == (std::string(vk::componentName(format, 0)) == "D"));
return vkb::is_depth_format(static_cast<VkFormat>(format));
}
inline bool is_dynamic_buffer_descriptor_type(vk::DescriptorType descriptor_type)
{
return vkb::is_dynamic_buffer_descriptor_type(static_cast<VkDescriptorType>(descriptor_type));
}
inline vk::ShaderModule load_shader(const std::string &filename, vk::Device device, vk::ShaderStageFlagBits stage)
{
return static_cast<vk::ShaderModule>(vkb::load_shader(filename, device, static_cast<VkShaderStageFlagBits>(stage)));
}
inline void image_layout_transition(vk::CommandBuffer command_buffer,
vk::Image image,
vk::ImageLayout old_layout,
vk::ImageLayout new_layout)
{
vkb::image_layout_transition(static_cast<VkCommandBuffer>(command_buffer),
static_cast<VkImage>(image),
static_cast<VkImageLayout>(old_layout),
static_cast<VkImageLayout>(new_layout));
}
inline void image_layout_transition(vk::CommandBuffer command_buffer,
vk::Image image,
vk::ImageLayout old_layout,
vk::ImageLayout new_layout,
vk::ImageSubresourceRange subresource_range)
{
vkb::image_layout_transition(static_cast<VkCommandBuffer>(command_buffer),
static_cast<VkImage>(image),
static_cast<VkImageLayout>(old_layout),
static_cast<VkImageLayout>(new_layout),
static_cast<VkImageSubresourceRange>(subresource_range));
}
inline void image_layout_transition(vk::CommandBuffer command_buffer,
vk::Image image,
vk::PipelineStageFlags src_stage_mask,
vk::PipelineStageFlags dst_stage_mask,
vk::AccessFlags src_access_mask,
vk::AccessFlags dst_access_mask,
vk::ImageLayout old_layout,
vk::ImageLayout new_layout,
vk::ImageSubresourceRange const &subresource_range)
{
vkb::image_layout_transition(static_cast<VkCommandBuffer>(command_buffer),
static_cast<VkImage>(image),
static_cast<VkPipelineStageFlags>(src_stage_mask),
static_cast<VkPipelineStageFlags>(dst_stage_mask),
static_cast<VkAccessFlags>(src_access_mask),
static_cast<VkAccessFlags>(dst_access_mask),
static_cast<VkImageLayout>(old_layout),
static_cast<VkImageLayout>(new_layout),
static_cast<VkImageSubresourceRange const &>(subresource_range));
}
inline void make_filters_valid(vk::PhysicalDevice physical_device, vk::Format format, vk::Filter *filter, vk::SamplerMipmapMode *mipmapMode = nullptr)
{
// Not all formats support linear filtering, so we need to adjust them if they don't
if (*filter == vk::Filter::eNearest && (mipmapMode == nullptr || *mipmapMode == vk::SamplerMipmapMode::eNearest))
{
return; // These must already be valid
}
vk::FormatProperties properties = physical_device.getFormatProperties(format);
if (!(properties.optimalTilingFeatures & vk::FormatFeatureFlagBits::eSampledImageFilterLinear))
{
*filter = vk::Filter::eNearest;
if (mipmapMode)
{
*mipmapMode = vk::SamplerMipmapMode::eNearest;
}
}
}
inline vk::SurfaceFormatKHR select_surface_format(vk::PhysicalDevice gpu,
vk::SurfaceKHR surface,
std::vector<vk::Format> const &preferred_formats = {
vk::Format::eR8G8B8A8Srgb, vk::Format::eB8G8R8A8Srgb, vk::Format::eA8B8G8R8SrgbPack32})
{
std::vector<vk::SurfaceFormatKHR> supported_surface_formats = gpu.getSurfaceFormatsKHR(surface);
assert(!supported_surface_formats.empty());
auto it = std::ranges::find_if(supported_surface_formats,
[&preferred_formats](vk::SurfaceFormatKHR surface_format) {
return std::ranges::any_of(preferred_formats,
[&surface_format](vk::Format format) { return format == surface_format.format; });
});
// We use the first supported format as a fallback in case none of the preferred formats is available
return it != supported_surface_formats.end() ? *it : supported_surface_formats[0];
}
inline vk::Format choose_blendable_format(vk::PhysicalDevice gpu, const std::vector<vk::Format> &format_priority_list)
{
for (const auto &format : format_priority_list)
{
vk::FormatProperties fmt_props = gpu.getFormatProperties(format);
if (fmt_props.optimalTilingFeatures & vk::FormatFeatureFlagBits::eColorAttachmentBlend)
return format;
}
throw std::runtime_error("No suitable blendable format could be determined");
}
// helper functions not backed by vk_common.h
inline vk::CommandBuffer
allocate_command_buffer(vk::Device device, vk::CommandPool command_pool, vk::CommandBufferLevel level = vk::CommandBufferLevel::ePrimary)
{
vk::CommandBufferAllocateInfo command_buffer_allocate_info{.commandPool = command_pool, .level = level, .commandBufferCount = 1};
return device.allocateCommandBuffers(command_buffer_allocate_info).front();
}
inline vk::DescriptorSet allocate_descriptor_set(vk::Device device, vk::DescriptorPool descriptor_pool, vk::DescriptorSetLayout descriptor_set_layout)
{
vk::DescriptorSetAllocateInfo descriptor_set_allocate_info{.descriptorPool = descriptor_pool,
.descriptorSetCount = 1,
.pSetLayouts = &descriptor_set_layout};
return device.allocateDescriptorSets(descriptor_set_allocate_info).front();
}
inline vk::Framebuffer
create_framebuffer(vk::Device device, vk::RenderPass render_pass, std::vector<vk::ImageView> const &attachments, vk::Extent2D const &extent)
{
vk::FramebufferCreateInfo framebuffer_create_info{.renderPass = render_pass,
.attachmentCount = static_cast<uint32_t>(attachments.size()),
.pAttachments = attachments.data(),
.width = extent.width,
.height = extent.height,
.layers = 1};
return device.createFramebuffer(framebuffer_create_info);
}
inline vk::Pipeline create_graphics_pipeline(vk::Device device,
vk::PipelineCache pipeline_cache,
std::vector<vk::PipelineShaderStageCreateInfo> const &shader_stages,
vk::PipelineVertexInputStateCreateInfo const &vertex_input_state,
vk::PrimitiveTopology primitive_topology,
uint32_t patch_control_points,
vk::PolygonMode polygon_mode,
vk::CullModeFlags cull_mode,
vk::FrontFace front_face,
std::vector<vk::PipelineColorBlendAttachmentState> const &blend_attachment_states,
vk::PipelineDepthStencilStateCreateInfo const &depth_stencil_state,
vk::PipelineLayout pipeline_layout,
vk::RenderPass render_pass)
{
vk::PipelineInputAssemblyStateCreateInfo input_assembly_state{.topology = primitive_topology};
vk::PipelineTessellationStateCreateInfo tessellation_state{.patchControlPoints = patch_control_points};
vk::PipelineViewportStateCreateInfo viewport_state{.viewportCount = 1, .scissorCount = 1};
vk::PipelineRasterizationStateCreateInfo rasterization_state{
.polygonMode = polygon_mode, .cullMode = cull_mode, .frontFace = front_face, .lineWidth = 1.0f};
vk::PipelineMultisampleStateCreateInfo multisample_state{.rasterizationSamples = vk::SampleCountFlagBits::e1};
vk::PipelineColorBlendStateCreateInfo color_blend_state{.attachmentCount = static_cast<uint32_t>(blend_attachment_states.size()),
.pAttachments = blend_attachment_states.data()};
std::array<vk::DynamicState, 2> dynamic_state_enables = {vk::DynamicState::eViewport, vk::DynamicState::eScissor};
vk::PipelineDynamicStateCreateInfo dynamic_state{.dynamicStateCount = static_cast<uint32_t>(dynamic_state_enables.size()),
.pDynamicStates = dynamic_state_enables.data()};
// Final fullscreen composition pass pipeline
vk::GraphicsPipelineCreateInfo pipeline_create_info{.stageCount = static_cast<uint32_t>(shader_stages.size()),
.pStages = shader_stages.data(),
.pVertexInputState = &vertex_input_state,
.pInputAssemblyState = &input_assembly_state,
.pTessellationState = &tessellation_state,
.pViewportState = &viewport_state,
.pRasterizationState = &rasterization_state,
.pMultisampleState = &multisample_state,
.pDepthStencilState = &depth_stencil_state,
.pColorBlendState = &color_blend_state,
.pDynamicState = &dynamic_state,
.layout = pipeline_layout,
.renderPass = render_pass,
.basePipelineIndex = -1};
vk::Result result;
vk::Pipeline pipeline;
std::tie(result, pipeline) = device.createGraphicsPipeline(pipeline_cache, pipeline_create_info);
assert(result == vk::Result::eSuccess);
return pipeline;
}
inline vk::ImageView create_image_view(vk::Device device,
vk::Image image,
vk::ImageViewType view_type,
vk::Format format,
vk::ImageAspectFlags aspect_mask = vk::ImageAspectFlagBits::eColor,
uint32_t base_mip_level = 0,
uint32_t level_count = 1,
uint32_t base_array_layer = 0,
uint32_t layer_count = 1)
{
vk::ImageViewCreateInfo image_view_create_info{.image = image,
.viewType = view_type,
.format = format,
.subresourceRange = {.aspectMask = aspect_mask,
.baseMipLevel = base_mip_level,
.levelCount = level_count,
.baseArrayLayer = base_array_layer,
.layerCount = layer_count}};
return device.createImageView(image_view_create_info);
}
inline vk::QueryPool create_query_pool(vk::Device device, vk::QueryType query_type, uint32_t query_count, vk::QueryPipelineStatisticFlags pipeline_statistics = {})
{
vk::QueryPoolCreateInfo query_pool_create_info{.queryType = query_type, .queryCount = query_count, .pipelineStatistics = pipeline_statistics};
return device.createQueryPool(query_pool_create_info);
}
inline vk::Sampler create_sampler(vk::Device device,
vk::Filter mag_filter,
vk::Filter min_filter,
vk::SamplerMipmapMode mipmap_mode,
vk::SamplerAddressMode sampler_address_mode,
float max_anisotropy,
float max_LOD)
{
vk::SamplerCreateInfo sampler_create_info{.magFilter = mag_filter,
.minFilter = min_filter,
.mipmapMode = mipmap_mode,
.addressModeU = sampler_address_mode,
.addressModeV = sampler_address_mode,
.addressModeW = sampler_address_mode,
.anisotropyEnable = (1.0f < max_anisotropy),
.maxAnisotropy = max_anisotropy,
.compareOp = vk::CompareOp::eNever,
.minLod = 0.0f,
.maxLod = max_LOD,
.borderColor = vk::BorderColor::eFloatOpaqueWhite};
return device.createSampler(sampler_create_info);
}
inline vk::Sampler create_sampler(vk::PhysicalDevice gpu,
vk::Device device,
vk::Format format,
vk::Filter filter,
vk::SamplerAddressMode sampler_address_mode,
float max_anisotropy,
float max_LOD)
{
const vk::FormatProperties fmt_props = gpu.getFormatProperties(format);
bool has_linear_filter = !!(fmt_props.optimalTilingFeatures & vk::FormatFeatureFlagBits::eSampledImageFilterLinear);
return create_sampler(device,
has_linear_filter ? filter : vk::Filter::eNearest,
has_linear_filter ? filter : vk::Filter::eNearest,
has_linear_filter ? vk::SamplerMipmapMode::eLinear : vk::SamplerMipmapMode::eNearest,
sampler_address_mode,
max_anisotropy,
max_LOD);
}
inline vk::ImageAspectFlags get_image_aspect_flags(vk::ImageUsageFlagBits usage, vk::Format format)
{
vk::ImageAspectFlags image_aspect_flags;
switch (usage)
{
case vk::ImageUsageFlagBits::eColorAttachment:
assert(!vkb::common::is_depth_format(format));
image_aspect_flags = vk::ImageAspectFlagBits::eColor;
break;
case vk::ImageUsageFlagBits::eDepthStencilAttachment:
assert(vkb::common::is_depth_format(format));
image_aspect_flags = vk::ImageAspectFlagBits::eDepth;
// Stencil aspect should only be set on depth + stencil formats
if (vkb::common::is_depth_stencil_format(format))
{
image_aspect_flags |= vk::ImageAspectFlagBits::eStencil;
}
break;
default:
assert(false);
}
return image_aspect_flags;
}
inline void submit_and_wait(vk::Device device, vk::Queue queue, std::vector<vk::CommandBuffer> command_buffers, std::vector<vk::Semaphore> semaphores = {})
{
// Submit command_buffer
vk::SubmitInfo submit_info{.commandBufferCount = static_cast<uint32_t>(command_buffers.size()),
.pCommandBuffers = command_buffers.data(),
.signalSemaphoreCount = static_cast<uint32_t>(semaphores.size()),
.pSignalSemaphores = semaphores.data()};
// Create fence to ensure that command_buffer has finished executing
vk::Fence fence = device.createFence({});
// Submit to the queue
queue.submit(submit_info, fence);
// Wait for the fence to signal that command_buffer has finished executing
vk::Result result = device.waitForFences(fence, true, DEFAULT_FENCE_TIMEOUT);
if (result != vk::Result::eSuccess)
{
LOGE("Vulkan error on waitForFences: {}", vk::to_string(result));
abort();
}
// Destroy the fence
device.destroyFence(fence);
}
inline uint32_t get_queue_family_index(std::vector<vk::QueueFamilyProperties> const &queue_family_properties, vk::QueueFlagBits queue_flag)
{
// Dedicated queue for compute
// Try to find a queue family index that supports compute but not graphics
if (queue_flag & vk::QueueFlagBits::eCompute)
{
auto propertyIt = std::ranges::find_if(queue_family_properties,
[queue_flag](const vk::QueueFamilyProperties &property) { return (property.queueFlags & queue_flag) && !(property.queueFlags & vk::QueueFlagBits::eGraphics); });
if (propertyIt != queue_family_properties.end())
{
return static_cast<uint32_t>(std::distance(queue_family_properties.begin(), propertyIt));
}
}
// Dedicated queue for transfer
// Try to find a queue family index that supports transfer but not graphics and compute
if (queue_flag & vk::QueueFlagBits::eTransfer)
{
auto propertyIt = std::ranges::find_if(queue_family_properties,
[queue_flag](const vk::QueueFamilyProperties &property) {
return (property.queueFlags & queue_flag) && !(property.queueFlags & vk::QueueFlagBits::eGraphics) &&
!(property.queueFlags & vk::QueueFlagBits::eCompute);
});
if (propertyIt != queue_family_properties.end())
{
return static_cast<uint32_t>(std::distance(queue_family_properties.begin(), propertyIt));
}
}
// For other queue types or if no separate compute queue is present, return the first one to support the requested flags
auto propertyIt = std::ranges::find_if(
queue_family_properties, [queue_flag](const vk::QueueFamilyProperties &property) { return (property.queueFlags & queue_flag) == queue_flag; });
if (propertyIt != queue_family_properties.end())
{
return static_cast<uint32_t>(std::distance(queue_family_properties.begin(), propertyIt));
}
throw std::runtime_error("Could not find a matching queue family index");
}
} // namespace common
} // namespace vkb
+36
View File
@@ -0,0 +1,36 @@
/* Copyright (c) 2023, NVIDIA CORPORATION. All rights reserved.
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 the "License";
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "ktx_common.h"
#include <stdexcept>
namespace vkb
{
namespace ktx
{
ktxTexture *load_texture(std::string const &filename)
{
ktxTexture *ktx_texture;
KTX_error_code result = ktxTexture_CreateFromNamedFile(filename.c_str(), KTX_TEXTURE_CREATE_LOAD_IMAGE_DATA_BIT, &ktx_texture);
if ((result != KTX_SUCCESS) || (ktx_texture == nullptr))
{
throw std::runtime_error("Couldn't load texture");
}
return ktx_texture;
}
} // namespace ktx
} // namespace vkb
+29
View File
@@ -0,0 +1,29 @@
/* Copyright (c) 2023, NVIDIA CORPORATION. All rights reserved.
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 the "License";
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#include <ktx.h>
#include <string>
namespace vkb
{
namespace ktx
{
ktxTexture *load_texture(std::string const &filename);
}
} // namespace vkb
+133
View File
@@ -0,0 +1,133 @@
/* Copyright (c) 2021, Arm Limited and Contributors
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 the "License";
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#include <cassert>
namespace vkb
{
// Simple optional class for pre c++17 std::optional
// May be replaced by std::optional in the future
template <typename Type>
class Optional
{
public:
virtual ~Optional() = default;
Optional() = default;
Optional(const Optional &optional);
template <typename U = Type>
Optional(const U &value);
bool has_value() const;
const Type &value() const;
const Type value_or(Type &&alternative) const;
const Type value_or(const Type &alternative = {}) const;
Optional &operator=(Optional &&opt)
{
_has_value = opt._has_value;
_value = opt._value;
return *this;
}
template <typename U = Type>
Optional &operator=(U &&value)
{
_has_value = true;
_value = value;
return *this;
}
Optional &operator=(const Optional &value)
{
_has_value = value._has_value;
_value = value._value;
return *this;
}
template <typename U = Type>
Optional &operator=(U *value)
{
if (value == nullptr)
{
_has_value = false;
return *this;
}
_has_value = true;
_value = *value;
return *this;
}
private:
bool _has_value = false;
Type _value;
};
template <typename Type>
Optional<Type>::Optional(const Optional &optional)
{
_has_value = optional._has_value;
_value = optional._value;
}
template <typename Type>
template <typename U>
Optional<Type>::Optional(const U &value)
{
_has_value = true;
_value = value;
}
template <typename Type>
bool Optional<Type>::has_value() const
{
return _has_value;
}
template <typename Type>
const Type &Optional<Type>::value() const
{
assert(_has_value && "Value does not exist");
return _value;
}
template <typename Type>
const Type Optional<Type>::value_or(Type &&alternative) const
{
if (has_value())
{
return _value;
}
return alternative;
}
template <typename Type>
const Type Optional<Type>::value_or(const Type &alternative) const
{
if (has_value())
{
return _value;
}
return alternative;
}
} // namespace vkb
+781
View File
@@ -0,0 +1,781 @@
/* Copyright (c) 2018-2025, Arm Limited and Contributors
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 the "License";
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#include "core/descriptor_pool.h"
#include "core/descriptor_set.h"
#include "core/descriptor_set_layout.h"
#include "core/framebuffer.h"
#include "core/image.h"
#include "core/pipeline.h"
#include "rendering/pipeline_state.h"
#include "rendering/render_target.h"
#include "resource_record.h"
#include "common/helpers.h"
namespace std
{
template <>
struct hash<vkb::ShaderSource>
{
std::size_t operator()(const vkb::ShaderSource &shader_source) const
{
std::size_t result = 0;
vkb::hash_combine(result, shader_source.get_id());
return result;
}
};
template <>
struct hash<vkb::ShaderVariant>
{
std::size_t operator()(const vkb::ShaderVariant &shader_variant) const
{
std::size_t result = 0;
vkb::hash_combine(result, shader_variant.get_id());
return result;
}
};
template <>
struct hash<vkb::ShaderModule>
{
std::size_t operator()(const vkb::ShaderModule &shader_module) const
{
std::size_t result = 0;
vkb::hash_combine(result, shader_module.get_id());
return result;
}
};
template <>
struct hash<vkb::DescriptorSetLayout>
{
std::size_t operator()(const vkb::DescriptorSetLayout &descriptor_set_layout) const
{
std::size_t result = 0;
vkb::hash_combine(result, descriptor_set_layout.get_handle());
return result;
}
};
template <>
struct hash<vkb::DescriptorPool>
{
std::size_t operator()(const vkb::DescriptorPool &descriptor_pool) const
{
std::size_t result = 0;
vkb::hash_combine(result, descriptor_pool.get_descriptor_set_layout());
return result;
}
};
template <>
struct hash<vkb::PipelineLayout>
{
std::size_t operator()(const vkb::PipelineLayout &pipeline_layout) const
{
std::size_t result = 0;
vkb::hash_combine(result, pipeline_layout.get_handle());
return result;
}
};
template <>
struct hash<vkb::RenderPass>
{
std::size_t operator()(const vkb::RenderPass &render_pass) const
{
std::size_t result = 0;
vkb::hash_combine(result, render_pass.get_handle());
return result;
}
};
template <>
struct hash<vkb::Attachment>
{
std::size_t operator()(const vkb::Attachment &attachment) const
{
std::size_t result = 0;
vkb::hash_combine(result, static_cast<std::underlying_type<VkFormat>::type>(attachment.format));
vkb::hash_combine(result, static_cast<std::underlying_type<VkSampleCountFlagBits>::type>(attachment.samples));
vkb::hash_combine(result, attachment.usage);
vkb::hash_combine(result, static_cast<std::underlying_type<VkImageLayout>::type>(attachment.initial_layout));
return result;
}
};
template <>
struct hash<vkb::LoadStoreInfo>
{
std::size_t operator()(const vkb::LoadStoreInfo &load_store_info) const
{
std::size_t result = 0;
vkb::hash_combine(result, static_cast<std::underlying_type<VkAttachmentLoadOp>::type>(load_store_info.load_op));
vkb::hash_combine(result, static_cast<std::underlying_type<VkAttachmentStoreOp>::type>(load_store_info.store_op));
return result;
}
};
template <>
struct hash<vkb::SubpassInfo>
{
std::size_t operator()(const vkb::SubpassInfo &subpass_info) const
{
std::size_t result = 0;
for (uint32_t output_attachment : subpass_info.output_attachments)
{
vkb::hash_combine(result, output_attachment);
}
for (uint32_t input_attachment : subpass_info.input_attachments)
{
vkb::hash_combine(result, input_attachment);
}
for (uint32_t resolve_attachment : subpass_info.color_resolve_attachments)
{
vkb::hash_combine(result, resolve_attachment);
}
vkb::hash_combine(result, subpass_info.disable_depth_stencil_attachment);
vkb::hash_combine(result, subpass_info.depth_stencil_resolve_attachment);
vkb::hash_combine(result, subpass_info.depth_stencil_resolve_mode);
return result;
}
};
template <>
struct hash<vkb::SpecializationConstantState>
{
std::size_t operator()(const vkb::SpecializationConstantState &specialization_constant_state) const
{
std::size_t result = 0;
for (auto constants : specialization_constant_state.get_specialization_constant_state())
{
vkb::hash_combine(result, constants.first);
for (const auto data : constants.second)
{
vkb::hash_combine(result, data);
}
}
return result;
}
};
template <>
struct hash<vkb::ShaderResource>
{
std::size_t operator()(const vkb::ShaderResource &shader_resource) const
{
std::size_t result = 0;
if (shader_resource.type == vkb::ShaderResourceType::Input ||
shader_resource.type == vkb::ShaderResourceType::Output ||
shader_resource.type == vkb::ShaderResourceType::PushConstant ||
shader_resource.type == vkb::ShaderResourceType::SpecializationConstant)
{
return result;
}
vkb::hash_combine(result, shader_resource.set);
vkb::hash_combine(result, shader_resource.binding);
vkb::hash_combine(result, static_cast<std::underlying_type<vkb::ShaderResourceType>::type>(shader_resource.type));
vkb::hash_combine(result, shader_resource.mode);
return result;
}
};
template <>
struct hash<VkDescriptorBufferInfo>
{
std::size_t operator()(const VkDescriptorBufferInfo &descriptor_buffer_info) const
{
std::size_t result = 0;
vkb::hash_combine(result, descriptor_buffer_info.buffer);
vkb::hash_combine(result, descriptor_buffer_info.range);
vkb::hash_combine(result, descriptor_buffer_info.offset);
return result;
}
};
template <>
struct hash<VkDescriptorImageInfo>
{
std::size_t operator()(const VkDescriptorImageInfo &descriptor_image_info) const
{
std::size_t result = 0;
vkb::hash_combine(result, descriptor_image_info.imageView);
vkb::hash_combine(result, static_cast<std::underlying_type<VkImageLayout>::type>(descriptor_image_info.imageLayout));
vkb::hash_combine(result, descriptor_image_info.sampler);
return result;
}
};
template <>
struct hash<VkWriteDescriptorSet>
{
std::size_t operator()(const VkWriteDescriptorSet &write_descriptor_set) const
{
std::size_t result = 0;
vkb::hash_combine(result, write_descriptor_set.dstSet);
vkb::hash_combine(result, write_descriptor_set.dstBinding);
vkb::hash_combine(result, write_descriptor_set.dstArrayElement);
vkb::hash_combine(result, write_descriptor_set.descriptorCount);
vkb::hash_combine(result, write_descriptor_set.descriptorType);
switch (write_descriptor_set.descriptorType)
{
case VK_DESCRIPTOR_TYPE_SAMPLER:
case VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER:
case VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE:
case VK_DESCRIPTOR_TYPE_STORAGE_IMAGE:
case VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT:
for (uint32_t i = 0; i < write_descriptor_set.descriptorCount; i++)
{
vkb::hash_combine(result, write_descriptor_set.pImageInfo[i]);
}
break;
case VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER:
case VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER:
for (uint32_t i = 0; i < write_descriptor_set.descriptorCount; i++)
{
vkb::hash_combine(result, write_descriptor_set.pTexelBufferView[i]);
}
break;
case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER:
case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER:
case VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC:
case VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC:
for (uint32_t i = 0; i < write_descriptor_set.descriptorCount; i++)
{
vkb::hash_combine(result, write_descriptor_set.pBufferInfo[i]);
}
break;
default:
// Not implemented
break;
};
return result;
}
};
template <>
struct hash<VkVertexInputAttributeDescription>
{
std::size_t operator()(const VkVertexInputAttributeDescription &vertex_attrib) const
{
std::size_t result = 0;
vkb::hash_combine(result, vertex_attrib.binding);
vkb::hash_combine(result, static_cast<std::underlying_type<VkFormat>::type>(vertex_attrib.format));
vkb::hash_combine(result, vertex_attrib.location);
vkb::hash_combine(result, vertex_attrib.offset);
return result;
}
};
template <>
struct hash<VkVertexInputBindingDescription>
{
std::size_t operator()(const VkVertexInputBindingDescription &vertex_binding) const
{
std::size_t result = 0;
vkb::hash_combine(result, vertex_binding.binding);
vkb::hash_combine(result, static_cast<std::underlying_type<VkVertexInputRate>::type>(vertex_binding.inputRate));
vkb::hash_combine(result, vertex_binding.stride);
return result;
}
};
template <>
struct hash<vkb::StencilOpState>
{
std::size_t operator()(const vkb::StencilOpState &stencil) const
{
std::size_t result = 0;
vkb::hash_combine(result, static_cast<std::underlying_type<VkCompareOp>::type>(stencil.compare_op));
vkb::hash_combine(result, static_cast<std::underlying_type<VkStencilOp>::type>(stencil.depth_fail_op));
vkb::hash_combine(result, static_cast<std::underlying_type<VkStencilOp>::type>(stencil.fail_op));
vkb::hash_combine(result, static_cast<std::underlying_type<VkStencilOp>::type>(stencil.pass_op));
return result;
}
};
template <>
struct hash<VkExtent2D>
{
size_t operator()(const VkExtent2D &extent) const
{
size_t result = 0;
vkb::hash_combine(result, extent.width);
vkb::hash_combine(result, extent.height);
return result;
}
};
template <>
struct hash<VkOffset2D>
{
size_t operator()(const VkOffset2D &offset) const
{
size_t result = 0;
vkb::hash_combine(result, offset.x);
vkb::hash_combine(result, offset.y);
return result;
}
};
template <>
struct hash<VkRect2D>
{
size_t operator()(const VkRect2D &rect) const
{
size_t result = 0;
vkb::hash_combine(result, rect.extent);
vkb::hash_combine(result, rect.offset);
return result;
}
};
template <>
struct hash<VkViewport>
{
size_t operator()(const VkViewport &viewport) const
{
size_t result = 0;
vkb::hash_combine(result, viewport.width);
vkb::hash_combine(result, viewport.height);
vkb::hash_combine(result, viewport.maxDepth);
vkb::hash_combine(result, viewport.minDepth);
vkb::hash_combine(result, viewport.x);
vkb::hash_combine(result, viewport.y);
return result;
}
};
template <>
struct hash<vkb::ColorBlendAttachmentState>
{
std::size_t operator()(const vkb::ColorBlendAttachmentState &color_blend_attachment) const
{
std::size_t result = 0;
vkb::hash_combine(result, static_cast<std::underlying_type<VkBlendOp>::type>(color_blend_attachment.alpha_blend_op));
vkb::hash_combine(result, color_blend_attachment.blend_enable);
vkb::hash_combine(result, static_cast<std::underlying_type<VkBlendOp>::type>(color_blend_attachment.color_blend_op));
vkb::hash_combine(result, color_blend_attachment.color_write_mask);
vkb::hash_combine(result, static_cast<std::underlying_type<VkBlendFactor>::type>(color_blend_attachment.dst_alpha_blend_factor));
vkb::hash_combine(result, static_cast<std::underlying_type<VkBlendFactor>::type>(color_blend_attachment.dst_color_blend_factor));
vkb::hash_combine(result, static_cast<std::underlying_type<VkBlendFactor>::type>(color_blend_attachment.src_alpha_blend_factor));
vkb::hash_combine(result, static_cast<std::underlying_type<VkBlendFactor>::type>(color_blend_attachment.src_color_blend_factor));
return result;
}
};
template <>
struct hash<vkb::RenderTarget>
{
std::size_t operator()(const vkb::RenderTarget &render_target) const
{
std::size_t result = 0;
for (auto &view : render_target.get_views())
{
vkb::hash_combine(result, view.get_handle());
vkb::hash_combine(result, view.get_image().get_handle());
}
return result;
}
};
template <>
struct hash<vkb::PipelineState>
{
std::size_t operator()(const vkb::PipelineState &pipeline_state) const
{
std::size_t result = 0;
vkb::hash_combine(result, pipeline_state.get_pipeline_layout().get_handle());
// For graphics only
if (auto render_pass = pipeline_state.get_render_pass())
{
vkb::hash_combine(result, render_pass->get_handle());
}
vkb::hash_combine(result, pipeline_state.get_specialization_constant_state());
vkb::hash_combine(result, pipeline_state.get_subpass_index());
for (auto shader_module : pipeline_state.get_pipeline_layout().get_shader_modules())
{
vkb::hash_combine(result, shader_module->get_id());
}
// VkPipelineVertexInputStateCreateInfo
for (auto &attribute : pipeline_state.get_vertex_input_state().attributes)
{
vkb::hash_combine(result, attribute);
}
for (auto &binding : pipeline_state.get_vertex_input_state().bindings)
{
vkb::hash_combine(result, binding);
}
// VkPipelineInputAssemblyStateCreateInfo
vkb::hash_combine(result, pipeline_state.get_input_assembly_state().primitive_restart_enable);
vkb::hash_combine(result, static_cast<std::underlying_type<VkPrimitiveTopology>::type>(pipeline_state.get_input_assembly_state().topology));
// VkPipelineViewportStateCreateInfo
vkb::hash_combine(result, pipeline_state.get_viewport_state().viewport_count);
vkb::hash_combine(result, pipeline_state.get_viewport_state().scissor_count);
// VkPipelineRasterizationStateCreateInfo
vkb::hash_combine(result, pipeline_state.get_rasterization_state().cull_mode);
vkb::hash_combine(result, pipeline_state.get_rasterization_state().depth_bias_enable);
vkb::hash_combine(result, pipeline_state.get_rasterization_state().depth_clamp_enable);
vkb::hash_combine(result, static_cast<std::underlying_type<VkFrontFace>::type>(pipeline_state.get_rasterization_state().front_face));
vkb::hash_combine(result, static_cast<std::underlying_type<VkPolygonMode>::type>(pipeline_state.get_rasterization_state().polygon_mode));
vkb::hash_combine(result, pipeline_state.get_rasterization_state().rasterizer_discard_enable);
// VkPipelineMultisampleStateCreateInfo
vkb::hash_combine(result, pipeline_state.get_multisample_state().alpha_to_coverage_enable);
vkb::hash_combine(result, pipeline_state.get_multisample_state().alpha_to_one_enable);
vkb::hash_combine(result, pipeline_state.get_multisample_state().min_sample_shading);
vkb::hash_combine(result, static_cast<std::underlying_type<VkSampleCountFlagBits>::type>(pipeline_state.get_multisample_state().rasterization_samples));
vkb::hash_combine(result, pipeline_state.get_multisample_state().sample_shading_enable);
vkb::hash_combine(result, pipeline_state.get_multisample_state().sample_mask);
// VkPipelineDepthStencilStateCreateInfo
vkb::hash_combine(result, pipeline_state.get_depth_stencil_state().back);
vkb::hash_combine(result, pipeline_state.get_depth_stencil_state().depth_bounds_test_enable);
vkb::hash_combine(result, static_cast<std::underlying_type<VkCompareOp>::type>(pipeline_state.get_depth_stencil_state().depth_compare_op));
vkb::hash_combine(result, pipeline_state.get_depth_stencil_state().depth_test_enable);
vkb::hash_combine(result, pipeline_state.get_depth_stencil_state().depth_write_enable);
vkb::hash_combine(result, pipeline_state.get_depth_stencil_state().front);
vkb::hash_combine(result, pipeline_state.get_depth_stencil_state().stencil_test_enable);
// VkPipelineColorBlendStateCreateInfo
vkb::hash_combine(result, static_cast<std::underlying_type<VkLogicOp>::type>(pipeline_state.get_color_blend_state().logic_op));
vkb::hash_combine(result, pipeline_state.get_color_blend_state().logic_op_enable);
for (auto &attachment : pipeline_state.get_color_blend_state().attachments)
{
vkb::hash_combine(result, attachment);
}
return result;
}
};
} // namespace std
namespace vkb
{
namespace
{
template <typename T>
inline void hash_param(size_t &seed, const T &value)
{
hash_combine(seed, value);
}
template <>
inline void hash_param(size_t & /*seed*/, const VkPipelineCache & /*value*/)
{
}
template <>
inline void hash_param<std::vector<uint8_t>>(
size_t &seed,
const std::vector<uint8_t> &value)
{
hash_combine(seed, std::string{value.begin(), value.end()});
}
template <>
inline void hash_param<std::vector<Attachment>>(
size_t &seed,
const std::vector<Attachment> &value)
{
for (auto &attachment : value)
{
hash_combine(seed, attachment);
}
}
template <>
inline void hash_param<std::vector<LoadStoreInfo>>(
size_t &seed,
const std::vector<LoadStoreInfo> &value)
{
for (auto &load_store_info : value)
{
hash_combine(seed, load_store_info);
}
}
template <>
inline void hash_param<std::vector<SubpassInfo>>(
size_t &seed,
const std::vector<SubpassInfo> &value)
{
for (auto &subpass_info : value)
{
hash_combine(seed, subpass_info);
}
}
template <>
inline void hash_param<std::vector<ShaderModule *>>(
size_t &seed,
const std::vector<ShaderModule *> &value)
{
for (auto &shader_module : value)
{
hash_combine(seed, shader_module->get_id());
}
}
template <>
inline void hash_param<std::vector<ShaderResource>>(
size_t &seed,
const std::vector<ShaderResource> &value)
{
for (auto &resource : value)
{
hash_combine(seed, resource);
}
}
template <>
inline void hash_param<std::map<uint32_t, std::map<uint32_t, VkDescriptorBufferInfo>>>(
size_t &seed,
const std::map<uint32_t, std::map<uint32_t, VkDescriptorBufferInfo>> &value)
{
for (auto &binding_set : value)
{
hash_combine(seed, binding_set.first);
for (auto &binding_element : binding_set.second)
{
hash_combine(seed, binding_element.first);
hash_combine(seed, binding_element.second);
}
}
}
template <>
inline void hash_param<std::map<uint32_t, std::map<uint32_t, VkDescriptorImageInfo>>>(
size_t &seed,
const std::map<uint32_t, std::map<uint32_t, VkDescriptorImageInfo>> &value)
{
for (auto &binding_set : value)
{
hash_combine(seed, binding_set.first);
for (auto &binding_element : binding_set.second)
{
hash_combine(seed, binding_element.first);
hash_combine(seed, binding_element.second);
}
}
}
template <typename T, typename... Args>
inline void hash_param(size_t &seed, const T &first_arg, const Args &...args)
{
hash_param(seed, first_arg);
hash_param(seed, args...);
}
template <class T, class... A>
struct RecordHelper
{
size_t record(ResourceRecord & /*recorder*/, A &.../*args*/)
{
return 0;
}
void index(ResourceRecord & /*recorder*/, size_t /*index*/, T & /*resource*/)
{
}
};
template <class... A>
struct RecordHelper<ShaderModule, A...>
{
size_t record(ResourceRecord &recorder, A &...args)
{
return recorder.register_shader_module(args...);
}
void index(ResourceRecord &recorder, size_t index, ShaderModule &shader_module)
{
recorder.set_shader_module(index, shader_module);
}
};
template <class... A>
struct RecordHelper<PipelineLayout, A...>
{
size_t record(ResourceRecord &recorder, A &...args)
{
return recorder.register_pipeline_layout(args...);
}
void index(ResourceRecord &recorder, size_t index, PipelineLayout &pipeline_layout)
{
recorder.set_pipeline_layout(index, pipeline_layout);
}
};
template <class... A>
struct RecordHelper<RenderPass, A...>
{
size_t record(ResourceRecord &recorder, A &...args)
{
return recorder.register_render_pass(args...);
}
void index(ResourceRecord &recorder, size_t index, RenderPass &render_pass)
{
recorder.set_render_pass(index, render_pass);
}
};
template <class... A>
struct RecordHelper<GraphicsPipeline, A...>
{
size_t record(ResourceRecord &recorder, A &...args)
{
return recorder.register_graphics_pipeline(args...);
}
void index(ResourceRecord &recorder, size_t index, GraphicsPipeline &graphics_pipeline)
{
recorder.set_graphics_pipeline(index, graphics_pipeline);
}
};
} // namespace
template <class T, class... A>
T &request_resource(vkb::core::DeviceC &device, ResourceRecord *recorder, std::unordered_map<std::size_t, T> &resources, A &...args)
{
RecordHelper<T, A...> record_helper;
std::size_t hash{0U};
hash_param(hash, args...);
auto res_it = resources.find(hash);
if (res_it != resources.end())
{
return res_it->second;
}
// If we do not have it already, create and cache it
const char *res_type = typeid(T).name();
size_t res_id = resources.size();
LOGD("Building #{} cache object ({})", res_id, res_type);
// Only error handle in release
#ifndef DEBUG
try
{
#endif
T resource(device, args...);
auto res_ins_it = resources.emplace(hash, std::move(resource));
if (!res_ins_it.second)
{
throw std::runtime_error{std::string{"Insertion error for #"} + std::to_string(res_id) + "cache object (" + res_type + ")"};
}
res_it = res_ins_it.first;
if (recorder)
{
size_t index = record_helper.record(*recorder, args...);
record_helper.index(*recorder, index, res_it->second);
}
#ifndef DEBUG
}
catch (const std::exception &e)
{
LOGE("Creation error for #{} cache object ({})", res_id, res_type);
throw e;
}
#endif
return res_it->second;
}
} // namespace vkb
File diff suppressed because it is too large Load Diff
+304
View File
@@ -0,0 +1,304 @@
/* Copyright (c) 2018-2024, Arm Limited and Contributors
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 the "License";
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#include <map>
#include <sstream>
#include <string>
#include <unordered_map>
#include <vector>
#include <volk.h>
namespace vkb
{
enum class ShaderResourceType;
namespace sg
{
enum class AlphaMode;
}
std::vector<std::string> split(const std::string &str, const std::string &delimiter);
std::string join(const std::vector<std::string> &str, const std::string &separator);
/**
* @brief Helper function to convert a VkFormat enum to a string
* @param format Vulkan format to convert.
* @return The string to return.
*/
const std::string to_string(VkFormat format);
/**
* @brief Helper function to convert a VkPresentModeKHR to a string
* @param present_mode Vulkan present mode to convert.
* @return The string to return.
*/
const std::string to_string(VkPresentModeKHR present_mode);
/**
* @brief Helper function to convert a VkResult enum to a string
* @param result Vulkan result to convert.
* @return The string to return.
*/
const std::string to_string(VkResult result);
/**
* @brief Helper function to convert a VkPhysicalDeviceType enum to a string
* @param type Vulkan physical device type to convert.
* @return The string to return.
*/
const std::string to_string(VkPhysicalDeviceType type);
/**
* @brief Helper function to convert a VkSurfaceTransformFlagBitsKHR flag to a string
* @param transform_flag Vulkan surface transform flag bit to convert.
* @return The string to return.
*/
const std::string to_string(VkSurfaceTransformFlagBitsKHR transform_flag);
/**
* @brief Helper function to convert a VkSurfaceFormatKHR format to a string
* @param surface_format Vulkan surface format to convert.
* @return The string to return.
*/
const std::string to_string(VkSurfaceFormatKHR surface_format);
/**
* @brief Helper function to convert a VkCompositeAlphaFlagBitsKHR flag to a string
* @param composite_alpha Vulkan composite alpha flag bit to convert.
* @return The string to return.
*/
const std::string to_string(VkCompositeAlphaFlagBitsKHR composite_alpha);
/**
* @brief Helper function to convert a VkImageUsageFlagBits flag to a string
* @param image_usage Vulkan image usage flag bit to convert.
* @return The string to return.
*/
const std::string to_string(VkImageUsageFlagBits image_usage);
/**
* @brief Helper function to convert a VkExtent2D flag to a string
* @param format Vulkan format to convert.
* @return The string to return.
*/
std::string to_string(VkExtent2D format);
/**
* @brief Helper function to convert VkSampleCountFlagBits to a string
* @param flags Vulkan sample count flags to convert
* @return const std::string
*/
const std::string to_string(VkSampleCountFlagBits flags);
/**
* @brief Helper function to convert VkImageTiling to a string
* @param tiling Vulkan VkImageTiling to convert
* @return The string to return
*/
const std::string to_string(VkImageTiling tiling);
/**
* @brief Helper function to convert VkImageType to a string
* @param type Vulkan VkImageType to convert
* @return The string to return
*/
const std::string to_string(VkImageType type);
/**
* @brief Helper function to convert VkBlendFactor to a string
* @param blend Vulkan VkBlendFactor to convert
* @return The string to return
*/
const std::string to_string(VkBlendFactor blend);
/**
* @brief Helper function to convert VkVertexInputRate to a string
* @param rate Vulkan VkVertexInputRate to convert
* @return The string to return
*/
const std::string to_string(VkVertexInputRate rate);
/**
* @brief Helper function to convert VkBool32 to a string
* @param state Vulkan VkBool32 to convert
* @return The string to return
*/
const std::string to_string_vk_bool(VkBool32 state);
/**
* @brief Helper function to convert VkPrimitiveTopology to a string
* @param topology Vulkan VkPrimitiveTopology to convert
* @return The string to return
*/
const std::string to_string(VkPrimitiveTopology topology);
/**
* @brief Helper function to convert VkFrontFace to a string
* @param face Vulkan VkFrontFace to convert
* @return The string to return
*/
const std::string to_string(VkFrontFace face);
/**
* @brief Helper function to convert VkPolygonMode to a string
* @param mode Vulkan VkPolygonMode to convert
* @return The string to return
*/
const std::string to_string(VkPolygonMode mode);
/**
* @brief Helper function to convert VkCompareOp to a string
* @param operation Vulkan VkCompareOp to convert
* @return The string to return
*/
const std::string to_string(VkCompareOp operation);
/**
* @brief Helper function to convert VkStencilOp to a string
* @param operation Vulkan VkStencilOp to convert
* @return The string to return
*/
const std::string to_string(VkStencilOp operation);
/**
* @brief Helper function to convert VkLogicOp to a string
* @param operation Vulkan VkLogicOp to convert
* @return The string to return
*/
const std::string to_string(VkLogicOp operation);
/**
* @brief Helper function to convert VkBlendOp to a string
* @param operation Vulkan VkBlendOp to convert
* @return The string to return
*/
const std::string to_string(VkBlendOp operation);
/**
* @brief Helper function to convert AlphaMode to a string
* @param mode Vulkan AlphaMode to convert
* @return The string to return
*/
const std::string to_string(sg::AlphaMode mode);
/**
* @brief Helper function to convert bool to a string
* @param flag Vulkan bool to convert (true/false)
* @return The string to return
*/
const std::string to_string(bool flag);
/**
* @brief Helper function to convert ShaderResourceType to a string
* @param type Vulkan ShaderResourceType to convert
* @return The string to return
*/
const std::string to_string(ShaderResourceType type);
/**
* @brief Helper generic function to convert a bitmask to a string of its components
* @param bitmask The bitmask to convert
* @param string_map A map of bitmask bits to the string that describe the Vulkan flag
* @returns A string of the enabled bits in the bitmask
*/
template <typename T>
inline const std::string to_string(uint32_t bitmask, const std::map<T, const char *> string_map)
{
std::stringstream result;
bool append = false;
for (const auto &s : string_map)
{
if (bitmask & s.first)
{
if (append)
{
result << " | ";
}
result << s.second;
append = true;
}
}
return result.str();
}
/**
* @brief Helper function to convert VkBufferUsageFlags to a string
* @param bitmask The buffer usage bitmask to convert to strings
* @return The converted string to return
*/
const std::string buffer_usage_to_string(VkBufferUsageFlags bitmask);
/**
* @brief Helper function to convert VkShaderStageFlags to a string
* @param bitmask The shader stage bitmask to convert
* @return The converted string to return
*/
const std::string shader_stage_to_string(VkShaderStageFlags bitmask);
/**
* @brief Helper function to convert VkImageUsageFlags to a string
* @param bitmask The image usage bitmask to convert
* @return The converted string to return
*/
const std::string image_usage_to_string(VkImageUsageFlags bitmask);
/**
* @brief Helper function to convert VkImageAspectFlags to a string
* @param bitmask The image aspect bitmask to convert
* @return The converted string to return
*/
const std::string image_aspect_to_string(VkImageAspectFlags bitmask);
/**
* @brief Helper function to convert VkCullModeFlags to a string
* @param bitmask The cull mode bitmask to convert
* @return The converted string to return
*/
const std::string cull_mode_to_string(VkCullModeFlags bitmask);
/**
* @brief Helper function to convert VkColorComponentFlags to a string
* @param bitmask The color component bitmask to convert
* @return The converted string to return
*/
const std::string color_component_to_string(VkColorComponentFlags bitmask);
/**
* @brief Helper function to convert VkImageCompressionFlagsEXT to a string
* @param flags The flags to convert
* @return The converted string to return
*/
const std::string image_compression_flags_to_string(VkImageCompressionFlagsEXT flags);
/**
* @brief Helper function to convert VkImageCompressionFixedRateFlagsEXT to a string
* @param flags The flags to convert
* @return The converted string to return
*/
const std::string image_compression_fixed_rate_flags_to_string(VkImageCompressionFixedRateFlagsEXT flags);
/**
* @brief Helper function to split a single string into a vector of strings by a delimiter
* @param input The input string to be split
* @param delim The character to delimit by
* @return The vector of tokenized strings
*/
std::vector<std::string> split(const std::string &input, char delim);
} // namespace vkb
+85
View File
@@ -0,0 +1,85 @@
/* Copyright (c) 2020-2025, Arm Limited and Contributors
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 the "License";
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#include <algorithm>
#include <vector>
/**
* @brief Used to represent a tag
*/
typedef void (*TagID)();
/**
* @brief Tag acts as a unique identifier to categories objects
*
* Tags are uniquely defined using different type names. The easiest way of creating a new tag is to use an empty struct
* struct TagName{};
* struct DifferentTag{};
* Tag<TagName>::ID == Tag<TagName>::member != Tag<DifferentTag>:ID
*
* @tparam TAGS A set of tags
*/
template <typename... TAGS>
class Tag
{
public:
Tag()
{
tags = {Tag<TAGS>::ID...};
}
static void member(){};
/**
* @brief Unique TagID for a given Tag<TagName>
*/
constexpr static TagID ID = &member;
static bool has_tag(TagID id)
{
return std::ranges::find(tags, id) != tags.end();
}
template <typename C>
static bool has_tag()
{
return has_tag(Tag<C>::ID);
}
template <typename... C>
static bool has_tags()
{
std::vector<TagID> query = {Tag<C>::ID...};
bool res = true;
for (auto id : query)
{
res &= has_tag(id);
}
return res;
}
private:
static std::vector<TagID> tags;
};
#ifndef DOXYGEN_SKIP
template <typename... TAGS>
std::vector<TagID> Tag<TAGS...>::tags;
#endif
+311
View File
@@ -0,0 +1,311 @@
/* Copyright (c) 2018-2025, Arm Limited and Contributors
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 the "License";
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "utils.h"
#include <queue>
#include <stdexcept>
#include "scene_graph/components/material.h"
#include "scene_graph/components/perspective_camera.h"
#include "scene_graph/components/sub_mesh.h"
#include "scene_graph/node.h"
#include "scene_graph/script.h"
#include "scene_graph/scripts/free_camera.h"
namespace vkb
{
std::string get_extension(const std::string &uri)
{
auto dot_pos = uri.find_last_of('.');
if (dot_pos == std::string::npos)
{
throw std::runtime_error{"Uri has no extension"};
}
return uri.substr(dot_pos + 1);
}
void screenshot(RenderContext &render_context, const std::string &filename)
{
assert(render_context.get_format() == VK_FORMAT_R8G8B8A8_UNORM ||
render_context.get_format() == VK_FORMAT_B8G8R8A8_UNORM ||
render_context.get_format() == VK_FORMAT_R8G8B8A8_SRGB ||
render_context.get_format() == VK_FORMAT_B8G8R8A8_SRGB);
// We want the last completed frame since we don't want to be reading from an incomplete framebuffer
auto &frame = render_context.get_last_rendered_frame();
assert(!frame.get_render_target().get_views().empty());
auto &src_image_view = frame.get_render_target().get_views()[0];
auto width = render_context.get_surface_extent().width;
auto height = render_context.get_surface_extent().height;
auto dst_size = width * height * 4;
vkb::core::BufferC dst_buffer{render_context.get_device(),
dst_size,
VK_BUFFER_USAGE_TRANSFER_DST_BIT,
VMA_MEMORY_USAGE_GPU_TO_CPU,
VMA_ALLOCATION_CREATE_MAPPED_BIT};
const auto &queue = render_context.get_device().get_queue_by_flags(VK_QUEUE_GRAPHICS_BIT, 0);
auto cmd_buf = render_context.get_device().get_command_pool().request_command_buffer();
cmd_buf->begin(VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT);
// Enable destination buffer to be written to
{
BufferMemoryBarrier memory_barrier{};
memory_barrier.src_access_mask = 0;
memory_barrier.dst_access_mask = VK_ACCESS_TRANSFER_WRITE_BIT;
memory_barrier.src_stage_mask = VK_PIPELINE_STAGE_TRANSFER_BIT;
memory_barrier.dst_stage_mask = VK_PIPELINE_STAGE_TRANSFER_BIT;
cmd_buf->buffer_memory_barrier(dst_buffer, 0, dst_size, memory_barrier);
}
// Enable framebuffer image view to be read from
{
ImageMemoryBarrier memory_barrier{};
memory_barrier.old_layout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
memory_barrier.new_layout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
memory_barrier.src_stage_mask = VK_PIPELINE_STAGE_TRANSFER_BIT;
memory_barrier.dst_stage_mask = VK_PIPELINE_STAGE_TRANSFER_BIT;
cmd_buf->image_memory_barrier(src_image_view, memory_barrier);
}
// Check if framebuffer images are in a BGR format
auto bgr_formats = {VK_FORMAT_B8G8R8A8_SRGB, VK_FORMAT_B8G8R8A8_UNORM, VK_FORMAT_B8G8R8A8_SNORM};
bool swizzle = std::ranges::find(bgr_formats, src_image_view.get_format()) != bgr_formats.end();
// Copy framebuffer image memory
VkBufferImageCopy image_copy_region{};
image_copy_region.bufferRowLength = width;
image_copy_region.bufferImageHeight = height;
image_copy_region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
image_copy_region.imageSubresource.layerCount = 1;
image_copy_region.imageExtent.width = width;
image_copy_region.imageExtent.height = height;
image_copy_region.imageExtent.depth = 1;
cmd_buf->copy_image_to_buffer(src_image_view.get_image(), VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, dst_buffer, {image_copy_region});
// Enable destination buffer to map memory
{
BufferMemoryBarrier memory_barrier{};
memory_barrier.src_access_mask = VK_ACCESS_TRANSFER_WRITE_BIT;
memory_barrier.dst_access_mask = VK_ACCESS_HOST_READ_BIT;
memory_barrier.src_stage_mask = VK_PIPELINE_STAGE_TRANSFER_BIT;
memory_barrier.dst_stage_mask = VK_PIPELINE_STAGE_HOST_BIT;
cmd_buf->buffer_memory_barrier(dst_buffer, 0, dst_size, memory_barrier);
}
// Revert back the framebuffer image view from transfer to present
{
ImageMemoryBarrier memory_barrier{};
memory_barrier.old_layout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL;
memory_barrier.new_layout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;
memory_barrier.src_stage_mask = VK_PIPELINE_STAGE_TRANSFER_BIT;
memory_barrier.dst_stage_mask = VK_PIPELINE_STAGE_TRANSFER_BIT;
cmd_buf->image_memory_barrier(src_image_view, memory_barrier);
}
cmd_buf->end();
queue.submit(*cmd_buf, frame.get_fence_pool().request_fence());
queue.wait_idle();
auto raw_data = dst_buffer.map();
// Creates a pointer to the address of the first byte of the image data
// Replace the A component with 255 (remove transparency)
// If swapchain format is BGR, swapping the R and B components
uint8_t *data = raw_data;
if (swizzle)
{
for (size_t i = 0; i < height; ++i)
{
// Iterate over each pixel, swapping R and B components and writing the max value for alpha
for (size_t j = 0; j < width; ++j)
{
auto temp = *(data + 2);
*(data + 2) = *(data);
*(data) = temp;
*(data + 3) = 255;
// Get next pixel
data += 4;
}
}
}
else
{
for (size_t i = 0; i < height; ++i)
{
// Iterate over each pixel, writing the max value for alpha
for (size_t j = 0; j < width; ++j)
{
*(data + 3) = 255;
// Get next pixel
data += 4;
}
}
}
vkb::fs::write_image(raw_data,
filename,
width,
height,
4,
width * 4);
dst_buffer.unmap();
} // namespace vkb
std::string to_snake_case(const std::string &text)
{
std::stringstream result;
for (const auto ch : text)
{
if (std::isalpha(ch))
{
if (std::isspace(ch))
{
result << "_";
}
else
{
if (std::isupper(ch))
{
result << "_";
}
result << static_cast<char>(std::tolower(ch));
}
}
else
{
result << ch;
}
}
return result.str();
}
sg::Light &add_light(sg::Scene &scene, sg::LightType type, const glm::vec3 &position, const glm::quat &rotation, const sg::LightProperties &props, sg::Node *parent_node)
{
auto light_ptr = std::make_unique<sg::Light>("light");
auto node = std::make_unique<sg::Node>(-1, "light node");
if (parent_node)
{
node->set_parent(*parent_node);
}
light_ptr->set_node(*node);
light_ptr->set_light_type(type);
light_ptr->set_properties(props);
auto &t = node->get_transform();
t.set_translation(position);
t.set_rotation(rotation);
// Storing the light component because the unique_ptr will be moved to the scene
auto &light = *light_ptr;
node->set_component(light);
scene.add_child(*node);
scene.add_component(std::move(light_ptr));
scene.add_node(std::move(node));
return light;
}
sg::Light &add_point_light(sg::Scene &scene, const glm::vec3 &position, const sg::LightProperties &props, sg::Node *parent_node)
{
return add_light(scene, sg::LightType::Point, position, {}, props, parent_node);
}
sg::Light &add_directional_light(sg::Scene &scene, const glm::quat &rotation, const sg::LightProperties &props, sg::Node *parent_node)
{
return add_light(scene, sg::LightType::Directional, {}, rotation, props, parent_node);
}
sg::Light &add_spot_light(sg::Scene &scene, const glm::vec3 &position, const glm::quat &rotation, const sg::LightProperties &props, sg::Node *parent_node)
{
return add_light(scene, sg::LightType::Spot, position, rotation, props, parent_node);
}
sg::Node &add_free_camera(sg::Scene &scene, const std::string &node_name, VkExtent2D extent)
{
auto camera_node = scene.find_node(node_name);
if (!camera_node)
{
LOGW("Camera node `{}` not found. Looking for `default_camera` node.", node_name.c_str());
camera_node = scene.find_node("default_camera");
}
if (!camera_node)
{
throw std::runtime_error("Camera node with name `" + node_name + "` not found.");
}
if (!camera_node->has_component<sg::Camera>())
{
throw std::runtime_error("No camera component found for `" + node_name + "` node.");
}
auto free_camera_script = std::make_unique<sg::FreeCamera>(*camera_node);
free_camera_script->resize(extent.width, extent.height);
scene.add_component(std::move(free_camera_script), *camera_node);
return *camera_node;
}
size_t calculate_hash(const std::vector<uint8_t> &data)
{
static_assert(sizeof(data[0]) == 1);
constexpr size_t chunk_size = sizeof(size_t) / sizeof(data[0]);
size_t data_hash = 0;
size_t offset = 0;
for (; offset + chunk_size < data.size(); offset += chunk_size)
{
glm::detail::hash_combine(data_hash, *reinterpret_cast<size_t const *>(&data[offset]));
}
if (offset < data.size())
{
size_t it = 0;
std::memcpy(&it, &data[offset], data.size() - offset);
glm::detail::hash_combine(data_hash, it);
}
return data_hash;
}
} // namespace vkb
+112
View File
@@ -0,0 +1,112 @@
/* Copyright (c) 2018-2025, Arm Limited and Contributors
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 the "License";
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#include "common/error.h"
#include "common/glm_common.h"
#include "glm/gtx/quaternion.hpp"
#include "filesystem/legacy.h"
#include "rendering/pipeline_state.h"
#include "rendering/render_context.h"
#include "scene_graph/components/sub_mesh.h"
#include "scene_graph/scene.h"
namespace vkb
{
/**
* @brief Extracts the extension from an uri
* @param uri An uniform Resource Identifier
* @return The extension
*/
std::string get_extension(const std::string &uri);
/**
* @brief Generates a hash from an array
* @param data
* @return data_hash hash of the data
*/
size_t calculate_hash(const std::vector<uint8_t> &data);
/**
* @param name String to convert to snake case
* @return a snake case version of the string
*/
std::string to_snake_case(const std::string &name);
/**
* @brief Takes a screenshot of the app by writing the swapchain image to file (slow function)
* @param render_context The RenderContext to use
* @param filename The name of the file to save the output to
*/
void screenshot(RenderContext &render_context, const std::string &filename);
/**
* @brief Adds a light to the scene with the specified parameters
* @param scene The scene to add the light to
* @param type The light type
* @param position The position of the light
* @param rotation The rotation of the light
* @param props The light properties, such as color and intensity
* @param parent_node The parent node for the line, defaults to root
* @return The newly created light component
*/
sg::Light &add_light(sg::Scene &scene, sg::LightType type, const glm::vec3 &position, const glm::quat &rotation = {}, const sg::LightProperties &props = {}, sg::Node *parent_node = nullptr);
/**
* @brief Adds a point light to the scene with the specified parameters
* @param scene The scene to add the light to
* @param position The position of the light
* @param props The light properties, such as color and intensity
* @param parent_node The parent node for the line, defaults to root
* @return The newly created light component
*/
sg::Light &add_point_light(sg::Scene &scene, const glm::vec3 &position, const sg::LightProperties &props = {}, sg::Node *parent_node = nullptr);
/**
* @brief Adds a directional light to the scene with the specified parameters
* @param scene The scene to add the light to
* @param rotation The rotation of the light
* @param props The light properties, such as color and intensity
* @param parent_node The parent node for the line, defaults to root
* @return The newly created light component
*/
sg::Light &add_directional_light(sg::Scene &scene, const glm::quat &rotation, const sg::LightProperties &props = {}, sg::Node *parent_node = nullptr);
/**
* @brief Adds a spot light to the scene with the specified parameters
* @param scene The scene to add the light to
* @param position The position of the light
* @param rotation The rotation of the light
* @param props The light properties, such as color and intensity
* @param parent_node The parent node for the line, defaults to root
* @return The newly created light component
*/
sg::Light &add_spot_light(sg::Scene &scene, const glm::vec3 &position, const glm::quat &rotation, const sg::LightProperties &props = {}, sg::Node *parent_node = nullptr);
/**
* @brief Add free camera script to a node with a camera object.
* Fallback to the default_camera if node not found.
* @param scene The scene to add the camera to
* @param node_name The scene node name
* @param extent The initial resolution of the camera
* @return Node where the script was attached as component
*/
sg::Node &add_free_camera(sg::Scene &scene, const std::string &node_name, VkExtent2D extent);
} // namespace vkb
+719
View File
@@ -0,0 +1,719 @@
/* Copyright (c) 2018-2025, Arm Limited and Contributors
* Copyright (c) 2019-2025, Sascha Willems
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 the "License";
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "vk_common.h"
#include <fmt/format.h>
#include "filesystem/legacy.h"
std::ostream &operator<<(std::ostream &os, const VkResult result)
{
#define WRITE_VK_ENUM(r) \
case VK_##r: \
os << #r; \
break;
switch (result)
{
WRITE_VK_ENUM(NOT_READY);
WRITE_VK_ENUM(TIMEOUT);
WRITE_VK_ENUM(EVENT_SET);
WRITE_VK_ENUM(EVENT_RESET);
WRITE_VK_ENUM(INCOMPLETE);
WRITE_VK_ENUM(ERROR_OUT_OF_HOST_MEMORY);
WRITE_VK_ENUM(ERROR_OUT_OF_DEVICE_MEMORY);
WRITE_VK_ENUM(ERROR_INITIALIZATION_FAILED);
WRITE_VK_ENUM(ERROR_DEVICE_LOST);
WRITE_VK_ENUM(ERROR_MEMORY_MAP_FAILED);
WRITE_VK_ENUM(ERROR_LAYER_NOT_PRESENT);
WRITE_VK_ENUM(ERROR_EXTENSION_NOT_PRESENT);
WRITE_VK_ENUM(ERROR_FEATURE_NOT_PRESENT);
WRITE_VK_ENUM(ERROR_INCOMPATIBLE_DRIVER);
WRITE_VK_ENUM(ERROR_TOO_MANY_OBJECTS);
WRITE_VK_ENUM(ERROR_FORMAT_NOT_SUPPORTED);
WRITE_VK_ENUM(ERROR_SURFACE_LOST_KHR);
WRITE_VK_ENUM(ERROR_NATIVE_WINDOW_IN_USE_KHR);
WRITE_VK_ENUM(SUBOPTIMAL_KHR);
WRITE_VK_ENUM(ERROR_OUT_OF_DATE_KHR);
WRITE_VK_ENUM(ERROR_INCOMPATIBLE_DISPLAY_KHR);
WRITE_VK_ENUM(ERROR_VALIDATION_FAILED_EXT);
WRITE_VK_ENUM(ERROR_INVALID_SHADER_NV);
default:
os << "UNKNOWN_ERROR";
}
#undef WRITE_VK_ENUM
return os;
}
namespace vkb
{
bool is_depth_only_format(VkFormat format)
{
return format == VK_FORMAT_D16_UNORM ||
format == VK_FORMAT_D32_SFLOAT;
}
bool is_depth_stencil_format(VkFormat format)
{
return format == VK_FORMAT_D16_UNORM_S8_UINT ||
format == VK_FORMAT_D24_UNORM_S8_UINT ||
format == VK_FORMAT_D32_SFLOAT_S8_UINT;
}
bool is_depth_format(VkFormat format)
{
return is_depth_only_format(format) || is_depth_stencil_format(format);
}
VkFormat get_suitable_depth_format(VkPhysicalDevice physical_device, bool depth_only, const std::vector<VkFormat> &depth_format_priority_list)
{
VkFormat depth_format{VK_FORMAT_UNDEFINED};
for (auto &format : depth_format_priority_list)
{
if (depth_only && !is_depth_only_format(format))
{
continue;
}
VkFormatProperties properties;
vkGetPhysicalDeviceFormatProperties(physical_device, format, &properties);
// Format must support depth stencil attachment for optimal tiling
if (properties.optimalTilingFeatures & VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT)
{
depth_format = format;
break;
}
}
if (depth_format != VK_FORMAT_UNDEFINED)
{
LOGI("Depth format selected: {}", to_string(depth_format));
return depth_format;
}
throw std::runtime_error("No suitable depth format could be determined");
}
VkFormat choose_blendable_format(VkPhysicalDevice physical_device, const std::vector<VkFormat> &format_priority_list)
{
for (const auto &format : format_priority_list)
{
VkFormatProperties properties;
vkGetPhysicalDeviceFormatProperties(physical_device, format, &properties);
if (properties.optimalTilingFeatures & VK_FORMAT_FEATURE_COLOR_ATTACHMENT_BLEND_BIT)
{
return format;
}
}
throw std::runtime_error("No suitable blendable format could be determined");
}
void make_filters_valid(VkPhysicalDevice physical_device, VkFormat format, VkFilter *filter, VkSamplerMipmapMode *mipmapMode)
{
// Not all formats support linear filtering, so we need to adjust them if they don't
if (*filter == VK_FILTER_NEAREST && (mipmapMode == nullptr || *mipmapMode == VK_SAMPLER_MIPMAP_MODE_NEAREST))
{
return; // These must already be valid
}
VkFormatProperties properties;
vkGetPhysicalDeviceFormatProperties(physical_device, format, &properties);
if (!(properties.optimalTilingFeatures & VK_FORMAT_FEATURE_SAMPLED_IMAGE_FILTER_LINEAR_BIT))
{
*filter = VK_FILTER_NEAREST;
if (mipmapMode)
{
*mipmapMode = VK_SAMPLER_MIPMAP_MODE_NEAREST;
}
}
}
bool is_dynamic_buffer_descriptor_type(VkDescriptorType descriptor_type)
{
return descriptor_type == VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC ||
descriptor_type == VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC;
}
bool is_buffer_descriptor_type(VkDescriptorType descriptor_type)
{
return descriptor_type == VK_DESCRIPTOR_TYPE_STORAGE_BUFFER ||
descriptor_type == VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER ||
is_dynamic_buffer_descriptor_type(descriptor_type);
}
int32_t get_bits_per_pixel(VkFormat format)
{
switch (format)
{
case VK_FORMAT_R4G4_UNORM_PACK8:
return 8;
case VK_FORMAT_R4G4B4A4_UNORM_PACK16:
case VK_FORMAT_B4G4R4A4_UNORM_PACK16:
case VK_FORMAT_R5G6B5_UNORM_PACK16:
case VK_FORMAT_B5G6R5_UNORM_PACK16:
case VK_FORMAT_R5G5B5A1_UNORM_PACK16:
case VK_FORMAT_B5G5R5A1_UNORM_PACK16:
case VK_FORMAT_A1R5G5B5_UNORM_PACK16:
return 16;
case VK_FORMAT_R8_UNORM:
case VK_FORMAT_R8_SNORM:
case VK_FORMAT_R8_USCALED:
case VK_FORMAT_R8_SSCALED:
case VK_FORMAT_R8_UINT:
case VK_FORMAT_R8_SINT:
case VK_FORMAT_R8_SRGB:
return 8;
case VK_FORMAT_R8G8_UNORM:
case VK_FORMAT_R8G8_SNORM:
case VK_FORMAT_R8G8_USCALED:
case VK_FORMAT_R8G8_SSCALED:
case VK_FORMAT_R8G8_UINT:
case VK_FORMAT_R8G8_SINT:
case VK_FORMAT_R8G8_SRGB:
return 16;
case VK_FORMAT_R8G8B8_UNORM:
case VK_FORMAT_R8G8B8_SNORM:
case VK_FORMAT_R8G8B8_USCALED:
case VK_FORMAT_R8G8B8_SSCALED:
case VK_FORMAT_R8G8B8_UINT:
case VK_FORMAT_R8G8B8_SINT:
case VK_FORMAT_R8G8B8_SRGB:
case VK_FORMAT_B8G8R8_UNORM:
case VK_FORMAT_B8G8R8_SNORM:
case VK_FORMAT_B8G8R8_USCALED:
case VK_FORMAT_B8G8R8_SSCALED:
case VK_FORMAT_B8G8R8_UINT:
case VK_FORMAT_B8G8R8_SINT:
case VK_FORMAT_B8G8R8_SRGB:
return 24;
case VK_FORMAT_R8G8B8A8_UNORM:
case VK_FORMAT_R8G8B8A8_SNORM:
case VK_FORMAT_R8G8B8A8_USCALED:
case VK_FORMAT_R8G8B8A8_SSCALED:
case VK_FORMAT_R8G8B8A8_UINT:
case VK_FORMAT_R8G8B8A8_SINT:
case VK_FORMAT_R8G8B8A8_SRGB:
case VK_FORMAT_B8G8R8A8_UNORM:
case VK_FORMAT_B8G8R8A8_SNORM:
case VK_FORMAT_B8G8R8A8_USCALED:
case VK_FORMAT_B8G8R8A8_SSCALED:
case VK_FORMAT_B8G8R8A8_UINT:
case VK_FORMAT_B8G8R8A8_SINT:
case VK_FORMAT_B8G8R8A8_SRGB:
case VK_FORMAT_A8B8G8R8_UNORM_PACK32:
case VK_FORMAT_A8B8G8R8_SNORM_PACK32:
case VK_FORMAT_A8B8G8R8_USCALED_PACK32:
case VK_FORMAT_A8B8G8R8_SSCALED_PACK32:
case VK_FORMAT_A8B8G8R8_UINT_PACK32:
case VK_FORMAT_A8B8G8R8_SINT_PACK32:
case VK_FORMAT_A8B8G8R8_SRGB_PACK32:
return 32;
case VK_FORMAT_A2R10G10B10_UNORM_PACK32:
case VK_FORMAT_A2R10G10B10_SNORM_PACK32:
case VK_FORMAT_A2R10G10B10_USCALED_PACK32:
case VK_FORMAT_A2R10G10B10_SSCALED_PACK32:
case VK_FORMAT_A2R10G10B10_UINT_PACK32:
case VK_FORMAT_A2R10G10B10_SINT_PACK32:
case VK_FORMAT_A2B10G10R10_UNORM_PACK32:
case VK_FORMAT_A2B10G10R10_SNORM_PACK32:
case VK_FORMAT_A2B10G10R10_USCALED_PACK32:
case VK_FORMAT_A2B10G10R10_SSCALED_PACK32:
case VK_FORMAT_A2B10G10R10_UINT_PACK32:
case VK_FORMAT_A2B10G10R10_SINT_PACK32:
return 32;
case VK_FORMAT_R16_UNORM:
case VK_FORMAT_R16_SNORM:
case VK_FORMAT_R16_USCALED:
case VK_FORMAT_R16_SSCALED:
case VK_FORMAT_R16_UINT:
case VK_FORMAT_R16_SINT:
case VK_FORMAT_R16_SFLOAT:
return 16;
case VK_FORMAT_R16G16_UNORM:
case VK_FORMAT_R16G16_SNORM:
case VK_FORMAT_R16G16_USCALED:
case VK_FORMAT_R16G16_SSCALED:
case VK_FORMAT_R16G16_UINT:
case VK_FORMAT_R16G16_SINT:
case VK_FORMAT_R16G16_SFLOAT:
return 32;
case VK_FORMAT_R16G16B16_UNORM:
case VK_FORMAT_R16G16B16_SNORM:
case VK_FORMAT_R16G16B16_USCALED:
case VK_FORMAT_R16G16B16_SSCALED:
case VK_FORMAT_R16G16B16_UINT:
case VK_FORMAT_R16G16B16_SINT:
case VK_FORMAT_R16G16B16_SFLOAT:
return 48;
case VK_FORMAT_R16G16B16A16_UNORM:
case VK_FORMAT_R16G16B16A16_SNORM:
case VK_FORMAT_R16G16B16A16_USCALED:
case VK_FORMAT_R16G16B16A16_SSCALED:
case VK_FORMAT_R16G16B16A16_UINT:
case VK_FORMAT_R16G16B16A16_SINT:
case VK_FORMAT_R16G16B16A16_SFLOAT:
return 64;
case VK_FORMAT_R32_UINT:
case VK_FORMAT_R32_SINT:
case VK_FORMAT_R32_SFLOAT:
return 32;
case VK_FORMAT_R32G32_UINT:
case VK_FORMAT_R32G32_SINT:
case VK_FORMAT_R32G32_SFLOAT:
return 64;
case VK_FORMAT_R32G32B32_UINT:
case VK_FORMAT_R32G32B32_SINT:
case VK_FORMAT_R32G32B32_SFLOAT:
return 96;
case VK_FORMAT_R32G32B32A32_UINT:
case VK_FORMAT_R32G32B32A32_SINT:
case VK_FORMAT_R32G32B32A32_SFLOAT:
return 128;
case VK_FORMAT_R64_UINT:
case VK_FORMAT_R64_SINT:
case VK_FORMAT_R64_SFLOAT:
return 64;
case VK_FORMAT_R64G64_UINT:
case VK_FORMAT_R64G64_SINT:
case VK_FORMAT_R64G64_SFLOAT:
return 128;
case VK_FORMAT_R64G64B64_UINT:
case VK_FORMAT_R64G64B64_SINT:
case VK_FORMAT_R64G64B64_SFLOAT:
return 192;
case VK_FORMAT_R64G64B64A64_UINT:
case VK_FORMAT_R64G64B64A64_SINT:
case VK_FORMAT_R64G64B64A64_SFLOAT:
return 256;
case VK_FORMAT_B10G11R11_UFLOAT_PACK32:
return 32;
case VK_FORMAT_E5B9G9R9_UFLOAT_PACK32:
return 32;
case VK_FORMAT_D16_UNORM:
return 16;
case VK_FORMAT_X8_D24_UNORM_PACK32:
return 32;
case VK_FORMAT_D32_SFLOAT:
return 32;
case VK_FORMAT_S8_UINT:
return 8;
case VK_FORMAT_D16_UNORM_S8_UINT:
return 24;
case VK_FORMAT_D24_UNORM_S8_UINT:
return 32;
case VK_FORMAT_D32_SFLOAT_S8_UINT:
return 40;
case VK_FORMAT_UNDEFINED:
default:
return -1;
}
}
VkShaderModule load_shader(const std::string &filename, VkDevice device, VkShaderStageFlagBits stage)
{
auto spirv = vkb::fs::read_shader_binary_u32(filename);
VkShaderModule shader_module;
VkShaderModuleCreateInfo module_create_info{};
module_create_info.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
module_create_info.codeSize = spirv.size() * sizeof(uint32_t);
module_create_info.pCode = spirv.data();
VK_CHECK(vkCreateShaderModule(device, &module_create_info, NULL, &shader_module));
return shader_module;
}
VkAccessFlags getAccessFlags(VkImageLayout layout)
{
switch (layout)
{
case VK_IMAGE_LAYOUT_UNDEFINED:
case VK_IMAGE_LAYOUT_PRESENT_SRC_KHR:
return 0;
case VK_IMAGE_LAYOUT_PREINITIALIZED:
return VK_ACCESS_HOST_WRITE_BIT;
case VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL:
return VK_ACCESS_COLOR_ATTACHMENT_READ_BIT | VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT;
case VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL:
return VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT;
case VK_IMAGE_LAYOUT_FRAGMENT_SHADING_RATE_ATTACHMENT_OPTIMAL_KHR:
return VK_ACCESS_FRAGMENT_SHADING_RATE_ATTACHMENT_READ_BIT_KHR;
case VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL:
return VK_ACCESS_SHADER_READ_BIT | VK_ACCESS_INPUT_ATTACHMENT_READ_BIT;
case VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL:
return VK_ACCESS_TRANSFER_READ_BIT;
case VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL:
return VK_ACCESS_TRANSFER_WRITE_BIT;
case VK_IMAGE_LAYOUT_GENERAL:
assert(false && "Don't know how to get a meaningful VkAccessFlags for VK_IMAGE_LAYOUT_GENERAL! Don't use it!");
return 0;
default:
assert(false);
return 0;
}
}
VkPipelineStageFlags getPipelineStageFlags(VkImageLayout layout)
{
switch (layout)
{
case VK_IMAGE_LAYOUT_UNDEFINED:
return VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT;
case VK_IMAGE_LAYOUT_PREINITIALIZED:
return VK_PIPELINE_STAGE_HOST_BIT;
case VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL:
case VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL:
return VK_PIPELINE_STAGE_TRANSFER_BIT;
case VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL:
return VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
case VK_IMAGE_LAYOUT_DEPTH_ATTACHMENT_OPTIMAL:
return VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT;
case VK_IMAGE_LAYOUT_FRAGMENT_SHADING_RATE_ATTACHMENT_OPTIMAL_KHR:
return VK_PIPELINE_STAGE_FRAGMENT_SHADING_RATE_ATTACHMENT_BIT_KHR;
case VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL:
return VK_PIPELINE_STAGE_VERTEX_SHADER_BIT | VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;
case VK_IMAGE_LAYOUT_PRESENT_SRC_KHR:
return VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT;
case VK_IMAGE_LAYOUT_GENERAL:
assert(false && "Don't know how to get a meaningful VkPipelineStageFlags for VK_IMAGE_LAYOUT_GENERAL! Don't use it!");
return 0;
default:
assert(false);
return 0;
}
}
// Create an image memory barrier for changing the layout of
// an image and put it into an active command buffer
// See chapter 12.4 "Image Layout" for details
void image_layout_transition(VkCommandBuffer command_buffer,
VkImage image,
VkPipelineStageFlags src_stage_mask,
VkPipelineStageFlags dst_stage_mask,
VkAccessFlags src_access_mask,
VkAccessFlags dst_access_mask,
VkImageLayout old_layout,
VkImageLayout new_layout,
VkImageSubresourceRange const &subresource_range)
{
// Create an image barrier object
VkImageMemoryBarrier image_memory_barrier{};
image_memory_barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
image_memory_barrier.srcAccessMask = src_access_mask;
image_memory_barrier.dstAccessMask = dst_access_mask;
image_memory_barrier.oldLayout = old_layout;
image_memory_barrier.newLayout = new_layout;
image_memory_barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
image_memory_barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
image_memory_barrier.image = image;
image_memory_barrier.subresourceRange = subresource_range;
// Put barrier inside setup command buffer
vkCmdPipelineBarrier(command_buffer, src_stage_mask, dst_stage_mask, 0, 0, nullptr, 0, nullptr, 1, &image_memory_barrier);
}
void image_layout_transition(VkCommandBuffer command_buffer,
VkImage image,
VkImageLayout old_layout,
VkImageLayout new_layout,
VkImageSubresourceRange const &subresource_range)
{
VkPipelineStageFlags src_stage_mask = getPipelineStageFlags(old_layout);
VkPipelineStageFlags dst_stage_mask = getPipelineStageFlags(new_layout);
VkAccessFlags src_access_mask = getAccessFlags(old_layout);
VkAccessFlags dst_access_mask = getAccessFlags(new_layout);
image_layout_transition(command_buffer, image, src_stage_mask, dst_stage_mask, src_access_mask, dst_access_mask, old_layout, new_layout, subresource_range);
}
// Fixed sub resource on first mip level and layer
void image_layout_transition(VkCommandBuffer command_buffer,
VkImage image,
VkImageLayout old_layout,
VkImageLayout new_layout)
{
VkImageSubresourceRange subresource_range = {};
subresource_range.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
subresource_range.baseMipLevel = 0;
subresource_range.levelCount = 1;
subresource_range.baseArrayLayer = 0;
subresource_range.layerCount = 1;
image_layout_transition(command_buffer, image, old_layout, new_layout, subresource_range);
}
void image_layout_transition(VkCommandBuffer command_buffer,
std::vector<std::pair<VkImage, VkImageSubresourceRange>> const &imagesAndRanges,
VkImageLayout old_layout,
VkImageLayout new_layout)
{
VkPipelineStageFlags src_stage_mask = getPipelineStageFlags(old_layout);
VkPipelineStageFlags dst_stage_mask = getPipelineStageFlags(new_layout);
VkAccessFlags src_access_mask = getAccessFlags(old_layout);
VkAccessFlags dst_access_mask = getAccessFlags(new_layout);
// Create image barrier objects
std::vector<VkImageMemoryBarrier> image_memory_barriers;
image_memory_barriers.reserve(imagesAndRanges.size());
for (size_t i = 0; i < imagesAndRanges.size(); i++)
{
image_memory_barriers.emplace_back(VkImageMemoryBarrier{VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
nullptr,
src_access_mask,
dst_access_mask,
old_layout,
new_layout,
VK_QUEUE_FAMILY_IGNORED,
VK_QUEUE_FAMILY_IGNORED,
imagesAndRanges[i].first,
imagesAndRanges[i].second});
}
// Put barriers inside setup command buffer
vkCmdPipelineBarrier(command_buffer,
src_stage_mask,
dst_stage_mask,
0,
0,
nullptr,
0,
nullptr,
static_cast<uint32_t>(image_memory_barriers.size()),
image_memory_barriers.data());
}
std::vector<VkImageCompressionFixedRateFlagBitsEXT> fixed_rate_compression_flags_to_vector(VkImageCompressionFixedRateFlagsEXT flags)
{
const std::vector<VkImageCompressionFixedRateFlagBitsEXT> all_flags = {VK_IMAGE_COMPRESSION_FIXED_RATE_1BPC_BIT_EXT, VK_IMAGE_COMPRESSION_FIXED_RATE_2BPC_BIT_EXT,
VK_IMAGE_COMPRESSION_FIXED_RATE_3BPC_BIT_EXT, VK_IMAGE_COMPRESSION_FIXED_RATE_4BPC_BIT_EXT,
VK_IMAGE_COMPRESSION_FIXED_RATE_5BPC_BIT_EXT, VK_IMAGE_COMPRESSION_FIXED_RATE_6BPC_BIT_EXT,
VK_IMAGE_COMPRESSION_FIXED_RATE_7BPC_BIT_EXT, VK_IMAGE_COMPRESSION_FIXED_RATE_8BPC_BIT_EXT,
VK_IMAGE_COMPRESSION_FIXED_RATE_9BPC_BIT_EXT, VK_IMAGE_COMPRESSION_FIXED_RATE_10BPC_BIT_EXT,
VK_IMAGE_COMPRESSION_FIXED_RATE_11BPC_BIT_EXT, VK_IMAGE_COMPRESSION_FIXED_RATE_12BPC_BIT_EXT,
VK_IMAGE_COMPRESSION_FIXED_RATE_13BPC_BIT_EXT, VK_IMAGE_COMPRESSION_FIXED_RATE_14BPC_BIT_EXT,
VK_IMAGE_COMPRESSION_FIXED_RATE_15BPC_BIT_EXT, VK_IMAGE_COMPRESSION_FIXED_RATE_16BPC_BIT_EXT,
VK_IMAGE_COMPRESSION_FIXED_RATE_17BPC_BIT_EXT, VK_IMAGE_COMPRESSION_FIXED_RATE_18BPC_BIT_EXT,
VK_IMAGE_COMPRESSION_FIXED_RATE_19BPC_BIT_EXT, VK_IMAGE_COMPRESSION_FIXED_RATE_20BPC_BIT_EXT,
VK_IMAGE_COMPRESSION_FIXED_RATE_21BPC_BIT_EXT, VK_IMAGE_COMPRESSION_FIXED_RATE_22BPC_BIT_EXT,
VK_IMAGE_COMPRESSION_FIXED_RATE_23BPC_BIT_EXT, VK_IMAGE_COMPRESSION_FIXED_RATE_24BPC_BIT_EXT};
std::vector<VkImageCompressionFixedRateFlagBitsEXT> flags_vector;
for (size_t i = 0; i < all_flags.size(); i++)
{
if (all_flags[i] & flags)
{
flags_vector.push_back(all_flags[i]);
}
}
return flags_vector;
}
VkImageCompressionPropertiesEXT query_supported_fixed_rate_compression(VkPhysicalDevice gpu, const VkImageCreateInfo &create_info)
{
VkImageCompressionPropertiesEXT supported_compression_properties{VK_STRUCTURE_TYPE_IMAGE_COMPRESSION_PROPERTIES_EXT};
VkImageCompressionControlEXT compression_control{VK_STRUCTURE_TYPE_IMAGE_COMPRESSION_CONTROL_EXT};
compression_control.flags = VK_IMAGE_COMPRESSION_FIXED_RATE_DEFAULT_EXT;
VkPhysicalDeviceImageFormatInfo2 image_format_info{VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_IMAGE_FORMAT_INFO_2};
image_format_info.format = create_info.format;
image_format_info.type = create_info.imageType;
image_format_info.tiling = create_info.tiling;
image_format_info.usage = create_info.usage;
image_format_info.pNext = &compression_control;
VkImageFormatProperties2 image_format_properties{VK_STRUCTURE_TYPE_IMAGE_FORMAT_PROPERTIES_2};
image_format_properties.pNext = &supported_compression_properties;
vkGetPhysicalDeviceImageFormatProperties2KHR(gpu, &image_format_info, &image_format_properties);
return supported_compression_properties;
}
VkImageCompressionPropertiesEXT query_applied_compression(VkDevice device, VkImage image)
{
VkImageSubresource2EXT image_subresource{VK_STRUCTURE_TYPE_IMAGE_SUBRESOURCE_2_KHR};
image_subresource.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
image_subresource.imageSubresource.mipLevel = 0;
image_subresource.imageSubresource.arrayLayer = 0;
VkImageCompressionPropertiesEXT compression_properties{VK_STRUCTURE_TYPE_IMAGE_COMPRESSION_PROPERTIES_EXT};
VkSubresourceLayout2EXT subresource_layout{VK_STRUCTURE_TYPE_SUBRESOURCE_LAYOUT_2_KHR};
subresource_layout.pNext = &compression_properties;
vkGetImageSubresourceLayout2EXT(device, image, &image_subresource, &subresource_layout);
return compression_properties;
}
VkSurfaceFormatKHR select_surface_format(VkPhysicalDevice gpu, VkSurfaceKHR surface, std::vector<VkFormat> const &preferred_formats)
{
uint32_t surface_format_count;
vkGetPhysicalDeviceSurfaceFormatsKHR(gpu, surface, &surface_format_count, nullptr);
assert(0 < surface_format_count);
std::vector<VkSurfaceFormatKHR> supported_surface_formats(surface_format_count);
vkGetPhysicalDeviceSurfaceFormatsKHR(gpu, surface, &surface_format_count, supported_surface_formats.data());
auto it = std::ranges::find_if(supported_surface_formats,
[&preferred_formats](VkSurfaceFormatKHR surface_format) {
return std::ranges::any_of(preferred_formats,
[&surface_format](VkFormat format) { return format == surface_format.format; });
});
// We use the first supported format as a fallback in case none of the preferred formats is available
return it != supported_surface_formats.end() ? *it : supported_surface_formats[0];
}
namespace gbuffer
{
std::vector<LoadStoreInfo> get_load_all_store_swapchain()
{
// Load every attachment and store only swapchain
std::vector<LoadStoreInfo> load_store{4};
// Swapchain
load_store[0].load_op = VK_ATTACHMENT_LOAD_OP_DONT_CARE;
load_store[0].store_op = VK_ATTACHMENT_STORE_OP_STORE;
// Depth
load_store[1].load_op = VK_ATTACHMENT_LOAD_OP_LOAD;
load_store[1].store_op = VK_ATTACHMENT_STORE_OP_DONT_CARE;
// Albedo
load_store[2].load_op = VK_ATTACHMENT_LOAD_OP_LOAD;
load_store[2].store_op = VK_ATTACHMENT_STORE_OP_DONT_CARE;
// Normal
load_store[3].load_op = VK_ATTACHMENT_LOAD_OP_LOAD;
load_store[3].store_op = VK_ATTACHMENT_STORE_OP_DONT_CARE;
return load_store;
}
std::vector<LoadStoreInfo> get_clear_all_store_swapchain()
{
// Clear every attachment and store only swapchain
std::vector<LoadStoreInfo> load_store{4};
// Swapchain
load_store[0].load_op = VK_ATTACHMENT_LOAD_OP_CLEAR;
load_store[0].store_op = VK_ATTACHMENT_STORE_OP_STORE;
// Depth
load_store[1].load_op = VK_ATTACHMENT_LOAD_OP_CLEAR;
load_store[1].store_op = VK_ATTACHMENT_STORE_OP_DONT_CARE;
// Albedo
load_store[2].load_op = VK_ATTACHMENT_LOAD_OP_CLEAR;
load_store[2].store_op = VK_ATTACHMENT_STORE_OP_DONT_CARE;
// Normal
load_store[3].load_op = VK_ATTACHMENT_LOAD_OP_CLEAR;
load_store[3].store_op = VK_ATTACHMENT_STORE_OP_DONT_CARE;
return load_store;
}
std::vector<LoadStoreInfo> get_clear_store_all()
{
// Clear and store every attachment
std::vector<LoadStoreInfo> load_store{4};
// Swapchain
load_store[0].load_op = VK_ATTACHMENT_LOAD_OP_CLEAR;
load_store[0].store_op = VK_ATTACHMENT_STORE_OP_STORE;
// Depth
load_store[1].load_op = VK_ATTACHMENT_LOAD_OP_CLEAR;
load_store[1].store_op = VK_ATTACHMENT_STORE_OP_STORE;
// Albedo
load_store[2].load_op = VK_ATTACHMENT_LOAD_OP_CLEAR;
load_store[2].store_op = VK_ATTACHMENT_STORE_OP_STORE;
// Normal
load_store[3].load_op = VK_ATTACHMENT_LOAD_OP_CLEAR;
load_store[3].store_op = VK_ATTACHMENT_STORE_OP_STORE;
return load_store;
}
std::vector<VkClearValue> get_clear_value()
{
// Clear values
std::vector<VkClearValue> clear_value{4};
clear_value[0].color = {{0.0f, 0.0f, 0.0f, 1.0f}};
clear_value[1].depthStencil = {0.0f, ~0U};
clear_value[2].color = {{0.0f, 0.0f, 0.0f, 1.0f}};
clear_value[3].color = {{0.0f, 0.0f, 0.0f, 1.0f}};
return clear_value;
}
} // namespace gbuffer
uint32_t get_queue_family_index(std::vector<VkQueueFamilyProperties> const &queue_family_properties, VkQueueFlagBits queue_flag)
{
// Dedicated queue for compute
// Try to find a queue family index that supports compute but not graphics
if (queue_flag & VK_QUEUE_COMPUTE_BIT)
{
auto propertyIt = std::ranges::find_if(queue_family_properties,
[queue_flag](const VkQueueFamilyProperties &property) { return (property.queueFlags & queue_flag) && !(property.queueFlags & VK_QUEUE_GRAPHICS_BIT); });
if (propertyIt != queue_family_properties.end())
{
return static_cast<uint32_t>(std::distance(queue_family_properties.begin(), propertyIt));
}
}
// Dedicated queue for transfer
// Try to find a queue family index that supports transfer but not graphics and compute
if (queue_flag & VK_QUEUE_TRANSFER_BIT)
{
auto propertyIt = std::ranges::find_if(queue_family_properties,
[queue_flag](const VkQueueFamilyProperties &property) {
return (property.queueFlags & queue_flag) && !(property.queueFlags & VK_QUEUE_GRAPHICS_BIT) &&
!(property.queueFlags & VK_QUEUE_COMPUTE_BIT);
});
if (propertyIt != queue_family_properties.end())
{
return static_cast<uint32_t>(std::distance(queue_family_properties.begin(), propertyIt));
}
}
// For other queue types or if no separate compute queue is present, return the first one to support the requested flags
auto propertyIt = std::ranges::find_if(
queue_family_properties, [queue_flag](const VkQueueFamilyProperties &property) { return (property.queueFlags & queue_flag) == queue_flag; });
if (propertyIt != queue_family_properties.end())
{
return static_cast<uint32_t>(std::distance(queue_family_properties.begin(), propertyIt));
}
throw std::runtime_error("Could not find a matching queue family index");
}
} // namespace vkb
+311
View File
@@ -0,0 +1,311 @@
/* Copyright (c) 2018-2025, Arm Limited and Contributors
* Copyright (c) 2019-2025, Sascha Willems
* Copyright (c) 2024-2025, Mobica Limited
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 the "License";
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#include <cstdio>
#include <map>
#include <sstream>
#include <string>
#include <unordered_map>
#include <vector>
#include "common/error.h"
#include <vk_mem_alloc.h>
#include <volk.h>
#define VK_FLAGS_NONE 0 // Custom define for better code readability
#define DEFAULT_FENCE_TIMEOUT 100000000000 // Default fence timeout in nanoseconds
template <class T>
using ShaderStageMap = std::map<VkShaderStageFlagBits, T>;
template <class T>
using BindingMap = std::map<uint32_t, std::map<uint32_t, T>>;
namespace vkb
{
enum class BindingType
{
C,
Cpp
};
enum class CommandBufferResetMode
{
ResetPool,
ResetIndividually,
AlwaysAllocate,
};
/**
* @brief Helper function to determine if a Vulkan format is depth only.
* @param format Vulkan format to check.
* @return True if format is a depth only, false otherwise.
*/
bool is_depth_only_format(VkFormat format);
/**
* @brief Helper function to determine if a Vulkan format is depth with stencil.
* @param format Vulkan format to check.
* @return True if format is a depth with stencil, false otherwise.
*/
bool is_depth_stencil_format(VkFormat format);
/**
* @brief Helper function to determine if a Vulkan format is depth.
* @param format Vulkan format to check.
* @return True if format is a depth, false otherwise.
*/
bool is_depth_format(VkFormat format);
/**
* @brief Helper function to determine a suitable supported depth format based on a priority list
* @param physical_device The physical device to check the depth formats against
* @param depth_only (Optional) Wether to include the stencil component in the format or not
* @param depth_format_priority_list (Optional) The list of depth formats to prefer over one another
* By default we start with the highest precision packed format
* @return The valid suited depth format
*/
VkFormat get_suitable_depth_format(VkPhysicalDevice physical_device,
bool depth_only = false,
const std::vector<VkFormat> &depth_format_priority_list = {
VK_FORMAT_D32_SFLOAT,
VK_FORMAT_D24_UNORM_S8_UINT,
VK_FORMAT_D16_UNORM});
/**
* @brief Helper function to pick a blendable format from a priority ordered list
* @param physical_device The physical device to check the formats against
* @param format_priority_list List of formats in order of priority
* @return The selected format
*/
VkFormat choose_blendable_format(VkPhysicalDevice physical_device, const std::vector<VkFormat> &format_priority_list);
/**
* @brief Helper function to check support for linear filtering and adjust its parameters if required
* @param physical_device The physical device to check the depth formats against
* @param format The format to check against
* @param filter The preferred filter to adjust
* @param mipmapMode (Optional) The preferred mipmap mode to adjust
*/
void make_filters_valid(VkPhysicalDevice physical_device, VkFormat format, VkFilter *filter, VkSamplerMipmapMode *mipmapMode = nullptr);
/**
* @brief Helper function to determine if a Vulkan descriptor type is a dynamic storage buffer or dynamic uniform buffer.
* @param descriptor_type Vulkan descriptor type to check.
* @return True if type is dynamic buffer, false otherwise.
*/
bool is_dynamic_buffer_descriptor_type(VkDescriptorType descriptor_type);
/**
* @brief Helper function to determine if a Vulkan descriptor type is a buffer (either uniform or storage buffer, dynamic or not).
* @param descriptor_type Vulkan descriptor type to check.
* @return True if type is buffer, false otherwise.
*/
bool is_buffer_descriptor_type(VkDescriptorType descriptor_type);
/**
* @brief Helper function to get the bits per pixel of a Vulkan format.
* @param format Vulkan format to check.
* @return The bits per pixel of the given format, -1 for invalid formats.
*/
int32_t get_bits_per_pixel(VkFormat format);
enum class ShadingLanguage
{
GLSL,
HLSL,
SLANG,
};
/**
* @brief Helper function to create a VkShaderModule from a SPIR-V shader file
* @param filename The shader location
* @param device The logical device
* @param stage The shader stage
* @return The shader module containing the loaded shader
*/
VkShaderModule load_shader(const std::string &filename, VkDevice device, VkShaderStageFlagBits stage);
/**
* @brief Helper function to select a VkSurfaceFormatKHR
* @param gpu The VkPhysicalDevice to select a format for.
* @param surface The VkSurfaceKHR to select a format for.
* @param preferred_formats List of preferred VkFormats to use.
* @return The preferred VkSurfaceFormatKHR.
*/
VkSurfaceFormatKHR select_surface_format(VkPhysicalDevice gpu,
VkSurfaceKHR surface,
std::vector<VkFormat> const &preferred_formats = {
VK_FORMAT_R8G8B8A8_SRGB, VK_FORMAT_B8G8R8A8_SRGB, VK_FORMAT_A8B8G8R8_SRGB_PACK32});
/**
* @brief Image memory barrier structure used to define
* memory access for an image view during command recording.
*/
struct ImageMemoryBarrier
{
VkPipelineStageFlags src_stage_mask{VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT};
VkPipelineStageFlags dst_stage_mask{VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT};
VkAccessFlags src_access_mask{0};
VkAccessFlags dst_access_mask{0};
VkImageLayout old_layout{VK_IMAGE_LAYOUT_UNDEFINED};
VkImageLayout new_layout{VK_IMAGE_LAYOUT_UNDEFINED};
uint32_t src_queue_family{VK_QUEUE_FAMILY_IGNORED};
uint32_t dst_queue_family{VK_QUEUE_FAMILY_IGNORED};
};
/**
* @brief Buffer memory barrier structure used to define
* memory access for a buffer during command recording.
*/
struct BufferMemoryBarrier
{
VkPipelineStageFlags src_stage_mask{VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT};
VkPipelineStageFlags dst_stage_mask{VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT};
VkAccessFlags src_access_mask{0};
VkAccessFlags dst_access_mask{0};
};
/**
* @brief Put an image memory barrier for a layout transition of an image, using explicitly give transition parameters.
* @param command_buffer The VkCommandBuffer to record the barrier.
* @param image The VkImage to transition.
* @param src_stage_mask The VkPipelineStageFlags to use as source.
* @param dst_stage_mask The VkPipelineStageFlags to use as destination.
* @param src_access_mask The VkAccessFlags to use as source.
* @param dst_access_mask The VkAccessFlags to use as destination.
* @param old_layout The VkImageLayout to transition from.
* @param new_layout The VkImageLayout to transition to.
* @param subresource_range The VkImageSubresourceRange to use with the transition.
*/
void image_layout_transition(VkCommandBuffer command_buffer,
VkImage image,
VkPipelineStageFlags src_stage_mask,
VkPipelineStageFlags dst_stage_mask,
VkAccessFlags src_access_mask,
VkAccessFlags dst_access_mask,
VkImageLayout old_layout,
VkImageLayout new_layout,
VkImageSubresourceRange const &subresource_range);
/**
* @brief Put an image memory barrier for a layout transition of an image, on a given subresource range.
*
* The src_stage_mask, dst_stage_mask, src_access_mask, and dst_access_mask used are determined from old_layout and new_layout.
*
* @param command_buffer The VkCommandBuffer to record the barrier.
* @param image The VkImage to transition.
* @param old_layout The VkImageLayout to transition from.
* @param new_layout The VkImageLayout to transition to.
* @param subresource_range The VkImageSubresourceRange to use with the transition.
*/
void image_layout_transition(VkCommandBuffer command_buffer,
VkImage image,
VkImageLayout old_layout,
VkImageLayout new_layout,
VkImageSubresourceRange const &subresource_range);
/**
* @brief Put an image memory barrier for a layout transition of an image, on a fixed subresource with first mip level and layer.
*
* The src_stage_mask, dst_stage_mask, src_access_mask, and dst_access_mask used are determined from old_layout and new_layout.
*
* @param command_buffer The VkCommandBuffer to record the barrier.
* @param image The VkImage to transition.
* @param old_layout The VkImageLayout to transition from.
* @param new_layout The VkImageLayout to transition to.
*/
void image_layout_transition(VkCommandBuffer command_buffer,
VkImage image,
VkImageLayout old_layout,
VkImageLayout new_layout);
/**
* @brief Put an image memory barrier for a layout transition of a vector of images, with a given subresource range per image.
*
* The src_stage_mask, dst_stage_mask, src_access_mask, and dst_access_mask used are determined from old_layout and new_layout.
*
* @param command_buffer The VkCommandBuffer to record the barrier.
* @param imagesAndRanges The images to transition, with accompanying subresource ranges.
* @param old_layout The VkImageLayout to transition from.
* @param new_layout The VkImageLayout to transition to.
*/
void image_layout_transition(VkCommandBuffer command_buffer,
std::vector<std::pair<VkImage, VkImageSubresourceRange>> const &imagesAndRanges,
VkImageLayout old_layout,
VkImageLayout new_layout);
/**
* @brief Helper functions for compression controls
*/
std::vector<VkImageCompressionFixedRateFlagBitsEXT> fixed_rate_compression_flags_to_vector(VkImageCompressionFixedRateFlagsEXT flags);
VkImageCompressionPropertiesEXT query_supported_fixed_rate_compression(VkPhysicalDevice gpu, const VkImageCreateInfo &create_info);
VkImageCompressionPropertiesEXT query_applied_compression(VkDevice device, VkImage image);
/**
* @brief Load and store info for a render pass attachment.
*/
struct LoadStoreInfo
{
VkAttachmentLoadOp load_op = VK_ATTACHMENT_LOAD_OP_CLEAR;
VkAttachmentStoreOp store_op = VK_ATTACHMENT_STORE_OP_STORE;
};
namespace gbuffer
{
/**
* @return Load store info to load all and store only the swapchain
*/
std::vector<LoadStoreInfo> get_load_all_store_swapchain();
/**
* @return Load store info to clear all and store only the swapchain
*/
std::vector<LoadStoreInfo> get_clear_all_store_swapchain();
/**
* @return Load store info to clear and store all images
*/
std::vector<LoadStoreInfo> get_clear_store_all();
/**
* @return Default clear values for the G-buffer
*/
std::vector<VkClearValue> get_clear_value();
} // namespace gbuffer
uint32_t get_queue_family_index(std::vector<VkQueueFamilyProperties> const &queue_family_properties, VkQueueFlagBits queue_flag);
} // namespace vkb
+667
View File
@@ -0,0 +1,667 @@
/* Copyright (c) 2019-2024, Sascha Willems
*
* SPDX-License-Identifier: Apache-2.0
*
* Licensed under the Apache License, Version 2.0 the "License";
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#pragma once
#include "volk.h"
#include <vector>
namespace vkb
{
namespace initializers
{
inline VkMemoryAllocateInfo memory_allocate_info()
{
VkMemoryAllocateInfo memory_allocation{};
memory_allocation.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
return memory_allocation;
}
inline VkMappedMemoryRange mapped_memory_range()
{
VkMappedMemoryRange mapped_memory_range{};
mapped_memory_range.sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE;
return mapped_memory_range;
}
inline VkCommandBufferAllocateInfo command_buffer_allocate_info(
VkCommandPool command_pool,
VkCommandBufferLevel level,
uint32_t buffer_count)
{
VkCommandBufferAllocateInfo command_buffer_allocate_info{};
command_buffer_allocate_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
command_buffer_allocate_info.commandPool = command_pool;
command_buffer_allocate_info.level = level;
command_buffer_allocate_info.commandBufferCount = buffer_count;
return command_buffer_allocate_info;
}
inline VkCommandPoolCreateInfo command_pool_create_info()
{
VkCommandPoolCreateInfo command_pool_create_info{};
command_pool_create_info.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;
return command_pool_create_info;
}
inline VkCommandBufferBeginInfo command_buffer_begin_info()
{
VkCommandBufferBeginInfo cmdBufferBeginInfo{};
cmdBufferBeginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
return cmdBufferBeginInfo;
}
inline VkCommandBufferInheritanceInfo command_buffer_inheritance_info()
{
VkCommandBufferInheritanceInfo command_buffer_inheritance_info{};
command_buffer_inheritance_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_INHERITANCE_INFO;
return command_buffer_inheritance_info;
}
inline VkRenderPassBeginInfo render_pass_begin_info()
{
VkRenderPassBeginInfo render_pass_begin_info{};
render_pass_begin_info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;
return render_pass_begin_info;
}
inline VkRenderPassCreateInfo render_pass_create_info()
{
VkRenderPassCreateInfo render_pass_create_info{};
render_pass_create_info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;
return render_pass_create_info;
}
/** @brief Initialize rendering_attachment_info */
inline VkRenderingAttachmentInfoKHR rendering_attachment_info()
{
VkRenderingAttachmentInfoKHR attachment_info{};
attachment_info.sType = VK_STRUCTURE_TYPE_RENDERING_ATTACHMENT_INFO_KHR;
attachment_info.pNext = VK_NULL_HANDLE;
return attachment_info;
}
/** @brief Initialize VkRenderingInfoKHR, e.g. for use with dynamic rendering extension */
inline VkRenderingInfoKHR rendering_info(VkRect2D render_area = {},
uint32_t color_attachment_count = 0,
const VkRenderingAttachmentInfoKHR *pColorAttachments = VK_NULL_HANDLE,
VkRenderingFlagsKHR flags = 0)
{
VkRenderingInfoKHR rendering_info = {};
rendering_info.sType = VK_STRUCTURE_TYPE_RENDERING_INFO_KHR;
rendering_info.pNext = VK_NULL_HANDLE;
rendering_info.flags = flags;
rendering_info.renderArea = render_area;
rendering_info.layerCount = 0;
rendering_info.viewMask = 0;
rendering_info.colorAttachmentCount = color_attachment_count;
rendering_info.pColorAttachments = pColorAttachments;
rendering_info.pDepthAttachment = VK_NULL_HANDLE;
rendering_info.pStencilAttachment = VK_NULL_HANDLE;
return rendering_info;
}
/** @brief Initialize an image memory barrier with no image transfer ownership */
inline VkImageMemoryBarrier image_memory_barrier()
{
VkImageMemoryBarrier image_memory_barrier{};
image_memory_barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;
image_memory_barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
image_memory_barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
return image_memory_barrier;
}
/** @brief Initialize a buffer memory barrier with no image transfer ownership */
inline VkBufferMemoryBarrier buffer_memory_barrier()
{
VkBufferMemoryBarrier buffer_memory_barrier{};
buffer_memory_barrier.sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER;
buffer_memory_barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
buffer_memory_barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;
return buffer_memory_barrier;
}
inline VkMemoryBarrier memory_barrier()
{
VkMemoryBarrier memory_barrier{};
memory_barrier.sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER;
return memory_barrier;
}
inline VkImageCreateInfo image_create_info()
{
VkImageCreateInfo image_create_info{};
image_create_info.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;
return image_create_info;
}
inline VkSamplerCreateInfo sampler_create_info()
{
VkSamplerCreateInfo sampler_create_info{};
sampler_create_info.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO;
sampler_create_info.maxAnisotropy = 1.0f;
return sampler_create_info;
}
inline VkImageViewCreateInfo image_view_create_info()
{
VkImageViewCreateInfo image_view_create_info{};
image_view_create_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
return image_view_create_info;
}
inline VkFramebufferCreateInfo framebuffer_create_info()
{
VkFramebufferCreateInfo framebuffer_create_info{};
framebuffer_create_info.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;
return framebuffer_create_info;
}
inline VkSemaphoreCreateInfo semaphore_create_info()
{
VkSemaphoreCreateInfo semaphore_create_info{};
semaphore_create_info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
return semaphore_create_info;
}
inline VkFenceCreateInfo fence_create_info(VkFenceCreateFlags flags = 0)
{
VkFenceCreateInfo fence_create_info{};
fence_create_info.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
fence_create_info.flags = flags;
return fence_create_info;
}
inline VkEventCreateInfo event_create_info()
{
VkEventCreateInfo event_create_info{};
event_create_info.sType = VK_STRUCTURE_TYPE_EVENT_CREATE_INFO;
return event_create_info;
}
inline VkSubmitInfo submit_info()
{
VkSubmitInfo submit_info{};
submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
return submit_info;
}
inline VkViewport viewport(
float width,
float height,
float min_depth,
float max_depth)
{
VkViewport viewport{};
viewport.width = width;
viewport.height = height;
viewport.minDepth = min_depth;
viewport.maxDepth = max_depth;
return viewport;
}
inline VkRect2D rect2D(
int32_t width,
int32_t height,
int32_t offset_x,
int32_t offset_y)
{
VkRect2D rect2D{};
rect2D.extent.width = width;
rect2D.extent.height = height;
rect2D.offset.x = offset_x;
rect2D.offset.y = offset_y;
return rect2D;
}
inline VkBufferCreateInfo buffer_create_info()
{
VkBufferCreateInfo buffer_create_info{};
buffer_create_info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
return buffer_create_info;
}
inline VkBufferCreateInfo buffer_create_info(
VkBufferUsageFlags usage,
VkDeviceSize size)
{
VkBufferCreateInfo buffer_create_info{};
buffer_create_info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
buffer_create_info.usage = usage;
buffer_create_info.size = size;
return buffer_create_info;
}
inline VkDescriptorPoolCreateInfo descriptor_pool_create_info(
uint32_t count,
VkDescriptorPoolSize *pool_sizes,
uint32_t max_sets)
{
VkDescriptorPoolCreateInfo descriptor_pool_info{};
descriptor_pool_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
descriptor_pool_info.poolSizeCount = count;
descriptor_pool_info.pPoolSizes = pool_sizes;
descriptor_pool_info.maxSets = max_sets;
return descriptor_pool_info;
}
inline VkDescriptorPoolCreateInfo descriptor_pool_create_info(
const std::vector<VkDescriptorPoolSize> &pool_sizes,
uint32_t max_sets)
{
VkDescriptorPoolCreateInfo descriptor_pool_info{};
descriptor_pool_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;
descriptor_pool_info.poolSizeCount = static_cast<uint32_t>(pool_sizes.size());
descriptor_pool_info.pPoolSizes = pool_sizes.data();
descriptor_pool_info.maxSets = max_sets;
return descriptor_pool_info;
}
inline VkDescriptorPoolSize descriptor_pool_size(
VkDescriptorType type,
uint32_t count)
{
VkDescriptorPoolSize descriptor_pool_size{};
descriptor_pool_size.type = type;
descriptor_pool_size.descriptorCount = count;
return descriptor_pool_size;
}
inline VkDescriptorSetLayoutBinding descriptor_set_layout_binding(
VkDescriptorType type,
VkShaderStageFlags flags,
uint32_t binding,
uint32_t count = 1)
{
VkDescriptorSetLayoutBinding set_layout_binding{};
set_layout_binding.descriptorType = type;
set_layout_binding.stageFlags = flags;
set_layout_binding.binding = binding;
set_layout_binding.descriptorCount = count;
return set_layout_binding;
}
inline VkDescriptorSetLayoutCreateInfo descriptor_set_layout_create_info(
const VkDescriptorSetLayoutBinding *bindings,
uint32_t binding_count)
{
VkDescriptorSetLayoutCreateInfo descriptor_set_layout_create_info{};
descriptor_set_layout_create_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
descriptor_set_layout_create_info.pBindings = bindings;
descriptor_set_layout_create_info.bindingCount = binding_count;
return descriptor_set_layout_create_info;
}
inline VkDescriptorSetLayoutCreateInfo descriptor_set_layout_create_info(
const std::vector<VkDescriptorSetLayoutBinding> &bindings)
{
VkDescriptorSetLayoutCreateInfo descriptor_set_layout_create_info{};
descriptor_set_layout_create_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;
descriptor_set_layout_create_info.pBindings = bindings.data();
descriptor_set_layout_create_info.bindingCount = static_cast<uint32_t>(bindings.size());
return descriptor_set_layout_create_info;
}
inline VkPipelineLayoutCreateInfo pipeline_layout_create_info(
const VkDescriptorSetLayout *set_layouts,
uint32_t set_layout_count = 1)
{
VkPipelineLayoutCreateInfo pipeline_layout_create_info{};
pipeline_layout_create_info.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
pipeline_layout_create_info.setLayoutCount = set_layout_count;
pipeline_layout_create_info.pSetLayouts = set_layouts;
return pipeline_layout_create_info;
}
inline VkPipelineLayoutCreateInfo pipeline_layout_create_info(
uint32_t set_layout_count = 1)
{
VkPipelineLayoutCreateInfo pipeline_layout_create_info{};
pipeline_layout_create_info.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;
pipeline_layout_create_info.setLayoutCount = set_layout_count;
return pipeline_layout_create_info;
}
inline VkDescriptorSetAllocateInfo descriptor_set_allocate_info(
VkDescriptorPool descriptor_pool,
const VkDescriptorSetLayout *set_layouts,
uint32_t descriptor_set_count)
{
VkDescriptorSetAllocateInfo descriptor_set_allocate_info{};
descriptor_set_allocate_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;
descriptor_set_allocate_info.descriptorPool = descriptor_pool;
descriptor_set_allocate_info.pSetLayouts = set_layouts;
descriptor_set_allocate_info.descriptorSetCount = descriptor_set_count;
return descriptor_set_allocate_info;
}
inline VkDescriptorImageInfo descriptor_image_info(VkSampler sampler, VkImageView image_view, VkImageLayout image_layout)
{
VkDescriptorImageInfo descriptor_image_info{};
descriptor_image_info.sampler = sampler;
descriptor_image_info.imageView = image_view;
descriptor_image_info.imageLayout = image_layout;
return descriptor_image_info;
}
inline VkWriteDescriptorSet write_descriptor_set(
VkDescriptorSet dst_set,
VkDescriptorType type,
uint32_t binding,
VkDescriptorBufferInfo *buffer_info,
uint32_t descriptor_count = 1)
{
VkWriteDescriptorSet write_descriptor_set{};
write_descriptor_set.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
write_descriptor_set.dstSet = dst_set;
write_descriptor_set.descriptorType = type;
write_descriptor_set.dstBinding = binding;
write_descriptor_set.pBufferInfo = buffer_info;
write_descriptor_set.descriptorCount = descriptor_count;
return write_descriptor_set;
}
inline VkWriteDescriptorSet write_descriptor_set(
VkDescriptorSet dst_set,
VkDescriptorType type,
uint32_t binding,
VkDescriptorImageInfo *image_info,
uint32_t descriptor_count = 1)
{
VkWriteDescriptorSet write_descriptor_set{};
write_descriptor_set.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;
write_descriptor_set.dstSet = dst_set;
write_descriptor_set.descriptorType = type;
write_descriptor_set.dstBinding = binding;
write_descriptor_set.pImageInfo = image_info;
write_descriptor_set.descriptorCount = descriptor_count;
return write_descriptor_set;
}
inline VkVertexInputBindingDescription vertex_input_binding_description(
uint32_t binding,
uint32_t stride,
VkVertexInputRate input_rate)
{
VkVertexInputBindingDescription vertex_input_binding_description{};
vertex_input_binding_description.binding = binding;
vertex_input_binding_description.stride = stride;
vertex_input_binding_description.inputRate = input_rate;
return vertex_input_binding_description;
}
inline VkVertexInputBindingDescription2EXT vertex_input_binding_description2ext(
uint32_t binding,
uint32_t stride,
VkVertexInputRate input_rate,
uint32_t divisor)
{
VkVertexInputBindingDescription2EXT vertex_input_binding_description2ext{};
vertex_input_binding_description2ext.sType = VK_STRUCTURE_TYPE_VERTEX_INPUT_BINDING_DESCRIPTION_2_EXT;
vertex_input_binding_description2ext.binding = binding;
vertex_input_binding_description2ext.stride = stride;
vertex_input_binding_description2ext.inputRate = input_rate;
vertex_input_binding_description2ext.divisor = divisor;
return vertex_input_binding_description2ext;
}
inline VkVertexInputAttributeDescription vertex_input_attribute_description(
uint32_t binding,
uint32_t location,
VkFormat format,
uint32_t offset)
{
VkVertexInputAttributeDescription vertex_input_attribute_description{};
vertex_input_attribute_description.location = location;
vertex_input_attribute_description.binding = binding;
vertex_input_attribute_description.format = format;
vertex_input_attribute_description.offset = offset;
return vertex_input_attribute_description;
}
inline VkVertexInputAttributeDescription2EXT vertex_input_attribute_description2ext(
uint32_t binding,
uint32_t location,
VkFormat format,
uint32_t offset)
{
VkVertexInputAttributeDescription2EXT vertex_input_attribute_description2ext{};
vertex_input_attribute_description2ext.sType = VK_STRUCTURE_TYPE_VERTEX_INPUT_ATTRIBUTE_DESCRIPTION_2_EXT;
vertex_input_attribute_description2ext.location = location;
vertex_input_attribute_description2ext.binding = binding;
vertex_input_attribute_description2ext.format = format;
vertex_input_attribute_description2ext.offset = offset;
return vertex_input_attribute_description2ext;
}
inline VkPipelineVertexInputStateCreateInfo pipeline_vertex_input_state_create_info()
{
VkPipelineVertexInputStateCreateInfo pipeline_vertex_input_state_create_info{};
pipeline_vertex_input_state_create_info.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;
return pipeline_vertex_input_state_create_info;
}
inline VkPipelineInputAssemblyStateCreateInfo pipeline_input_assembly_state_create_info(
VkPrimitiveTopology topology,
VkPipelineInputAssemblyStateCreateFlags flags,
VkBool32 primitive_restart_enable)
{
VkPipelineInputAssemblyStateCreateInfo pipeline_input_assembly_state_create_info{};
pipeline_input_assembly_state_create_info.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;
pipeline_input_assembly_state_create_info.topology = topology;
pipeline_input_assembly_state_create_info.flags = flags;
pipeline_input_assembly_state_create_info.primitiveRestartEnable = primitive_restart_enable;
return pipeline_input_assembly_state_create_info;
}
inline VkPipelineRasterizationStateCreateInfo pipeline_rasterization_state_create_info(
VkPolygonMode polygon_mode,
VkCullModeFlags cull_mode,
VkFrontFace front_face,
VkPipelineRasterizationStateCreateFlags flags = 0)
{
VkPipelineRasterizationStateCreateInfo pipeline_rasterization_state_create_info{};
pipeline_rasterization_state_create_info.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;
pipeline_rasterization_state_create_info.polygonMode = polygon_mode;
pipeline_rasterization_state_create_info.cullMode = cull_mode;
pipeline_rasterization_state_create_info.frontFace = front_face;
pipeline_rasterization_state_create_info.flags = flags;
pipeline_rasterization_state_create_info.depthClampEnable = VK_FALSE;
pipeline_rasterization_state_create_info.lineWidth = 1.0f;
return pipeline_rasterization_state_create_info;
}
inline VkPipelineColorBlendAttachmentState pipeline_color_blend_attachment_state(
VkColorComponentFlags color_write_mask,
VkBool32 blend_enable)
{
VkPipelineColorBlendAttachmentState pipeline_color_blend_attachment_state{};
pipeline_color_blend_attachment_state.colorWriteMask = color_write_mask;
pipeline_color_blend_attachment_state.blendEnable = blend_enable;
return pipeline_color_blend_attachment_state;
}
inline VkPipelineColorBlendStateCreateInfo pipeline_color_blend_state_create_info(
uint32_t attachment_count,
const VkPipelineColorBlendAttachmentState *attachments)
{
VkPipelineColorBlendStateCreateInfo pipeline_color_blend_state_create_info{};
pipeline_color_blend_state_create_info.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;
pipeline_color_blend_state_create_info.attachmentCount = attachment_count;
pipeline_color_blend_state_create_info.pAttachments = attachments;
return pipeline_color_blend_state_create_info;
}
inline VkPipelineDepthStencilStateCreateInfo pipeline_depth_stencil_state_create_info(
VkBool32 depth_test_enable,
VkBool32 depth_write_enable,
VkCompareOp depth_compare_op)
{
VkPipelineDepthStencilStateCreateInfo pipeline_depth_stencil_state_create_info{};
pipeline_depth_stencil_state_create_info.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO;
pipeline_depth_stencil_state_create_info.depthTestEnable = depth_test_enable;
pipeline_depth_stencil_state_create_info.depthWriteEnable = depth_write_enable;
pipeline_depth_stencil_state_create_info.depthCompareOp = depth_compare_op;
pipeline_depth_stencil_state_create_info.front = pipeline_depth_stencil_state_create_info.back;
pipeline_depth_stencil_state_create_info.back.compareOp = VK_COMPARE_OP_ALWAYS;
return pipeline_depth_stencil_state_create_info;
}
inline VkPipelineViewportStateCreateInfo pipeline_viewport_state_create_info(
uint32_t viewport_count,
uint32_t scissor_count,
VkPipelineViewportStateCreateFlags flags = 0)
{
VkPipelineViewportStateCreateInfo pipeline_viewport_state_create_info{};
pipeline_viewport_state_create_info.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;
pipeline_viewport_state_create_info.viewportCount = viewport_count;
pipeline_viewport_state_create_info.scissorCount = scissor_count;
pipeline_viewport_state_create_info.flags = flags;
return pipeline_viewport_state_create_info;
}
inline VkPipelineMultisampleStateCreateInfo pipeline_multisample_state_create_info(
VkSampleCountFlagBits rasterization_samples,
VkPipelineMultisampleStateCreateFlags flags = 0)
{
VkPipelineMultisampleStateCreateInfo pipeline_multisample_state_create_info{};
pipeline_multisample_state_create_info.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;
pipeline_multisample_state_create_info.rasterizationSamples = rasterization_samples;
pipeline_multisample_state_create_info.flags = flags;
return pipeline_multisample_state_create_info;
}
inline VkPipelineDynamicStateCreateInfo pipeline_dynamic_state_create_info(
const VkDynamicState *dynamic_states,
uint32_t dynamicStateCount,
VkPipelineDynamicStateCreateFlags flags = 0)
{
VkPipelineDynamicStateCreateInfo pipeline_dynamic_state_create_info{};
pipeline_dynamic_state_create_info.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO;
pipeline_dynamic_state_create_info.pDynamicStates = dynamic_states;
pipeline_dynamic_state_create_info.dynamicStateCount = dynamicStateCount;
pipeline_dynamic_state_create_info.flags = flags;
return pipeline_dynamic_state_create_info;
}
inline VkPipelineDynamicStateCreateInfo pipeline_dynamic_state_create_info(
const std::vector<VkDynamicState> &dynamic_states,
VkPipelineDynamicStateCreateFlags flags = 0)
{
VkPipelineDynamicStateCreateInfo pipeline_dynamic_state_create_info{};
pipeline_dynamic_state_create_info.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO;
pipeline_dynamic_state_create_info.pDynamicStates = dynamic_states.data();
pipeline_dynamic_state_create_info.dynamicStateCount = static_cast<uint32_t>(dynamic_states.size());
pipeline_dynamic_state_create_info.flags = flags;
return pipeline_dynamic_state_create_info;
}
inline VkPipelineTessellationStateCreateInfo pipeline_tessellation_state_create_info(uint32_t patch_control_points)
{
VkPipelineTessellationStateCreateInfo pipeline_tessellation_state_create_info{};
pipeline_tessellation_state_create_info.sType = VK_STRUCTURE_TYPE_PIPELINE_TESSELLATION_STATE_CREATE_INFO;
pipeline_tessellation_state_create_info.patchControlPoints = patch_control_points;
return pipeline_tessellation_state_create_info;
}
inline VkGraphicsPipelineCreateInfo pipeline_create_info(
VkPipelineLayout layout,
VkRenderPass render_pass,
VkPipelineCreateFlags flags = 0)
{
VkGraphicsPipelineCreateInfo pipeline_create_info{};
pipeline_create_info.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
pipeline_create_info.layout = layout;
pipeline_create_info.renderPass = render_pass;
pipeline_create_info.flags = flags;
pipeline_create_info.basePipelineIndex = -1;
pipeline_create_info.basePipelineHandle = VK_NULL_HANDLE;
return pipeline_create_info;
}
inline VkGraphicsPipelineCreateInfo pipeline_create_info()
{
VkGraphicsPipelineCreateInfo pipeline_create_info{};
pipeline_create_info.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;
pipeline_create_info.basePipelineIndex = -1;
pipeline_create_info.basePipelineHandle = VK_NULL_HANDLE;
return pipeline_create_info;
}
inline VkComputePipelineCreateInfo compute_pipeline_create_info(
VkPipelineLayout layout,
VkPipelineCreateFlags flags = 0)
{
VkComputePipelineCreateInfo compute_pipeline_create_info{};
compute_pipeline_create_info.sType = VK_STRUCTURE_TYPE_COMPUTE_PIPELINE_CREATE_INFO;
compute_pipeline_create_info.layout = layout;
compute_pipeline_create_info.flags = flags;
return compute_pipeline_create_info;
}
inline VkPushConstantRange push_constant_range(
VkShaderStageFlags stage_flags,
uint32_t size,
uint32_t offset)
{
VkPushConstantRange push_constant_range{};
push_constant_range.stageFlags = stage_flags;
push_constant_range.offset = offset;
push_constant_range.size = size;
return push_constant_range;
}
inline VkBindSparseInfo bind_sparse_info()
{
VkBindSparseInfo bind_sparse_info{};
bind_sparse_info.sType = VK_STRUCTURE_TYPE_BIND_SPARSE_INFO;
return bind_sparse_info;
}
/** @brief Initialize a map entry for a shader specialization constant */
inline VkSpecializationMapEntry specialization_map_entry(uint32_t constant_id, uint32_t offset, size_t size)
{
VkSpecializationMapEntry specialization_map_entry{};
specialization_map_entry.constantID = constant_id;
specialization_map_entry.offset = offset;
specialization_map_entry.size = size;
return specialization_map_entry;
}
/** @brief Initialize a specialization constant info structure to pass to a shader stage */
inline VkSpecializationInfo specialization_info(uint32_t map_entry_count, const VkSpecializationMapEntry *map_entries, size_t data_size, const void *data)
{
VkSpecializationInfo specialization_info{};
specialization_info.mapEntryCount = map_entry_count;
specialization_info.pMapEntries = map_entries;
specialization_info.dataSize = data_size;
specialization_info.pData = data;
return specialization_info;
}
inline VkTimelineSemaphoreSubmitInfo timeline_semaphore_submit_info(uint32_t wait_value_count, uint64_t *wait_values, uint32_t signal_value_count, uint64_t *signal_values)
{
return VkTimelineSemaphoreSubmitInfo{
VK_STRUCTURE_TYPE_TIMELINE_SEMAPHORE_SUBMIT_INFO,
NULL,
wait_value_count,
wait_values,
signal_value_count,
signal_values};
}
} // namespace initializers
} // namespace vkb