Last active
October 16, 2019 15:40
-
-
Save qis/006025b15d9a16689d33ebb2c77ef45d to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <result.hpp> | |
#include <vulkan.hpp> | |
#include <windows.h> | |
#include <version.h> | |
#include <algorithm> | |
#include <string> | |
#include <string_view> | |
// TODO: Read the specification chapter 6 about synchronization. | |
// https://www.khronos.org/registry/vulkan/specs/1.1-extensions/html/vkspec.html#synchronization | |
class Window { | |
public: | |
static constexpr PCWSTR error_text = TEXT(PROJECT_DESCRIPTION " Error"); | |
static constexpr PCWSTR title_text = TEXT(PROJECT_DESCRIPTION " " PROJECT_VERSION); | |
static constexpr PCWSTR class_name = TEXT(PROJECT_VENDOR PROJECT_DESCRIPTION PROJECT_VERSION); | |
static constexpr PCWSTR class_path = TEXT("Software\\" PROJECT_VENDOR "\\" PROJECT_DESCRIPTION); | |
Window(HINSTANCE hinstance, LPWSTR cmd, int show) noexcept : hinstance_(hinstance) | |
{ | |
WNDCLASSEX wc = {}; | |
wc.cbSize = sizeof(wc); | |
wc.style = CS_OWNDC | CS_VREDRAW | CS_HREDRAW; | |
wc.lpfnWndProc = Proc; | |
wc.hInstance = hinstance_; | |
wc.hIcon = wc.hIconSm = LoadIcon(hinstance_, MAKEINTRESOURCE(101)); | |
wc.hCursor = LoadCursor(nullptr, IDC_ARROW); | |
wc.hbrBackground = reinterpret_cast<HBRUSH>(COLOR_WINDOWFRAME); | |
wc.lpszClassName = class_name; | |
RegisterClassEx(&wc); | |
const auto ws = WS_OVERLAPPEDWINDOW; | |
const auto ex = WS_EX_APPWINDOW; | |
CreateWindowEx(ex, class_name, title_text, ws, 0, 0, 640, 480, nullptr, nullptr, hinstance_, this); | |
} | |
Window(Window&& other) = delete; | |
Window(const Window& other) = delete; | |
Window& operator=(Window&& other) = delete; | |
Window& operator=(const Window& other) = delete; | |
~Window() | |
{ | |
UnregisterClass(class_name, hinstance_); | |
} | |
result<void> Initialize() noexcept | |
{ | |
// Load functions. | |
co_await vk::Load(); | |
// Create instance. | |
#ifdef DEBUG | |
std::string_view layers_requested[] = { | |
"VK_LAYER_LUNARG_standard_validation", | |
"VK_LAYER_LUNARG_parameter_validation", | |
}; | |
for (const auto& e : co_await vk::EnumerateInstanceLayerProperties()) { | |
const auto it = std::find(std::begin(layers_requested), std::end(layers_requested), e.layerName); | |
if (it != std::end(layers_requested)) { | |
layers_.push_back(it->data()); | |
} | |
} | |
#endif | |
std::vector<const char*> extensions; | |
std::string_view extensions_requested[] = { | |
"VK_KHR_surface", | |
"VK_KHR_win32_surface", | |
#ifdef DEBUG | |
"VK_EXT_debug_report", | |
#endif | |
}; | |
for (const auto& e : co_await vk::EnumerateInstanceExtensionProperties()) { | |
const auto it = std::find(std::begin(extensions_requested), std::end(extensions_requested), e.extensionName); | |
if (it != std::end(extensions_requested)) { | |
extensions.push_back(it->data()); | |
} | |
} | |
assert(extensions.size() == std::size(extensions_requested)); | |
VkApplicationInfo application_info = {}; | |
application_info.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; | |
application_info.pApplicationName = PROJECT_DESCRIPTION; | |
application_info.applicationVersion = VK_MAKE_VERSION(PROJECT_VERSION_MAJOR, PROJECT_VERSION_MINOR, PROJECT_VERSION_PATCH); | |
application_info.pEngineName = PROJECT_VENDOR " " PROJECT_DESCRIPTION; | |
application_info.engineVersion = VK_MAKE_VERSION(PROJECT_VERSION_MAJOR, PROJECT_VERSION_MINOR, 0); | |
application_info.apiVersion = VK_API_VERSION_1_1; | |
VkInstanceCreateInfo instance_create_info = {}; | |
instance_create_info.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; | |
instance_create_info.pApplicationInfo = &application_info; | |
instance_create_info.enabledLayerCount = static_cast<uint32_t>(layers_.size()); | |
instance_create_info.ppEnabledLayerNames = layers_.data(); | |
instance_create_info.enabledExtensionCount = static_cast<uint32_t>(extensions.size()); | |
instance_create_info.ppEnabledExtensionNames = extensions.data(); | |
instance_ = co_await vk::CreateInstance(&instance_create_info); | |
#ifdef DEBUG | |
debug_report_callback_ = vk::CreateDebugReportCallback( | |
instance_, VK_DEBUG_REPORT_ERROR_BIT_EXT | VK_DEBUG_REPORT_WARNING_BIT_EXT | VK_DEBUG_REPORT_PERFORMANCE_WARNING_BIT_EXT, | |
[](VkDebugReportFlagsEXT flags, const char* layer, const char* message) -> VkBool32 { | |
__debugbreak(); | |
OutputDebugStringA(fmt::format("{}: {}\n", layer, message).data()); | |
return VK_FALSE; | |
}); | |
#endif | |
co_return error::success; | |
} | |
result<void> CreateSurface() noexcept | |
{ | |
// Create surface. | |
VkWin32SurfaceCreateInfoKHR surface_create_info = {}; | |
surface_create_info.sType = VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR; | |
surface_create_info.hinstance = hinstance_; | |
surface_create_info.hwnd = hwnd_; | |
surface_ = co_await vk::CreateWin32Surface(instance_, &surface_create_info); | |
co_return error::success; | |
} | |
void DestroySurface() noexcept | |
{ | |
surface_ = {}; | |
} | |
result<void> CreateDevice() noexcept | |
{ | |
// Select physical device. | |
for (const auto e : co_await vk::EnumeratePhysicalDevices(instance_)) { | |
const auto queue_family_properties = vk::GetPhysicalDeviceQueueFamilyProperties(e); | |
for (uint32_t i = 0, max = static_cast<uint32_t>(queue_family_properties.size()); i < max; i++) { | |
const auto supports_present = vk::GetPhysicalDeviceSurfaceSupport(e, i, surface_); | |
if (supports_present && (queue_family_properties[i].queueFlags & VK_QUEUE_GRAPHICS_BIT)) { | |
physical_device_ = e; | |
physical_device_properties_ = vk::GetPhysicalDeviceProperties(e); | |
present_queue_index_ = i; | |
break; | |
} | |
} | |
} | |
if (!physical_device_) { | |
co_return error::vulkan_device_not_found; | |
} | |
// Create device. | |
VkDeviceQueueCreateInfo device_queue_create_nfo = {}; | |
device_queue_create_nfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; | |
device_queue_create_nfo.queueFamilyIndex = present_queue_index_; | |
device_queue_create_nfo.queueCount = 1; | |
float queue_priorities[] = { 1.0f }; | |
device_queue_create_nfo.pQueuePriorities = queue_priorities; | |
VkDeviceCreateInfo device_create_info = {}; | |
device_create_info.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; | |
device_create_info.queueCreateInfoCount = 1; | |
device_create_info.pQueueCreateInfos = &device_queue_create_nfo; | |
device_create_info.enabledLayerCount = static_cast<uint32_t>(layers_.size()); | |
device_create_info.ppEnabledLayerNames = layers_.data(); | |
device_create_info.enabledExtensionCount = 1; | |
const char* device_extensions[] = { "VK_KHR_swapchain" }; | |
device_create_info.ppEnabledExtensionNames = device_extensions; | |
VkPhysicalDeviceFeatures features = {}; | |
features.shaderClipDistance = VK_TRUE; | |
device_create_info.pEnabledFeatures = &features; | |
device_ = co_await vk::CreateDevice(physical_device_, &device_create_info); | |
co_return error::success; | |
} | |
void DestroyDevice() noexcept | |
{ | |
device_ = {}; | |
present_queue_index_ = 0; | |
physical_device_properties_ = {}; | |
physical_device_ = nullptr; | |
} | |
result<void> CreatePresentQueue() noexcept | |
{ | |
// Get present queue. | |
present_queue_ = vk::GetDeviceQueue(device_, present_queue_index_, 0); | |
co_return present_queue_ ? error::success : error::vk_device_lost; | |
} | |
void DestroyPresentQueue() noexcept | |
{ | |
present_queue_ = nullptr; | |
} | |
result<void> CreateCommandBuffers() noexcept | |
{ | |
// Create command buffers. | |
VkCommandPoolCreateInfo command_pool_create_info = {}; | |
command_pool_create_info.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; | |
command_pool_create_info.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT; | |
command_pool_create_info.queueFamilyIndex = present_queue_index_; | |
command_pool_ = co_await vk::CreateCommandPool(device_, &command_pool_create_info); | |
VkCommandBufferAllocateInfo command_buffer_allocation_nfo = {}; | |
command_buffer_allocation_nfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; | |
command_buffer_allocation_nfo.commandPool = command_pool_; | |
command_buffer_allocation_nfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; | |
command_buffer_allocation_nfo.commandBufferCount = 2; | |
command_buffers_ = co_await vk::AllocateCommandBuffers(device_, &command_buffer_allocation_nfo); | |
co_return error::success; | |
} | |
void DestroyCommandBuffers() noexcept | |
{ | |
command_buffers_ = {}; | |
command_pool_ = {}; | |
} | |
result<void> CreateSwapchain() noexcept | |
{ | |
// Choose color format. | |
color_format_ = VK_FORMAT_B8G8R8_UNORM; | |
const auto surface_formats = co_await GetPhysicalDeviceSurfaceFormats(physical_device_, surface_); | |
if (surface_formats.empty()) { | |
co_return error::vulkan_device_missing_surface_formats; | |
} else if (surface_formats.size() == 1 && surface_formats[0].format == VK_FORMAT_UNDEFINED) { | |
color_format_ = VK_FORMAT_B8G8R8_UNORM; | |
} else { | |
color_format_ = surface_formats[0].format; | |
} | |
// Choose color space. | |
color_space_ = surface_formats[0].colorSpace; | |
// Choose buffer count. | |
const auto surface_capabilities = co_await vk::GetPhysicalDeviceSurfaceCapabilities(physical_device_, surface_); | |
uint32_t buffer_count = 2; | |
if (buffer_count < surface_capabilities.minImageCount) { | |
buffer_count = surface_capabilities.minImageCount; | |
} else if (surface_capabilities.maxImageCount != 0 && buffer_count > surface_capabilities.maxImageCount) { | |
buffer_count = surface_capabilities.maxImageCount; | |
} | |
// Choose resolution. | |
RECT rc = {}; | |
if (!GetClientRect(hwnd_, &rc)) { | |
co_return error::window_connection_error; | |
} | |
cx_ = static_cast<decltype(cx_)>(rc.right - rc.left); | |
cy_ = static_cast<decltype(cy_)>(rc.bottom - rc.top); | |
auto surface_resolution = surface_capabilities.currentExtent; | |
if (surface_resolution.width == -1) { | |
surface_resolution.width = cx_; | |
surface_resolution.height = cy_; | |
} else { | |
cx_ = surface_resolution.width; | |
cy_ = surface_resolution.height; | |
} | |
// Choose transform. | |
auto transform = surface_capabilities.currentTransform; | |
if (surface_capabilities.supportedTransforms & VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR) { | |
transform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR; | |
} | |
// Choose present mode. | |
auto present_mode = VK_PRESENT_MODE_FIFO_KHR; | |
for (const auto e : co_await vk::GetPhysicalDeviceSurfacePresentModes(physical_device_, surface_)) { | |
if (e == VK_PRESENT_MODE_MAILBOX_KHR) { | |
present_mode = e; | |
break; | |
} | |
if (e == VK_PRESENT_MODE_IMMEDIATE_KHR) { | |
present_mode = e; | |
} | |
} | |
// Create swap chain. | |
VkSwapchainCreateInfoKHR swap_chain_create_info = {}; | |
swap_chain_create_info.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR; | |
swap_chain_create_info.surface = surface_; | |
swap_chain_create_info.minImageCount = buffer_count; | |
swap_chain_create_info.imageFormat = color_format_; | |
swap_chain_create_info.imageColorSpace = color_space_; | |
swap_chain_create_info.imageExtent = surface_resolution; | |
swap_chain_create_info.imageArrayLayers = 1; | |
swap_chain_create_info.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; | |
swap_chain_create_info.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; | |
swap_chain_create_info.preTransform = transform; | |
swap_chain_create_info.compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR; | |
swap_chain_create_info.presentMode = present_mode; | |
swap_chain_create_info.clipped = VK_TRUE; | |
swapchain_ = co_await vk::CreateSwapchain(device_, &swap_chain_create_info); | |
// Move swapchain images to a present layout. | |
swapchain_images_ = co_await vk::GetSwapchainImages(device_, swapchain_); | |
VkImageViewCreateInfo present_image_view_create_info = {}; | |
present_image_view_create_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; | |
present_image_view_create_info.viewType = VK_IMAGE_VIEW_TYPE_2D; | |
present_image_view_create_info.format = color_format_; | |
present_image_view_create_info.components = { | |
VK_COMPONENT_SWIZZLE_R, | |
VK_COMPONENT_SWIZZLE_G, | |
VK_COMPONENT_SWIZZLE_B, | |
VK_COMPONENT_SWIZZLE_A, | |
}; | |
present_image_view_create_info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; | |
present_image_view_create_info.subresourceRange.baseMipLevel = 0; | |
present_image_view_create_info.subresourceRange.levelCount = 1; | |
present_image_view_create_info.subresourceRange.baseArrayLayer = 0; | |
present_image_view_create_info.subresourceRange.layerCount = 1; | |
VkCommandBufferBeginInfo command_buffer_begin_info = {}; | |
command_buffer_begin_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; | |
command_buffer_begin_info.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; | |
std::vector<VkImageView> present_image_views; | |
VkFenceCreateInfo fence_create_info = {}; | |
fence_create_info.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO; | |
const auto submit_fence = co_await vk::CreateFence(device_, &fence_create_info); | |
for (const auto& e : swapchain_images_) { | |
present_image_view_create_info.image = e; | |
// Record on the setup command buffer. | |
co_await vk::BeginCommandBuffer(setup_command_buffer(), &command_buffer_begin_info); | |
VkImageMemoryBarrier layout_transition_barrier = {}; | |
layout_transition_barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; | |
layout_transition_barrier.srcAccessMask = 0; | |
layout_transition_barrier.dstAccessMask = VK_ACCESS_MEMORY_READ_BIT; | |
layout_transition_barrier.oldLayout = VK_IMAGE_LAYOUT_UNDEFINED; | |
layout_transition_barrier.newLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; | |
layout_transition_barrier.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; | |
layout_transition_barrier.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; | |
layout_transition_barrier.image = e; | |
layout_transition_barrier.subresourceRange = { VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 }; | |
VkCommandPipelineBarrierInfo command_pipeline_barrier_info = {}; | |
command_pipeline_barrier_info.commandBuffer = setup_command_buffer(); | |
command_pipeline_barrier_info.srcStageMask = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT; | |
command_pipeline_barrier_info.dstStageMask = VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT; | |
command_pipeline_barrier_info.dependencyFlags = 0; | |
command_pipeline_barrier_info.memoryBarrierCount = 0; | |
command_pipeline_barrier_info.pMemoryBarriers = nullptr; | |
command_pipeline_barrier_info.bufferMemoryBarrierCount = 0; | |
command_pipeline_barrier_info.pBufferMemoryBarriers = nullptr; | |
command_pipeline_barrier_info.imageMemoryBarrierCount = 1; | |
command_pipeline_barrier_info.pImageMemoryBarriers = &layout_transition_barrier; | |
vk::CommandPipelineBarrier(&command_pipeline_barrier_info); | |
co_await vk::EndCommandBuffer(setup_command_buffer()); | |
// Submit code to the queue. | |
VkSubmitInfo submit_info = {}; | |
submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; | |
submit_info.waitSemaphoreCount = 0; | |
submit_info.pWaitSemaphores = nullptr; | |
const VkPipelineStageFlags wait_stage_mask[] = { VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT }; | |
submit_info.pWaitDstStageMask = wait_stage_mask; | |
submit_info.commandBufferCount = 1; | |
submit_info.pCommandBuffers = &setup_command_buffer(); | |
submit_info.signalSemaphoreCount = 0; | |
submit_info.pSignalSemaphores = nullptr; | |
co_await vk::QueueSubmit(present_queue_, 1, &submit_info, submit_fence); | |
// Waiting for commands to finish executing. | |
co_await vk::WaitForFence(device_, submit_fence, UINT64_MAX); | |
co_await vk::ResetFence(device_, submit_fence); | |
co_await vk::ResetCommandBuffer(setup_command_buffer()); | |
// Create image view. | |
present_image_views.push_back(co_await vk::CreateImageView(device_, &present_image_view_create_info)); | |
} | |
co_return error::success; | |
} | |
void DestroySwapchain() noexcept | |
{ | |
swapchain_images_ = {}; | |
swapchain_ = {}; | |
color_space_ = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR; | |
color_format_ = VK_FORMAT_B8G8R8_UNORM; | |
cy_ = 1; | |
cx_ = 1; | |
} | |
result<void> Render() noexcept | |
{ | |
//Sleep(10); | |
constexpr VkSemaphoreCreateInfo semaphore_create_info = { | |
VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO, | |
nullptr, | |
0, | |
}; | |
const auto present_complete_semaphore = co_await vk::CreateSemaphore(device_, &semaphore_create_info); | |
const auto next_image_index = co_await vk::AcquireNextImage(device_, swapchain_, UINT64_MAX, present_complete_semaphore, VK_NULL_HANDLE); | |
VkPresentInfoKHR present_info = {}; | |
present_info.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR; | |
present_info.pNext = nullptr; | |
present_info.waitSemaphoreCount = 0; | |
present_info.pWaitSemaphores = nullptr; | |
present_info.swapchainCount = 1; | |
present_info.pSwapchains = &swapchain_.get(); | |
present_info.pImageIndices = &next_image_index; | |
present_info.pResults = nullptr; | |
co_await vk::QueuePresent(present_queue_, &present_info); | |
co_return error::success; | |
} | |
void OnCreate() noexcept | |
{ | |
WINDOWPLACEMENT wp = {}; | |
DWORD wt = REG_BINARY; | |
DWORD ws = sizeof(wp); | |
if (RegGetValue(HKEY_CURRENT_USER, class_path, L"Window", RRF_RT_REG_BINARY, &wt, &wp, &ws) == ERROR_SUCCESS) { | |
wp.showCmd = SW_HIDE; | |
SetWindowPlacement(hwnd_, &wp); | |
} else { | |
if (const auto monitor = MonitorFromWindow(hwnd_, MONITOR_DEFAULTTONULL)) { | |
MONITORINFO mi = {}; | |
mi.cbSize = sizeof(mi); | |
if (GetMonitorInfo(monitor, &mi)) { | |
const auto cx = mi.rcMonitor.right - mi.rcMonitor.left; | |
const auto cy = mi.rcMonitor.bottom - mi.rcMonitor.top; | |
RECT rc = {}; | |
if (GetWindowRect(hwnd_, &rc) && cx > rc.right - rc.left && cy > rc.bottom - rc.top) { | |
const auto x = (cx - (rc.right - rc.left)) / 2; | |
const auto y = (cy - (rc.bottom - rc.top)) / 2; | |
SetWindowPos(hwnd_, nullptr, x, y, rc.right - rc.left, rc.bottom - rc.top, SWP_NOACTIVATE); | |
} | |
} | |
} | |
} | |
if (const auto result = Initialize(); !result) { | |
return Close("Could not initialize application: {}", result.error()); | |
} | |
if (const auto result = CreateSurface(); !result) { | |
return Close("Could not create surface: {}", result.error()); | |
} | |
if (const auto result = CreateDevice(); !result) { | |
return Close("Could not create device: {}", result.error()); | |
} | |
if (const auto result = CreatePresentQueue(); !result) { | |
return Close("Could not create present queue: {}", result.error()); | |
} | |
if (const auto result = CreateCommandBuffers(); !result) { | |
return Close("Could not create command buffers: {}", result.error()); | |
} | |
if (const auto result = CreateSwapchain(); !result) { | |
return Close("Could not create swapchain: {}", result.error()); | |
} | |
Show(); | |
} | |
void OnSize(WORD cx, WORD cy) noexcept | |
{ | |
// TODO: return Close("Vulkan resize not yet implemented. ({}:{})", cx, cy); | |
} | |
void OnDestroy() noexcept | |
{ | |
if (render_) { | |
WINDOWPLACEMENT wp = {}; | |
if (GetWindowPlacement(hwnd_, &wp) && wp.showCmd == SW_SHOWNORMAL) { | |
RegSetKeyValue(HKEY_CURRENT_USER, class_path, L"Window", REG_BINARY, &wp, sizeof(wp)); | |
} | |
} | |
Hide(); | |
DestroySwapchain(); | |
DestroyCommandBuffers(); | |
DestroyPresentQueue(); | |
DestroyDevice(); | |
DestroySurface(); | |
PostQuitMessage(0); | |
} | |
void OnRenderError(error error) noexcept | |
{ | |
return Close("Vulkan recovery failed: {}", error); | |
} | |
void Show() noexcept | |
{ | |
render_ = true; | |
ShowWindow(hwnd_, SW_SHOW); | |
} | |
void Hide() noexcept | |
{ | |
render_ = false; | |
ShowWindow(hwnd_, SW_HIDE); | |
} | |
void Close() noexcept | |
{ | |
PostMessage(hwnd_, WM_CLOSE, 0, 0); | |
} | |
template <std::size_t N, typename... Args> | |
void Close(char const (&format)[N], Args&&... args) noexcept | |
{ | |
Hide(); | |
std::wstring wcs; | |
fmt::memory_buffer str; | |
fmt::format_to(str, format, std::forward<Args>(args)...); | |
const auto str_size = static_cast<int>(str.size()); | |
const auto wcs_size = MultiByteToWideChar(CP_UTF8, 0, str.data(), str_size, nullptr, 0); | |
wcs.resize(static_cast<std::size_t>(wcs_size + 1)); | |
wcs.resize(static_cast<std::size_t>(MultiByteToWideChar(CP_UTF8, 0, str.data(), str_size, wcs.data(), wcs_size + 1))); | |
MessageBox(nullptr, wcs.data(), error_text, MB_OK | MB_ICONERROR | MB_SETFOREGROUND); | |
PostMessage(hwnd_, WM_CLOSE, 0, 0); | |
} | |
int Run() noexcept | |
{ | |
MSG msg = {}; | |
while (GetMessage(&msg, nullptr, 0, 0)) { | |
DispatchMessage(&msg); | |
while (render_) { | |
if (const auto result = Render(); !result) { | |
OnRenderError(result.error()); | |
} | |
if (PeekMessage(&msg, nullptr, 0, 0, PM_REMOVE)) { | |
if (msg.message == WM_QUIT) { | |
goto exit; | |
} | |
DispatchMessage(&msg); | |
} | |
} | |
} | |
exit: | |
return static_cast<int>(msg.wParam); | |
} | |
private: | |
static LRESULT __stdcall Proc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) noexcept | |
{ | |
switch (msg) { | |
case WM_CREATE: | |
if (const auto window = reinterpret_cast<Window*>(reinterpret_cast<LPCREATESTRUCT>(lparam)->lpCreateParams)) { | |
SetWindowLongPtr(hwnd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(window)); | |
window->hwnd_ = hwnd; | |
window->OnCreate(); | |
} | |
return 0; | |
case WM_SIZE: | |
if (const auto window = reinterpret_cast<Window*>(GetWindowLongPtr(hwnd, GWLP_USERDATA))) { | |
window->OnSize(LOWORD(lparam), HIWORD(lparam)); | |
} | |
return 0; | |
case WM_DESTROY: | |
if (const auto window = reinterpret_cast<Window*>(GetWindowLongPtr(hwnd, GWLP_USERDATA))) { | |
SetWindowLongPtr(hwnd, GWLP_USERDATA, 0); | |
window->OnDestroy(); | |
} | |
return 0; | |
case WM_ERASEBKGND: | |
return 1; | |
case WM_PAINT: | |
ValidateRect(hwnd, nullptr); | |
return 0; | |
} | |
return DefWindowProc(hwnd, msg, wparam, lparam); | |
} | |
// Window | |
HINSTANCE hinstance_; | |
HWND hwnd_ = nullptr; | |
bool render_ = false; | |
// Instance | |
std::vector<const char*> layers_; | |
vk::Instance instance_; | |
// Surface | |
vk::Surface surface_; | |
// Device | |
VkPhysicalDevice physical_device_ = nullptr; | |
VkPhysicalDeviceProperties physical_device_properties_ = {}; | |
uint32_t present_queue_index_ = 0; | |
vk::Device device_; | |
// Present Queue | |
VkQueue present_queue_ = nullptr; | |
// Command Buffers | |
vk::CommandPool command_pool_; | |
vk::CommandBuffers command_buffers_; | |
const VkCommandBuffer& setup_command_buffer() const noexcept | |
{ | |
return command_buffers_[0]; | |
} | |
VkCommandBuffer& setup_command_buffer() noexcept | |
{ | |
return command_buffers_[0]; | |
} | |
const VkCommandBuffer& render_command_buffer() const noexcept | |
{ | |
return command_buffers_[1]; | |
} | |
VkCommandBuffer& render_command_buffer() noexcept | |
{ | |
return command_buffers_[1]; | |
} | |
// Swapchain | |
uint32_t cx_ = 1; | |
uint32_t cy_ = 1; | |
VkFormat color_format_ = VK_FORMAT_B8G8R8_UNORM; | |
VkColorSpaceKHR color_space_ = VK_COLOR_SPACE_SRGB_NONLINEAR_KHR; | |
vk::Swapchain swapchain_; | |
std::vector<VkImage> swapchain_images_; | |
#ifdef DEBUG | |
vk::DebugReportCallback debug_report_callback_; | |
#endif | |
}; | |
int WINAPI wWinMain(HINSTANCE hinstance, HINSTANCE, LPWSTR cmd, int show) | |
{ | |
Window Window(hinstance, cmd, show); | |
return Window.Run(); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment