파일 분리, 셰이더 로드까지 수강.
This commit is contained in:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -1,3 +1,2 @@
|
|||||||
.vs
|
.vs
|
||||||
build/*
|
|
||||||
out
|
out
|
||||||
@@ -3,6 +3,7 @@ project(VulkanEngine)
|
|||||||
|
|
||||||
find_package(Vulkan REQUIRED)
|
find_package(Vulkan REQUIRED)
|
||||||
|
|
||||||
|
include(cmake/Shaders.cmake)
|
||||||
include(FetchContent)
|
include(FetchContent)
|
||||||
|
|
||||||
FetchContent_Declare(
|
FetchContent_Declare(
|
||||||
@@ -53,4 +54,12 @@ target_link_libraries(VulkanEngine PRIVATE spdlog)
|
|||||||
target_include_directories(VulkanEngine PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/include")
|
target_include_directories(VulkanEngine PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/include")
|
||||||
target_compile_features(VulkanEngine PRIVATE cxx_std_20)
|
target_compile_features(VulkanEngine PRIVATE cxx_std_20)
|
||||||
|
|
||||||
target_precompile_headers(VulkanEngine PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/include/precomp.h")
|
target_precompile_headers(VulkanEngine PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/include/precomp.h")
|
||||||
|
|
||||||
|
file(GLOB_RECURSE ShaderSources CONFIGURE_DEPENDS
|
||||||
|
"${CMAKE_CURRENT_SOURCE_DIR}/shaders/*.vert"
|
||||||
|
"${CMAKE_CURRENT_SOURCE_DIR}/shaders/*.frag"
|
||||||
|
)
|
||||||
|
|
||||||
|
add_shaders(VulkanEngineShaders ${ShaderSources})
|
||||||
|
add_dependencies(VulkanEngine VulkanEngineShaders)
|
||||||
31
cmake/Shaders.cmake
Normal file
31
cmake/Shaders.cmake
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
function(add_shaders TARGET_NAME)
|
||||||
|
set(SHADER_SOURCE_FILES ${ARGN})
|
||||||
|
list(LENGTH SHADER_SOURCE_FILES FILE_COUNT)
|
||||||
|
if(FILE_COUNT EQUAL 0)
|
||||||
|
message(FATAL_ERROR "Cannot add shaders target without shader files!")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
set(SHADER_COMMANDS)
|
||||||
|
set(SHADER_PRODUCTS)
|
||||||
|
|
||||||
|
foreach(SHADER_SOURCE IN LISTS SHADER_SOURCE_FILES)
|
||||||
|
cmake_path(ABSOLUTE_PATH SHADER_SOURCE NORMALIZE)
|
||||||
|
cmake_path(GET SHADER_SOURCE FILENAME SHADER_NAME)
|
||||||
|
|
||||||
|
# COMMANDS
|
||||||
|
list(APPEND SHADER_COMMANDS COMMAND)
|
||||||
|
list(APPEND SHADER_COMMANDS Vulkan::glslc)
|
||||||
|
list(APPEND SHADER_COMMANDS "${SHADER_SOURCE}")
|
||||||
|
list(APPEND SHADER_COMMANDS "-o")
|
||||||
|
list(APPEND SHADER_COMMANDS "${CMAKE_CURRENT_BINARY_DIR}/${SHADER_NAME}.spv")
|
||||||
|
# PRODUCTS
|
||||||
|
list(APPEND SHADER_PRODUCTS "${CMAKE_CURRENT_BINARY_DIR}/${SHADER_NAME}.spv")
|
||||||
|
endforeach()
|
||||||
|
|
||||||
|
add_custom_target(${TARGET_NAME} ALL
|
||||||
|
${SHADER_COMMANDS}
|
||||||
|
COMMENT "Compiling shaders..."
|
||||||
|
SOURCES ${SHADER_SOURCE_FILES}
|
||||||
|
BYPRODUCTS ${SHADER_PRODUCTS}
|
||||||
|
)
|
||||||
|
endfunction()
|
||||||
@@ -10,6 +10,7 @@ class Window {
|
|||||||
~Window();
|
~Window();
|
||||||
|
|
||||||
glm::ivec2 GetWindowSize() const;
|
glm::ivec2 GetWindowSize() const;
|
||||||
|
glm::ivec2 GetFramebufferSize() const;
|
||||||
bool ShouldClose() const;
|
bool ShouldClose() const;
|
||||||
GLFWwindow* GetHandle() const;
|
GLFWwindow* GetHandle() const;
|
||||||
|
|
||||||
|
|||||||
@@ -2,12 +2,13 @@
|
|||||||
#include <cmath>
|
#include <cmath>
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <cstdlib>
|
#include <cstdlib>
|
||||||
|
#include <cstring>
|
||||||
#include <functional>
|
#include <functional>
|
||||||
#include <glm/glm.hpp>
|
#include <glm/glm.hpp>
|
||||||
#include <gsl/gsl>
|
#include <gsl/gsl>
|
||||||
|
#include <optional>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <cstring>
|
|
||||||
#include <string_view>
|
#include <string_view>
|
||||||
|
|
||||||
#include "utilities.h"
|
|
||||||
#include "spdlog/spdlog.h"
|
#include "spdlog/spdlog.h"
|
||||||
|
#include "utilities.h"
|
||||||
|
|||||||
@@ -1,5 +1,8 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <filesystem>
|
||||||
|
|
||||||
namespace veng {
|
namespace veng {
|
||||||
bool streq(gsl::czstring left, gsl::czstring right);
|
bool streq(gsl::czstring left, gsl::czstring right);
|
||||||
|
std::vector<std::uint8_t> ReadFile(std::filesystem::path shader_path);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
#include <vulkan/vulkan.h>
|
#include <vulkan/vulkan.h>
|
||||||
|
|
||||||
#include "glfw/glfw_window.h"
|
#include "glfw/glfw_window.h"
|
||||||
|
#include "precomp.h"
|
||||||
|
|
||||||
namespace veng {
|
namespace veng {
|
||||||
class Graphics final {
|
class Graphics final {
|
||||||
@@ -9,9 +10,33 @@ class Graphics final {
|
|||||||
Graphics(gsl::not_null<Window *> window);
|
Graphics(gsl::not_null<Window *> window);
|
||||||
~Graphics();
|
~Graphics();
|
||||||
|
|
||||||
|
private:
|
||||||
|
struct QueueFamilyIndices {
|
||||||
|
std::optional<std::uint32_t> graphics_family = std::nullopt;
|
||||||
|
std::optional<std::uint32_t> presentation_family = std::nullopt;
|
||||||
|
|
||||||
|
bool IsValid() const {
|
||||||
|
return graphics_family.has_value() && presentation_family.has_value();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct SwapChainProperties {
|
||||||
|
VkSurfaceCapabilitiesKHR capabilities;
|
||||||
|
std::vector<VkSurfaceFormatKHR> formats;
|
||||||
|
std::vector<VkPresentModeKHR> present_modes;
|
||||||
|
|
||||||
|
bool IsValid() const { return !formats.empty() && !present_modes.empty(); }
|
||||||
|
};
|
||||||
|
|
||||||
void InitializeVulkan();
|
void InitializeVulkan();
|
||||||
void CreateInstance();
|
void CreateInstance();
|
||||||
void SetupDebugMessenger();
|
void SetupDebugMessenger();
|
||||||
|
void PickPhysicalDevice();
|
||||||
|
void CreateLogicalDeviceAndQueues();
|
||||||
|
void CreateSurface();
|
||||||
|
void CreateSwapChain();
|
||||||
|
void CreateImageViews();
|
||||||
|
void CreateGraphicsPipeline();
|
||||||
|
|
||||||
std::vector<gsl::czstring> GetRequiredInstanceExtentions();
|
std::vector<gsl::czstring> GetRequiredInstanceExtentions();
|
||||||
|
|
||||||
@@ -22,9 +47,48 @@ class Graphics final {
|
|||||||
static std::vector<VkLayerProperties> GetSupprotedValidationLayers();
|
static std::vector<VkLayerProperties> GetSupprotedValidationLayers();
|
||||||
static bool AreAllLayersSupported(gsl::span<gsl::czstring> extensions);
|
static bool AreAllLayersSupported(gsl::span<gsl::czstring> extensions);
|
||||||
|
|
||||||
VkInstance instance_ = nullptr;
|
QueueFamilyIndices FindQueueFamilies(VkPhysicalDevice device);
|
||||||
|
SwapChainProperties GetSwapChainProperties(VkPhysicalDevice device);
|
||||||
|
bool IsDeviceSuitable(VkPhysicalDevice device);
|
||||||
|
std::vector<VkPhysicalDevice> GetAvailableDevices();
|
||||||
|
bool AreAllDeviceExtensionsSupported(VkPhysicalDevice device);
|
||||||
|
std::vector<VkExtensionProperties> GetDeviceAvailableExtensions(
|
||||||
|
VkPhysicalDevice device);
|
||||||
|
|
||||||
|
VkSurfaceFormatKHR ChooseSwapSurfaceFormat(
|
||||||
|
gsl::span<VkSurfaceFormatKHR> formats);
|
||||||
|
VkPresentModeKHR ChooseSwapPresentMode(
|
||||||
|
gsl::span<VkPresentModeKHR> present_modes);
|
||||||
|
VkExtent2D ChooseSwapExtent(const VkSurfaceCapabilitiesKHR& capabilities);
|
||||||
|
std::uint32_t ChooseSwapImageCount(const VkSurfaceCapabilitiesKHR& capabilities);
|
||||||
|
|
||||||
|
VkShaderModule CreateShaderModule(gsl::span<std::uint8_t> buffer);
|
||||||
|
|
||||||
|
std::array<gsl::czstring, 1> required_device_extentions_ = {
|
||||||
|
VK_KHR_SWAPCHAIN_EXTENSION_NAME};
|
||||||
|
|
||||||
|
VkInstance instance_ = VK_NULL_HANDLE;
|
||||||
VkDebugUtilsMessengerEXT debug_messenger_;
|
VkDebugUtilsMessengerEXT debug_messenger_;
|
||||||
|
|
||||||
|
VkPhysicalDevice physical_device_ = VK_NULL_HANDLE;
|
||||||
|
VkDevice logical_device_ = VK_NULL_HANDLE;
|
||||||
|
VkQueue graphics_queue_ = VK_NULL_HANDLE;
|
||||||
|
VkQueue present_queue_ = VK_NULL_HANDLE;
|
||||||
|
|
||||||
|
VkSurfaceKHR surface_ = VK_NULL_HANDLE;
|
||||||
|
VkSwapchainKHR swap_chain_ = VK_NULL_HANDLE;
|
||||||
|
VkSurfaceFormatKHR surface_format_;
|
||||||
|
VkPresentModeKHR present_mode_;
|
||||||
|
VkExtent2D extent_;
|
||||||
|
std::vector<VkImage> swap_chain_images_;
|
||||||
|
std::vector<VkImageView> swap_chain_image_views_;
|
||||||
|
|
||||||
gsl::not_null<Window *> window_;
|
gsl::not_null<Window *> window_;
|
||||||
bool validation_enabled_ = false;
|
bool validation_enabled_ = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
VkDebugUtilsMessengerCreateInfoEXT GetCreateMessengerInfo();
|
||||||
|
bool IsExtensionSupported(gsl::span<VkExtensionProperties> extensions,
|
||||||
|
gsl::czstring name);
|
||||||
|
|
||||||
} // namespace veng
|
} // namespace veng
|
||||||
|
|||||||
8
shaders/basic.frag
Normal file
8
shaders/basic.frag
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
#version 450
|
||||||
|
#include "common.glsl"
|
||||||
|
|
||||||
|
layout(location = 0) out vec4 out_color;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
out_color = vec4(1.0, 0.0, 0.5, 1.0);
|
||||||
|
}
|
||||||
13
shaders/basic.vert
Normal file
13
shaders/basic.vert
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
#version 450
|
||||||
|
#include "common.glsl"
|
||||||
|
|
||||||
|
vec2 hardcoded_positions[3] = vec2[](
|
||||||
|
vec2(0.0, -0.5),
|
||||||
|
vec2(0.5, 0.5),
|
||||||
|
vec2(-0.5, 0.5)
|
||||||
|
);
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
vec2 current_Position = hardcoded_positions[gl_VertexIndex];
|
||||||
|
gl_Position = vec4(current_Position, 0.0, 1.0);
|
||||||
|
}
|
||||||
1
shaders/common.glsl
Normal file
1
shaders/common.glsl
Normal file
@@ -0,0 +1 @@
|
|||||||
|
#extension GL_KHR_vulkan_glsl : enable
|
||||||
@@ -22,6 +22,12 @@ glm::ivec2 Window::GetWindowSize() const {
|
|||||||
return window_size;
|
return window_size;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
glm::ivec2 Window::GetFramebufferSize() const {
|
||||||
|
glm::ivec2 size;
|
||||||
|
glfwGetFramebufferSize(window_, &size.x, &size.y);
|
||||||
|
return size;
|
||||||
|
}
|
||||||
|
|
||||||
bool Window::ShouldClose() const { return glfwWindowShouldClose(window_); }
|
bool Window::ShouldClose() const { return glfwWindowShouldClose(window_); }
|
||||||
|
|
||||||
GLFWwindow* Window::GetHandle() const { return window_; }
|
GLFWwindow* Window::GetHandle() const { return window_; }
|
||||||
|
|||||||
@@ -1,9 +1,28 @@
|
|||||||
#include "utilities.h"
|
#include "utilities.h"
|
||||||
|
|
||||||
|
#include <fstream>
|
||||||
|
|
||||||
#include "precomp.h"
|
#include "precomp.h"
|
||||||
|
|
||||||
namespace veng {
|
namespace veng {
|
||||||
bool streq(gsl::czstring left, gsl::czstring right) {
|
bool streq(gsl::czstring left, gsl::czstring right) {
|
||||||
return std::strcmp(left, right) == 0;
|
return std::strcmp(left, right) == 0;
|
||||||
}
|
}
|
||||||
|
std::vector<std::uint8_t> ReadFile(std::filesystem::path shader_path) {
|
||||||
|
if (!std::filesystem::exists(shader_path)) return {};
|
||||||
|
if (!std::filesystem::is_regular_file(shader_path)) return {};
|
||||||
|
|
||||||
|
std::ifstream file(shader_path, std::ios::binary);
|
||||||
|
|
||||||
|
if (!file.is_open()) return {};
|
||||||
|
|
||||||
|
std::uintmax_t file_size = std::filesystem::file_size(shader_path);
|
||||||
|
if (file_size > std::numeric_limits<std::uint32_t>::max()) return {}; // 방어
|
||||||
|
|
||||||
|
std::uint32_t size = static_cast<std::uint32_t>(file_size);
|
||||||
|
std::vector<std::uint8_t> buffer(size);
|
||||||
|
file.read(reinterpret_cast<char*>(buffer.data()), size);
|
||||||
|
|
||||||
|
return buffer;
|
||||||
|
}
|
||||||
} // namespace veng
|
} // namespace veng
|
||||||
|
|||||||
164
src/vulkan/devices_and_queues.cpp
Normal file
164
src/vulkan/devices_and_queues.cpp
Normal file
@@ -0,0 +1,164 @@
|
|||||||
|
#include <set>
|
||||||
|
|
||||||
|
#include "precomp.h"
|
||||||
|
#include "vulkan/graphics.h"
|
||||||
|
|
||||||
|
namespace veng {
|
||||||
|
|
||||||
|
Graphics::QueueFamilyIndices Graphics::FindQueueFamilies(
|
||||||
|
VkPhysicalDevice device) {
|
||||||
|
std::uint32_t queue_familiy_count = 0;
|
||||||
|
vkGetPhysicalDeviceQueueFamilyProperties(device, &queue_familiy_count,
|
||||||
|
nullptr);
|
||||||
|
std::vector<VkQueueFamilyProperties> families(queue_familiy_count);
|
||||||
|
vkGetPhysicalDeviceQueueFamilyProperties(device, &queue_familiy_count,
|
||||||
|
families.data());
|
||||||
|
|
||||||
|
auto graphics_family_it =
|
||||||
|
std::find_if(families.begin(), families.end(),
|
||||||
|
[](const VkQueueFamilyProperties& props) {
|
||||||
|
return props.queueFlags &
|
||||||
|
(VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_TRANSFER_BIT);
|
||||||
|
});
|
||||||
|
|
||||||
|
QueueFamilyIndices result;
|
||||||
|
result.graphics_family = graphics_family_it - families.begin();
|
||||||
|
|
||||||
|
for (std::uint32_t i = 0; i < families.size(); i++) {
|
||||||
|
VkBool32 has_presentation_support = false;
|
||||||
|
vkGetPhysicalDeviceSurfaceSupportKHR(device, i, surface_,
|
||||||
|
&has_presentation_support);
|
||||||
|
if (has_presentation_support) {
|
||||||
|
result.presentation_family = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
Graphics::SwapChainProperties Graphics::GetSwapChainProperties(
|
||||||
|
VkPhysicalDevice device) {
|
||||||
|
SwapChainProperties properties;
|
||||||
|
|
||||||
|
vkGetPhysicalDeviceSurfaceCapabilitiesKHR(device, surface_,
|
||||||
|
&properties.capabilities);
|
||||||
|
|
||||||
|
std::uint32_t format_count;
|
||||||
|
vkGetPhysicalDeviceSurfaceFormatsKHR(device, surface_, &format_count,
|
||||||
|
nullptr);
|
||||||
|
properties.formats.resize(format_count);
|
||||||
|
vkGetPhysicalDeviceSurfaceFormatsKHR(device, surface_, &format_count,
|
||||||
|
properties.formats.data());
|
||||||
|
|
||||||
|
std::uint32_t modes_count;
|
||||||
|
vkGetPhysicalDeviceSurfacePresentModesKHR(device, surface_, &modes_count,
|
||||||
|
nullptr);
|
||||||
|
properties.present_modes.resize(modes_count);
|
||||||
|
vkGetPhysicalDeviceSurfacePresentModesKHR(device, surface_, &modes_count,
|
||||||
|
properties.present_modes.data());
|
||||||
|
|
||||||
|
return properties;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<VkExtensionProperties> Graphics::GetDeviceAvailableExtensions(
|
||||||
|
VkPhysicalDevice device) {
|
||||||
|
std::uint32_t available_extentions_count;
|
||||||
|
vkEnumerateDeviceExtensionProperties(device, nullptr,
|
||||||
|
&available_extentions_count, nullptr);
|
||||||
|
std::vector<VkExtensionProperties> available_extentions(
|
||||||
|
available_extentions_count);
|
||||||
|
vkEnumerateDeviceExtensionProperties(device, nullptr,
|
||||||
|
&available_extentions_count,
|
||||||
|
available_extentions.data());
|
||||||
|
return available_extentions;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Graphics::AreAllDeviceExtensionsSupported(VkPhysicalDevice device) {
|
||||||
|
std::vector<VkExtensionProperties> available_extentions =
|
||||||
|
GetDeviceAvailableExtensions(device);
|
||||||
|
return std::all_of(
|
||||||
|
required_device_extentions_.begin(), required_device_extentions_.end(),
|
||||||
|
std::bind_front(IsExtensionSupported, available_extentions));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Graphics::IsDeviceSuitable(VkPhysicalDevice device) {
|
||||||
|
QueueFamilyIndices families = FindQueueFamilies(device);
|
||||||
|
return families.IsValid() && AreAllDeviceExtensionsSupported(device) &&
|
||||||
|
GetSwapChainProperties(device).IsValid();
|
||||||
|
}
|
||||||
|
|
||||||
|
void Graphics::PickPhysicalDevice() {
|
||||||
|
std::vector<VkPhysicalDevice> devices = GetAvailableDevices();
|
||||||
|
|
||||||
|
std::erase_if(
|
||||||
|
devices, std::not_fn(std::bind_front(&Graphics::IsDeviceSuitable, this)));
|
||||||
|
|
||||||
|
if (devices.empty()) {
|
||||||
|
spdlog::error("No physical devices that match the criteria");
|
||||||
|
std::exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
physical_device_ = devices[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<VkPhysicalDevice> Graphics::GetAvailableDevices() {
|
||||||
|
std::uint32_t device_count;
|
||||||
|
vkEnumeratePhysicalDevices(instance_, &device_count, nullptr);
|
||||||
|
|
||||||
|
if (device_count == 0) return {};
|
||||||
|
|
||||||
|
std::vector<VkPhysicalDevice> devices(device_count);
|
||||||
|
vkEnumeratePhysicalDevices(instance_, &device_count, devices.data());
|
||||||
|
|
||||||
|
return devices;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Graphics::CreateLogicalDeviceAndQueues() {
|
||||||
|
QueueFamilyIndices picked_device_families =
|
||||||
|
FindQueueFamilies(physical_device_);
|
||||||
|
|
||||||
|
if (!picked_device_families.IsValid()) {
|
||||||
|
std::exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::set<std::uint32_t> unique_queue_families = {
|
||||||
|
picked_device_families.graphics_family.value(),
|
||||||
|
picked_device_families.presentation_family.value(),
|
||||||
|
};
|
||||||
|
|
||||||
|
std::float_t queue_priority = 1.f;
|
||||||
|
|
||||||
|
std::vector<VkDeviceQueueCreateInfo> queue_create_infos;
|
||||||
|
for (std::uint32_t unique_queue_family : unique_queue_families) {
|
||||||
|
VkDeviceQueueCreateInfo queue_info = {};
|
||||||
|
queue_info.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;
|
||||||
|
queue_info.queueFamilyIndex = unique_queue_family;
|
||||||
|
queue_info.queueCount = 1;
|
||||||
|
queue_info.pQueuePriorities = &queue_priority;
|
||||||
|
queue_create_infos.push_back(queue_info);
|
||||||
|
}
|
||||||
|
|
||||||
|
VkPhysicalDeviceFeatures required_features = {};
|
||||||
|
|
||||||
|
VkDeviceCreateInfo device_info = {};
|
||||||
|
device_info.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
|
||||||
|
device_info.queueCreateInfoCount = queue_create_infos.size();
|
||||||
|
device_info.pQueueCreateInfos = queue_create_infos.data();
|
||||||
|
device_info.pEnabledFeatures = &required_features;
|
||||||
|
device_info.enabledExtensionCount = required_device_extentions_.size();
|
||||||
|
device_info.ppEnabledExtensionNames = required_device_extentions_.data();
|
||||||
|
|
||||||
|
VkResult result =
|
||||||
|
vkCreateDevice(physical_device_, &device_info, nullptr, &logical_device_);
|
||||||
|
if (result != VK_SUCCESS) std::exit(EXIT_FAILURE);
|
||||||
|
|
||||||
|
vkGetDeviceQueue(logical_device_,
|
||||||
|
picked_device_families.graphics_family.value(), 0,
|
||||||
|
&graphics_queue_);
|
||||||
|
vkGetDeviceQueue(logical_device_,
|
||||||
|
picked_device_families.presentation_family.value(), 0,
|
||||||
|
&present_queue_);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace veng
|
||||||
@@ -6,58 +6,8 @@
|
|||||||
|
|
||||||
#include "precomp.h"
|
#include "precomp.h"
|
||||||
|
|
||||||
VKAPI_ATTR VkResult VKAPI_CALL vkCreateDebugUtilsMessengerEXT(
|
|
||||||
VkInstance instance, const VkDebugUtilsMessengerCreateInfoEXT* pCreateInfo,
|
|
||||||
const VkAllocationCallbacks* pAllocator,
|
|
||||||
VkDebugUtilsMessengerEXT* pMessenger) {
|
|
||||||
PFN_vkCreateDebugUtilsMessengerEXT function =
|
|
||||||
reinterpret_cast<PFN_vkCreateDebugUtilsMessengerEXT>(
|
|
||||||
vkGetInstanceProcAddr(instance, "vkCreateDebugUtilsMessengerEXT"));
|
|
||||||
if (function != nullptr) {
|
|
||||||
return function(instance, pCreateInfo, pAllocator, pMessenger);
|
|
||||||
} else {
|
|
||||||
return VK_ERROR_EXTENSION_NOT_PRESENT;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
VKAPI_ATTR void VKAPI_CALL vkDestroyDebugUtilsMessengerEXT(
|
|
||||||
VkInstance instance, VkDebugUtilsMessengerEXT messenger,
|
|
||||||
const VkAllocationCallbacks* pAllocator) {
|
|
||||||
PFN_vkDestroyDebugUtilsMessengerEXT function =
|
|
||||||
reinterpret_cast<PFN_vkDestroyDebugUtilsMessengerEXT>(
|
|
||||||
vkGetInstanceProcAddr(instance, "vkDestroyDebugUtilsMessengerEXT"));
|
|
||||||
if (function != nullptr) function(instance, messenger, pAllocator);
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace veng {
|
namespace veng {
|
||||||
|
|
||||||
static VKAPI_ATTR VkBool32 VKAPI_CALL
|
|
||||||
ValidationCallback(VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity,
|
|
||||||
VkDebugUtilsMessageTypeFlagsEXT messageTypes,
|
|
||||||
const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData,
|
|
||||||
void* pUserData) {
|
|
||||||
if (messageSeverity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT) {
|
|
||||||
spdlog::warn("Vulkan Validation: {}", pCallbackData->pMessage);
|
|
||||||
} else {
|
|
||||||
spdlog::error("Vulkan Validation: {}", pCallbackData->pMessage);
|
|
||||||
}
|
|
||||||
return VK_FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
static VkDebugUtilsMessengerCreateInfoEXT GetCreateMessengerInfo() {
|
|
||||||
VkDebugUtilsMessengerCreateInfoEXT creation_info = {};
|
|
||||||
creation_info.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT;
|
|
||||||
creation_info.messageSeverity =
|
|
||||||
VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT |
|
|
||||||
VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT;
|
|
||||||
creation_info.messageType = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT |
|
|
||||||
VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT;
|
|
||||||
creation_info.pfnUserCallback = ValidationCallback;
|
|
||||||
creation_info.pUserData = nullptr;
|
|
||||||
|
|
||||||
return creation_info;
|
|
||||||
}
|
|
||||||
|
|
||||||
Graphics::Graphics(gsl::not_null<Window*> window) : window_(window) {
|
Graphics::Graphics(gsl::not_null<Window*> window) : window_(window) {
|
||||||
#if !defined(NDEBUG)
|
#if !defined(NDEBUG)
|
||||||
validation_enabled_ = true;
|
validation_enabled_ = true;
|
||||||
@@ -66,10 +16,18 @@ Graphics::Graphics(gsl::not_null<Window*> window) : window_(window) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Graphics::~Graphics() {
|
Graphics::~Graphics() {
|
||||||
if (instance_ != nullptr) {
|
if (logical_device_ != VK_NULL_HANDLE) {
|
||||||
if (debug_messenger_ != nullptr) {
|
for (VkImageView image_view : swap_chain_image_views_)
|
||||||
|
vkDestroyImageView(logical_device_, image_view, nullptr);
|
||||||
|
if (swap_chain_ != VK_NULL_HANDLE)
|
||||||
|
vkDestroySwapchainKHR(logical_device_, swap_chain_, nullptr);
|
||||||
|
vkDestroyDevice(logical_device_, nullptr);
|
||||||
|
}
|
||||||
|
if (instance_ != VK_NULL_HANDLE) {
|
||||||
|
if (surface_ != VK_NULL_HANDLE)
|
||||||
|
vkDestroySurfaceKHR(instance_, surface_, nullptr);
|
||||||
|
if (debug_messenger_ != VK_NULL_HANDLE)
|
||||||
vkDestroyDebugUtilsMessengerEXT(instance_, debug_messenger_, nullptr);
|
vkDestroyDebugUtilsMessengerEXT(instance_, debug_messenger_, nullptr);
|
||||||
}
|
|
||||||
vkDestroyInstance(instance_, nullptr);
|
vkDestroyInstance(instance_, nullptr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -77,140 +35,11 @@ Graphics::~Graphics() {
|
|||||||
void Graphics::InitializeVulkan() {
|
void Graphics::InitializeVulkan() {
|
||||||
CreateInstance();
|
CreateInstance();
|
||||||
SetupDebugMessenger();
|
SetupDebugMessenger();
|
||||||
}
|
CreateSurface();
|
||||||
|
PickPhysicalDevice();
|
||||||
void Graphics::CreateInstance() {
|
CreateLogicalDeviceAndQueues();
|
||||||
std::array<gsl::czstring, 1> validation_layers = {
|
CreateSwapChain();
|
||||||
"VK_LAYER_KHRONOS_validation"};
|
CreateGraphicsPipeline();
|
||||||
|
|
||||||
if (!AreAllLayersSupported(validation_layers)) validation_enabled_ = false;
|
|
||||||
|
|
||||||
std::vector<gsl::czstring> required_extentions =
|
|
||||||
GetRequiredInstanceExtentions();
|
|
||||||
|
|
||||||
VkApplicationInfo app_info = {};
|
|
||||||
app_info.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
|
|
||||||
app_info.pNext = nullptr;
|
|
||||||
app_info.pApplicationName = "Udemy Course";
|
|
||||||
app_info.applicationVersion = VK_MAKE_API_VERSION(0, 0, 0, 0);
|
|
||||||
app_info.pEngineName = "VEng";
|
|
||||||
app_info.engineVersion = VK_MAKE_API_VERSION(0, 1, 0, 0);
|
|
||||||
app_info.apiVersion = VK_API_VERSION_1_0;
|
|
||||||
|
|
||||||
VkInstanceCreateInfo instance_creation_info = {};
|
|
||||||
instance_creation_info.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
|
|
||||||
instance_creation_info.pNext = nullptr;
|
|
||||||
instance_creation_info.pApplicationInfo = &app_info;
|
|
||||||
instance_creation_info.enabledExtensionCount = required_extentions.size();
|
|
||||||
instance_creation_info.ppEnabledExtensionNames = required_extentions.data();
|
|
||||||
|
|
||||||
VkDebugUtilsMessengerCreateInfoEXT messenger_create_info =
|
|
||||||
GetCreateMessengerInfo();
|
|
||||||
if (validation_enabled_) {
|
|
||||||
instance_creation_info.pNext = &messenger_create_info;
|
|
||||||
instance_creation_info.enabledLayerCount = validation_layers.size();
|
|
||||||
instance_creation_info.ppEnabledLayerNames = validation_layers.data();
|
|
||||||
} else {
|
|
||||||
instance_creation_info.enabledLayerCount = 0;
|
|
||||||
instance_creation_info.ppEnabledLayerNames = nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
VkResult result =
|
|
||||||
vkCreateInstance(&instance_creation_info, nullptr, &instance_);
|
|
||||||
if (result != VK_SUCCESS) std::exit(EXIT_FAILURE);
|
|
||||||
}
|
|
||||||
|
|
||||||
void Graphics::SetupDebugMessenger() {
|
|
||||||
if (!validation_enabled_) return;
|
|
||||||
|
|
||||||
VkDebugUtilsMessengerCreateInfoEXT info = GetCreateMessengerInfo();
|
|
||||||
VkResult result = vkCreateDebugUtilsMessengerEXT(instance_, &info, nullptr,
|
|
||||||
&debug_messenger_);
|
|
||||||
if (result != VK_SUCCESS) {
|
|
||||||
spdlog::error("Cannot create debug messenger");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
gsl::span<gsl::czstring> Graphics::GetSuggestedInstanceExtentions() {
|
|
||||||
std::uint32_t glfw_extention_count = 0;
|
|
||||||
gsl::czstring* glfw_extentions =
|
|
||||||
glfwGetRequiredInstanceExtensions(&glfw_extention_count);
|
|
||||||
return {glfw_extentions, glfw_extention_count};
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<gsl::czstring> Graphics::GetRequiredInstanceExtentions() {
|
|
||||||
gsl::span<gsl::czstring> suggested_extentions =
|
|
||||||
GetSuggestedInstanceExtentions();
|
|
||||||
std::vector<gsl::czstring> required_extentions(suggested_extentions.size());
|
|
||||||
std::copy(suggested_extentions.begin(), suggested_extentions.end(),
|
|
||||||
required_extentions.begin());
|
|
||||||
|
|
||||||
if (validation_enabled_)
|
|
||||||
required_extentions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
|
|
||||||
|
|
||||||
if (!AreAllExtensionsSupported(suggested_extentions)) std::exit(EXIT_FAILURE);
|
|
||||||
|
|
||||||
return required_extentions;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<VkExtensionProperties> Graphics::GetSupprotedInstanceExtensions() {
|
|
||||||
std::uint32_t count;
|
|
||||||
vkEnumerateInstanceExtensionProperties(nullptr, &count, nullptr);
|
|
||||||
|
|
||||||
if (count == 0) return {};
|
|
||||||
|
|
||||||
std::vector<VkExtensionProperties> properties(count);
|
|
||||||
vkEnumerateInstanceExtensionProperties(nullptr, &count, properties.data());
|
|
||||||
return properties;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ExtentionMatchesName(gsl::czstring name,
|
|
||||||
const VkExtensionProperties& properties) {
|
|
||||||
return streq(properties.extensionName, name);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool IsExtensionSupported(gsl::span<VkExtensionProperties> extensions,
|
|
||||||
gsl::czstring name) {
|
|
||||||
return std::any_of(extensions.begin(), extensions.end(),
|
|
||||||
std::bind_front(ExtentionMatchesName, name));
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Graphics::AreAllExtensionsSupported(gsl::span<gsl::czstring> extensions) {
|
|
||||||
std::vector<VkExtensionProperties> supported_extensions =
|
|
||||||
GetSupprotedInstanceExtensions();
|
|
||||||
|
|
||||||
return std::all_of(
|
|
||||||
extensions.begin(), extensions.end(),
|
|
||||||
std::bind_front(IsExtensionSupported, supported_extensions));
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<VkLayerProperties> Graphics::GetSupprotedValidationLayers() {
|
|
||||||
std::uint32_t count;
|
|
||||||
vkEnumerateInstanceLayerProperties(&count, nullptr);
|
|
||||||
|
|
||||||
if (count == 0) return {};
|
|
||||||
|
|
||||||
std::vector<VkLayerProperties> properties(count);
|
|
||||||
vkEnumerateInstanceLayerProperties(&count, properties.data());
|
|
||||||
return properties;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool LayerMatchesName(gsl::czstring name, const VkLayerProperties& properties) {
|
|
||||||
return streq(properties.layerName, name);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool IsLayerSupported(gsl::span<VkLayerProperties> layers, gsl::czstring name) {
|
|
||||||
return std::any_of(layers.begin(), layers.end(),
|
|
||||||
std::bind_front(LayerMatchesName, name));
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Graphics::AreAllLayersSupported(gsl::span<gsl::czstring> layers) {
|
|
||||||
std::vector<VkLayerProperties> supported_layers =
|
|
||||||
GetSupprotedValidationLayers();
|
|
||||||
|
|
||||||
return std::all_of(layers.begin(), layers.end(),
|
|
||||||
std::bind_front(IsLayerSupported, supported_layers));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace veng
|
} // namespace veng
|
||||||
|
|||||||
56
src/vulkan/graphics_pipeline.cpp
Normal file
56
src/vulkan/graphics_pipeline.cpp
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
#include <GLFW/glfw3.h>
|
||||||
|
|
||||||
|
#include "precomp.h"
|
||||||
|
#include "vulkan/graphics.h"
|
||||||
|
|
||||||
|
namespace veng {
|
||||||
|
|
||||||
|
VkShaderModule Graphics::CreateShaderModule(gsl::span<std::uint8_t> buffer) {
|
||||||
|
if (buffer.empty()) return VK_NULL_HANDLE;
|
||||||
|
|
||||||
|
VkShaderModuleCreateInfo info = {};
|
||||||
|
info.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;
|
||||||
|
info.codeSize = buffer.size();
|
||||||
|
info.pCode = reinterpret_cast<std::uint32_t*>(buffer.data());
|
||||||
|
|
||||||
|
VkShaderModule shader_module;
|
||||||
|
VkResult result =
|
||||||
|
vkCreateShaderModule(logical_device_, &info, nullptr, &shader_module);
|
||||||
|
if (result != VK_SUCCESS) return VK_NULL_HANDLE;
|
||||||
|
|
||||||
|
return shader_module;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Graphics::CreateGraphicsPipeline() {
|
||||||
|
std::vector<std::uint8_t> basic_vertex_data = ReadFile("./basic.vert.spv");
|
||||||
|
VkShaderModule vertex_shader = CreateShaderModule(basic_vertex_data);
|
||||||
|
gsl::final_action _destroy_vertex([this, vertex_shader]() {
|
||||||
|
vkDestroyShaderModule(logical_device_, vertex_shader, nullptr);
|
||||||
|
});
|
||||||
|
|
||||||
|
std::vector<std::uint8_t> basic_fragment_data = ReadFile("./basic.frag.spv");
|
||||||
|
VkShaderModule fragment_shader = CreateShaderModule(basic_fragment_data);
|
||||||
|
gsl::final_action _destroy_fragment([this, fragment_shader]() {
|
||||||
|
vkDestroyShaderModule(logical_device_, fragment_shader, nullptr);
|
||||||
|
});
|
||||||
|
|
||||||
|
if (vertex_shader == VK_NULL_HANDLE || fragment_shader == VK_NULL_HANDLE)
|
||||||
|
std::exit(EXIT_FAILURE);
|
||||||
|
|
||||||
|
VkPipelineShaderStageCreateInfo vertex_stage_info = {};
|
||||||
|
vertex_stage_info.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
|
||||||
|
vertex_stage_info.stage = VK_SHADER_STAGE_VERTEX_BIT;
|
||||||
|
vertex_stage_info.module = vertex_shader;
|
||||||
|
vertex_stage_info.pName = "main";
|
||||||
|
|
||||||
|
VkPipelineShaderStageCreateInfo fragment_stage_info = {};
|
||||||
|
fragment_stage_info.sType =
|
||||||
|
VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;
|
||||||
|
fragment_stage_info.stage = VK_SHADER_STAGE_FRAGMENT_BIT;
|
||||||
|
fragment_stage_info.module = fragment_shader;
|
||||||
|
fragment_stage_info.pName = "main";
|
||||||
|
|
||||||
|
std::array<VkPipelineShaderStageCreateInfo, 2> stage_infos = {
|
||||||
|
vertex_stage_info, fragment_stage_info};
|
||||||
|
}
|
||||||
|
} // namespace veng
|
||||||
102
src/vulkan/instance_and_extensions.cpp
Normal file
102
src/vulkan/instance_and_extensions.cpp
Normal file
@@ -0,0 +1,102 @@
|
|||||||
|
#include <GLFW/glfw3.h>
|
||||||
|
|
||||||
|
#include "precomp.h"
|
||||||
|
#include "vulkan/graphics.h"
|
||||||
|
|
||||||
|
namespace veng {
|
||||||
|
|
||||||
|
gsl::span<gsl::czstring> Graphics::GetSuggestedInstanceExtentions() {
|
||||||
|
std::uint32_t glfw_extention_count = 0;
|
||||||
|
gsl::czstring* glfw_extentions =
|
||||||
|
glfwGetRequiredInstanceExtensions(&glfw_extention_count);
|
||||||
|
return {glfw_extentions, glfw_extention_count};
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<gsl::czstring> Graphics::GetRequiredInstanceExtentions() {
|
||||||
|
gsl::span<gsl::czstring> suggested_extentions =
|
||||||
|
GetSuggestedInstanceExtentions();
|
||||||
|
std::vector<gsl::czstring> required_extentions(suggested_extentions.size());
|
||||||
|
std::copy(suggested_extentions.begin(), suggested_extentions.end(),
|
||||||
|
required_extentions.begin());
|
||||||
|
|
||||||
|
if (validation_enabled_)
|
||||||
|
required_extentions.push_back(VK_EXT_DEBUG_UTILS_EXTENSION_NAME);
|
||||||
|
|
||||||
|
if (!AreAllExtensionsSupported(suggested_extentions)) std::exit(EXIT_FAILURE);
|
||||||
|
|
||||||
|
return required_extentions;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<VkExtensionProperties> Graphics::GetSupprotedInstanceExtensions() {
|
||||||
|
std::uint32_t count;
|
||||||
|
vkEnumerateInstanceExtensionProperties(nullptr, &count, nullptr);
|
||||||
|
|
||||||
|
if (count == 0) return {};
|
||||||
|
|
||||||
|
std::vector<VkExtensionProperties> properties(count);
|
||||||
|
vkEnumerateInstanceExtensionProperties(nullptr, &count, properties.data());
|
||||||
|
return properties;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ExtentionMatchesName(gsl::czstring name,
|
||||||
|
const VkExtensionProperties& properties) {
|
||||||
|
return streq(properties.extensionName, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsExtensionSupported(gsl::span<VkExtensionProperties> extensions,
|
||||||
|
gsl::czstring name) {
|
||||||
|
return std::any_of(extensions.begin(), extensions.end(),
|
||||||
|
std::bind_front(ExtentionMatchesName, name));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Graphics::AreAllExtensionsSupported(gsl::span<gsl::czstring> extensions) {
|
||||||
|
std::vector<VkExtensionProperties> supported_extensions =
|
||||||
|
GetSupprotedInstanceExtensions();
|
||||||
|
|
||||||
|
return std::all_of(
|
||||||
|
extensions.begin(), extensions.end(),
|
||||||
|
std::bind_front(IsExtensionSupported, supported_extensions));
|
||||||
|
}
|
||||||
|
|
||||||
|
void Graphics::CreateInstance() {
|
||||||
|
std::array<gsl::czstring, 1> validation_layers = {
|
||||||
|
"VK_LAYER_KHRONOS_validation"};
|
||||||
|
|
||||||
|
if (!AreAllLayersSupported(validation_layers)) validation_enabled_ = false;
|
||||||
|
|
||||||
|
std::vector<gsl::czstring> required_extentions =
|
||||||
|
GetRequiredInstanceExtentions();
|
||||||
|
|
||||||
|
VkApplicationInfo app_info = {};
|
||||||
|
app_info.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;
|
||||||
|
app_info.pNext = nullptr;
|
||||||
|
app_info.pApplicationName = "Udemy Course";
|
||||||
|
app_info.applicationVersion = VK_MAKE_API_VERSION(0, 0, 0, 0);
|
||||||
|
app_info.pEngineName = "VEng";
|
||||||
|
app_info.engineVersion = VK_MAKE_API_VERSION(0, 1, 0, 0);
|
||||||
|
app_info.apiVersion = VK_API_VERSION_1_0;
|
||||||
|
|
||||||
|
VkInstanceCreateInfo instance_creation_info = {};
|
||||||
|
instance_creation_info.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
|
||||||
|
instance_creation_info.pNext = nullptr;
|
||||||
|
instance_creation_info.pApplicationInfo = &app_info;
|
||||||
|
instance_creation_info.enabledExtensionCount = required_extentions.size();
|
||||||
|
instance_creation_info.ppEnabledExtensionNames = required_extentions.data();
|
||||||
|
|
||||||
|
VkDebugUtilsMessengerCreateInfoEXT messenger_create_info =
|
||||||
|
GetCreateMessengerInfo();
|
||||||
|
if (validation_enabled_) {
|
||||||
|
instance_creation_info.pNext = &messenger_create_info;
|
||||||
|
instance_creation_info.enabledLayerCount = validation_layers.size();
|
||||||
|
instance_creation_info.ppEnabledLayerNames = validation_layers.data();
|
||||||
|
} else {
|
||||||
|
instance_creation_info.enabledLayerCount = 0;
|
||||||
|
instance_creation_info.ppEnabledLayerNames = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
VkResult result =
|
||||||
|
vkCreateInstance(&instance_creation_info, nullptr, &instance_);
|
||||||
|
if (result != VK_SUCCESS) std::exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace veng
|
||||||
170
src/vulkan/presentation.cpp
Normal file
170
src/vulkan/presentation.cpp
Normal file
@@ -0,0 +1,170 @@
|
|||||||
|
#include "vulkan/graphics.h"
|
||||||
|
#include <GLFW/glfw3.h>
|
||||||
|
|
||||||
|
#include "precomp.h"
|
||||||
|
|
||||||
|
namespace veng {
|
||||||
|
|
||||||
|
void Graphics::CreateSurface() {
|
||||||
|
VkResult result = glfwCreateWindowSurface(instance_, window_->GetHandle(),
|
||||||
|
nullptr, &surface_);
|
||||||
|
if (result != VK_SUCCESS) std::exit(EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsRgbaTypeFormat(const VkSurfaceFormatKHR& format_properties) {
|
||||||
|
return format_properties.format == VK_FORMAT_R8G8B8A8_SRGB ||
|
||||||
|
format_properties.format == VK_FORMAT_B8G8R8A8_SRGB;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsSrgbColorSpace(const VkSurfaceFormatKHR& format_properties) {
|
||||||
|
return format_properties.colorSpace == VK_COLORSPACE_SRGB_NONLINEAR_KHR;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsCorrectFormat(const VkSurfaceFormatKHR& format_properties) {
|
||||||
|
return IsSrgbColorSpace(format_properties) &&
|
||||||
|
IsRgbaTypeFormat(format_properties);
|
||||||
|
}
|
||||||
|
|
||||||
|
VkSurfaceFormatKHR Graphics::ChooseSwapSurfaceFormat(
|
||||||
|
gsl::span<VkSurfaceFormatKHR> formats) {
|
||||||
|
if (formats.size() == 1 && formats[0].format == VK_FORMAT_UNDEFINED) {
|
||||||
|
return {VkFormat::VK_FORMAT_R8G8B8A8_SRGB,
|
||||||
|
VkColorSpaceKHR::VK_COLORSPACE_SRGB_NONLINEAR_KHR};
|
||||||
|
}
|
||||||
|
|
||||||
|
auto it = std::find_if(formats.begin(), formats.end(), IsCorrectFormat);
|
||||||
|
if (it != formats.end()) return *it;
|
||||||
|
|
||||||
|
for (const VkSurfaceFormatKHR& format : formats) {
|
||||||
|
if (format.format == VK_FORMAT_R8G8B8A8_SRGB &&
|
||||||
|
format.colorSpace == VK_COLORSPACE_SRGB_NONLINEAR_KHR) {
|
||||||
|
return format;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return formats[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
VkPresentModeKHR Graphics::ChooseSwapPresentMode(
|
||||||
|
gsl::span<VkPresentModeKHR> present_modes) {
|
||||||
|
constexpr std::array<VkPresentModeKHR, 3> preferred_modes = {
|
||||||
|
VK_PRESENT_MODE_MAILBOX_KHR, VK_PRESENT_MODE_IMMEDIATE_KHR,
|
||||||
|
VK_PRESENT_MODE_FIFO_KHR};
|
||||||
|
|
||||||
|
for (const auto& preferred : preferred_modes)
|
||||||
|
if (std::find(present_modes.begin(), present_modes.end(), preferred) !=
|
||||||
|
present_modes.end())
|
||||||
|
return preferred;
|
||||||
|
|
||||||
|
return VK_PRESENT_MODE_FIFO_KHR;
|
||||||
|
}
|
||||||
|
|
||||||
|
VkExtent2D Graphics::ChooseSwapExtent(
|
||||||
|
const VkSurfaceCapabilitiesKHR& capabilities) {
|
||||||
|
constexpr std::uint32_t kInvalidSize =
|
||||||
|
std::numeric_limits<std::uint32_t>::max();
|
||||||
|
|
||||||
|
if (capabilities.currentExtent.width != kInvalidSize) {
|
||||||
|
return capabilities.currentExtent;
|
||||||
|
} else {
|
||||||
|
glm::ivec2 size = window_->GetFramebufferSize();
|
||||||
|
VkExtent2D actual_extent = {static_cast<std::uint32_t>(size.x),
|
||||||
|
static_cast<std::uint32_t>(size.y)};
|
||||||
|
|
||||||
|
actual_extent.width =
|
||||||
|
std::clamp(actual_extent.width, capabilities.minImageExtent.width,
|
||||||
|
capabilities.maxImageExtent.width);
|
||||||
|
actual_extent.height =
|
||||||
|
std::clamp(actual_extent.height, capabilities.minImageExtent.height,
|
||||||
|
capabilities.maxImageExtent.height);
|
||||||
|
|
||||||
|
return actual_extent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
std::uint32_t Graphics::ChooseSwapImageCount(
|
||||||
|
const VkSurfaceCapabilitiesKHR& capabilities) {
|
||||||
|
std::uint32_t image_count = capabilities.minImageCount + 1;
|
||||||
|
if (capabilities.maxImageCount > 0 &&
|
||||||
|
capabilities.maxImageCount < image_count)
|
||||||
|
image_count = capabilities.maxImageCount;
|
||||||
|
|
||||||
|
return image_count;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Graphics::CreateSwapChain() {
|
||||||
|
SwapChainProperties properties = GetSwapChainProperties(physical_device_);
|
||||||
|
|
||||||
|
surface_format_ = ChooseSwapSurfaceFormat(properties.formats);
|
||||||
|
present_mode_ = ChooseSwapPresentMode(properties.present_modes);
|
||||||
|
extent_ = ChooseSwapExtent(properties.capabilities);
|
||||||
|
|
||||||
|
std::uint32_t image_count = ChooseSwapImageCount(properties.capabilities);
|
||||||
|
|
||||||
|
VkSwapchainCreateInfoKHR info = {};
|
||||||
|
info.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
|
||||||
|
info.surface = surface_;
|
||||||
|
info.minImageCount = image_count;
|
||||||
|
info.imageFormat = surface_format_.format;
|
||||||
|
info.imageColorSpace = surface_format_.colorSpace;
|
||||||
|
info.imageExtent = extent_;
|
||||||
|
info.imageArrayLayers = 1;
|
||||||
|
info.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT;
|
||||||
|
info.presentMode = present_mode_;
|
||||||
|
info.preTransform = properties.capabilities.currentTransform;
|
||||||
|
info.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
|
||||||
|
info.clipped = VK_TRUE;
|
||||||
|
info.oldSwapchain = VK_NULL_HANDLE;
|
||||||
|
|
||||||
|
QueueFamilyIndices indices = FindQueueFamilies(physical_device_);
|
||||||
|
|
||||||
|
if (indices.graphics_family != indices.presentation_family) {
|
||||||
|
std::array<std::uint32_t, 2> family_indices = {
|
||||||
|
indices.graphics_family.value(), indices.presentation_family.value()};
|
||||||
|
info.imageSharingMode = VK_SHARING_MODE_CONCURRENT;
|
||||||
|
info.queueFamilyIndexCount = family_indices.size();
|
||||||
|
info.pQueueFamilyIndices = family_indices.data();
|
||||||
|
} else {
|
||||||
|
info.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
|
||||||
|
}
|
||||||
|
|
||||||
|
VkResult result =
|
||||||
|
vkCreateSwapchainKHR(logical_device_, &info, nullptr, &swap_chain_);
|
||||||
|
if (result != VK_SUCCESS) std::exit(EXIT_FAILURE);
|
||||||
|
|
||||||
|
std::uint32_t actual_image_count;
|
||||||
|
vkGetSwapchainImagesKHR(logical_device_, swap_chain_, &actual_image_count,
|
||||||
|
nullptr);
|
||||||
|
swap_chain_images_.resize(actual_image_count);
|
||||||
|
vkGetSwapchainImagesKHR(logical_device_, swap_chain_, &actual_image_count,
|
||||||
|
swap_chain_images_.data());
|
||||||
|
}
|
||||||
|
|
||||||
|
void Graphics::CreateImageViews() {
|
||||||
|
swap_chain_image_views_.resize(swap_chain_images_.size());
|
||||||
|
|
||||||
|
auto image_view_it = swap_chain_image_views_.begin();
|
||||||
|
for (VkImage image : swap_chain_images_) {
|
||||||
|
VkImageViewCreateInfo info = {};
|
||||||
|
info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
|
||||||
|
info.image = image;
|
||||||
|
info.viewType = VK_IMAGE_VIEW_TYPE_2D;
|
||||||
|
info.format = surface_format_.format;
|
||||||
|
info.components.r = VK_COMPONENT_SWIZZLE_IDENTITY;
|
||||||
|
info.components.g = VK_COMPONENT_SWIZZLE_IDENTITY;
|
||||||
|
info.components.b = VK_COMPONENT_SWIZZLE_IDENTITY;
|
||||||
|
info.components.a = VK_COMPONENT_SWIZZLE_IDENTITY;
|
||||||
|
info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
||||||
|
info.subresourceRange.baseMipLevel = 0;
|
||||||
|
info.subresourceRange.layerCount = 1;
|
||||||
|
info.subresourceRange.baseArrayLayer = 0;
|
||||||
|
info.subresourceRange.layerCount = 1;
|
||||||
|
|
||||||
|
VkResult result =
|
||||||
|
vkCreateImageView(logical_device_, &info, nullptr, &*image_view_it);
|
||||||
|
if (result != VK_SUCCESS) std::exit(EXIT_FAILURE);
|
||||||
|
std::next(image_view_it);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace veng
|
||||||
73
src/vulkan/validation_layers.cpp
Normal file
73
src/vulkan/validation_layers.cpp
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
#include "precomp.h"
|
||||||
|
#include "vulkan/graphics.h"
|
||||||
|
|
||||||
|
namespace veng {
|
||||||
|
|
||||||
|
static VKAPI_ATTR VkBool32 VKAPI_CALL
|
||||||
|
ValidationCallback(VkDebugUtilsMessageSeverityFlagBitsEXT messageSeverity,
|
||||||
|
VkDebugUtilsMessageTypeFlagsEXT messageTypes,
|
||||||
|
const VkDebugUtilsMessengerCallbackDataEXT* pCallbackData,
|
||||||
|
void* pUserData) {
|
||||||
|
if (messageSeverity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT) {
|
||||||
|
spdlog::warn("Vulkan Validation: {}", pCallbackData->pMessage);
|
||||||
|
} else {
|
||||||
|
spdlog::error("Vulkan Validation: {}", pCallbackData->pMessage);
|
||||||
|
}
|
||||||
|
return VK_FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
VkDebugUtilsMessengerCreateInfoEXT GetCreateMessengerInfo() {
|
||||||
|
VkDebugUtilsMessengerCreateInfoEXT creation_info = {};
|
||||||
|
creation_info.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT;
|
||||||
|
creation_info.messageSeverity =
|
||||||
|
VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT |
|
||||||
|
VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT;
|
||||||
|
creation_info.messageType = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT |
|
||||||
|
VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT;
|
||||||
|
creation_info.pfnUserCallback = ValidationCallback;
|
||||||
|
creation_info.pUserData = nullptr;
|
||||||
|
|
||||||
|
return creation_info;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<VkLayerProperties> Graphics::GetSupprotedValidationLayers() {
|
||||||
|
std::uint32_t count;
|
||||||
|
vkEnumerateInstanceLayerProperties(&count, nullptr);
|
||||||
|
|
||||||
|
if (count == 0) return {};
|
||||||
|
|
||||||
|
std::vector<VkLayerProperties> properties(count);
|
||||||
|
vkEnumerateInstanceLayerProperties(&count, properties.data());
|
||||||
|
return properties;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool LayerMatchesName(gsl::czstring name, const VkLayerProperties& properties) {
|
||||||
|
return streq(properties.layerName, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool IsLayerSupported(gsl::span<VkLayerProperties> layers, gsl::czstring name) {
|
||||||
|
return std::any_of(layers.begin(), layers.end(),
|
||||||
|
std::bind_front(LayerMatchesName, name));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Graphics::AreAllLayersSupported(gsl::span<gsl::czstring> layers) {
|
||||||
|
std::vector<VkLayerProperties> supported_layers =
|
||||||
|
GetSupprotedValidationLayers();
|
||||||
|
|
||||||
|
return std::all_of(layers.begin(), layers.end(),
|
||||||
|
std::bind_front(IsLayerSupported, supported_layers));
|
||||||
|
}
|
||||||
|
|
||||||
|
void Graphics::SetupDebugMessenger() {
|
||||||
|
if (!validation_enabled_) return;
|
||||||
|
|
||||||
|
VkDebugUtilsMessengerCreateInfoEXT info = GetCreateMessengerInfo();
|
||||||
|
VkResult result = vkCreateDebugUtilsMessengerEXT(instance_, &info, nullptr,
|
||||||
|
&debug_messenger_);
|
||||||
|
if (result != VK_SUCCESS) {
|
||||||
|
spdlog::error("Cannot create debug messenger");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace veng
|
||||||
25
src/vulkan/vk_function_ext_impl.cpp
Normal file
25
src/vulkan/vk_function_ext_impl.cpp
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
#include "precomp.h"
|
||||||
|
#include "vulkan/graphics.h"
|
||||||
|
|
||||||
|
VKAPI_ATTR VkResult VKAPI_CALL vkCreateDebugUtilsMessengerEXT(
|
||||||
|
VkInstance instance, const VkDebugUtilsMessengerCreateInfoEXT* pCreateInfo,
|
||||||
|
const VkAllocationCallbacks* pAllocator,
|
||||||
|
VkDebugUtilsMessengerEXT* pMessenger) {
|
||||||
|
PFN_vkCreateDebugUtilsMessengerEXT function =
|
||||||
|
reinterpret_cast<PFN_vkCreateDebugUtilsMessengerEXT>(
|
||||||
|
vkGetInstanceProcAddr(instance, "vkCreateDebugUtilsMessengerEXT"));
|
||||||
|
if (function != nullptr) {
|
||||||
|
return function(instance, pCreateInfo, pAllocator, pMessenger);
|
||||||
|
} else {
|
||||||
|
return VK_ERROR_EXTENSION_NOT_PRESENT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
VKAPI_ATTR void VKAPI_CALL vkDestroyDebugUtilsMessengerEXT(
|
||||||
|
VkInstance instance, VkDebugUtilsMessengerEXT messenger,
|
||||||
|
const VkAllocationCallbacks* pAllocator) {
|
||||||
|
PFN_vkDestroyDebugUtilsMessengerEXT function =
|
||||||
|
reinterpret_cast<PFN_vkDestroyDebugUtilsMessengerEXT>(
|
||||||
|
vkGetInstanceProcAddr(instance, "vkDestroyDebugUtilsMessengerEXT"));
|
||||||
|
if (function != nullptr) function(instance, messenger, pAllocator);
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user