diff --git a/aster_core/aster.cpp b/aster_core/aster.cpp index 5574ad8..1f293a2 100644 --- a/aster_core/aster.cpp +++ b/aster_core/aster.cpp @@ -1,13 +1,36 @@ #include "constants.h" #include "glfw_context.h" +#include "physical_device.h" #include "window.h" -#include + +bool suitable_device(const PhysicalDevice *physical_device) { + constexpr auto required_support = QueueSupportFlags{} | QueueSupportFlagBits::eGraphics | QueueSupportFlagBits::ePresent | QueueSupportFlagBits::eTransfer; + + return physical_device->properties.deviceType != vk::PhysicalDeviceType::eCpu && + !physical_device->surface_formats.empty() && + !physical_device->present_modes.empty() && + cast(physical_device->queue_support & required_support); +} + +PhysicalDevice find_suitable_device(PhysicalDevices &&_physical_devices) { + for (auto physdev : _physical_devices) { + VERBOSE(fmt::format("Checking device: {}", physdev.properties.deviceName.data())); + if (suitable_device(&physdev)) { + VERBOSE(fmt::format("Found suitable device {}.", physdev.properties.deviceName.data())); + return physdev; + } + } + throw std::runtime_error("No suitable physical device found."); +} int main(int, char **) { GlfwContext glfw = {}; Context context = { "Aster", VERSION }; Window window = { "Aster1", &context, { 640, 480 } }; + PhysicalDevice physical_device = find_suitable_device({ &window, &context }); + INFO(fmt::format("Using Device {}", physical_device.properties.deviceName.data())); + while (window.poll()) { } diff --git a/aster_core/device.cpp b/aster_core/device.cpp index 369adbd..bb25530 100644 --- a/aster_core/device.cpp +++ b/aster_core/device.cpp @@ -15,7 +15,6 @@ Device::Device(Device &&_other) noexcept : physical_device{ std::move(_other.physical_device) }, device{ std::exchange(_other.device, nullptr) }, - queues{ _other.queues }, allocator{ std::exchange(_other.allocator, nullptr) }, name{ std::move(_other.name) } {} @@ -24,31 +23,23 @@ Device &Device::operator=(Device &&_other) noexcept { return *this; physical_device = std::move(_other.physical_device); device = std::exchange(_other.device, nullptr); - queues = _other.queues; allocator = std::exchange(_other.allocator, nullptr); name = std::move(_other.name); return *this; } -Device::Device(const std::string_view &_name, Context *_context, const PhysicalDevice &_physical_device_info, const vk::PhysicalDeviceFeatures &_enabled_features) : +Device::Device(const std::string_view &_name, Context *_context, const PhysicalDevice &_physical_device_info, const vk::PhysicalDeviceFeatures &_enabled_features, std::set &&_enabled_queues) : physical_device{ _physical_device_info }, name{ _name } { const auto &physical_device = _physical_device_info.device; - const auto &queue_families = _physical_device_info.queue_families; // Logical Device - std::map unique_queue_families; - unique_queue_families[queue_families.graphics_idx]++; - unique_queue_families[queue_families.present_idx]++; - unique_queue_families[queue_families.transfer_idx]++; - unique_queue_families[queue_families.compute_idx]++; - - std::array queue_priority = { 1.0f, 1.0f, 1.0f, 1.0f }; + std::vector queue_priority(_enabled_queues.size(), 1.0f); std::vector queue_create_infos; - queue_create_infos.reserve(unique_queue_families.size()); - for (auto &[index_, count_] : unique_queue_families) { + queue_create_infos.reserve(_enabled_queues.size()); + for (auto &[family_, count_] : _enabled_queues) { queue_create_infos.push_back({ - .queueFamilyIndex = index_, + .queueFamilyIndex = family_, .queueCount = count_, .pQueuePriorities = queue_priority.data(), }); @@ -69,7 +60,7 @@ Device::Device(const std::string_view &_name, Context *_context, const PhysicalD } INFO("Logical Device Created!"); - VmaAllocatorCreateInfo allocator_create_info = { + const VmaAllocatorCreateInfo allocator_create_info = { .physicalDevice = *physical_device, .device = *device, .instance = *_context->instance, @@ -85,54 +76,7 @@ Device::Device(const std::string_view &_name, Context *_context, const PhysicalD INFO(fmt::format("Created Device '{}' Successfully", _name)); - // Setup queues - { - u32 compute_idx = --unique_queue_families[queue_families.compute_idx]; - u32 transfer_idx = --unique_queue_families[queue_families.transfer_idx]; - u32 present_idx = --unique_queue_families[queue_families.present_idx]; - u32 graphics_idx = --unique_queue_families[queue_families.graphics_idx]; - - queues.graphics = device.getQueue(queue_families.graphics_idx, graphics_idx); - queues.present = device.getQueue(queue_families.present_idx, present_idx); - queues.transfer = device.getQueue(queue_families.transfer_idx, transfer_idx); - queues.compute = device.getQueue(queue_families.graphics_idx, compute_idx); - INFO(fmt::format("Graphics Queue Index: ({}, {})", queue_families.graphics_idx, graphics_idx)); - INFO(fmt::format("Present Queue Index: ({}, {})", queue_families.present_idx, present_idx)); - INFO(fmt::format("Transfer Queue Index: ({}, {})", queue_families.transfer_idx, transfer_idx)); - INFO(fmt::format("Compute Queue Index: ({}, {})", queue_families.compute_idx, compute_idx)); - } - - // transfer_cmd_pool = device.createCommandPool({ - // .flags = vk::CommandPoolCreateFlagBits::eTransient, - // .queueFamilyIndex = queue_families.transfer_idx, - // }); - // if (failed(result)) { - // allocator.destroy(); - // device.destroy(); - // return Err::make(fmt::format("Transfer command pool creation failed with {}" CODE_LOC, to_cstr(result)), result); - // } - // VERBOSE("Transfer Command Pool Created"); - // - // vk::CommandPool graphics_cmd_pool; - // tie(result, graphics_cmd_pool) = device.createCommandPool({ - // .flags = vk::CommandPoolCreateFlagBits::eTransient | vk::CommandPoolCreateFlagBits::eResetCommandBuffer, - // .queueFamilyIndex = queue_families.graphics_idx, - // }); - // if (failed(result)) { - // device.destroyCommandPool(transfer_cmd_pool); - // allocator.destroy(); - // device.destroy(); - // return Err::make(fmt::format("Graphics command pool creation failed with {}" CODE_LOC, to_cstr(result)), result); - // } - // VERBOSE("Graphics Command Pool Created"); - - // transfer_cmd_pool, - // graphics_cmd_pool - // }; - set_name(_name); - // final_device.set_object_name(transfer_cmd_pool, "Async transfer command pool"); - // final_device.set_object_name(graphics_cmd_pool, "Single use Graphics command pool"); } Device::~Device() { @@ -141,85 +85,6 @@ Device::~Device() { } INFO("Device '" + name + "' Destroyed"); } -// -// Res Device::alloc_temp_command_buffer(vk::CommandPool _pool) const { -// vk::CommandBuffer cmd; -// vk::CommandBufferAllocateInfo cmd_buf_alloc_info = { -// .commandPool = _pool, -// .level = vk::CommandBufferLevel::ePrimary, -// .commandBufferCount = 1, -// }; -// if (const auto result = device.allocateCommandBuffers(&cmd_buf_alloc_info, &cmd); failed(result)) { -// return Err::make(fmt::format("Temp Command buffer allocation failed with {}" CODE_LOC, to_cstr(result)), result); -// } -// return cmd; -// } -// -// Res> Device::upload_data(const Borrowed &_host_buffer, Buffer &&_staging_buffer) { -// ERROR_IF(!(_host_buffer->usage & vk::BufferUsageFlagBits::eTransferDst), fmt::format("Buffer {} is not a transfer dst. Use vk::BufferUsageFlagBits::eTransferDst during creation", _host_buffer->name.data())) -// ELSE_IF_ERROR(!(_staging_buffer.usage & vk::BufferUsageFlagBits::eTransferSrc), fmt::format("Buffer {} is not a transfer src. Use vk::BufferUsageFlagBits::eTransferSrc during creation", _staging_buffer.name.data())) -// ELSE_IF_WARN(_host_buffer->memory_usage != vma::MemoryUsage::eGpuOnly, fmt::format("Memory {} is not GPU only. Upload not required", _host_buffer->name.data())) -// ELSE_IF_WARN(_host_buffer->memory_usage != vma::MemoryUsage::eCpuOnly, fmt::format("Memory {} is not CPU only. Staging should ideally be a CPU only buffer", _staging_buffer.name.data())); -// -// vk::CommandBuffer cmd; -// vk::CommandBufferAllocateInfo allocate_info = { -// .commandPool = transfer_cmd_pool, -// .level = vk::CommandBufferLevel::ePrimary, -// .commandBufferCount = 1, -// }; -// -// auto result = device.allocateCommandBuffers(&allocate_info, &cmd); -// if (failed(result)) { -// return Err::make(fmt::format("Transfer command pool allocation failed with {}" CODE_LOC, to_cstr(result)), result); -// } -// set_object_name(cmd, fmt::format("{} transfer command", _host_buffer->name.data())); -// -// result = cmd.begin({ -// .flags = vk::CommandBufferUsageFlagBits::eOneTimeSubmit, -// }); -// if (failed(result)) { -// return Err::make(fmt::format("Command buffer begin failed with {}" CODE_LOC, to_cstr(result)), result); -// } -// -// cmd.copyBuffer(_staging_buffer.buffer, _host_buffer->buffer, { { .srcOffset = 0, .dstOffset = 0, .size = cast(_staging_buffer.size) } }); -// result = cmd.end(); -// if (failed(result)) { -// return Err::make(fmt::format("Command buffer end failed with {}" CODE_LOC, to_cstr(result)), result); -// } -// -// return SubmitTask::create(borrow(this), std::move(_staging_buffer), queues.transfer, transfer_cmd_pool, { cmd }); -// } -// -// Res> Device::upload_data(const Borrowed &_host_buffer, const std::span &_data) { -// ERROR_IF(!(_host_buffer->usage & vk::BufferUsageFlagBits::eTransferDst), fmt::format("Buffer {} is not a transfer dst. Use vk::BufferUsageFlagBits::eTransferDst during creation", _host_buffer->name.data())) -// ELSE_IF_WARN(_host_buffer->memory_usage != vma::MemoryUsage::eGpuOnly, fmt::format("Memory {} is not GPU only. Upload not required", _host_buffer->name.data())); -// -// if (auto res = Buffer::create("_stage " + _host_buffer->name, borrow(this), _data.size(), vk::BufferUsageFlagBits::eTransferSrc, vma::MemoryUsage::eCpuOnly)) { -// return Err::make("Staging buffer creation failed", std::move(res.error())); -// } else { -// if (auto result = update_data(borrow(res.value()), _data)) { -// return upload_data(_host_buffer, std::move(res.value())); -// } else { -// return Err::make(std::move(result.error())); -// } -// } -// } -// -// Res<> Device::update_data(const Borrowed &_host_buffer, const std::span &_data) const { -// if (_host_buffer->memory_usage != vma::MemoryUsage::eCpuToGpu && -// _host_buffer->memory_usage != vma::MemoryUsage::eCpuOnly) { -// return Err::make("Memory is not on CPU so mapping can't be done. Use upload_data" CODE_LOC); -// } -// -// auto [result, mapped_memory] = allocator.mapMemory(_host_buffer->allocation); -// if (failed(result)) { -// return Err::make(fmt::format("Memory mapping failed with {}" CODE_LOC, to_cstr(result)), result); -// } -// memcpy(mapped_memory, _data.data(), _data.size()); -// allocator.unmapMemory(_host_buffer->allocation); -// -// return {}; -// } void Device::set_name(const std::string_view &_name) { VERBOSE(fmt::format("Device {} -> {}", name.data(), _name.data())); diff --git a/aster_core/device.h b/aster_core/device.h index 0f65458..3d0faba 100644 --- a/aster_core/device.h +++ b/aster_core/device.h @@ -11,20 +11,16 @@ #include "window.h" #include +#include #include #include #include -struct Queues { - vk::raii::Queue graphics{ nullptr }; - vk::raii::Queue present{ nullptr }; - vk::raii::Queue transfer{ nullptr }; - Option compute; +struct QueueAllocation { + u32 family; + u32 count; }; -template -struct SubmitTask; - class Device { public: Device(const Device &_other) = delete; @@ -32,7 +28,7 @@ public: Device &operator=(const Device &_other) = delete; Device &operator=(Device &&_other) noexcept; - Device(const std::string_view &_name, Context *_context, const PhysicalDevice &_physical_device_info, const vk::PhysicalDeviceFeatures &_enabled_features); + Device(const std::string_view &_name, Context *_context, const PhysicalDevice &_physical_device_info, const vk::PhysicalDeviceFeatures &_enabled_features, std::set &&_enabled_queues); ~Device(); @@ -49,153 +45,13 @@ public: } } - // - // [[nodiscard]] - // Res alloc_temp_command_buffer(vk::CommandPool _pool) const; - // - // [[nodiscard]] Res> upload_data(const Borrowed& _host_buffer, Buffer&& _staging_buffer); - // [[nodiscard]] Res> upload_data(const Borrowed& _host_buffer, const std::span& _data); - // Res<> update_data(const Borrowed& _host_buffer, const std::span& _data) const; - // fields PhysicalDevice physical_device; vk::raii::Device device{ nullptr }; - Queues queues; VmaAllocator allocator; - // vk::CommandPool transfer_cmd_pool; - // vk::CommandPool graphics_cmd_pool; - std::string name; private: void set_name(const std::string_view &_name); }; -// -// template -// struct SubmitTask { -// vk::Fence fence; -// Borrowed device{}; -// T payload; -// std::vector cmd; -// vk::CommandPool pool; -// -// [[nodiscard]] -// static Res> create(const Borrowed& _device, T&& _payload, vk::Queue _queue, vk::CommandPool _pool, const std::vector& _cmd, const std::vector& _wait_on = {}, const std::vector& _signal_to = {}) { -// -// SubmitTask task; -// if (auto res = task.submit(_device, std::forward(_payload), _queue, _pool, _cmd, _wait_on, _signal_to)) { -// return std::move(task); -// } else { -// return Err::make(std::move(res.error())); -// } -// } -// -// [[nodiscard]] -// Res<> submit(const Borrowed& _device, T&& _payload, vk::Queue _queue, vk::CommandPool _pool, std::vector _cmd, std::vector _wait_on = {}, std::vector _signal_to = {}) { -// device = _device; -// auto [result, _fence] = device->device.createFence({}); -// if (failed(result)) { -// return Err::make(std::fmt("Fence creation failed with %s" CODE_LOC, to_cstr(result)), result); -// } -// fence = _fence; -// payload = std::move(_payload); -// cmd = _cmd; -// pool = _pool; -// -// result = _queue.submit({ -// { -// .waitSemaphoreCount = cast(_wait_on.size()), -// .pWaitSemaphores = _wait_on.data(), -// .commandBufferCount = cast(_cmd.size()), -// .pCommandBuffers = _cmd.data(), -// .signalSemaphoreCount = cast(_signal_to.size()), -// .pSignalSemaphores = _signal_to.data(), -// } -// }, -// _fence); -// if (failed(result)) { -// return Err::make(std::fmt("Submit failed with %s" CODE_LOC, to_cstr(result)), result); -// } -// -// return {}; -// } -// -// [[nodiscard]] -// Res<> wait_and_destroy() { -// return this->destroy(); -// } -// -// [[nodiscard]] -// Res<> destroy() { -// const auto result = device->device.waitForFences({ fence }, true, max_value); -// if (failed(result)) return Err::make(std::fmt("Fence wait failed with %s" CODE_LOC, to_cstr(result)), result); -// device->device.destroyFence(fence); -// device->device.freeCommandBuffers(pool, cast(cmd.size()), cmd.data()); -// return {}; -// } -// }; -// -// template <> -// struct SubmitTask { -// vk::Fence fence; -// Borrowed device; -// std::vector cmd; -// vk::CommandPool pool; -// -// [[nodiscard]] -// static Res> create(const Borrowed& _device, vk::Queue _queue, vk::CommandPool _pool, const std::vector& _cmd, const std::vector& _wait_on = {}, const vk::PipelineStageFlags& _wait_stage = vk::PipelineStageFlagBits::eBottomOfPipe, const std::vector& _signal_to = {}) { -// -// SubmitTask<> task; -// if (auto res = task.submit(_device, _queue, _pool, _cmd, _wait_on, _wait_stage, _signal_to)) { -// return std::move(task); -// } else { -// return Err::make(std::move(res.error())); -// } -// } -// -// [[nodiscard]] -// Res<> wait_and_destroy() { -// return this->destroy(); -// } -// -// [[nodiscard]] -// Res<> destroy() { -// const auto result = device->device.waitForFences({ fence }, true, max_value); -// if (failed(result)) return Err::make(std::fmt("Fence wait failed with %s", to_cstr(result)), result); -// device->device.destroyFence(fence); -// device->device.freeCommandBuffers(pool, cast(cmd.size()), cmd.data()); -// return {}; -// } -// -// private: -// [[nodiscard]] -// Res<> submit(const Borrowed& _device, vk::Queue _queue, vk::CommandPool _pool, std::vector _cmd, std::vector _wait_on = {}, -// const vk::PipelineStageFlags& _wait_stage = vk::PipelineStageFlagBits::eBottomOfPipe, std::vector _signal_to = {}) { -// device = _device; -// auto [result, _fence] = device->device.createFence({}); -// if (failed(result)) { -// return Err::make(std::fmt("Fence creation failed with %s" CODE_LOC, to_cstr(result)), result); -// } -// fence = _fence; -// cmd = _cmd; -// pool = _pool; -// result = _queue.submit({ -// vk::SubmitInfo{ -// .waitSemaphoreCount = cast(_wait_on.size()), -// .pWaitSemaphores = _wait_on.data(), -// .pWaitDstStageMask = &_wait_stage, -// .commandBufferCount = cast(_cmd.size()), -// .pCommandBuffers = _cmd.data(), -// .signalSemaphoreCount = cast(_signal_to.size()), -// .pSignalSemaphores = _signal_to.data(), -// } }, -// _fence); -// if (failed(result)) { -// return Err::make(std::fmt("Submit failed with %s" CODE_LOC, to_cstr(result)), result); -// } -// -// return {}; -// } -// }; -// \ No newline at end of file diff --git a/aster_core/physical_device.cpp b/aster_core/physical_device.cpp index 9d88e3b..90d4d09 100644 --- a/aster_core/physical_device.cpp +++ b/aster_core/physical_device.cpp @@ -5,59 +5,45 @@ #include "physical_device.h" -QueueFamilyIndices PhysicalDevice::get_queue_families(const Window *_window, const vk::raii::PhysicalDevice *_device) { - QueueFamilyIndices indices; +std::vector PhysicalDevice::get_queue_families(const Window *_window) const { + auto queue_families_ = device.getQueueFamilyProperties(); - auto queue_families_ = _device->getQueueFamilyProperties(); + std::vector queue_family_infos; + queue_family_infos.reserve(queue_families_.size()); u32 family_index = 0; for (const auto &queue_family : queue_families_) { - u32 this_family_count = 0; - VERBOSE(fmt::format("Queue({}): {}", family_index, to_string(queue_family.queueFlags).data())); + QueueSupportFlags support; - if (!indices.has_graphics() && (queue_family.queueFlags & vk::QueueFlagBits::eGraphics)) { - if (queue_family.queueCount > this_family_count) { - indices.graphics_idx = family_index; - ++this_family_count; - } else { - continue; - } + if (queue_family.queueFlags & vk::QueueFlagBits::eGraphics) { + support |= QueueSupportFlagBits::eGraphics; } - - if (!indices.has_compute() && (queue_family.queueFlags & vk::QueueFlagBits::eCompute)) { - if (queue_family.queueCount > this_family_count) { - indices.compute_idx = family_index; - ++this_family_count; - } else { - continue; - } + if (queue_family.queueFlags & vk::QueueFlagBits::eCompute) { + support |= QueueSupportFlagBits::eCompute; } - - if (!indices.has_transfer() && (queue_family.queueFlags & vk::QueueFlagBits::eTransfer)) { - if (queue_family.queueCount > this_family_count) { - indices.transfer_idx = family_index; - ++this_family_count; - } else { - continue; - } + if (queue_family.queueFlags & vk::QueueFlagBits::eTransfer) { + support |= QueueSupportFlagBits::eTransfer; } try { - if (!indices.has_present() && _device->getSurfaceSupportKHR(family_index, *_window->surface)) { - if (queue_family.queueCount > this_family_count) { - indices.present_idx = family_index; - ++this_family_count; - } else { - continue; - } + if (device.getSurfaceSupportKHR(family_index, *_window->surface)) { + support |= QueueSupportFlagBits::ePresent; } } catch (const std::exception &err) { ERROR("Failure in finding surface support, all possibilities fatal. Failed with "s + err.what()); throw; } + VERBOSE(fmt::format("Queue({}): {}", family_index, to_string(support))); + + queue_family_infos.push_back({ + .index = family_index, + .count = queue_family.queueCount, + .queue_support = support, + }); + ++family_index; } - return indices; + return queue_family_infos; } diff --git a/aster_core/physical_device.h b/aster_core/physical_device.h index 4cad5ce..9f2bf9a 100644 --- a/aster_core/physical_device.h +++ b/aster_core/physical_device.h @@ -8,28 +8,43 @@ #include "global.h" #include "window.h" -struct QueueFamilyIndices { - static constexpr u32 invalid_value = 0xFFFFFFFFu; +enum class QueueSupportFlagBits { + eGraphics = 0b0000'0001, + eTransfer = 0b0000'0010, + eCompute = 0b0000'0100, + ePresent = 0b0000'1000, +}; - u32 graphics_idx{ invalid_value }; - u32 present_idx{ invalid_value }; - u32 compute_idx{ invalid_value }; - u32 transfer_idx{ invalid_value }; +using QueueSupportFlags = vk::Flags; - [[nodiscard]] b8 has_graphics() const { - return graphics_idx != invalid_value; +inline std::string to_string(QueueSupportFlags queue_support) { + if (! queue_support) + return {}; + + std::string result = ""; + if (queue_support & QueueSupportFlagBits::eGraphics) { + result += "Graphics | "; + } + if (queue_support & QueueSupportFlagBits::eTransfer) { + result += "Transfer | "; + } + if (queue_support & QueueSupportFlagBits::eCompute) { + result += "Compute | "; + } + if (queue_support & QueueSupportFlagBits::ePresent) { + result += "Present | "; } - [[nodiscard]] b8 has_present() const { - return present_idx != invalid_value; - } + return "{ " + result.substr( 0, result.size() - 3 ) + " }"; +} - [[nodiscard]] b8 has_compute() const { - return compute_idx != invalid_value; - } +struct QueueFamilyInfo { + u32 index; + u32 count; + QueueSupportFlags queue_support; - [[nodiscard]] b8 has_transfer() const { - return transfer_idx != invalid_value; + [[nodiscard]] bool has_support(const QueueSupportFlags &support) const { + return cast(queue_support & support); } }; @@ -37,15 +52,33 @@ struct PhysicalDevice { vk::raii::PhysicalDevice device; vk::PhysicalDeviceProperties properties; vk::PhysicalDeviceFeatures features; - QueueFamilyIndices queue_families; + std::vector surface_formats; + std::vector present_modes; + std::vector queue_family_infos; + QueueSupportFlags queue_support; - PhysicalDevice(const Window *const _window, vk::raii::PhysicalDevice _device) : + PhysicalDevice(const Window *const _window, vk::raii::PhysicalDevice &&_device) : device{ std::move(_device) } { properties = device.getProperties(); features = device.getFeatures(); - queue_families = get_queue_families(_window, &device); + surface_formats = device.getSurfaceFormatsKHR(*_window->surface); + present_modes = device.getSurfacePresentModesKHR(*_window->surface); + queue_family_infos = get_queue_families(_window); + for (auto [family, count, support] : queue_family_infos) { + queue_support |= support; + } } -private: - [[nodiscard]] static QueueFamilyIndices get_queue_families(const Window *_window, const vk::raii::PhysicalDevice *_device); + std::vector get_queue_families(const Window *_window) const; +}; + +struct PhysicalDevices : std::vector { + PhysicalDevices(const Window *const _window, const Context *_context) { + auto phys_devs = vk::raii::PhysicalDevices(_context->instance); + reserve(phys_devs.size()); + + for (auto physdev : phys_devs) { + emplace_back(_window, std::move(physdev)); + } + } };