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(spdlog CONFIG REQUIRED)
find_package(Vulkan REQUIRED) find_package(Vulkan REQUIRED)
find_package(glm CONFIG REQUIRED) find_package(glm CONFIG REQUIRED)
find_package(glfw3 CONFIG REQUIRED)
add_library (Nitric STATIC ${SourceFiles}) add_library (Nitric STATIC ${SourceFiles})
target_link_libraries(Nitric PRIVATE glm::glm) target_link_libraries(Nitric PRIVATE glm::glm)
target_link_libraries(Nitric PRIVATE spdlog::spdlog) target_link_libraries(Nitric PRIVATE spdlog::spdlog)
target_link_libraries(Nitric PRIVATE Vulkan::Vulkan) target_link_libraries(Nitric PRIVATE Vulkan::Vulkan)
target_link_libraries(Nitric PRIVATE glfw)
target_include_directories(Nitric PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/Source") target_include_directories(Nitric PUBLIC "${CMAKE_CURRENT_SOURCE_DIR}/Source")
set(NITRIC_LIBRARY "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/Nitric.lib") set(NITRIC_LIBRARY "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/Nitric.lib")

View File

@ -1,11 +1,53 @@
#include <Core/Application.h> #include <Core/Application.h>
namespace Nitric { 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() { 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 <Render/GraphicsContext.h>
#include <Core/Window.h> #include <Core/Window.h>
#include <Core/LogInternal.h>
namespace Nitric { namespace Nitric {
@ -17,11 +18,22 @@ namespace Nitric {
class Application { class Application {
public: public:
explicit Application(const ApplicationSettings& settings); explicit Application(const ApplicationSettings& settings);
~Application();
void Run(); 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(); static Application* CreateApplication();

View File

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

View File

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

View File

@ -13,13 +13,13 @@ namespace Nitric {
template <typename T> template <typename T>
static void Debug(T& msg) { static void Debug(T& msg) {
Get()->debug(msg); Get()->log(spdlog::level::debug, msg);
} }
template<typename... Args> template<typename... Args>
static void Debug(spdlog::format_string_t<Args...> fmt, Args &&...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> template <typename T>
@ -30,7 +30,7 @@ namespace Nitric {
template<typename... Args> template<typename... Args>
static void Info(spdlog::format_string_t<Args...> fmt, Args &&...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> template <typename T>
@ -41,14 +41,14 @@ namespace Nitric {
template<typename... Args> template<typename... Args>
static void Critical(spdlog::format_string_t<Args...> fmt, Args &&...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> template <typename T>
static void Fatal(T& msg) { static void Fatal(T& msg) {
LogInternal::Error("The next error is fatal. This will terminate the program"); LogInternal::Error("The next error is fatal. This will terminate the program");
Get()->log(spdlog::level::from_str("fatal"), msg); Get()->log(spdlog::level::critical, msg);
auto api = GraphicsAPI::APIToString(GraphicsAPI::GetAPI()); auto api = GraphicsAPI::APIToString(GraphicsAPI::NoErrGetAPI());
Info("GraphicsAPI is set to {}", api); Info("GraphicsAPI is set to {}", api);
throw std::runtime_error(msg); throw std::runtime_error(msg);
} }
@ -56,7 +56,11 @@ namespace Nitric {
template<typename... Args> template<typename... Args>
static void Fatal(spdlog::format_string_t<Args...> fmt, Args &&...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> template <typename T>
@ -67,7 +71,18 @@ namespace Nitric {
template<typename... Args> template<typename... Args>
static void Error(spdlog::format_string_t<Args...> fmt, Args &&...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: 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 <string>
#include <cstdint> #include <cstdint>
#include <glm/glm.hpp> #include <glm/glm.hpp>
#include <functional>
namespace Nitric { namespace Nitric {
struct WindowProperties { struct WindowProperties {
@ -12,9 +13,27 @@ namespace Nitric {
uint32_t Height; uint32_t Height;
std::string Title; std::string Title;
}; };
enum class WindowEvent {
CLOSE
};
struct EventData {
WindowEvent Event;
};
using EventHandler =
std::function<void(EventData&)>;
class Window { class Window {
public: public:
virtual ~Window() = default;
virtual void SetSize(const glm::vec2& size) = 0; 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/vulkan.h>
#include <vulkan/vk_enum_string_helper.h> #include <vulkan/vk_enum_string_helper.h>
#include <set>
#include <limits>
#include <algorithm>
#include <Core/LogInternal.h> #include <Core/LogInternal.h>
#include <Core/Window.h>
namespace Nitric { namespace Nitric {
#ifdef NDEBUG #ifdef NDEBUG
@ -12,18 +17,55 @@ namespace Nitric {
#endif #endif
namespace Vk { namespace Vk {
template <typename ...Args> void Validate(VkResult result, bool fatal, const std::string& msg);
void Validate(VkResult result, bool fatal, spdlog::format_string_t<Args...> fmt, Args &&...args) { void PopulateDebugCreateInfo(VkDebugUtilsMessengerCreateInfoEXT& createInfo);
if (result != VK_SUCCESS) {
auto error = string_VkResult(result); static VKAPI_ATTR VkBool32 VKAPI_CALL DebugCallbacks(
if (fatal) { VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity,
LogInternal::Fatal("({}) -> {}", error, fmt::format(fmt, std::forward<Args>(args)...)) VkDebugUtilsMessageTypeFlagsEXT messageType,
} else { const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData,
LogInternal::Error("({}) -> {}", error, fmt::format(fmt, std::forward<Args>(args)...)) void* pUserData);
}
} else { VkResult CreateDebugUtilsMessenger(
LogInternal::Debug("Vk::Validate success.") 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() { void VkGraphicsContext::Init() {
LogInternal::Debug("Creating instance");
this->CreateInstance(); 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() { void VkGraphicsContext::CreateInstance() {
if (!CheckValidationLayerSupport() && VkValidationLayersEnabled) { if (!CheckValidationLayerSupport() && VkValidationLayersEnabled) {
LogInternal::Fatal("Requested validation layers but they were not available.");
} }
VkApplicationInfo appInfo{}; VkApplicationInfo appInfo{};
appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; appInfo.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
@ -36,12 +45,18 @@ namespace Nitric {
if (VkValidationLayersEnabled) { if (VkValidationLayersEnabled) {
createInfo.enabledLayerCount = static_cast<uint32_t>(s_ValidationLayers.size()); createInfo.enabledLayerCount = static_cast<uint32_t>(s_ValidationLayers.size());
createInfo.ppEnabledLayerNames = s_ValidationLayers.data(); createInfo.ppEnabledLayerNames = s_ValidationLayers.data();
VkDebugUtilsMessengerCreateInfoEXT debugCreateInfo{};
Vk::PopulateDebugCreateInfo(debugCreateInfo);
createInfo.pNext = &debugCreateInfo;
} else { } else {
createInfo.enabledLayerCount = 0; createInfo.enabledLayerCount = 0;
createInfo.ppEnabledLayerNames = nullptr; 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.");
} }
@ -71,5 +86,124 @@ namespace Nitric {
"VK_LAYER_KHRONOS_validation" "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 <Platform/Vulkan/VkBase.h>
#include <vulkan/vulkan.h> #include <vulkan/vulkan.h>
#include <map>
#include <set>
namespace Nitric { namespace Nitric {
class VkGraphicsContext : public GraphicsContext { class VkGraphicsContext : public GraphicsContext {
public: public:
explicit VkGraphicsContext(Ref<VkWindow> window); explicit VkGraphicsContext(Ref<VkWindow> window);
~VkGraphicsContext(); ~VkGraphicsContext() override;
void Init() override; void Init() override;
private: private:
void CreateInstance(); void CreateInstance();
void CreateDebugMessenger();
void PickPhysicalDevice();
void CreateLogicalDevice();
void CreateSwapchain();
static bool CheckValidationLayerSupport(); static bool CheckValidationLayerSupport();
static std::vector<const char*> s_ValidationLayers; static std::vector<const char*> s_ValidationLayers;
Ref<VkWindow> m_Window; Ref<VkWindow> m_Window;
VkInstance m_Instance{}; 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 { namespace Nitric {
VkWindow::VkWindow(const WindowProperties& properties) { 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() { std::vector<const char *> VkWindow::GetExtensions() {
uint32_t extensionCount; uint32_t extensionCount = 0;
auto extensions = glfwGetRequiredInstanceExtensions(&extensionCount); auto extensions = glfwGetRequiredInstanceExtensions(&extensionCount);
std::vector<const char*> returnExt(extensionCount); std::vector<const char*> returnExt(extensions, extensions + extensionCount);
for (int i = 0; i < extensionCount; i++) { if (VkValidationLayersEnabled) {
returnExt.push_back(extensions[i]); returnExt.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
} }
return returnExt; 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 #pragma once
#include <Core/Window.h> #include <Core/Window.h>
#include <Core/LogInternal.h>
#include <Platform/Vulkan/VkBase.h>
#include <vector> #include <vector>
#define GLFW_INCLUDE_VULKAN #define GLFW_INCLUDE_VULKAN
@ -10,7 +12,26 @@ namespace Nitric {
class VkWindow : public Window { class VkWindow : public Window {
public: public:
explicit VkWindow(const WindowProperties& properties); 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 { namespace Nitric {
GraphicsAPI::API GraphicsAPI::s_API = None; GraphicsAPI::API GraphicsAPI::s_API = None;
@ -9,9 +11,12 @@ namespace Nitric {
GraphicsAPI::API GraphicsAPI::GetAPI() { GraphicsAPI::API GraphicsAPI::GetAPI() {
if (s_API == None) { if (s_API == None) {
spdlog::critical("Graphics API requested but not set. Failing here."); LogInternal::Fatal("Graphics API requested but not set. Failing here.");
throw std::runtime_error("GraphicsAPI = None;");
} }
return s_API; return s_API;
} }
GraphicsAPI::API GraphicsAPI::NoErrGetAPI() {
return s_API;
}
} }

View File

@ -1,7 +1,5 @@
#pragma once #pragma once
#include <spdlog/spdlog.h>
namespace Nitric { namespace Nitric {
class GraphicsAPI { class GraphicsAPI {
@ -11,7 +9,7 @@ namespace Nitric {
None None
}; };
static inline std::string& APIToString(API api) { static inline const char* APIToString(API api) {
switch (api) { switch (api) {
case Vulkan: case Vulkan:
return "Vulkan"; return "Vulkan";
@ -22,6 +20,7 @@ namespace Nitric {
static void SetAPI(API api); static void SetAPI(API api);
static API GetAPI(); static API GetAPI();
static API NoErrGetAPI();
private: private:
static API s_API; 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 #pragma once
#include <Core/Base.h>
#include <Render/GraphicsAPI.h>
#include <Core/Window.h>
namespace Nitric { namespace Nitric {
class GraphicsContext { class GraphicsContext {
public: public:
virtual ~GraphicsContext() = default;
virtual void Init() = 0; virtual void Init() = 0;
static Scope<GraphicsContext> Create(Ref<Window> window);
}; };
} }

View File

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