Skip to content

Commit

Permalink
Add vulkan instance class
Browse files Browse the repository at this point in the history
  • Loading branch information
albin-johansson committed Apr 28, 2024
1 parent 8cdbed4 commit c992901
Show file tree
Hide file tree
Showing 3 changed files with 208 additions and 0 deletions.
4 changes: 4 additions & 0 deletions source/vulkan/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,10 @@ tactile_prepare_target(tactile-vulkan)

target_compile_definitions(tactile-vulkan PRIVATE "TACTILE_BUILDING_VULKAN_RENDERER")

if (APPLE)
target_compile_definitions(tactile-vulkan PRIVATE "TACTILE_USE_VULKAN_SUBSET")
endif ()

target_include_directories(tactile-vulkan
PUBLIC
"${PROJECT_SOURCE_DIR}/inc"
Expand Down
67 changes: 67 additions & 0 deletions source/vulkan/inc/tactile/vulkan/vulkan_instance.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
// Copyright (C) 2023 Albin Johansson (GNU General Public License v3.0)

#pragma once

#include <vulkan/vulkan.h>

#include "tactile/base/container/expected.hpp"
#include "tactile/base/container/smart_ptr.hpp"
#include "tactile/base/prelude.hpp"
#include "tactile/vulkan/api.hpp"

struct SDL_Window;

namespace tactile {

/// \addtogroup Vulkan
/// \{

/**
* Wrapper around a \c VkInstance object.
*/
class VulkanInstance final
{
public:
TACTILE_DELETE_COPY(VulkanInstance);

/**
* Creates a Vulkan instance.
*
* \details
* In debug builds, the instance will use the \c VK_LAYER_KHRONOS_validation
* layer for API usage validation.
*
* \param window The associated window handle.
*
* \return
* A Vulkan instance if successful; an error code otherwise.
*/
[[nodiscard]]
static auto make(SDL_Window* window) -> Result<VulkanInstance>;

VulkanInstance(VulkanInstance&& other) noexcept;

auto operator=(VulkanInstance&& other) noexcept -> VulkanInstance&;

~VulkanInstance() noexcept;

/**
* Returns the associated \c VkInstance.
*
* \return
* A \c VkInstance handle.
*/
[[nodiscard]]
auto get() -> VkInstance;

private:
VkInstance mInstance {VK_NULL_HANDLE};

explicit VulkanInstance(VkInstance instance) noexcept;

void _dispose() noexcept;
};

/// \}

} // namespace tactile
137 changes: 137 additions & 0 deletions source/vulkan/src/tactile/vulkan/vulkan_instance.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
// Copyright (C) 2023 Albin Johansson (GNU General Public License v3.0)

#include "tactile/vulkan/vulkan_instance.hpp"

#include <utility> // exchange

#include <SDL2/SDL_vulkan.h>

#include "tactile/base/container/vector.hpp"
#include "tactile/base/int.hpp"
#include "tactile/vulkan/vulkan_error.hpp"

namespace tactile {
namespace {

inline constexpr uint32 kValidationLayerCount = 1;
inline constexpr const char* kValidationLayerNames[] {
"VK_LAYER_KHRONOS_validation",
};

[[nodiscard]]
auto _get_instance_extensions(SDL_Window* window) -> Vector<const char*>
{
Vector<const char*> instance_extensions {};

uint extension_count = 0;
if (!SDL_Vulkan_GetInstanceExtensions(window, &extension_count, nullptr)) {
return instance_extensions;
}

instance_extensions.reserve(extension_count + 1);
instance_extensions.resize(extension_count);

SDL_Vulkan_GetInstanceExtensions(window,
&extension_count,
instance_extensions.data());

#ifdef TACTILE_USE_VULKAN_SUBSET
instance_extensions.push_back(VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME);
#endif

return instance_extensions;
}

} // namespace

auto VulkanInstance::make(SDL_Window* window) -> Result<VulkanInstance>
{
uint32 instance_flags = 0;

#ifdef TACTILE_USE_VULKAN_SUBSET
// Allow partial implementations of the Vulkan spec, such as MoltenVK.
instance_flags |= VK_INSTANCE_CREATE_ENUMERATE_PORTABILITY_BIT_KHR;
#endif // TACTILE_USE_VULKAN_SUBSET

const char* const* enabled_layer_names = nullptr;
uint32 enabled_layer_count = 0;
if constexpr (kIsDebugBuild) {
enabled_layer_names = kValidationLayerNames;
enabled_layer_count = kValidationLayerCount;
}

const auto instance_extensions = _get_instance_extensions(window);

const VkApplicationInfo app_info {
.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO,
.pNext = nullptr,
.pApplicationName = "Tactile",
.applicationVersion = VK_MAKE_VERSION(TACTILE_MAJOR_VERSION,
TACTILE_MINOR_VERSION,
TACTILE_PATCH_VERSION),
.pEngineName = "tactile-vulkan",
.engineVersion = VK_MAKE_VERSION(TACTILE_MAJOR_VERSION,
TACTILE_MINOR_VERSION,
TACTILE_PATCH_VERSION),
.apiVersion = VK_API_VERSION_1_2,
};

const VkInstanceCreateInfo create_info {
.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO,
.pNext = nullptr,
.flags = instance_flags,
.pApplicationInfo = &app_info,
.enabledLayerCount = enabled_layer_count,
.ppEnabledLayerNames = enabled_layer_names,
.enabledExtensionCount = static_cast<uint32>(instance_extensions.size()),
.ppEnabledExtensionNames = instance_extensions.data(),
};

VkInstance instance {};
const auto create_result = vkCreateInstance(&create_info, nullptr, &instance);

if (create_result != VK_SUCCESS) {
return unexpected(make_error(map_vulkan_result(create_result)));
}

return VulkanInstance {instance};
}

VulkanInstance::VulkanInstance(VkInstance instance) noexcept
: mInstance {instance}
{}

VulkanInstance::VulkanInstance(VulkanInstance&& other) noexcept
: mInstance {std::exchange(other.mInstance, VK_NULL_HANDLE)}
{}

auto VulkanInstance::operator=(VulkanInstance&& other) noexcept
-> VulkanInstance&
{
if (this != &other) {
_dispose();
mInstance = std::exchange(other.mInstance, VK_NULL_HANDLE);
}

return *this;
}

VulkanInstance::~VulkanInstance() noexcept
{
_dispose();
}

void VulkanInstance::_dispose() noexcept
{
if (mInstance != VK_NULL_HANDLE) {
vkDestroyInstance(mInstance, nullptr);
mInstance = VK_NULL_HANDLE;
}
}

auto VulkanInstance::get() -> VkInstance
{
return mInstance;
}

} // namespace tactile

0 comments on commit c992901

Please sign in to comment.