Swapchain almost finished.
This commit is contained in:
parent
ce5fd7e93d
commit
11725fb9f4
|
|
@ -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")
|
||||
|
|
|
|||
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
|
@ -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();
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -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() {
|
||||
|
|
|
|||
|
|
@ -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:
|
||||
|
|
|
|||
16
Nitric/Source/Core/Window.cpp
Normal file
16
Nitric/Source/Core/Window.cpp
Normal 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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
};
|
||||
}
|
||||
223
Nitric/Source/Platform/Vulkan/VkBase.cpp
Normal file
223
Nitric/Source/Platform/Vulkan/VkBase.cpp
Normal 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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{};
|
||||
};
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
};
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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;
|
||||
|
|
|
|||
17
Nitric/Source/Render/GraphicsContext.cpp
Normal file
17
Nitric/Source/Render/GraphicsContext.cpp
Normal 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;
|
||||
}
|
||||
}
|
||||
|
|
@ -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);
|
||||
};
|
||||
|
||||
}
|
||||
|
|
@ -1,6 +1,8 @@
|
|||
#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);
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user