Skip to content

Commit d72745e

Browse files
committed
feat: add vk12/vk13 and presenter builder abstraction
1 parent ded98d0 commit d72745e

8 files changed

Lines changed: 534 additions & 357 deletions

File tree

src/backend/vulkan/CMakeLists.txt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,17 @@ add_library(quark::backend::vulkan ALIAS quark_backend_vulkan)
66
add_subdirectory(instance)
77
add_subdirectory(device)
88
add_subdirectory(frame)
9+
add_subdirectory(presentation)
910
add_subdirectory(sync)
1011

1112
target_link_libraries(quark_backend_vulkan
1213
INTERFACE
1314
quark::backend::vulkan::instance
1415
quark::backend::vulkan::device
1516
quark::backend::vulkan::frame
17+
quark::backend::vulkan::presentation
1618
quark::backend::vulkan::sync
17-
PUBLIC
19+
PUBLIC
1820
quark::headers::vk
1921
)
2022

src/backend/vulkan/device/device.cpp

Lines changed: 31 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
#include <algorithm>
2-
#include <array>
32
#include <cstdint>
43
#include <cstring>
54
#include <quark/vk/device/details/device.hpp>
@@ -13,15 +12,6 @@ namespace quark::vk::details {
1312

1413
namespace {
1514

16-
struct FeatureDesc {
17-
Device::Features bit;
18-
const char *name;
19-
};
20-
21-
static constexpr std::array<FeatureDesc, 1> kFeatureDescs = {{
22-
{.bit = Device::Features::TimelineSemaphore, .name = "timelineSemaphore"},
23-
}};
24-
2515
struct DeviceFeatureChain {
2616
VkPhysicalDeviceFeatures2 features2{};
2717
VkPhysicalDeviceVulkan12Features vk12{};
@@ -46,7 +36,9 @@ struct DeviceFeatureChain {
4636

4737
[[nodiscard]] util::Result<DeviceFeatureChain>
4838
build_device_feature_chain(VkPhysicalDevice physical_device,
49-
const Device::FeatureFlags requested_flags) {
39+
const Device::FeatureFlags required_flags,
40+
const Device::FeatureFlags preferred_flags,
41+
Device::Capabilities &capabilities) {
5042
QUARK_ENSURE(physical_device != VK_NULL_HANDLE,
5143
QUARK_ERR(util::Errc::InvalidArg, "physical_device is null"));
5244

@@ -58,17 +50,25 @@ build_device_feature_chain(VkPhysicalDevice physical_device,
5850
auto maybe_enable = [&](Device::Features bit, const char *name,
5951
VkBool32 supported_value,
6052
VkBool32 &out_enable) -> util::Status {
61-
if (!has_feature(requested_flags, bit)) {
53+
const bool required = has_feature(required_flags, bit);
54+
const bool preferred = has_feature(preferred_flags, bit);
55+
if (!required && !preferred) {
6256
QUARK_OK();
6357
}
64-
QUARK_LOG_INFO("device feature requested: {} supported={}", name,
58+
QUARK_LOG_INFO("device feature requested: {} required={} supported={}",
59+
name, required ? "true" : "false",
6560
supported_value ? "true" : "false");
6661

67-
QUARK_ENSURE(supported_value == VK_TRUE,
68-
QUARK_ERR(util::Errc::Unsupported,
69-
"Required device feature unsupported: {}", name));
62+
if (supported_value != VK_TRUE) {
63+
QUARK_ENSURE(required,
64+
QUARK_ERR(util::Errc::Unsupported,
65+
"Required device feature unsupported: {}", name));
66+
QUARK_LOG_INFO("device feature unavailable: {}", name);
67+
QUARK_OK();
68+
}
7069

7170
out_enable = VK_TRUE;
71+
capabilities.enabled_features |= static_cast<Device::FeatureFlags>(bit);
7272
QUARK_LOG_INFO("device feature enabled: {}", name);
7373
QUARK_OK();
7474
};
@@ -77,9 +77,13 @@ build_device_feature_chain(VkPhysicalDevice physical_device,
7777
Device::Features::TimelineSemaphore, "timelineSemaphore",
7878
supported.vk12.timelineSemaphore, requested.vk12.timelineSemaphore));
7979

80-
// TODO: UNCOMMENT TO TEST DEBUG MESSENGER
81-
// ITS NOT WORKING FOR make run AND ONLY UBSAN/ASAN RUNS
82-
// return supported;
80+
QUARK_TRY_STATUS(maybe_enable(
81+
Device::Features::DynamicRendering, "dynamicRendering",
82+
supported.vk13.dynamicRendering, requested.vk13.dynamicRendering));
83+
84+
QUARK_TRY_STATUS(maybe_enable(
85+
Device::Features::Synchronization2, "synchronization2",
86+
supported.vk13.synchronization2, requested.vk13.synchronization2));
8387

8488
return requested;
8589
}
@@ -106,6 +110,11 @@ util::Status Device::create(const Device::CreateInfo &ci) {
106110
graphics_queue_family_index_ = selection.graphics_queue_family_index;
107111
present_queue_family_index_ = selection.present_queue_family_index;
108112

113+
VkPhysicalDeviceProperties properties{};
114+
vkGetPhysicalDeviceProperties(physical_device_, &properties);
115+
capabilities_.api_version = properties.apiVersion;
116+
capabilities_.enabled_features = 0;
117+
109118
constexpr float queue_priority{1.0F};
110119
const std::set<uint32_t> unique_queue_families{graphics_queue_family_index_,
111120
present_queue_family_index_};
@@ -128,7 +137,8 @@ util::Status Device::create(const Device::CreateInfo &ci) {
128137

129138
DeviceFeatureChain feature_chain{};
130139
QUARK_TRY_ASSIGN(feature_chain, build_device_feature_chain(
131-
physical_device_, ci.requested_features));
140+
physical_device_, ci.required_features,
141+
ci.preferred_features, capabilities_));
132142

133143
VkDeviceCreateInfo create_info{};
134144
create_info.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
@@ -165,6 +175,7 @@ void Device::destroy() noexcept {
165175
graphics_queue_family_index_ = 0;
166176
present_queue_family_index_ = 0;
167177
alloc_ = nullptr;
178+
capabilities_ = Capabilities{};
168179
}
169180

170181
util::Result<Device::DeviceSelection> Device::pick_physical_device_(

src/backend/vulkan/device/device_bundle.cpp

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,4 +73,10 @@ uint32_t DeviceBundle::present_queue_family_index() const noexcept {
7373
return (device != nullptr) ? device->present_queue_family_index() : 0;
7474
}
7575

76+
details::Device::Capabilities DeviceBundle::capabilities() const noexcept {
77+
const details::Device *device = registry_.get(handle_);
78+
return (device != nullptr) ? device->capabilities()
79+
: details::Device::Capabilities{};
80+
}
81+
7682
} // namespace quark::vk
Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,17 @@
1-
add_library(quark_backend_vulkan_presentation STATIC
1+
add_library(quark_backend_vulkan_presentation STATIC
2+
presenter_bundle.cpp
23
swapchain.cpp
34
)
45
add_library(quark::backend::vulkan::presentation ALIAS quark_backend_vulkan_presentation)
56

67
target_link_libraries(quark_backend_vulkan_presentation
78
PUBLIC
8-
quark::headers
9-
PRIVATE
9+
quark::headers::vk
10+
PRIVATE
1011
Vulkan::Vulkan
1112
)
1213

1314
target_include_directories(quark_backend_vulkan_presentation
14-
PRIVATE
15+
PRIVATE
1516
${CMAKE_CURRENT_SOURCE_DIR}
1617
)
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
#include <quark/platform/window/interface_query.hpp>
2+
#include <quark/utils/diagnostic.hpp>
3+
#include <quark/vk/presentation/presenter_bundle.hpp>
4+
#include <quark/vk/surface_source.hpp>
5+
6+
#include <exception>
7+
8+
namespace quark::vk {
9+
10+
namespace {
11+
12+
util::Result<VkSurfaceKHR> create_surface(VkInstance instance,
13+
const platform::IWindow *window) {
14+
QUARK_ENSURE(instance != VK_NULL_HANDLE,
15+
QUARK_ERR(util::Errc::InvalidArg,
16+
"PresenterBundle::create_surface: instance is null"));
17+
QUARK_ENSURE(window != nullptr,
18+
QUARK_ERR(util::Errc::InvalidArg,
19+
"PresenterBundle::create_surface: window is null"));
20+
21+
const auto *source = platform::query<IVulkanSurfaceSource>(*window);
22+
QUARK_ENSURE(
23+
source != nullptr,
24+
QUARK_ERR(util::Errc::Unsupported,
25+
"PresenterBundle::create_surface: window does not expose "
26+
"IVulkanSurfaceSource"));
27+
28+
VkSurfaceKHR surface = source->create_surface(instance);
29+
QUARK_ENSURE(
30+
surface != VK_NULL_HANDLE,
31+
QUARK_ERR(util::Errc::ApiError,
32+
"PresenterBundle::create_surface: create_surface returned "
33+
"VK_NULL_HANDLE"));
34+
35+
return surface;
36+
}
37+
38+
void destroy_surface(VkInstance instance, VkSurfaceKHR &surface) noexcept {
39+
if (instance == VK_NULL_HANDLE || surface == VK_NULL_HANDLE) {
40+
return;
41+
}
42+
43+
vkDestroySurfaceKHR(instance, surface, nullptr);
44+
surface = VK_NULL_HANDLE;
45+
}
46+
47+
} // namespace
48+
49+
util::Status PresenterBundle::create(const CreateInfo &ci) {
50+
destroy();
51+
52+
create_info_ = ci;
53+
54+
QUARK_TRY_ASSIGN(surface_, create_surface(ci.instance, ci.window));
55+
56+
auto swapchain_res = create_swapchain_(VK_NULL_HANDLE);
57+
if (!swapchain_res) {
58+
destroy_surface(ci.instance, surface_);
59+
return util::unexpected(std::move(swapchain_res.error()));
60+
}
61+
62+
swapchain_ = std::move(swapchain_res.value());
63+
QUARK_OK();
64+
}
65+
66+
util::Result<Swapchain>
67+
PresenterBundle::create_swapchain_(VkSwapchainKHR old_swapchain) const {
68+
QUARK_ENSURE(
69+
surface_ != VK_NULL_HANDLE,
70+
QUARK_ERR(util::Errc::InvalidState, "presenter surface is null"));
71+
72+
Swapchain swapchain;
73+
Swapchain::CreateInfo ci{};
74+
ci.physical_device = create_info_.physical_device;
75+
ci.device = create_info_.device;
76+
ci.surface = surface_;
77+
ci.graphics_queue_family_index = create_info_.graphics_queue_family_index;
78+
ci.present_queue_family_index = create_info_.present_queue_family_index;
79+
ci.window = create_info_.window;
80+
ci.old_swapchain = old_swapchain;
81+
ci.preferred_present_mode = create_info_.preferred_present_mode;
82+
ci.preferred_format = create_info_.preferred_format;
83+
ci.preferred_color_space = create_info_.preferred_color_space;
84+
85+
try {
86+
swapchain.create(ci);
87+
} catch (const std::exception &e) {
88+
QUARK_FAIL(QUARK_ERR(util::Errc::ApiError,
89+
"PresenterBundle::create_swapchain_ failed: {}",
90+
e.what()));
91+
}
92+
93+
return swapchain;
94+
}
95+
96+
util::Status PresenterBundle::recreate_swapchain() {
97+
auto *old_swapchain = swapchain_.handle();
98+
99+
Swapchain next_swapchain;
100+
QUARK_TRY_ASSIGN(next_swapchain, create_swapchain_(old_swapchain));
101+
102+
swapchain_ = std::move(next_swapchain);
103+
QUARK_OK();
104+
}
105+
106+
void PresenterBundle::destroy_swapchain() noexcept { swapchain_.reset(); }
107+
108+
void PresenterBundle::destroy() noexcept {
109+
destroy_swapchain();
110+
destroy_surface(create_info_.instance, surface_);
111+
create_info_ = CreateInfo{};
112+
}
113+
114+
} // namespace quark::vk

src/backend/vulkan/presentation/swapchain.cpp

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
#include <algorithm>
2+
#include <array>
3+
#include <limits>
24
#include <quark/platform/window/IWindow.hpp>
35
#include <quark/vk/presentation/details/swapchain.hpp>
46
#include <stdexcept>
7+
#include <utility>
58
#include <vector>
69
#include <vulkan/vulkan_core.h>
710

@@ -93,13 +96,31 @@ VkExtent2D choose_extent(const platform::IWindow &window,
9396
return actual_extent;
9497
}
9598

96-
void throw_if_vk(VkResult result, const char *what) {
97-
if (result != VK_SUCCESS) {
98-
throw std::runtime_error(what);
99+
} // namespace
100+
101+
Swapchain::Swapchain(Swapchain &&other) noexcept
102+
: device_(std::exchange(other.device_, VK_NULL_HANDLE)),
103+
swapchain_(std::exchange(other.swapchain_, VK_NULL_HANDLE)),
104+
image_format_(std::exchange(other.image_format_, VK_FORMAT_UNDEFINED)),
105+
extent_(std::exchange(other.extent_, VkExtent2D{})),
106+
images_(std::move(other.images_)),
107+
image_views_(std::move(other.image_views_)) {}
108+
109+
Swapchain &Swapchain::operator=(Swapchain &&other) noexcept {
110+
if (this == &other) {
111+
return *this;
99112
}
100-
}
101113

102-
} // namespace
114+
reset();
115+
116+
device_ = std::exchange(other.device_, VK_NULL_HANDLE);
117+
swapchain_ = std::exchange(other.swapchain_, VK_NULL_HANDLE);
118+
image_format_ = std::exchange(other.image_format_, VK_FORMAT_UNDEFINED);
119+
extent_ = std::exchange(other.extent_, VkExtent2D{});
120+
images_ = std::move(other.images_);
121+
image_views_ = std::move(other.image_views_);
122+
return *this;
123+
}
103124

104125
void Swapchain::create(const CreateInfo &ci) {
105126
reset();

0 commit comments

Comments
 (0)