feat(renderer): swapchain creation
Some checks failed
continuous-integration/drone/push Build is failing
Some checks failed
continuous-integration/drone/push Build is failing
This commit is contained in:
parent
26dd49188b
commit
030556c733
4 changed files with 224 additions and 28 deletions
|
@ -7,8 +7,6 @@ target_link_libraries(renderer
|
||||||
PUBLIC
|
PUBLIC
|
||||||
app
|
app
|
||||||
ecs
|
ecs
|
||||||
vulkan
|
|
||||||
memory
|
|
||||||
surface
|
surface
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -47,7 +47,7 @@ struct RendererContext
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
[[nodiscard]] auto create_system() -> std::pair<RendererContext, SurfaceContext>
|
[[nodiscard]] auto create_system() -> std::pair<SurfaceContext, RendererContext>
|
||||||
{
|
{
|
||||||
auto surface_context = create_surface();
|
auto surface_context = create_surface();
|
||||||
auto &[surface_system, surface_entity] = surface_context;
|
auto &[surface_system, surface_entity] = surface_context;
|
||||||
|
@ -55,6 +55,7 @@ struct RendererContext
|
||||||
auto stats = create_ref<app::SystemStats>();
|
auto stats = create_ref<app::SystemStats>();
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
std::move(surface_context),
|
||||||
RendererContext {
|
RendererContext {
|
||||||
.registry = registry,
|
.registry = registry,
|
||||||
.system = System(
|
.system = System(
|
||||||
|
@ -65,8 +66,6 @@ struct RendererContext
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
|
|
||||||
std::move(surface_context),
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -76,7 +75,7 @@ Suite raii = [] {
|
||||||
};
|
};
|
||||||
|
|
||||||
Case { "happy path has no validation errors" } = [&] {
|
Case { "happy path has no validation errors" } = [&] {
|
||||||
auto [renderer, surface] = create_system();
|
auto [surface, renderer] = create_system();
|
||||||
expect_true(renderer.system.get_stats().empty_diagnosis());
|
expect_true(renderer.system.get_stats().empty_diagnosis());
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
#elif defined(__unix__)
|
#elif defined(__unix__)
|
||||||
#include <dlfcn.h>
|
#include <dlfcn.h>
|
||||||
namespace {
|
namespace {
|
||||||
void *library; // NOLINT
|
void *library = nullptr; // NOLINT
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
@ -84,6 +84,10 @@ PFN_vkCmdDraw vk_cmd_draw;
|
||||||
PFN_vkCmdSetViewport vk_cmd_set_viewport;
|
PFN_vkCmdSetViewport vk_cmd_set_viewport;
|
||||||
PFN_vkCmdSetScissor vk_cmd_set_scissors;
|
PFN_vkCmdSetScissor vk_cmd_set_scissors;
|
||||||
|
|
||||||
|
PFN_vkGetPhysicalDeviceSurfaceSupportKHR vk_get_physical_device_surface_support;
|
||||||
|
PFN_vkGetPhysicalDeviceSurfaceCapabilitiesKHR vk_get_physical_device_surface_capabilities;
|
||||||
|
PFN_vkGetPhysicalDeviceSurfaceFormatsKHR vk_get_physical_device_surface_formats;
|
||||||
|
|
||||||
PFN_vkCreateXlibSurfaceKHR vk_create_xlib_surface_khr;
|
PFN_vkCreateXlibSurfaceKHR vk_create_xlib_surface_khr;
|
||||||
PFN_vkDestroySurfaceKHR vk_destroy_surface_khr;
|
PFN_vkDestroySurfaceKHR vk_destroy_surface_khr;
|
||||||
// NOLINTEND(cppcoreguidelines-avoid-non-const-global-variables)
|
// NOLINTEND(cppcoreguidelines-avoid-non-const-global-variables)
|
||||||
|
@ -118,21 +122,43 @@ Context::Context(const ecs::Entity &surface_entity, Ref<app::SystemStats> system
|
||||||
initialize_logical_device();
|
initialize_logical_device();
|
||||||
load_device_functions();
|
load_device_functions();
|
||||||
|
|
||||||
|
initialize_surface(surface_entity);
|
||||||
initialize_queue();
|
initialize_queue();
|
||||||
initialize_surface(surface_entity);
|
initialize_swapchain();
|
||||||
}
|
}
|
||||||
|
|
||||||
Context::~Context()
|
Context::~Context()
|
||||||
{
|
{
|
||||||
vk_destroy_device(m_device, nullptr);
|
try
|
||||||
|
|
||||||
if (m_instance)
|
|
||||||
{
|
{
|
||||||
vk_destroy_surface_khr(m_instance, m_surface, nullptr);
|
log_trc("Fucking destructing shit at address: {}", (size_t)this);
|
||||||
vk_destroy_debug_messenger(m_instance, m_debug_messenger, nullptr);
|
|
||||||
}
|
|
||||||
|
|
||||||
vk_destroy_instance(m_instance, nullptr);
|
|
||||||
|
if (m_device)
|
||||||
|
{
|
||||||
|
vkc(vk_device_wait_idle(m_device));
|
||||||
|
vk_destroy_swapchain_khr(m_device, m_swapchain, nullptr);
|
||||||
|
vk_destroy_device(m_device, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (m_instance)
|
||||||
|
{
|
||||||
|
vk_destroy_surface_khr(m_instance, m_surface, nullptr);
|
||||||
|
vk_destroy_debug_messenger(m_instance, m_debug_messenger, nullptr);
|
||||||
|
vk_destroy_instance(m_instance, nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (library)
|
||||||
|
{
|
||||||
|
dlclose(library);
|
||||||
|
library = nullptr;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (const std::exception &exp)
|
||||||
|
{
|
||||||
|
log_err("Exception: {}", exp.what());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -150,7 +176,7 @@ void Context::initialize_instance()
|
||||||
auto extensions = std::vector<const char *> {
|
auto extensions = std::vector<const char *> {
|
||||||
VK_EXT_DEBUG_UTILS_EXTENSION_NAME,
|
VK_EXT_DEBUG_UTILS_EXTENSION_NAME,
|
||||||
VK_KHR_SURFACE_EXTENSION_NAME,
|
VK_KHR_SURFACE_EXTENSION_NAME,
|
||||||
"VK_KHR_xlib_surface",
|
VK_KHR_XLIB_SURFACE_EXTENSION_NAME,
|
||||||
};
|
};
|
||||||
auto layers = std::vector<const char *> {
|
auto layers = std::vector<const char *> {
|
||||||
"VK_LAYER_KHRONOS_validation",
|
"VK_LAYER_KHRONOS_validation",
|
||||||
|
@ -167,10 +193,10 @@ void Context::initialize_instance()
|
||||||
|
|
||||||
{
|
{
|
||||||
auto count = 0u;
|
auto count = 0u;
|
||||||
vk_enumerate_instance_extension_properties(nullptr, &count, nullptr);
|
vkc(vk_enumerate_instance_extension_properties(nullptr, &count, nullptr));
|
||||||
|
|
||||||
auto extensions = std::vector<VkExtensionProperties>(count);
|
auto extensions = std::vector<VkExtensionProperties>(count);
|
||||||
vk_enumerate_instance_extension_properties(nullptr, &count, extensions.data());
|
vkc(vk_enumerate_instance_extension_properties(nullptr, &count, extensions.data()));
|
||||||
|
|
||||||
// log_inf("Available vulkan instance extensions:");
|
// log_inf("Available vulkan instance extensions:");
|
||||||
for (auto &ext : extensions)
|
for (auto &ext : extensions)
|
||||||
|
@ -179,7 +205,7 @@ void Context::initialize_instance()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
vk_create_instance(&instance_info, nullptr, &m_instance);
|
vkc(vk_create_instance(&instance_info, nullptr, &m_instance));
|
||||||
|
|
||||||
ensure(m_instance, "Failed to create vulkan instance");
|
ensure(m_instance, "Failed to create vulkan instance");
|
||||||
}
|
}
|
||||||
|
@ -213,11 +239,11 @@ void Context::initialize_debug_messenger()
|
||||||
void Context::initialize_physical_device()
|
void Context::initialize_physical_device()
|
||||||
{
|
{
|
||||||
auto count = 0u;
|
auto count = 0u;
|
||||||
vk_enumerate_physical_devices(m_instance, &count, nullptr);
|
vkc(vk_enumerate_physical_devices(m_instance, &count, nullptr));
|
||||||
ensure(count != 0u, "Failed to find any physical devices with Vulkan support");
|
ensure(count != 0u, "Failed to find any physical devices with Vulkan support");
|
||||||
|
|
||||||
auto devices = std::vector<VkPhysicalDevice>(count);
|
auto devices = std::vector<VkPhysicalDevice>(count);
|
||||||
vk_enumerate_physical_devices(m_instance, &count, devices.data());
|
vkc(vk_enumerate_physical_devices(m_instance, &count, devices.data()));
|
||||||
|
|
||||||
for (auto &device : devices)
|
for (auto &device : devices)
|
||||||
{
|
{
|
||||||
|
@ -268,22 +294,156 @@ void Context::initialize_logical_device()
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Context::initialize_queue()
|
|
||||||
{
|
|
||||||
vk_get_device_queue(m_device, find_suitable_queue_family(), 0, &m_queue);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Context::initialize_surface(const ecs::Entity &surface_entity)
|
void Context::initialize_surface(const ecs::Entity &surface_entity)
|
||||||
{
|
{
|
||||||
const auto &component = surface_entity.get<surface::SurfaceComponent>();
|
const auto &component = surface_entity.get<surface::SurfaceComponent>();
|
||||||
|
|
||||||
auto xlib_surface_create_info = VkXlibSurfaceCreateInfoKHR {
|
auto create_info = VkXlibSurfaceCreateInfoKHR {
|
||||||
.sType = VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR,
|
.sType = VK_STRUCTURE_TYPE_XLIB_SURFACE_CREATE_INFO_KHR,
|
||||||
.dpy = component.get_native_data().display,
|
.dpy = component.get_native_data().display,
|
||||||
.window = component.get_native_data().window,
|
.window = component.get_native_data().window,
|
||||||
};
|
};
|
||||||
|
|
||||||
vk_create_xlib_surface_khr(m_instance, &xlib_surface_create_info, nullptr, &m_surface);
|
vkc(vk_create_xlib_surface_khr(m_instance, &create_info, nullptr, &m_surface));
|
||||||
|
|
||||||
|
const auto &[width, height] = component.get_resolution();
|
||||||
|
log_dbg("{} x {}", width, height);
|
||||||
|
m_framebuffer_size = {
|
||||||
|
.width = width,
|
||||||
|
.height = height,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
void Context::initialize_queue()
|
||||||
|
{
|
||||||
|
vk_get_device_queue(m_device, find_suitable_queue_family(), 0, &m_queue);
|
||||||
|
|
||||||
|
auto count = uint32_t { 0u };
|
||||||
|
vk_get_physical_device_queue_family_properties(m_physical_device, &count, nullptr);
|
||||||
|
|
||||||
|
auto properties = std::vector<VkQueueFamilyProperties>(count);
|
||||||
|
vk_get_physical_device_queue_family_properties(m_physical_device, &count, properties.data());
|
||||||
|
|
||||||
|
for (auto idx = uint32_t { 0u }; const auto &property : properties)
|
||||||
|
{
|
||||||
|
if (property.queueFlags & VK_QUEUE_GRAPHICS_BIT)
|
||||||
|
{
|
||||||
|
m_graphics_queue_family_index = idx;
|
||||||
|
}
|
||||||
|
|
||||||
|
auto has_presentation_support = VkBool32 { false };
|
||||||
|
vkc(vk_get_physical_device_surface_support(
|
||||||
|
m_physical_device,
|
||||||
|
idx,
|
||||||
|
m_surface,
|
||||||
|
&has_presentation_support
|
||||||
|
));
|
||||||
|
if (has_presentation_support)
|
||||||
|
{
|
||||||
|
m_present_queue_family_index = idx;
|
||||||
|
}
|
||||||
|
|
||||||
|
++idx;
|
||||||
|
|
||||||
|
if (m_graphics_queue_family_index != VK_QUEUE_FAMILY_IGNORED
|
||||||
|
&& m_present_queue_family_index != VK_QUEUE_FAMILY_IGNORED)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ensure(
|
||||||
|
m_graphics_queue_family_index != VK_QUEUE_FAMILY_IGNORED,
|
||||||
|
"Failed to find graphics queue family"
|
||||||
|
);
|
||||||
|
|
||||||
|
ensure(
|
||||||
|
m_present_queue_family_index != VK_QUEUE_FAMILY_IGNORED,
|
||||||
|
"Failed to find presentation queue family"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void Context::initialize_swapchain()
|
||||||
|
{
|
||||||
|
auto capabilities = VkSurfaceCapabilitiesKHR {};
|
||||||
|
vkc(vk_get_physical_device_surface_capabilities(m_physical_device, m_surface, &capabilities));
|
||||||
|
|
||||||
|
auto count = uint32_t { 0 };
|
||||||
|
vkc(vk_get_physical_device_surface_formats(m_physical_device, m_surface, &count, nullptr));
|
||||||
|
|
||||||
|
auto formats = std::vector<VkSurfaceFormatKHR>(count);
|
||||||
|
vkc(
|
||||||
|
vk_get_physical_device_surface_formats(m_physical_device, m_surface, &count, formats.data())
|
||||||
|
);
|
||||||
|
ensure(!formats.empty(), "Surface has no formats!");
|
||||||
|
|
||||||
|
// TODO(Light): parameterize
|
||||||
|
constexpr auto desired_swapchain_image_count = uint32_t { 3 };
|
||||||
|
const auto surface_format = formats.front();
|
||||||
|
|
||||||
|
const auto queue_indices = std::array<uint32_t, 2> {
|
||||||
|
m_graphics_queue_family_index,
|
||||||
|
m_present_queue_family_index,
|
||||||
|
};
|
||||||
|
|
||||||
|
auto create_info = VkSwapchainCreateInfoKHR {
|
||||||
|
.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR,
|
||||||
|
.surface = m_surface,
|
||||||
|
.minImageCount = get_optimal_swapchain_image_count(
|
||||||
|
capabilities,
|
||||||
|
desired_swapchain_image_count
|
||||||
|
),
|
||||||
|
.imageFormat = surface_format.format,
|
||||||
|
.imageColorSpace = surface_format.colorSpace,
|
||||||
|
.imageExtent = m_framebuffer_size,
|
||||||
|
.imageArrayLayers = 1u,
|
||||||
|
.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT,
|
||||||
|
.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE,
|
||||||
|
.queueFamilyIndexCount = queue_indices.size(),
|
||||||
|
.pQueueFamilyIndices = queue_indices.data(),
|
||||||
|
.preTransform = capabilities.currentTransform,
|
||||||
|
.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR,
|
||||||
|
.presentMode = VK_PRESENT_MODE_FIFO_RELAXED_KHR, // TODO(Light): parameterize
|
||||||
|
.clipped = VK_TRUE,
|
||||||
|
.oldSwapchain = nullptr,
|
||||||
|
};
|
||||||
|
|
||||||
|
vkc(vk_create_swapchain_khr(m_device, &create_info, nullptr, &m_swapchain));
|
||||||
|
vkc(vk_device_wait_idle(m_device));
|
||||||
|
|
||||||
|
// auto image_count = uint32_t { 0u };
|
||||||
|
// vk_get_swapchain_images_khr(m_device, m_swapchain, &image_count, nullptr);
|
||||||
|
|
||||||
|
// m_swapchain_images.resize(image_count);
|
||||||
|
// vk_get_swapchain_images_khr(m_device, m_swapchain, &image_count, m_swapchain_images.data());
|
||||||
|
}
|
||||||
|
|
||||||
|
auto Context::get_optimal_swapchain_image_count(
|
||||||
|
VkSurfaceCapabilitiesKHR capabilities,
|
||||||
|
uint32_t desired_image_count
|
||||||
|
) -> uint32_t
|
||||||
|
{
|
||||||
|
const auto min_image_count = capabilities.minImageCount;
|
||||||
|
const auto max_image_count = capabilities.maxImageCount;
|
||||||
|
|
||||||
|
const auto has_max_limit = max_image_count != 0;
|
||||||
|
|
||||||
|
// Desired image count is in range
|
||||||
|
if ((!has_max_limit || max_image_count >= desired_image_count)
|
||||||
|
&& min_image_count <= desired_image_count)
|
||||||
|
{
|
||||||
|
return desired_image_count;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fall-back to 2 if in ange
|
||||||
|
if (min_image_count <= 2 && max_image_count >= 2)
|
||||||
|
{
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Fall-back to min_image_count
|
||||||
|
return min_image_count;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Context::load_library()
|
void Context::load_library()
|
||||||
|
@ -346,6 +506,12 @@ void Context::load_instance_functions()
|
||||||
load_fn(vk_set_debug_object_tag, "vkSetDebugUtilsObjectTagEXT");
|
load_fn(vk_set_debug_object_tag, "vkSetDebugUtilsObjectTagEXT");
|
||||||
load_fn(vk_submit_debug_message, "vkSubmitDebugUtilsMessageEXT");
|
load_fn(vk_submit_debug_message, "vkSubmitDebugUtilsMessageEXT");
|
||||||
|
|
||||||
|
load_fn(vk_get_physical_device_surface_support, "vkGetPhysicalDeviceSurfaceSupportKHR");
|
||||||
|
load_fn(
|
||||||
|
vk_get_physical_device_surface_capabilities,
|
||||||
|
"vkGetPhysicalDeviceSurfaceCapabilitiesKHR"
|
||||||
|
);
|
||||||
|
load_fn(vk_get_physical_device_surface_formats, "vkGetPhysicalDeviceSurfaceFormatsKHR");
|
||||||
load_fn(vk_create_xlib_surface_khr, "vkCreateXlibSurfaceKHR");
|
load_fn(vk_create_xlib_surface_khr, "vkCreateXlibSurfaceKHR");
|
||||||
load_fn(vk_destroy_surface_khr, "vkDestroySurfaceKHR");
|
load_fn(vk_destroy_surface_khr, "vkDestroySurfaceKHR");
|
||||||
}
|
}
|
||||||
|
@ -469,6 +635,9 @@ auto validation_layers_callback(
|
||||||
void *const vulkan_user_data
|
void *const vulkan_user_data
|
||||||
) -> VkBool32
|
) -> VkBool32
|
||||||
{
|
{
|
||||||
|
log_dbg("VALIDATION: {}", callback_data->pMessage);
|
||||||
|
return VK_FALSE;
|
||||||
|
|
||||||
auto stats = *(Ref<app::SystemStats> *)vulkan_user_data; // NOLINT
|
auto stats = *(Ref<app::SystemStats> *)vulkan_user_data; // NOLINT
|
||||||
|
|
||||||
const auto &type = parse_message_type(message_types);
|
const auto &type = parse_message_type(message_types);
|
||||||
|
|
|
@ -88,10 +88,25 @@ extern PFN_vkCmdDraw vk_cmd_draw;
|
||||||
extern PFN_vkCmdSetViewport vk_cmd_set_viewport;
|
extern PFN_vkCmdSetViewport vk_cmd_set_viewport;
|
||||||
extern PFN_vkCmdSetScissor vk_cmd_set_scissors;
|
extern PFN_vkCmdSetScissor vk_cmd_set_scissors;
|
||||||
|
|
||||||
|
// Surface
|
||||||
|
extern PFN_vkGetPhysicalDeviceSurfaceSupportKHR vk_get_physical_device_surface_support;
|
||||||
|
extern PFN_vkGetPhysicalDeviceSurfaceCapabilitiesKHR vk_get_physical_device_surface_capabilities;
|
||||||
|
extern PFN_vkGetPhysicalDeviceSurfaceFormatsKHR vk_get_physical_device_surface_formats;
|
||||||
|
|
||||||
extern PFN_vkCreateXlibSurfaceKHR vk_create_xlib_surface_khr;
|
extern PFN_vkCreateXlibSurfaceKHR vk_create_xlib_surface_khr;
|
||||||
extern PFN_vkDestroySurfaceKHR vk_destroy_surface_khr;
|
extern PFN_vkDestroySurfaceKHR vk_destroy_surface_khr;
|
||||||
// NOLINTEND(cppcoreguidelines-avoid-non-const-global-variables)
|
// NOLINTEND(cppcoreguidelines-avoid-non-const-global-variables)
|
||||||
|
|
||||||
|
inline void vkc(VkResult result)
|
||||||
|
{
|
||||||
|
if (result)
|
||||||
|
{
|
||||||
|
throw std::runtime_error {
|
||||||
|
std::format("Vulkan call failed with result: {}", std::to_underlying(result))
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class Context
|
class Context
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
@ -150,6 +165,8 @@ private:
|
||||||
|
|
||||||
void initialize_surface(const ecs::Entity &surface_entity);
|
void initialize_surface(const ecs::Entity &surface_entity);
|
||||||
|
|
||||||
|
void initialize_swapchain();
|
||||||
|
|
||||||
void load_library();
|
void load_library();
|
||||||
|
|
||||||
void load_global_functions();
|
void load_global_functions();
|
||||||
|
@ -158,6 +175,11 @@ private:
|
||||||
|
|
||||||
void load_device_functions();
|
void load_device_functions();
|
||||||
|
|
||||||
|
auto get_optimal_swapchain_image_count(
|
||||||
|
VkSurfaceCapabilitiesKHR capabilities,
|
||||||
|
uint32_t desired_image_count = 3
|
||||||
|
) -> uint32_t;
|
||||||
|
|
||||||
[[nodiscard]] auto find_suitable_queue_family() const -> uint32_t;
|
[[nodiscard]] auto find_suitable_queue_family() const -> uint32_t;
|
||||||
|
|
||||||
Ref<ecs::Registry> m_registry;
|
Ref<ecs::Registry> m_registry;
|
||||||
|
@ -174,8 +196,16 @@ private:
|
||||||
|
|
||||||
NullOnMove<VkSurfaceKHR> m_surface = VK_NULL_HANDLE;
|
NullOnMove<VkSurfaceKHR> m_surface = VK_NULL_HANDLE;
|
||||||
|
|
||||||
|
VkExtent2D m_framebuffer_size {};
|
||||||
|
|
||||||
|
uint32_t m_graphics_queue_family_index = VK_QUEUE_FAMILY_IGNORED;
|
||||||
|
|
||||||
|
uint32_t m_present_queue_family_index = VK_QUEUE_FAMILY_IGNORED;
|
||||||
|
|
||||||
NullOnMove<VkSwapchainKHR> m_swapchain = VK_NULL_HANDLE;
|
NullOnMove<VkSwapchainKHR> m_swapchain = VK_NULL_HANDLE;
|
||||||
|
|
||||||
|
std::vector<VkImage> m_swapchain_images;
|
||||||
|
|
||||||
Ref<app::SystemStats> m_stats;
|
Ref<app::SystemStats> m_stats;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue