Weird MacOS MoltenVK EXC_BAD_ACCESS on destruction of vulkan objects.

This commit is contained in:
Neo 2025-02-24 14:19:03 -05:00
parent d0e0718b1e
commit 14f0c05b68
15 changed files with 444 additions and 36 deletions

View File

@ -1,4 +1,4 @@
cmake_minimum_required(VERSION 3.31) cmake_minimum_required(VERSION 3.30.5)
project(Dyn) project(Dyn)
set(CMAKE_CXX_STANDARD 26) set(CMAKE_CXX_STANDARD 26)
@ -8,6 +8,7 @@ include_directories("${CMAKE_CURRENT_SOURCE_DIR}/inc")
find_package(Vulkan REQUIRED) find_package(Vulkan REQUIRED)
find_package(glfw3 CONFIG REQUIRED) find_package(glfw3 CONFIG REQUIRED)
find_package(VulkanUtilityLibraries CONFIG REQUIRED)
foreach (file ${sourceFiles}) foreach (file ${sourceFiles})
message("Source File Found ${file}") message("Source File Found ${file}")
@ -16,4 +17,5 @@ endforeach ()
add_executable(Dyn ${sourceFiles}) add_executable(Dyn ${sourceFiles})
target_link_libraries(Dyn PRIVATE Vulkan::Vulkan) target_link_libraries(Dyn PRIVATE Vulkan::Vulkan)
target_link_libraries(Dyn PRIVATE glfw) target_link_libraries(Dyn PRIVATE glfw)
target_link_libraries(Dyn PRIVATE Vulkan::SafeStruct Vulkan::LayerSettings Vulkan::UtilityHeaders Vulkan::CompilerConfiguration)

View File

@ -15,6 +15,11 @@ namespace dyn {
return std::dynamic_pointer_cast<To>(from); return std::dynamic_pointer_cast<To>(from);
} }
template <typename T, typename ...Args>
ref<T> create_ref(Args&&... args) {
return std::make_shared<T>(std::forward<Args>(args)...);
}
namespace core { namespace core {
enum class RenderPlatform { enum class RenderPlatform {
None, None,

View File

@ -10,7 +10,11 @@
#define DYN_DEBUG #define DYN_DEBUG
#endif #endif
#define DYN_WARNING_LOG(...) ::dyn::core::warnln(__VA_ARGS__)
#define DYN_FATAL_LOG(...) ::dyn::core::fatalln(__VA_ARGS__) #define DYN_FATAL_LOG(...) ::dyn::core::fatalln(__VA_ARGS__)
#define DYN_DEBUG_LOG(...) ::dyn::core::debugln(__VA_ARGS__)
#define DYN_ERROR_LOG(...) ::dyn::core::errorln(__VA_ARGS__)
#define DYN_INFO_LOG(...) ::dyn::core::logln(__VA_ARGS__)
#ifdef _WIN_VER #ifdef _WIN_VER
#define DYN_BREAKPOINT __debugbreak() #define DYN_BREAKPOINT __debugbreak()
@ -29,8 +33,6 @@
#ifdef DYN_DEBUG #ifdef DYN_DEBUG
#define DYN_ENABLE_ASSERTIONS #define DYN_ENABLE_ASSERTIONS
#define DYN_DEBUG_LOG(...) ::dyn::core::debugln(__VA_ARGS__)
#else #else
#define DYN_DEBUG_LOG(...) #define DYN_DEBUG_LOG(...)
@ -39,7 +41,7 @@
#ifdef DYN_ENABLE_ASSERTIONS #ifdef DYN_ENABLE_ASSERTIONS
#define DYN_ASSERT(cond, ...) if (!cond) { DYN_FATAL_LOG("Assert failed", ##__VA_ARGS__); DYN_BREAKPOINT; } #define DYN_ASSERT(cond, ...) if (!(cond)) { DYN_FATAL_LOG("Assert failed", __VA_ARGS__); DYN_BREAKPOINT; }
#else #else

View File

@ -23,6 +23,16 @@ namespace dyn::core {
print("fatal", msg, std::forward<Args>(args)...); print("fatal", msg, std::forward<Args>(args)...);
} }
template <typename... Args>
void warn(const std::string& msg, Args&&... args) {
print("warn", msg, std::forward<Args>(args)...);
}
template <typename... Args>
void error(const std::string& msg, Args&&... args) {
print("error", msg, std::forward<Args>(args)...);
}
template <typename... Args> template <typename... Args>
void debug(const std::string& msg, Args&&... args) { void debug(const std::string& msg, Args&&... args) {
print("debug", msg, std::forward<Args>(args)...); print("debug", msg, std::forward<Args>(args)...);
@ -35,7 +45,7 @@ namespace dyn::core {
void print(const std::string& type, const std::string& msg, Args&&... args) { void print(const std::string& type, const std::string& msg, Args&&... args) {
std::array<char, sizeof...(Args) * 3 + 4> buffer{}; std::array<char, sizeof...(Args) * 3 + 4> buffer{};
constexpr char c[4] = "{} "; constexpr char c[4] = "{} ";
for (auto i = 0; i != buffer.size() - 1; ++i) { for (size_t i = 0; i != buffer.size() - 1; ++i) {
buffer[i] = c[i % 3]; buffer[i] = c[i % 3];
} }
buffer.back() = '\0'; buffer.back() = '\0';
@ -62,4 +72,14 @@ namespace dyn::core {
void debugln(const std::string& msg, Args&&... args) { void debugln(const std::string& msg, Args&&... args) {
logger::def().debug(msg, std::forward<Args>(args)...); logger::def().debug(msg, std::forward<Args>(args)...);
} }
template <typename... Args>
void warnln(const std::string& msg, Args&&... args) {
logger::def().warn(msg, std::forward<Args>(args)...);
}
template <typename... Args>
void errorln(const std::string& msg, Args&&... args) {
logger::def().error(msg, std::forward<Args>(args)...);
}
} }

View File

@ -8,7 +8,11 @@
#define GLFW_INCLUDE_VULKAN #define GLFW_INCLUDE_VULKAN
#include <glfw/glfw3.h> #include <glfw/glfw3.h>
#include "base.h"
namespace dyn::core { namespace dyn::core {
class vulkan_context;
struct window_spec { struct window_spec {
uint32_t width; uint32_t width;
uint32_t height; uint32_t height;
@ -18,11 +22,25 @@ namespace dyn::core {
class window { class window {
public: public:
window(const window_spec& spec); explicit window(window_spec spec);
~window(); ~window();
static std::vector<const char*> get_required_extensions(); [[nodiscard]] static std::vector<const char*> get_required_extensions();
static ref<window> create(const window_spec& spec);
protected:
static void set_window_hints();
[[nodiscard]] GLFWwindow* get_window_handle() { return _window; }
private: private:
window_spec spec; VkSurfaceKHR _surface = VK_NULL_HANDLE;
window_spec _spec;
GLFWwindow* _window;
friend class vulkan_context;
}; };
} }

View File

@ -5,8 +5,41 @@
#pragma once #pragma once
#include <vulkan/vulkan.h> #include <vulkan/vulkan.h>
#include <dyn/core/assertion.h> #include <dyn/core/debug.h>
#include <vulkan/vk_enum_string_helper.h>
namespace dyn::utils {
VkResult vkCreateDebugUtilsMessengerEXT(VkInstance instance,
const VkDebugUtilsMessengerCreateInfoEXT* pCreateInfo,
const VkAllocationCallbacks* pAllocator,
VkDebugUtilsMessengerEXT* pMessenger);
void vkDestroyDebugUtilsMessengerEXT(VkInstance instance,
VkDebugUtilsMessengerEXT messenger,
const VkAllocationCallbacks* pAllocator);
void populate_debug_messenger_create_info(VkDebugUtilsMessengerCreateInfoEXT* pCreateInfo);
}
namespace dyn::utils {
extern const std::vector<const char*> vk_validation_layers;
VkBool32 vk_debug_message_callback(
VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity,
VkDebugUtilsMessageTypeFlagsEXT messageTypes,
const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData,
void* pUserData);
[[nodiscard]] bool check_validation_support();
#ifdef DYN_DEBUG
constexpr bool vk_enable_validation_layers = true;
#else
constexpr bool vk_enable_validation_layers = false;
#endif
}
#define dyn_vk_verify(f) { \ #define dyn_vk_verify(f) { \
VkResult res = f; \ VkResult res = f; \
DYN_ASSERT(res == VK_SUCCESS, __FILE__, "at line", __LINE__); } DYN_ASSERT(res == VK_SUCCESS, "Vulkan error occured", string_VkResult(res), "at line", __LINE__, "in file", __FILE__); }

View File

@ -5,13 +5,16 @@
#pragma once #pragma once
#include <vulkan/vulkan.h> #include <vulkan/vulkan.h>
#include <dyn/core/base.h>
#include <dyn/renderer/render_context.h> #include <dyn/renderer/render_context.h>
#include "vulkan_devices.h"
namespace dyn::core { namespace dyn::core {
class vulkan_context : public render_context { class vulkan_context : public render_context {
public: public:
vulkan_context(); explicit vulkan_context(const ref<window>& window);
~vulkan_context() override; ~vulkan_context() override;
void init() override; void init() override;
@ -22,12 +25,22 @@ namespace dyn::core {
void cmd_end() override; void cmd_end() override;
VkInstance get_instance(); [[nodiscard]] ref<window> get_window() override { return _window; }
static bool check_api_version_support(uint32_t api_version); [[nodiscard]] VkInstance get_instance() const { return _instance; }
VkSurfaceKHR& get_surface();
[[nodiscard]] static bool check_api_version_support(uint32_t api_version);
private: private:
VkInstance _instance = nullptr; VkInstance _instance = nullptr;
ref<vulkan_physical_device> _physical_device;
VkDebugUtilsMessengerEXT _debug_messenger = nullptr;
VkSurfaceKHR _surface = nullptr;
ref<window> _window;
}; };
} }

View File

@ -0,0 +1,41 @@
//
// Created by Neo on 2/24/25.
//
#pragma once
#include <vulkan/vulkan.h>
#include <dyn/core/debug.h>
#include <dyn/core/base.h>
namespace dyn::core {
class vulkan_context;
class vulkan_physical_device {
public:
explicit vulkan_physical_device(const ref<vulkan_context>& ctx);
~vulkan_physical_device();
struct queue_families {
std::optional<uint32_t> graphics_family;
std::optional<uint32_t> present_family;
[[nodiscard]] bool is_complete() const;
static queue_families find(const VkPhysicalDevice& physical_device, const VkSurfaceKHR& surface);
};
[[nodiscard]] VkPhysicalDevice get_physical_device() const { return _physical_device; }
[[nodiscard]] queue_families get_queue_families() const { return _queue_families; }
static VkPhysicalDevice determine_suitable_device(const std::vector<VkPhysicalDevice>& available_devices, const VkSurfaceKHR& surface);
static uint32_t score_device(const VkPhysicalDevice& device);
static ref<vulkan_physical_device> create(const ref<vulkan_context>& ctx);
private:
VkPhysicalDevice _physical_device = nullptr;
queue_families _queue_families;
ref<vulkan_context> _ctx;
};
}

View File

@ -10,10 +10,12 @@
#include <dyn/core/base.h> #include <dyn/core/base.h>
#include <dyn/renderer/mesh.h> #include <dyn/renderer/mesh.h>
#include "dyn/core/window.h"
namespace dyn::core { namespace dyn::core {
class render_context; class render_context;
using render_command = std::function<void(ref<render_context>)>; using render_command = std::function<void()>;
using render_command_queue = command_queue<render_command>; using render_command_queue = command_queue<render_command>;
class render_context { class render_context {
@ -35,10 +37,12 @@ namespace dyn::core {
// end the frame, render to the screen // end the frame, render to the screen
virtual void cmd_end() = 0; virtual void cmd_end() = 0;
virtual ref<window> get_window() = 0;
static void set_api(RenderPlatform platform); static void set_api(RenderPlatform platform);
static RenderPlatform get_api(); static RenderPlatform get_api();
static ref<render_context> create(); static ref<render_context> create(const ref<window> &window);
protected: protected:
render_command_queue _render_command_queue; render_command_queue _render_command_queue;

View File

@ -3,16 +3,29 @@
// //
#include <cstdint> #include <cstdint>
#include <utility>
#include <dyn/core/window.h> #include <dyn/core/window.h>
#include "dyn/core/assertion.h" #include "dyn/core/debug.h"
#include "dyn/renderer/render_context.h"
namespace dyn::core { namespace dyn::core {
window::window(const window_spec &spec) { window::window(window_spec spec): _spec(std::move(spec)) {
DYN_ASSERT(glfwInit(), "GLFW failed to initialize"); DYN_ASSERT(glfwInit(), "GLFW failed to initialize");
set_window_hints();
_window = glfwCreateWindow(
static_cast<int>(spec.width),
static_cast<int>(spec.height),
spec.title.c_str(),
nullptr, nullptr);
DYN_ASSERT(_window != nullptr, "Failed to create GLFW window");
} }
window::~window() { window::~window() {
glfwDestroyWindow(_window);
glfwTerminate();
} }
std::vector<const char *> window::get_required_extensions() { std::vector<const char *> window::get_required_extensions() {
@ -23,8 +36,21 @@ namespace dyn::core {
std::vector<const char *> result; std::vector<const char *> result;
result.reserve(extensions_count); result.reserve(extensions_count);
result.insert(result.begin(), extensions, extensions + extensions_count); result.insert(result.begin(), extensions, extensions + extensions_count);
// support MoltenVK (https://stackoverflow.com/questions/58732459/vk-error-incompatible-driver-with-mac-os-and-vulkan-moltenvk)
result.push_back("VK_KHR_portability_enumeration");
return result; return result;
} }
void window::set_window_hints() {
auto render_api = render_context::get_api();
if (render_api == RenderPlatform::Vulkan) {
glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
}
glfwWindowHint(GLFW_RESIZABLE, GLFW_TRUE);
}
ref<window> window::create(const window_spec &spec) {
return create_ref<window>(spec);
}
} }

View File

@ -4,29 +4,30 @@
#include <dyn/core/logging.h> #include <dyn/core/logging.h>
#include <dyn/core/command_queue.h> #include <dyn/core/command_queue.h>
#include "../inc/dyn/core/assertion.h" #include "../inc/dyn/core/debug.h"
#include "dyn/core/window.h" #include "dyn/core/window.h"
#include "dyn/platform/vulkan/vulkan.h"
#include "dyn/platform/vulkan/vulkan_context.h"
#include "dyn/renderer/render_context.h" #include "dyn/renderer/render_context.h"
using namespace dyn; using namespace dyn;
using namespace dyn::core; using namespace dyn::core;
int main() { int main() {
// render_context::set_api(RenderPlatform::Vulkan); render_context::set_api(RenderPlatform::Vulkan);
// ref<render_context> ctx = render_context::create(); // ref<render_context> ctx = render_context::create();
// //
// ctx->init(); // ctx->init();
window win = window({ ref<window> win = window::create({
.width = 1280, .width = 1280,
.height = 720, .height = 720,
.title = "Dyn Engine", .title = "Dyn Engine",
.fullscreen = false .fullscreen = false
}); });
auto extensions = window::get_required_extensions(); ref<render_context> ctx = render_context::create(win);
DYN_DEBUG_LOG("Extensions Found:", extensions.size()); ctx->init();
for (auto extension : extensions) { auto vkctx = ref_as<vulkan_context>(ctx);
DYN_DEBUG_LOG("Extension Found:", extension);
}
} }

View File

@ -0,0 +1,80 @@
//
// Created by Neo on 2/24/25.
//
#include <dyn/platform/vulkan/vulkan.h>
namespace dyn::utils {
const std::vector<const char*> vk_validation_layers = {
"VK_LAYER_KHRONOS_validation"
};
VkResult vkCreateDebugUtilsMessengerEXT(VkInstance instance, const VkDebugUtilsMessengerCreateInfoEXT *pCreateInfo, const VkAllocationCallbacks *pAllocator, VkDebugUtilsMessengerEXT *pMessenger) {
auto func = (PFN_vkCreateDebugUtilsMessengerEXT) vkGetInstanceProcAddr(instance, "vkCreateDebugUtilsMessengerEXT");
if (func != nullptr) {
return func(instance, pCreateInfo, pAllocator, pMessenger);
}
return VK_ERROR_EXTENSION_NOT_PRESENT;
}
void vkDestroyDebugUtilsMessengerEXT(VkInstance instance, VkDebugUtilsMessengerEXT messenger, const VkAllocationCallbacks *pAllocator) {
auto func = (PFN_vkDestroyDebugUtilsMessengerEXT) vkGetInstanceProcAddr(instance, "vkDestroyDebugUtilsMessengerEXT");
if (func != nullptr) {
func(instance, messenger, pAllocator);
}
}
void populate_debug_messenger_create_info(VkDebugUtilsMessengerCreateInfoEXT *create_info) {
create_info->sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT;
create_info->messageSeverity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT;
create_info->messageType = VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT;
create_info->pUserData = nullptr;
create_info->pfnUserCallback = vk_debug_message_callback;
}
VkBool32 vk_debug_message_callback(VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity,
VkDebugUtilsMessageTypeFlagsEXT messageType, const VkDebugUtilsMessengerCallbackDataEXT *pCallbackData,
void *pUserData) {
switch (messageSeverity) {
case VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT:
DYN_DEBUG_LOG("[VkValidation Layer Verbose]", pCallbackData->pMessage);
break;
case VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT:
DYN_WARNING_LOG("[VkValidation Layer Warn]", pCallbackData->pMessage);
break;
case VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT:
DYN_ERROR_LOG("[VkValidation Layer Error]", pCallbackData->pMessage);
break;
case VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT:
DYN_INFO_LOG("[VkValidation Layer Info]", pCallbackData->pMessage);
break;
default:
DYN_WARNING_LOG("[VkValidation Layer Uncaptured]", pCallbackData->pMessage);
break;
}
return VK_FALSE;
}
bool check_validation_support() {
uint32_t layer_count = 0;
vkEnumerateInstanceLayerProperties(&layer_count, nullptr);
std::vector<VkLayerProperties> available_layers(layer_count);
vkEnumerateInstanceLayerProperties(&layer_count, available_layers.data());
for (const auto &layer : vk_validation_layers) {
bool layer_found = false;
for (const auto& property : available_layers) {
if (strcmp(layer, property.layerName) == 0) {
layer_found = true;
break;
}
}
if (!layer_found) {
return false;
}
}
return true;
}
}

View File

@ -9,9 +9,15 @@
namespace dyn::core { namespace dyn::core {
vulkan_context::vulkan_context() {} vulkan_context::vulkan_context(const ref<window>& window) : _window(window) {}
vulkan_context::~vulkan_context() { vulkan_context::~vulkan_context() {
if (utils::vk_enable_validation_layers) {
utils::vkDestroyDebugUtilsMessengerEXT(_instance, _debug_messenger, nullptr);
}
vkDestroySurfaceKHR(_instance, _surface, nullptr);
vkDestroyInstance(_instance, nullptr); vkDestroyInstance(_instance, nullptr);
} }
@ -19,6 +25,9 @@ namespace dyn::core {
if (!check_api_version_support(VK_API_VERSION_1_2)) { if (!check_api_version_support(VK_API_VERSION_1_2)) {
} }
auto self = ref<vulkan_context>(this);
// create vulkan instance // create vulkan instance
{ {
DYN_DEBUG_LOG("Creating Vulkan context"); DYN_DEBUG_LOG("Creating Vulkan context");
@ -33,20 +42,58 @@ namespace dyn::core {
VkInstanceCreateInfo create_info{}; VkInstanceCreateInfo create_info{};
create_info.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; create_info.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
create_info.pApplicationInfo = &app_info; create_info.pApplicationInfo = &app_info;
// support MoltenVK (https://stackoverflow.com/questions/58732459/vk-error-incompatible-driver-with-mac-os-and-vulkan-moltenvk)
create_info.flags = VK_INSTANCE_CREATE_ENUMERATE_PORTABILITY_BIT_KHR;
// extensions // extensions
{ auto extensions = window::get_required_extensions();
auto extensions = window::get_required_extensions(); for (const auto& ext : extensions) {
create_info.enabledExtensionCount = static_cast<uint32_t>(extensions.size()); DYN_DEBUG_LOG(ext);
create_info.ppEnabledExtensionNames = extensions.data();
} }
DYN_DEBUG_LOG("Extensions Found: ", extensions.size());
if (utils::vk_enable_validation_layers) {
extensions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
}
create_info.enabledExtensionCount = static_cast<uint32_t>(extensions.size());
create_info.ppEnabledExtensionNames = extensions.data();
create_info.enabledLayerCount = 0; create_info.enabledLayerCount = 0;
create_info.ppEnabledLayerNames = nullptr; create_info.ppEnabledLayerNames = nullptr;
if (utils::vk_enable_validation_layers) {
create_info.enabledLayerCount = static_cast<uint32_t>(utils::vk_validation_layers.size());
create_info.ppEnabledLayerNames = utils::vk_validation_layers.data();
VkDebugUtilsMessengerCreateInfoEXT debug_utils_create_info{};
utils::populate_debug_messenger_create_info(&debug_utils_create_info);
create_info.pNext = &debug_utils_create_info;
}
dyn_vk_verify(vkCreateInstance(&create_info, nullptr, &_instance)); dyn_vk_verify(vkCreateInstance(&create_info, nullptr, &_instance));
} }
// debug messenger
{
if (utils::vk_enable_validation_layers && utils::check_validation_support()) {
DYN_DEBUG_LOG("Creating debug messenger");
VkDebugUtilsMessengerCreateInfoEXT create_info{};
utils::populate_debug_messenger_create_info(&create_info);
dyn_vk_verify(utils::vkCreateDebugUtilsMessengerEXT(_instance, &create_info, nullptr, &_debug_messenger));
}
}
// surface
{
glfwCreateWindowSurface(_instance, _window->get_window_handle(), nullptr, &_surface);
}
// physical device
{
_physical_device = vulkan_physical_device::create(self);
DYN_DEBUG_LOG("Physical Device Created");
}
} }
void vulkan_context::cmd_begin() { void vulkan_context::cmd_begin() {
@ -58,8 +105,8 @@ namespace dyn::core {
void vulkan_context::cmd_end() { void vulkan_context::cmd_end() {
} }
VkInstance vulkan_context::get_instance() { VkSurfaceKHR& vulkan_context::get_surface() {
return _instance; return _surface;
} }
bool vulkan_context::check_api_version_support(uint32_t requested_version) { bool vulkan_context::check_api_version_support(uint32_t requested_version) {

View File

@ -0,0 +1,116 @@
//
// Created by Neo on 2/24/25.
//
#include <utility>
#include "dyn/platform/vulkan/vulkan_context.h"
#include "dyn/platform/vulkan/vulkan_devices.h"
namespace dyn::core {
vulkan_physical_device::vulkan_physical_device(const ref<vulkan_context>& ctx) :
_ctx(ctx) {
auto instance = ref_as<vulkan_context>(_ctx)->get_instance();
uint32_t device_count = 0;
vkEnumeratePhysicalDevices(instance, &device_count, nullptr);
DYN_ASSERT(device_count > 0, "No devices found.");
std::vector<VkPhysicalDevice> devices(device_count);
vkEnumeratePhysicalDevices(instance, &device_count, devices.data());
_physical_device = determine_suitable_device(devices, ctx->get_surface());
_queue_families = queue_families::find(_physical_device, ctx->get_surface());
}
vulkan_physical_device::~vulkan_physical_device() = default;
bool vulkan_physical_device::queue_families::is_complete() const {
return this->graphics_family.has_value() && this->present_family.has_value();
}
vulkan_physical_device::queue_families vulkan_physical_device::queue_families::find(
const VkPhysicalDevice &physical_device,
const VkSurfaceKHR &surface) {
uint32_t queue_family_count = 0;
vkGetPhysicalDeviceQueueFamilyProperties(physical_device, &queue_family_count, nullptr);
DYN_ASSERT(queue_family_count > 0, "No queue families found.");
std::vector<VkQueueFamilyProperties> found_families(queue_family_count);
vkGetPhysicalDeviceQueueFamilyProperties(physical_device, &queue_family_count, found_families.data());
queue_families families{};
uint32_t idx = 0;
for (const auto &queue_family : found_families) {
if (queue_family.queueFlags & VK_QUEUE_GRAPHICS_BIT) {
families.graphics_family = idx;
}
VkBool32 present_support = false;
vkGetPhysicalDeviceSurfaceSupportKHR(physical_device, idx, surface, &present_support);
if (present_support) {
families.present_family = idx;
}
if (families.is_complete()) {
break;
}
idx++;
}
return families;
}
VkPhysicalDevice vulkan_physical_device::determine_suitable_device(
const std::vector<VkPhysicalDevice> &available_devices,
const VkSurfaceKHR &surface) {
uint32_t prev_score = 0;
VkPhysicalDevice current_device = VK_NULL_HANDLE;
for (const auto& device : available_devices) {
if (queue_families::find(device, surface).is_complete()) {
auto score = score_device(device);
if (prev_score < score) {
prev_score = score;
current_device = device;
}
}
}
return current_device;
}
uint32_t vulkan_physical_device::score_device(const VkPhysicalDevice &device) {
uint32_t score = 0;
VkPhysicalDeviceProperties properties;
VkPhysicalDeviceFeatures features;
vkGetPhysicalDeviceProperties(device, &properties);
vkGetPhysicalDeviceFeatures(device, &features);
if (properties.deviceType == VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU) {
score += 1000;
} else if (properties.deviceType == VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU) {
score += 500;
}
score += properties.limits.maxImageDimension2D;
if (properties.apiVersion >= VK_API_VERSION_1_2) {
score += 80;
}
if (features.geometryShader == VK_TRUE) {
score = 100;
}
DYN_DEBUG_LOG("Device Name:", std::string_view{properties.deviceName});
DYN_DEBUG_LOG("Device Score:", score);
return score;
}
ref<vulkan_physical_device> vulkan_physical_device::create(const ref<vulkan_context>& ctx) {
return create_ref<vulkan_physical_device>(ctx);
}
}

View File

@ -20,9 +20,9 @@ namespace dyn::core {
return s_api; return s_api;
} }
ref<render_context> render_context::create() { ref<render_context> render_context::create(const ref<window>& window) {
DYN_PLATFORM_CREATE_BEGIN(get_api()) DYN_PLATFORM_CREATE_BEGIN(get_api())
DYN_PLATFORM_CREATE_VK(vulkan_context); DYN_PLATFORM_CREATE_VK(vulkan_context, window);
DYN_PLATFORM_CREATE_END(render_context) DYN_PLATFORM_CREATE_END(render_context)
} }
} }