Swapchain almost finished.

This commit is contained in:
Nepgfurmixpro 2023-11-04 12:37:21 -04:00
parent ce5fd7e93d
commit 11725fb9f4
19 changed files with 693 additions and 42 deletions

View File

@ -8,12 +8,14 @@ file(GLOB_RECURSE SourceFiles "${CMAKE_CURRENT_SOURCE_DIR}/Source/*.cpp" "${CMAK
find_package(spdlog CONFIG REQUIRED)
find_package(Vulkan REQUIRED)
find_package(glm CONFIG REQUIRED)
find_package(glfw3 CONFIG REQUIRED)
add_library (Nitric STATIC ${SourceFiles})
target_link_libraries(Nitric PRIVATE glm::glm)
target_link_libraries(Nitric PRIVATE spdlog::spdlog)
target_link_libraries(Nitric PRIVATE Vulkan::Vulkan)
target_link_libraries(Nitric PRIVATE glfw)
target_include_directories(Nitric PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/Source")
set(NITRIC_LIBRARY "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/Nitric.lib")

View File

@ -1,11 +1,53 @@
#include <Core/Application.h>
namespace Nitric {
Application::Application(const ApplicationSettings& settings) {
Application* Application::s_Instance = nullptr;
Application::Application(const ApplicationSettings& settings) {
if (s_Instance != nullptr) {
LogInternal::Fatal("Application already created.");
}
s_Instance = this;
WindowProperties winProps{};
winProps.Width = settings.Width;
winProps.Height = settings.Height;
winProps.Title = settings.Title;
m_Window = Window::Create(winProps);
m_Context = GraphicsContext::Create(m_Window);
m_Running = true;
m_Window->AttachEventHandler([this](EventData& event) {
switch (event.Event) {
case WindowEvent::CLOSE:
m_Running = false;
break;
}
});
m_Context->Init();
}
void Application::Run() {
while (m_Running) {
this->PollEvents();
this->RenderFrame();
}
}
void Application::PollEvents() {
m_Window->Update();
}
void Application::RenderFrame() {
}
Application::~Application() {
m_Context.reset();
m_Window.reset();
}
}

View File

@ -5,6 +5,7 @@
#include <Render/GraphicsContext.h>
#include <Core/Window.h>
#include <Core/LogInternal.h>
namespace Nitric {
@ -17,11 +18,22 @@ namespace Nitric {
class Application {
public:
explicit Application(const ApplicationSettings& settings);
~Application();
void Run();
private:
static Application* Get();
private:
static Application* s_Instance;
void PollEvents();
void RenderFrame();
Scope<GraphicsContext> m_Context;
Ref<Window> m_Window;
bool m_Running;
};
static Application* CreateApplication();

View File

@ -14,5 +14,10 @@ int main(int argc, char** argv) {
Nitric::LogInternal::Info("Creating Nitric Application");
auto application = Nitric::CreateApplication();
Nitric::LogInternal::Info("Starting Nitric Application");
application->Run();
Nitric::LogInternal::Info("Stopping Nitric Application");
delete application;
}
#endif

View File

@ -5,6 +5,7 @@ namespace Nitric {
void LogInternal::Init() {
s_Logger = spdlog::stdout_color_mt("Nitric Internal");
s_Logger->set_level(spdlog::level::debug);
}
Ref<spdlog::logger> LogInternal::Get() {

View File

@ -13,13 +13,13 @@ namespace Nitric {
template <typename T>
static void Debug(T& msg) {
Get()->debug(msg);
Get()->log(spdlog::level::debug, msg);
}
template<typename... Args>
static void Debug(spdlog::format_string_t<Args...> fmt, Args &&...args)
{
Debug(fmt::format(fmt, std::forward(args)...));
Get()->log(spdlog::level::debug, fmt, std::forward<Args>(args)...);
}
template <typename T>
@ -30,7 +30,7 @@ namespace Nitric {
template<typename... Args>
static void Info(spdlog::format_string_t<Args...> fmt, Args &&...args)
{
Info(fmt::format(fmt, std::forward(args)...));
Get()->info(fmt, std::forward<Args>(args)...);
}
template <typename T>
@ -41,14 +41,14 @@ namespace Nitric {
template<typename... Args>
static void Critical(spdlog::format_string_t<Args...> fmt, Args &&...args)
{
Critical(fmt::format(fmt, std::forward(args)...));
Get()->critical(fmt, std::forward<Args>(args)...);
}
template <typename T>
static void Fatal(T& msg) {
LogInternal::Error("The next error is fatal. This will terminate the program");
Get()->log(spdlog::level::from_str("fatal"), msg);
auto api = GraphicsAPI::APIToString(GraphicsAPI::GetAPI());
Get()->log(spdlog::level::critical, msg);
auto api = GraphicsAPI::APIToString(GraphicsAPI::NoErrGetAPI());
Info("GraphicsAPI is set to {}", api);
throw std::runtime_error(msg);
}
@ -56,7 +56,11 @@ namespace Nitric {
template<typename... Args>
static void Fatal(spdlog::format_string_t<Args...> fmt, Args &&...args)
{
Fatal(fmt::format(fmt, std::forward(args)...));
LogInternal::Error("The next error is fatal. This will terminate the program");
Get()->log(spdlog::level::critical, fmt, std::forward<Args>(args)...);
auto api = GraphicsAPI::APIToString(GraphicsAPI::NoErrGetAPI());
Info("GraphicsAPI is set to {}", api);
throw std::runtime_error(fmt::format(fmt, std::forward<Args>(args)...));
}
template <typename T>
@ -67,7 +71,18 @@ namespace Nitric {
template<typename... Args>
static void Error(spdlog::format_string_t<Args...> fmt, Args &&...args)
{
Error(fmt::format(fmt, std::forward(args)...));
Get()->error(fmt, std::forward<Args>(args)...);
}
template <typename T>
static void Warn(T& msg) {
Get()->warn(msg);
}
template<typename... Args>
static void Warn(spdlog::format_string_t<Args...> fmt, Args &&...args)
{
Get()->warn(fmt, std::forward<Args>(args)...);
}
private:

View File

@ -0,0 +1,16 @@
#include <Core/Window.h>
#include <Render/GraphicsAPI.h>
#include <Platform/Vulkan/VkWindow.h>
namespace Nitric {
Ref<Window> Window::Create(const WindowProperties &properties) {
switch (GraphicsAPI::GetAPI()) {
case GraphicsAPI::Vulkan:
return CreateRef<VkWindow>(properties);
default:
break;
}
return nullptr;
}
}

View File

@ -5,6 +5,7 @@
#include <string>
#include <cstdint>
#include <glm/glm.hpp>
#include <functional>
namespace Nitric {
struct WindowProperties {
@ -12,9 +13,27 @@ namespace Nitric {
uint32_t Height;
std::string Title;
};
enum class WindowEvent {
CLOSE
};
struct EventData {
WindowEvent Event;
};
using EventHandler =
std::function<void(EventData&)>;
class Window {
public:
virtual ~Window() = default;
virtual void SetSize(const glm::vec2& size) = 0;
virtual glm::vec2 GetSize() const = 0;
virtual glm::vec2 GetSize() = 0;
virtual void Update() = 0;
virtual void AttachEventHandler(EventHandler handler) = 0;
static Ref<Window> Create(const WindowProperties& properties);
};
}

View File

@ -0,0 +1,223 @@
#include <Platform/Vulkan/VkBase.h>
#include <Platform/Vulkan/VkWindow.h>
#include <utility>
namespace Nitric {
namespace Vk {
void Validate(VkResult result, bool fatal, const std::string &msg) {
if (result != VK_SUCCESS) {
const char* error = string_VkResult(result);
if (fatal) {
LogInternal::Fatal("({}) -> {}", error, msg);
} else {
LogInternal::Error("({}) -> {}", error, msg);
}
} else {
LogInternal::Debug("Vk::Validate success.");
}
}
void PopulateDebugCreateInfo(VkDebugUtilsMessengerCreateInfoEXT& createInfo) {
createInfo.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT;
createInfo.messageSeverity =
VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT |
VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT |
VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT |
VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT;
createInfo.messageType =
VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT |
VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT |
VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT;
createInfo.pfnUserCallback = DebugCallbacks;
}
VKAPI_ATTR VkBool32 VKAPI_CALL DebugCallbacks(VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity,
VkDebugUtilsMessageTypeFlagsEXT messageType,
const VkDebugUtilsMessengerCallbackDataEXT *pCallbackData, void *pUserData) {
switch (messageSeverity) {
case VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT:
LogInternal::Info("Validation Layer -> {}", pCallbackData->pMessage);
break;
case VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT:
LogInternal::Warn("Validation Layer -> {}", pCallbackData->pMessage);
break;
case VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT:
LogInternal::Error("Validation Layer -> {}", pCallbackData->pMessage);
break;
default:
LogInternal::Debug("Validation Layer -> {}", pCallbackData->pMessage);
break;
}
return VK_FALSE;
}
VkResult CreateDebugUtilsMessenger(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 DestroyDebugUtilsMessenger(VkInstance instance, VkDebugUtilsMessengerEXT messenger,
const VkAllocationCallbacks *pAllocator) {
auto func = (PFN_vkDestroyDebugUtilsMessengerEXT) vkGetInstanceProcAddr(instance, "vkDestroyDebugUtilsMessengerEXT");
if (func != nullptr) {
func(instance, messenger, pAllocator);
}
}
void ScorePhysicalDevice(VkPhysicalDevice physicalDevice, uint32_t* pScore) {
if (pScore == nullptr) {
LogInternal::Error("pScore == nullptr");
return;
}
VkPhysicalDeviceFeatures features{};
vkGetPhysicalDeviceFeatures(physicalDevice, &features);
VkPhysicalDeviceProperties properties{};
vkGetPhysicalDeviceProperties(physicalDevice, &properties);
if (!features.geometryShader) {
*pScore = 0;
return;
}
*pScore += properties.limits.maxImageDimension2D;
if (properties.deviceType == VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU) {
*pScore += 2500;
}
}
QueueFamilyIndices FindQueueFamilies(VkPhysicalDevice physicalDevice, VkSurfaceKHR surface) {
uint32_t queueCount;
vkGetPhysicalDeviceQueueFamilyProperties(
physicalDevice, &queueCount,
nullptr);
std::vector<VkQueueFamilyProperties> properties(queueCount);
vkGetPhysicalDeviceQueueFamilyProperties(
physicalDevice, &queueCount,
properties.data());
QueueFamilyIndices indices{};
for (uint32_t i = 0; i < queueCount; i++) {
VkQueueFamilyProperties property = properties[i];
if (property.queueFlags & VK_QUEUE_GRAPHICS_BIT) {
indices.GraphicsFamily = i;
}
VkBool32 presentSupport = false;
vkGetPhysicalDeviceSurfaceSupportKHR(physicalDevice, i, surface, &presentSupport);
if (presentSupport) {
indices.PresentFamily = i;
}
if (indices.IsComplete()) {
break;
}
}
return indices;
}
std::string GetPhysicalDeviceName(VkPhysicalDevice physicalDevice) {
VkPhysicalDeviceProperties properties{};
vkGetPhysicalDeviceProperties(physicalDevice, &properties);
return {properties.deviceName};
}
bool IsDeviceSuitable(VkPhysicalDevice physicalDevice, VkSurfaceKHR surface) {
QueueFamilyIndices indices = FindQueueFamilies(physicalDevice, surface);
bool extensionSupport = CheckDeviceExtensionSupport(physicalDevice);
bool swapChainAdequate = false;
if (extensionSupport) {
SwapChainSupportDetails swapChainSupport = QuerySwapChainSupport(physicalDevice, surface);
swapChainAdequate = !swapChainSupport.Formats.empty() && !swapChainSupport.PresentModes.empty();
}
return indices.IsComplete() && extensionSupport && swapChainAdequate;
}
bool CheckDeviceExtensionSupport(VkPhysicalDevice physicalDevice) {
uint32_t extensionCount = 0;
vkEnumerateDeviceExtensionProperties(physicalDevice, nullptr, &extensionCount, nullptr);
std::vector<VkExtensionProperties> availableExtensions(extensionCount);
vkEnumerateDeviceExtensionProperties(physicalDevice, nullptr, &extensionCount, availableExtensions.data());
std::set<std::string> requiredExtensions(DeviceExtensions.begin(), DeviceExtensions.end());
for (const auto& extension : availableExtensions) {
requiredExtensions.erase(extension.extensionName);
}
return requiredExtensions.empty();
}
SwapChainSupportDetails QuerySwapChainSupport(VkPhysicalDevice physicalDevice, VkSurfaceKHR surface) {
SwapChainSupportDetails details;
vkGetPhysicalDeviceSurfaceCapabilitiesKHR(physicalDevice, surface, &details.Capabilities);
uint32_t formatCount;
vkGetPhysicalDeviceSurfaceFormatsKHR(physicalDevice, surface, &formatCount, nullptr);
if (formatCount != 0) {
details.Formats.resize(formatCount);
vkGetPhysicalDeviceSurfaceFormatsKHR(physicalDevice, surface, &formatCount, details.Formats.data());
}
uint32_t presentModeCount;
vkGetPhysicalDeviceSurfacePresentModesKHR(physicalDevice, surface, &presentModeCount, details.PresentModes.data());
if (presentModeCount != 0) {
details.PresentModes.resize(presentModeCount);
vkGetPhysicalDeviceSurfacePresentModesKHR(physicalDevice, surface, &presentModeCount, details.PresentModes.data());
}
return details;
}
VkSurfaceFormatKHR ChooseSwapSurfaceFormat(const std::vector<VkSurfaceFormatKHR> &availableFormats) {
for (const auto& format : availableFormats) {
if (format.format == VK_FORMAT_B8G8R8A8_SRGB && format.colorSpace == VK_COLOR_SPACE_SRGB_NONLINEAR_KHR) {
return format;
}
}
return availableFormats[0];
}
VkPresentModeKHR ChooseSwapPresentMode(const std::vector<VkPresentModeKHR> &availablePresentModes) {
for (const auto& presentMode : availablePresentModes) {
if (presentMode == VK_PRESENT_MODE_MAILBOX_KHR) {
return presentMode;
}
}
return VK_PRESENT_MODE_FIFO_KHR;
}
VkExtent2D ChooseSwapExtent(Ref<Window> window, const VkSurfaceCapabilitiesKHR &capabilities) {
if (capabilities.currentExtent.width != std::numeric_limits<uint32_t>::max()) {
return capabilities.currentExtent;
} else {
auto vulkanWindow = CastPtr<VkWindow>(std::move(window));
auto size = vulkanWindow->GetFramebufferSize();
VkExtent2D actualExtent = {
static_cast<uint32_t>(size.x),
static_cast<uint32_t>(size.y)
};
actualExtent.width = std::clamp(actualExtent.width, capabilities.minImageExtent.width, capabilities.maxImageExtent.width);
actualExtent.height = std::clamp(actualExtent.height, capabilities.minImageExtent.height, capabilities.maxImageExtent.height);
return actualExtent;
}
}
}
}

View File

@ -2,7 +2,12 @@
#include <vulkan/vulkan.h>
#include <vulkan/vk_enum_string_helper.h>
#include <set>
#include <limits>
#include <algorithm>
#include <Core/LogInternal.h>
#include <Core/Window.h>
namespace Nitric {
#ifdef NDEBUG
@ -12,18 +17,55 @@ namespace Nitric {
#endif
namespace Vk {
template <typename ...Args>
void Validate(VkResult result, bool fatal, spdlog::format_string_t<Args...> fmt, Args &&...args) {
if (result != VK_SUCCESS) {
auto error = string_VkResult(result);
if (fatal) {
LogInternal::Fatal("({}) -> {}", error, fmt::format(fmt, std::forward<Args>(args)...))
} else {
LogInternal::Error("({}) -> {}", error, fmt::format(fmt, std::forward<Args>(args)...))
}
} else {
LogInternal::Debug("Vk::Validate success.")
void Validate(VkResult result, bool fatal, const std::string& msg);
void PopulateDebugCreateInfo(VkDebugUtilsMessengerCreateInfoEXT& createInfo);
static VKAPI_ATTR VkBool32 VKAPI_CALL DebugCallbacks(
VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity,
VkDebugUtilsMessageTypeFlagsEXT messageType,
const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData,
void* pUserData);
VkResult CreateDebugUtilsMessenger(
VkInstance instance,
const VkDebugUtilsMessengerCreateInfoEXT* pCreateInfo,
const VkAllocationCallbacks* pAllocator,
VkDebugUtilsMessengerEXT* pMessenger);
void DestroyDebugUtilsMessenger(
VkInstance instance,
VkDebugUtilsMessengerEXT messenger,
const VkAllocationCallbacks* pAllocator);
void ScorePhysicalDevice(VkPhysicalDevice physicalDevice, uint32_t* pScore);
std::string GetPhysicalDeviceName(VkPhysicalDevice physicalDevice);
bool IsDeviceSuitable(VkPhysicalDevice physicalDevice, VkSurfaceKHR surface);
bool CheckDeviceExtensionSupport(VkPhysicalDevice device);
const static std::vector<const char*> DeviceExtensions = {
VK_KHR_SWAPCHAIN_EXTENSION_NAME
};
struct QueueFamilyIndices {
std::optional<uint32_t> GraphicsFamily;
std::optional<uint32_t> PresentFamily;
bool IsComplete() {
return this->GraphicsFamily.has_value() && this->PresentFamily.has_value();
}
}
};
QueueFamilyIndices FindQueueFamilies(VkPhysicalDevice physicalDevice, VkSurfaceKHR surface);
struct SwapChainSupportDetails {
VkSurfaceCapabilitiesKHR Capabilities;
std::vector<VkSurfaceFormatKHR> Formats;
std::vector<VkPresentModeKHR> PresentModes;
};
SwapChainSupportDetails QuerySwapChainSupport(VkPhysicalDevice physicalDevice, VkSurfaceKHR surface);
VkSurfaceFormatKHR ChooseSwapSurfaceFormat(const std::vector<VkSurfaceFormatKHR>& availableFormats);
VkPresentModeKHR ChooseSwapPresentMode(const std::vector<VkPresentModeKHR>& availablePresentModes);
VkExtent2D ChooseSwapExtent(Ref<Window> window, const VkSurfaceCapabilitiesKHR& capabilities);
}
}

View File

@ -9,12 +9,21 @@ namespace Nitric {
}
void VkGraphicsContext::Init() {
LogInternal::Debug("Creating instance");
this->CreateInstance();
LogInternal::Debug("Creating debug messenger");
this->CreateDebugMessenger();
LogInternal::Debug("Creating surface");
m_Window->CreateSurface(m_Instance, &m_Surface);
LogInternal::Debug("Getting physical device");
this->PickPhysicalDevice();
LogInternal::Debug("Creating logical device");
this->CreateLogicalDevice();
}
void VkGraphicsContext::CreateInstance() {
if (!CheckValidationLayerSupport() && VkValidationLayersEnabled) {
LogInternal::Fatal("Requested validation layers but they were not available.");
}
VkApplicationInfo appInfo{};
appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
@ -36,13 +45,19 @@ namespace Nitric {
if (VkValidationLayersEnabled) {
createInfo.enabledLayerCount = static_cast<uint32_t>(s_ValidationLayers.size());
createInfo.ppEnabledLayerNames = s_ValidationLayers.data();
VkDebugUtilsMessengerCreateInfoEXT debugCreateInfo{};
Vk::PopulateDebugCreateInfo(debugCreateInfo);
createInfo.pNext = &debugCreateInfo;
} else {
createInfo.enabledLayerCount = 0;
createInfo.ppEnabledLayerNames = nullptr;
}
Vk::Validate(vkCreateInstance(&createInfo, nullptr, &m_Instance), true, "VkInstance VkGraphicsContext::m_Instance failed on creation.")
}
Vk::Validate(
vkCreateInstance(&createInfo, nullptr, &m_Instance),
true,
"VkInstance VkGraphicsContext::m_Instance failed on creation.");
}
bool VkGraphicsContext::CheckValidationLayerSupport() {
@ -71,5 +86,124 @@ namespace Nitric {
"VK_LAYER_KHRONOS_validation"
};
VkGraphicsContext::~VkGraphicsContext() = default;
VkGraphicsContext::~VkGraphicsContext() {
LogInternal::Debug("Destroying Vulkan Device");
vkDestroyDevice(m_Device, nullptr);
LogInternal::Debug("Destroying Vulkan Surface");
vkDestroySurfaceKHR(m_Instance, m_Surface, nullptr);
if (VkValidationLayersEnabled) {
LogInternal::Debug("Destroying Vulkan Debug Messenger");
Vk::DestroyDebugUtilsMessenger(m_Instance, m_DebugMessenger, nullptr);
}
LogInternal::Debug("Destroying Vulkan Instance");
vkDestroyInstance(m_Instance, nullptr);
}
void VkGraphicsContext::CreateDebugMessenger() {
if (!VkValidationLayersEnabled) return;
VkDebugUtilsMessengerCreateInfoEXT createInfo{};
Vk::PopulateDebugCreateInfo(createInfo);
Vk::Validate(
Vk::CreateDebugUtilsMessenger(
m_Instance,&createInfo,
nullptr,&m_DebugMessenger),
false,
"Failed to create debug messenger. This is not fatal, however it should be investigated.");
}
void VkGraphicsContext::PickPhysicalDevice() {
uint32_t physicalDeviceCount = 0;
vkEnumeratePhysicalDevices(m_Instance, &physicalDeviceCount, nullptr);
std::vector<VkPhysicalDevice> physicalDevices(physicalDeviceCount);
vkEnumeratePhysicalDevices(m_Instance, &physicalDeviceCount, physicalDevices.data());
std::multimap<uint32_t, VkPhysicalDevice> devices;
for (const auto& device : physicalDevices) {
if (Vk::IsDeviceSuitable(device, m_Surface)) {
uint32_t score = 0;
Vk::ScorePhysicalDevice(device, &score);
devices.insert(std::make_pair(score, device));
LogInternal::Debug("Device \"{}\" scored {}", Vk::GetPhysicalDeviceName(device), score);
}
}
if (devices.rbegin()->first > 0) {
m_PhysicalDevice = devices.rbegin()->second;
} else {
LogInternal::Fatal("Failed to find a physical device that can support this engine.");
}
}
void VkGraphicsContext::CreateLogicalDevice() {
VkDeviceCreateInfo createInfo{};
createInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
VkPhysicalDeviceFeatures deviceFeatures{};
deviceFeatures.geometryShader = true;
createInfo.pEnabledFeatures = &deviceFeatures;
Vk::QueueFamilyIndices indices = Vk::FindQueueFamilies(m_PhysicalDevice, m_Surface);
std::set<uint32_t> queueFamilies = {indices.GraphicsFamily.value(), indices.PresentFamily.value()};
std::vector<VkDeviceQueueCreateInfo> queueCreateInfos;
float queuePriorities = 1.0f;
for (const auto& queueFamily : queueFamilies) {
VkDeviceQueueCreateInfo queueCreateInfo{};
queueCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
queueCreateInfo.pQueuePriorities = &queuePriorities;
queueCreateInfo.queueFamilyIndex = queueFamily;
queueCreateInfo.queueCount = 1;
queueCreateInfos.push_back(queueCreateInfo);
}
createInfo.queueCreateInfoCount = static_cast<uint32_t>(queueCreateInfos.size());
createInfo.pQueueCreateInfos = queueCreateInfos.data();
Vk::Validate(
vkCreateDevice(m_PhysicalDevice, &createInfo, nullptr, &m_Device),
true,
"VkDevice VkGraphicsQueue::m_Device failed to create.");
vkGetDeviceQueue(m_Device, indices.GraphicsFamily.value(), 0, &m_GraphicsQueue);
vkGetDeviceQueue(m_Device, indices.PresentFamily.value(), 0, &m_PresentQueue);
}
void VkGraphicsContext::CreateSwapchain() {
Vk::SwapChainSupportDetails swapChainSupport = Vk::QuerySwapChainSupport(m_PhysicalDevice, m_Surface);
VkSurfaceFormatKHR surfaceFormat = Vk::ChooseSwapSurfaceFormat(swapChainSupport.Formats);
VkPresentModeKHR presentMode = Vk::ChooseSwapPresentMode(swapChainSupport.PresentModes);
VkExtent2D extent = Vk::ChooseSwapExtent(m_Window, swapChainSupport.Capabilities);
uint32_t imageCount = swapChainSupport.Capabilities.minImageCount + 1;
if (swapChainSupport.Capabilities.maxImageCount > 0 && imageCount > swapChainSupport.Capabilities.maxImageCount) {
imageCount = swapChainSupport.Capabilities.maxImageCount;
}
VkSwapchainCreateInfoKHR createInfo{};
createInfo.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
createInfo.surface = m_Surface;
createInfo.minImageCount = imageCount;
createInfo.imageFormat = surfaceFormat.format;
createInfo.imageColorSpace = surfaceFormat.colorSpace;
createInfo.imageArrayLayers = 1;
createInfo.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
Vk::QueueFamilyIndices indices = Vk::FindQueueFamilies(m_PhysicalDevice, m_Surface);
uint32_t queueFamilyIncices[] = {indices.GraphicsFamily.value(), indices.PresentFamily.value()};
if (indices.GraphicsFamily != indices.PresentFamily) {
createInfo.imageSharingMode = VK_SHARING_MODE_CONCURRENT;
createInfo.queueFamilyIndexCount = 2;
createInfo.pQueueFamilyIndices = queueFamilyIncices;
} else {
createInfo.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
}
createInfo.preTransform = swapChainSupport.Capabilities.currentTransform;
createInfo.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
}
}

View File

@ -6,23 +6,41 @@
#include <Platform/Vulkan/VkBase.h>
#include <vulkan/vulkan.h>
#include <map>
#include <set>
namespace Nitric {
class VkGraphicsContext : public GraphicsContext {
public:
explicit VkGraphicsContext(Ref<VkWindow> window);
~VkGraphicsContext();
~VkGraphicsContext() override;
void Init() override;
private:
void CreateInstance();
void CreateDebugMessenger();
void PickPhysicalDevice();
void CreateLogicalDevice();
void CreateSwapchain();
static bool CheckValidationLayerSupport();
static std::vector<const char*> s_ValidationLayers;
Ref<VkWindow> m_Window;
VkInstance m_Instance{};
VkPhysicalDevice m_PhysicalDevice;
VkSurfaceKHR m_Surface;
VkDevice m_Device;
VkQueue m_GraphicsQueue;
VkQueue m_PresentQueue;
VkDebugUtilsMessengerEXT m_DebugMessenger{};
};
}

View File

@ -2,17 +2,87 @@
namespace Nitric {
VkWindow::VkWindow(const WindowProperties& properties) {
m_Size = glm::vec2(properties.Width, properties.Height);
m_Title = properties.Title;
if (!glfwInit()) {
LogInternal::Fatal("glfwInit() failed to create. This is critical for making a window.");
}
glfwWindowHint(GLFW_CLIENT_API, GLFW_NO_API);
m_Window = glfwCreateWindow(
static_cast<int>(m_Size.x),
static_cast<int>(m_Size.y),
m_Title.c_str(),
nullptr,
nullptr);
glfwSetWindowUserPointer(m_Window, this);
glfwSetWindowCloseCallback(m_Window, [](GLFWwindow* window) {
EventData data{};
data.Event = WindowEvent::CLOSE;
auto self = static_cast<VkWindow *>(glfwGetWindowUserPointer(window));
self->DispatchEvent(data);
});
}
std::vector<const char *> VkWindow::GetExtensions() {
uint32_t extensionCount;
uint32_t extensionCount = 0;
auto extensions = glfwGetRequiredInstanceExtensions(&extensionCount);
std::vector<const char*> returnExt(extensionCount);
for (int i = 0; i < extensionCount; i++) {
returnExt.push_back(extensions[i]);
std::vector<const char*> returnExt(extensions, extensions + extensionCount);
if (VkValidationLayersEnabled) {
returnExt.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
}
return returnExt;
}
glm::vec2 VkWindow::GetSize() {
return m_Size;
}
void VkWindow::SetSize(const glm::vec2 &size) {
m_Size = size;
glfwSetWindowSize(
m_Window,
static_cast<int>(size.x),
static_cast<int>(size.y));
}
void VkWindow::Update() {
glfwPollEvents();
}
void VkWindow::AttachEventHandler(EventHandler handler) {
m_Handlers.push_back(handler);
}
void VkWindow::DispatchEvent(EventData& event) {
for (const auto& handler : m_Handlers) {
handler(event);
}
}
VkWindow::~VkWindow() {
glfwDestroyWindow(m_Window);
glfwTerminate();
}
void VkWindow::CreateSurface(VkInstance instance, VkSurfaceKHR *pSurface) {
Vk::Validate(
glfwCreateWindowSurface(instance, this->m_Window, nullptr, pSurface),
true,
"VkWindow::CreateSurface(VkInstance, VkSurfaceKHR*) failed to create vulkan surface.");
}
glm::vec2 VkWindow::GetFramebufferSize() {
int width, height;
glfwGetFramebufferSize(m_Window, &width, &height);
return glm::vec2(width, height);
}
}

View File

@ -1,6 +1,8 @@
#pragma once
#include <Core/Window.h>
#include <Core/LogInternal.h>
#include <Platform/Vulkan/VkBase.h>
#include <vector>
#define GLFW_INCLUDE_VULKAN
@ -10,7 +12,26 @@ namespace Nitric {
class VkWindow : public Window {
public:
explicit VkWindow(const WindowProperties& properties);
~VkWindow() override;
virtual std::vector<const char*> GetExtensions() = 0;
std::vector<const char*> GetExtensions();
glm::vec2 GetSize() override;
void SetSize(const glm::vec2& size) override;
void Update() override;
void AttachEventHandler(EventHandler handler) override;
void CreateSurface(VkInstance instance, VkSurfaceKHR* pSurface);
glm::vec2 GetFramebufferSize();
private:
void DispatchEvent(EventData& event);
glm::vec2 m_Size;
std::string m_Title;
GLFWwindow* m_Window;
std::vector<EventHandler> m_Handlers;
};
}

View File

@ -1,4 +1,6 @@
#include "GraphicsAPI.h"
#include <Render/GraphicsAPI.h>
#include <Core/LogInternal.h>
namespace Nitric {
GraphicsAPI::API GraphicsAPI::s_API = None;
@ -9,9 +11,12 @@ namespace Nitric {
GraphicsAPI::API GraphicsAPI::GetAPI() {
if (s_API == None) {
spdlog::critical("Graphics API requested but not set. Failing here.");
throw std::runtime_error("GraphicsAPI = None;");
LogInternal::Fatal("Graphics API requested but not set. Failing here.");
}
return s_API;
}
GraphicsAPI::API GraphicsAPI::NoErrGetAPI() {
return s_API;
}
}

View File

@ -1,7 +1,5 @@
#pragma once
#include <spdlog/spdlog.h>
namespace Nitric {
class GraphicsAPI {
@ -11,7 +9,7 @@ namespace Nitric {
None
};
static inline std::string& APIToString(API api) {
static inline const char* APIToString(API api) {
switch (api) {
case Vulkan:
return "Vulkan";
@ -22,6 +20,7 @@ namespace Nitric {
static void SetAPI(API api);
static API GetAPI();
static API NoErrGetAPI();
private:
static API s_API;

View File

@ -0,0 +1,17 @@
#include <Render/GraphicsContext.h>
#include <Platform/Vulkan/VkGraphicsContext.h>
#include <utility>
namespace Nitric {
Scope<GraphicsContext> GraphicsContext::Create(Ref<Window> window) {
switch (GraphicsAPI::GetAPI()) {
case GraphicsAPI::Vulkan:
return CreateScope<VkGraphicsContext>(CastPtr<VkWindow>(std::move(window)));
default:
break;
}
return nullptr;
}
}

View File

@ -1,11 +1,17 @@
#pragma once
#include <Core/Base.h>
#include <Render/GraphicsAPI.h>
#include <Core/Window.h>
namespace Nitric {
class GraphicsContext {
public:
virtual ~GraphicsContext() = default;
virtual void Init() = 0;
static Scope<GraphicsContext> Create(Ref<Window> window);
};
}

View File

@ -1,10 +1,12 @@
#include <Core/Application.h>
#include <Core/EntryPoint.h>
#include <Render/GraphicsAPI.h>
class NitricEngine : public Nitric::Application {
public:
NitricEngine(const Nitric::ApplicationSettings& settings) : Application(settings) {
}
};
@ -14,5 +16,7 @@ Nitric::Application* Nitric::CreateApplication() {
settings.Height = 720;
settings.Title = "Nitric Engine";
Nitric::GraphicsAPI::SetAPI(Nitric::GraphicsAPI::Vulkan);
return new NitricEngine(settings);
}