Crafter.Graphics/implementations/Crafter.Graphics-Window_vulkan_windows.cpp

678 lines
29 KiB
C++
Raw Normal View History

2026-02-24 02:32:37 +01:00
/*
Crafter®.Graphics
Copyright (C) 2025 Catcrafts®
catcrafts.net
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License version 3.0 as published by the Free Software Foundation;
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
module;
#include <windows.h>
#include <vulkan/vulkan.h>
#include <vulkan/vulkan_win32.h>
#include <cassert>
module Crafter.Graphics:Window_wayland_impl;
import :Window;
import :RenderingElement;
import :MouseElement;
import std;
import :Types;
import :VulkanTransition;
import Crafter.Event;
using namespace Crafter;
CrafterKeys vk_to_crafter_key(WPARAM vk)
{
switch (vk)
{
// Alphabet
case 'A': return CrafterKeys::A;
case 'B': return CrafterKeys::B;
case 'C': return CrafterKeys::C;
case 'D': return CrafterKeys::D;
case 'E': return CrafterKeys::E;
case 'F': return CrafterKeys::F;
case 'G': return CrafterKeys::G;
case 'H': return CrafterKeys::H;
case 'I': return CrafterKeys::I;
case 'J': return CrafterKeys::J;
case 'K': return CrafterKeys::K;
case 'L': return CrafterKeys::L;
case 'M': return CrafterKeys::M;
case 'N': return CrafterKeys::N;
case 'O': return CrafterKeys::O;
case 'P': return CrafterKeys::P;
case 'Q': return CrafterKeys::Q;
case 'R': return CrafterKeys::R;
case 'S': return CrafterKeys::S;
case 'T': return CrafterKeys::T;
case 'U': return CrafterKeys::U;
case 'V': return CrafterKeys::V;
case 'W': return CrafterKeys::W;
case 'X': return CrafterKeys::X;
case 'Y': return CrafterKeys::Y;
case 'Z': return CrafterKeys::Z;
// Numbers
case '0': return CrafterKeys::_0;
case '1': return CrafterKeys::_1;
case '2': return CrafterKeys::_2;
case '3': return CrafterKeys::_3;
case '4': return CrafterKeys::_4;
case '5': return CrafterKeys::_5;
case '6': return CrafterKeys::_6;
case '7': return CrafterKeys::_7;
case '8': return CrafterKeys::_8;
case '9': return CrafterKeys::_9;
// Function keys
case VK_F1: return CrafterKeys::F1;
case VK_F2: return CrafterKeys::F2;
case VK_F3: return CrafterKeys::F3;
case VK_F4: return CrafterKeys::F4;
case VK_F5: return CrafterKeys::F5;
case VK_F6: return CrafterKeys::F6;
case VK_F7: return CrafterKeys::F7;
case VK_F8: return CrafterKeys::F8;
case VK_F9: return CrafterKeys::F9;
case VK_F10: return CrafterKeys::F10;
case VK_F11: return CrafterKeys::F11;
case VK_F12: return CrafterKeys::F12;
// Control keys
case VK_ESCAPE: return CrafterKeys::Escape;
case VK_TAB: return CrafterKeys::Tab;
case VK_RETURN: return CrafterKeys::Enter;
case VK_SPACE: return CrafterKeys::Space;
case VK_BACK: return CrafterKeys::Backspace;
case VK_DELETE: return CrafterKeys::Delete;
case VK_INSERT: return CrafterKeys::Insert;
case VK_HOME: return CrafterKeys::Home;
case VK_END: return CrafterKeys::End;
case VK_PRIOR: return CrafterKeys::PageUp;
case VK_NEXT: return CrafterKeys::PageDown;
case VK_CAPITAL: return CrafterKeys::CapsLock;
case VK_NUMLOCK: return CrafterKeys::NumLock;
case VK_SCROLL: return CrafterKeys::ScrollLock;
// Modifiers
case VK_LSHIFT: return CrafterKeys::LeftShift;
case VK_RSHIFT: return CrafterKeys::RightShift;
case VK_LCONTROL: return CrafterKeys::LeftCtrl;
case VK_RCONTROL: return CrafterKeys::RightCtrl;
case VK_LMENU: return CrafterKeys::LeftAlt;
case VK_RMENU: return CrafterKeys::RightAlt;
case VK_LWIN: return CrafterKeys::LeftSuper;
case VK_RWIN: return CrafterKeys::RightSuper;
// Arrows
case VK_UP: return CrafterKeys::Up;
case VK_DOWN: return CrafterKeys::Down;
case VK_LEFT: return CrafterKeys::Left;
case VK_RIGHT: return CrafterKeys::Right;
// Keypad
case VK_NUMPAD0: return CrafterKeys::keypad_0;
case VK_NUMPAD1: return CrafterKeys::keypad_1;
case VK_NUMPAD2: return CrafterKeys::keypad_2;
case VK_NUMPAD3: return CrafterKeys::keypad_3;
case VK_NUMPAD4: return CrafterKeys::keypad_4;
case VK_NUMPAD5: return CrafterKeys::keypad_5;
case VK_NUMPAD6: return CrafterKeys::keypad_6;
case VK_NUMPAD7: return CrafterKeys::keypad_7;
case VK_NUMPAD8: return CrafterKeys::keypad_8;
case VK_NUMPAD9: return CrafterKeys::keypad_9;
case VK_SEPARATOR: return CrafterKeys::keypad_enter;
case VK_ADD: return CrafterKeys::keypad_plus;
case VK_SUBTRACT: return CrafterKeys::keypad_minus;
case VK_MULTIPLY: return CrafterKeys::keypad_multiply;
case VK_DIVIDE: return CrafterKeys::keypad_divide;
case VK_DECIMAL: return CrafterKeys::keypad_decimal;
// Punctuation
case VK_OEM_3: return CrafterKeys::grave; // `
case VK_OEM_MINUS: return CrafterKeys::minus; // -
case VK_OEM_PLUS: return CrafterKeys::equal; // =
case VK_OEM_4: return CrafterKeys::bracket_left; // [
case VK_OEM_6: return CrafterKeys::bracket_right; // ]
case VK_OEM_5: return CrafterKeys::backslash; // \
case VK_OEM_1: return CrafterKeys::semicolon; // ;
case VK_OEM_7: return CrafterKeys::quote; // '
case VK_OEM_COMMA:return CrafterKeys::comma; // ,
case VK_OEM_PERIOD:return CrafterKeys::period; // .
case VK_OEM_2: return CrafterKeys::slash; // /
default:
return CrafterKeys::CrafterKeysMax;
}
}
void WindowVulkan::CreateSwapchain()
{
// Store the current swap chain handle so we can use it later on to ease up recreation
VkSwapchainKHR oldSwapchain = swapChain;
// Get physical device surface properties and formats
VkSurfaceCapabilitiesKHR surfCaps;
VulkanDevice::CheckVkResult(vkGetPhysicalDeviceSurfaceCapabilitiesKHR(VulkanDevice::physDevice, vulkanSurface, &surfCaps));
VkExtent2D swapchainExtent = {};
// If width (and height) equals the special value 0xFFFFFFFF, the size of the surface will be set by the swapchain
if (surfCaps.currentExtent.width == (uint32_t)-1)
{
// If the surface size is undefined, the size is set to the size of the images requested
swapchainExtent.width = width;
swapchainExtent.height = height;
}
else
{
// If the surface size is defined, the swap chain size must match
swapchainExtent = surfCaps.currentExtent;
width = surfCaps.currentExtent.width;
height = surfCaps.currentExtent.height;
}
// Select a present mode for the swapchain
uint32_t presentModeCount;
VulkanDevice::CheckVkResult(vkGetPhysicalDeviceSurfacePresentModesKHR(VulkanDevice::physDevice, vulkanSurface, &presentModeCount, NULL));
assert(presentModeCount > 0);
std::vector<VkPresentModeKHR> presentModes(presentModeCount);
VulkanDevice::CheckVkResult(vkGetPhysicalDeviceSurfacePresentModesKHR(VulkanDevice::physDevice, vulkanSurface, &presentModeCount, presentModes.data()));
// The VK_PRESENT_MODE_FIFO_KHR mode must always be present as per spec
// This mode waits for the vertical blank ("v-sync")
VkPresentModeKHR swapchainPresentMode = VK_PRESENT_MODE_FIFO_KHR;
// Find the transformation of the surface
VkSurfaceTransformFlagsKHR preTransform;
if (surfCaps.supportedTransforms & VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR)
{
// We prefer a non-rotated transform
preTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
}
else
{
preTransform = surfCaps.currentTransform;
}
// Find a supported composite alpha format (not all devices support alpha opaque)
VkCompositeAlphaFlagBitsKHR compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
// Simply select the first composite alpha format available
std::vector<VkCompositeAlphaFlagBitsKHR> compositeAlphaFlags = {
VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR,
VK_COMPOSITE_ALPHA_PRE_MULTIPLIED_BIT_KHR,
VK_COMPOSITE_ALPHA_POST_MULTIPLIED_BIT_KHR,
VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR,
};
for (auto& compositeAlphaFlag : compositeAlphaFlags) {
if (surfCaps.supportedCompositeAlpha & compositeAlphaFlag) {
compositeAlpha = compositeAlphaFlag;
break;
};
}
VkSwapchainCreateInfoKHR swapchainCI = {};
swapchainCI.sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR;
swapchainCI.surface = vulkanSurface;
swapchainCI.minImageCount = numFrames;
swapchainCI.imageFormat = colorFormat;
swapchainCI.imageColorSpace = colorSpace;
swapchainCI.imageExtent = { swapchainExtent.width, swapchainExtent.height };
swapchainCI.imageUsage = VK_IMAGE_USAGE_STORAGE_BIT;
swapchainCI.preTransform = (VkSurfaceTransformFlagBitsKHR)preTransform;
swapchainCI.imageArrayLayers = 1;
swapchainCI.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE;
swapchainCI.queueFamilyIndexCount = 0;
swapchainCI.presentMode = swapchainPresentMode;
// Setting oldSwapChain to the saved handle of the previous swapchain aids in resource reuse and makes sure that we can still present already acquired images
swapchainCI.oldSwapchain = oldSwapchain;
// Setting clipped to VK_TRUE allows the implementation to discard rendering outside of the surface area
swapchainCI.clipped = VK_TRUE;
swapchainCI.compositeAlpha = compositeAlpha;
VulkanDevice::CheckVkResult(vkCreateSwapchainKHR(VulkanDevice::device, &swapchainCI, nullptr, &swapChain));
// If an existing swap chain is re-created, destroy the old swap chain and the ressources owned by the application (image views, images are owned by the swap chain)
if (oldSwapchain != VK_NULL_HANDLE) {
for (auto i = 0; i < numFrames; i++) {
vkDestroyImageView(VulkanDevice::device, imageViews[i], nullptr);
}
vkDestroySwapchainKHR(VulkanDevice::device, oldSwapchain, nullptr);
}
uint32_t imageCount{ 0 };
VulkanDevice::CheckVkResult(vkGetSwapchainImagesKHR(VulkanDevice::device, swapChain, &imageCount, nullptr));
// Get the swap chain images
VulkanDevice::CheckVkResult(vkGetSwapchainImagesKHR(VulkanDevice::device, swapChain, &imageCount, images));
for (auto i = 0; i < numFrames; i++)
{
VkImageViewCreateInfo colorAttachmentView = {};
colorAttachmentView.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
colorAttachmentView.pNext = NULL;
colorAttachmentView.format = colorFormat;
colorAttachmentView.components = {
VK_COMPONENT_SWIZZLE_R,
VK_COMPONENT_SWIZZLE_G,
VK_COMPONENT_SWIZZLE_B,
VK_COMPONENT_SWIZZLE_A
};
colorAttachmentView.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
colorAttachmentView.subresourceRange.baseMipLevel = 0;
colorAttachmentView.subresourceRange.levelCount = 1;
colorAttachmentView.subresourceRange.baseArrayLayer = 0;
colorAttachmentView.subresourceRange.layerCount = 1;
colorAttachmentView.viewType = VK_IMAGE_VIEW_TYPE_2D;
colorAttachmentView.flags = 0;
colorAttachmentView.image = images[i];
VulkanDevice::CheckVkResult(vkCreateImageView(VulkanDevice::device, &colorAttachmentView, nullptr, &imageViews[i]));
}
}
// Define a window class name
const char g_szClassName[] = "myWindowClass";
// Window procedure function that processes messages
LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
WindowVulkan* window = nullptr;
if (msg == WM_NCCREATE)
{
CREATESTRUCT* pCreate = reinterpret_cast<CREATESTRUCT*>(lParam);
window = static_cast<WindowVulkan*>(pCreate->lpCreateParams);
SetWindowLongPtr(hwnd, GWLP_USERDATA, reinterpret_cast<LONG_PTR>(window));
return TRUE;
}
else
{
window = reinterpret_cast<WindowVulkan*>(
GetWindowLongPtr(hwnd, GWLP_USERDATA)
);
}
switch (msg) {
case WM_DESTROY:{
PostQuitMessage(0);
break;
}
case WM_KEYDOWN:{
if ((lParam & (1 << 30)) == 0) { // only first press
CrafterKeys crafterKey = vk_to_crafter_key(wParam);
if(window->heldkeys[static_cast<std::uint8_t>(crafterKey)]) {
window->onKeyHold[static_cast<std::uint8_t>(crafterKey)].Invoke();
window->onAnyKeyHold.Invoke(crafterKey);
} else{
window->heldkeys[static_cast<std::uint8_t>(crafterKey)] = true;
window->onKeyDown[static_cast<std::uint8_t>(crafterKey)].Invoke();
window->onAnyKeyDown.Invoke(crafterKey);
}
}
break;
}
case WM_KEYUP: {
CrafterKeys crafterKey = vk_to_crafter_key(wParam);
window->heldkeys[static_cast<std::uint8_t>(crafterKey)] = false;
window->onKeyUp[static_cast<std::uint8_t>(crafterKey)].Invoke();
window->onAnyKeyUp.Invoke(crafterKey);
break;
}
case WM_MOUSEMOVE: {
int x = LOWORD(lParam);
int y = HIWORD(lParam);
MousePoint pos = {FractionalToMappedBoundless<std::uint32_t>(static_cast<float>(x) / window->width), FractionalToMappedBoundless<std::uint32_t>(static_cast<float>(y) / window->height)};
window->currentMousePos = pos;
window->onMouseMove.Invoke({window->lastMousePos, window->currentMousePos, window->mouseDelta});
for(MouseElement* element : window->mouseElements) {
if(element) {
if(window->currentMousePos.x >= element->mouseScaled.x && window->currentMousePos.x <= element->mouseScaled.x+element->mouseScaled.width && window->currentMousePos.y > element->mouseScaled.y && window->currentMousePos.y < element->mouseScaled.y+element->mouseScaled.height) {
element->onMouseMove.Invoke({AbsoluteToMappedBoundless(window->currentMousePos.x - element->mouseScaled.x, element->mouseScaled.width), AbsoluteToMappedBoundless(window->currentMousePos.y - element->mouseScaled.y, element->mouseScaled.height)});
if(!(window->lastMousePos.x >= element->mouseScaled.x && window->lastMousePos.x <= element->mouseScaled.x+element->mouseScaled.width && window->lastMousePos.y > element->mouseScaled.y && window->lastMousePos.y < element->mouseScaled.y+element->mouseScaled.height)) {
element->onMouseEnter.Invoke({AbsoluteToMappedBoundless(window->currentMousePos.x - element->mouseScaled.x, element->mouseScaled.width), AbsoluteToMappedBoundless(window->currentMousePos.y - element->mouseScaled.y, element->mouseScaled.height)});
}
} else if(window->lastMousePos.x >= element->mouseScaled.x && window->lastMousePos.x <= element->mouseScaled.x+element->mouseScaled.width && window->lastMousePos.y > element->mouseScaled.y && window->lastMousePos.y < element->mouseScaled.y+element->mouseScaled.height) {
element->onMouseLeave.Invoke({AbsoluteToMappedBoundless(window->currentMousePos.x - element->mouseScaled.x, element->mouseScaled.width), AbsoluteToMappedBoundless(window->currentMousePos.y - element->mouseScaled.y, element->mouseScaled.height)});
}
}
}
window->mouseElements.erase(std::remove(window->mouseElements.begin(), window->mouseElements.end(), static_cast<MouseElement*>(nullptr)), window->mouseElements.end());
break;
}
case WM_LBUTTONDOWN: {
window->mouseLeftHeld = true;
window->onMouseLeftClick.Invoke(window->currentMousePos);
for(MouseElement* element : window->mouseElements) {
if(element) {
if(window->currentMousePos.x >= element->mouseScaled.x && window->currentMousePos.x <= element->mouseScaled.x+element->mouseScaled.width && window->currentMousePos.y > element->mouseScaled.y && window->currentMousePos.y < element->mouseScaled.y+element->mouseScaled.height) {
element->onMouseLeftClick.Invoke({AbsoluteToMappedBoundless(window->currentMousePos.x - element->mouseScaled.x, element->mouseScaled.width), AbsoluteToMappedBoundless(window->currentMousePos.y - element->mouseScaled.y, element->mouseScaled.height)});
}
}
}
break;
}
case WM_LBUTTONUP: {
window->mouseLeftHeld = false;
window->onMouseLeftRelease.Invoke(window->currentMousePos);
for(MouseElement* element : window->mouseElements) {
if(element) {
if(window->currentMousePos.x >= element->mouseScaled.x && window->currentMousePos.x <= element->mouseScaled.x+element->mouseScaled.width && window->currentMousePos.y > element->mouseScaled.y && window->currentMousePos.y < element->mouseScaled.y+element->mouseScaled.height) {
element->onMouseLeftRelease.Invoke({AbsoluteToMappedBoundless(window->currentMousePos.x - element->mouseScaled.x, element->mouseScaled.width), AbsoluteToMappedBoundless(window->currentMousePos.y - element->mouseScaled.y, element->mouseScaled.height)});
}
}
}
break;
}
case WM_RBUTTONDOWN: {
window->mouseRightHeld = true;
window->onMouseRightClick.Invoke(window->currentMousePos);
for(MouseElement* element : window->mouseElements) {
if(element) {
if(window->currentMousePos.x >= element->mouseScaled.x && window->currentMousePos.x <= element->mouseScaled.x+element->mouseScaled.width && window->currentMousePos.y > element->mouseScaled.y && window->currentMousePos.y < element->mouseScaled.y+element->mouseScaled.height) {
element->onMouseRightClick.Invoke({AbsoluteToMappedBoundless(window->currentMousePos.x - element->mouseScaled.x, element->mouseScaled.width), AbsoluteToMappedBoundless(window->currentMousePos.y - element->mouseScaled.y, element->mouseScaled.height)});
}
}
}
break;
}
case WM_RBUTTONUP: {
window->mouseRightHeld = false;
window->onMouseRightRelease.Invoke(window->currentMousePos);
for(MouseElement* element : window->mouseElements) {
if(element) {
if(window->currentMousePos.x >= element->mouseScaled.x && window->currentMousePos.x <= element->mouseScaled.x+element->mouseScaled.width && window->currentMousePos.y > element->mouseScaled.y && window->currentMousePos.y < element->mouseScaled.y+element->mouseScaled.height) {
element->onMouseRightRelease.Invoke({AbsoluteToMappedBoundless(window->currentMousePos.x - element->mouseScaled.x, element->mouseScaled.width), AbsoluteToMappedBoundless(window->currentMousePos.y - element->mouseScaled.y, element->mouseScaled.height)});
}
}
}
break;
}
default:
return DefWindowProc(hwnd, msg, wParam, lParam);
}
return 0;
}
WindowVulkan::WindowVulkan(std::uint32_t width, std::uint32_t height) : Window(width, height) {
}
WindowVulkan::WindowVulkan(std::uint32_t width, std::uint32_t height, std::string_view title) : Window(width, height) {
// Initialize the window class
WNDCLASS wc = {0};
wc.lpfnWndProc = WndProc; // Set window procedure
wc.hInstance = GetModuleHandle(NULL); // Get instance handle
wc.lpszClassName = g_szClassName;
wc.hCursor = LoadCursor(NULL, IDC_ARROW);
if (!RegisterClass(&wc)) {
MessageBox(NULL, "Window Class Registration Failed!", "Error", MB_ICONERROR);
}
RECT rc = {0, 0, static_cast<LONG>(width), static_cast<LONG>(height)};
AdjustWindowRect(&rc, WS_OVERLAPPEDWINDOW, FALSE);
HWND hwnd = CreateWindowEx(
0,
g_szClassName,
title.data(),
WS_OVERLAPPEDWINDOW,
CW_USEDEFAULT, CW_USEDEFAULT,
rc.right - rc.left,
rc.bottom - rc.top,
NULL, NULL, wc.hInstance, this
);
if (hwnd == NULL) {
MessageBox(NULL, "Window Creation Failed!", "Error", MB_ICONERROR);
}
// Show the window
ShowWindow(hwnd, SW_SHOWNORMAL);
UpdateWindow(hwnd);
MSG msg;
while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
VkWin32SurfaceCreateInfoKHR createInfo = {};
createInfo.sType = VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR;
createInfo.hinstance = wc.hInstance;
createInfo.hwnd = hwnd;
VulkanDevice::CheckVkResult(vkCreateWin32SurfaceKHR(VulkanDevice::instance, &createInfo, NULL, &vulkanSurface));
// Get list of supported surface formats
std::uint32_t formatCount;
VulkanDevice::CheckVkResult(vkGetPhysicalDeviceSurfaceFormatsKHR(VulkanDevice::physDevice, vulkanSurface, &formatCount, NULL));
assert(formatCount > 0);
std::vector<VkSurfaceFormatKHR> surfaceFormats(formatCount);
VulkanDevice::CheckVkResult(vkGetPhysicalDeviceSurfaceFormatsKHR(VulkanDevice::physDevice, vulkanSurface, &formatCount, surfaceFormats.data()));
// We want to get a format that best suits our needs, so we try to get one from a set of preferred formats
// Initialize the format to the first one returned by the implementation in case we can't find one of the preffered formats
VkSurfaceFormatKHR selectedFormat = surfaceFormats[0];
std::vector<VkFormat> preferredImageFormats = {
VK_FORMAT_R8G8B8A8_UNORM,
VK_FORMAT_B8G8R8A8_UNORM
};
for (auto& availableFormat : surfaceFormats) {
if (std::find(preferredImageFormats.begin(), preferredImageFormats.end(), availableFormat.format) != preferredImageFormats.end()) {
selectedFormat = availableFormat;
break;
}
}
colorFormat = selectedFormat.format;
colorSpace = selectedFormat.colorSpace;
CreateSwapchain();
VkCommandBufferAllocateInfo cmdBufAllocateInfo {};
cmdBufAllocateInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;
cmdBufAllocateInfo.commandPool = VulkanDevice::commandPool;
cmdBufAllocateInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;
cmdBufAllocateInfo.commandBufferCount = numFrames;
VulkanDevice::CheckVkResult(vkAllocateCommandBuffers(VulkanDevice::device, &cmdBufAllocateInfo, drawCmdBuffers));
VkSemaphoreCreateInfo semaphoreCreateInfo {};
semaphoreCreateInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
VulkanDevice::CheckVkResult(vkCreateSemaphore(VulkanDevice::device, &semaphoreCreateInfo, nullptr, &semaphores.presentComplete));
VulkanDevice::CheckVkResult(vkCreateSemaphore(VulkanDevice::device, &semaphoreCreateInfo, nullptr, &semaphores.renderComplete));
// Set up submit info structure
// Semaphores will stay the same during application lifetime
// Command buffer submission info is set by each example
submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
submitInfo.pWaitDstStageMask = &submitPipelineStages;
submitInfo.waitSemaphoreCount = 1;
submitInfo.pWaitSemaphores = &semaphores.presentComplete;
submitInfo.signalSemaphoreCount = 1;
submitInfo.pSignalSemaphores = &semaphores.renderComplete;
submitInfo.pNext = VK_NULL_HANDLE;
}
WindowVulkan::~WindowVulkan() {
}
void WindowVulkan::StartSync() {
MSG msg;
while (GetMessage(&msg, NULL, 0, 0) > 0) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
void WindowVulkan::SetTitle(const std::string_view title) {
}
void WindowVulkan::StartUpdate() {
lastFrameBegin = std::chrono::high_resolution_clock::now();
updating = true;
while(updating) {
MSG msg;
while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
mouseDelta = {std::int64_t(currentMousePos.x)-lastMousePos.x, std::int64_t(currentMousePos.y)-lastMousePos.y};
auto start = std::chrono::high_resolution_clock::now();
Render();
lastMousePos = currentMousePos;
currentFrameTime = {start, start-lastFrameBegin};
}
}
void WindowVulkan::StopUpdate() {
updating = false;
}
VkCommandBuffer WindowVulkan::StartInit() {
VkCommandBufferBeginInfo cmdBufInfo {};
cmdBufInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
VulkanDevice::CheckVkResult(vkBeginCommandBuffer(drawCmdBuffers[currentBuffer], &cmdBufInfo));
VkImageSubresourceRange range{};
range.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
range.baseMipLevel = 0;
range.levelCount = VK_REMAINING_MIP_LEVELS;
range.baseArrayLayer = 0;
range.layerCount = VK_REMAINING_ARRAY_LAYERS;
for(std::uint32_t i = 0; i < numFrames; i++) {
image_layout_transition(drawCmdBuffers[currentBuffer],
images[i],
VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT,
VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT,
0,
0,
VK_IMAGE_LAYOUT_UNDEFINED,
VK_IMAGE_LAYOUT_PRESENT_SRC_KHR,
range
);
}
return drawCmdBuffers[currentBuffer];
}
void WindowVulkan::FinishInit() {
VkSubmitInfo submitInfo{};
submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
submitInfo.commandBufferCount = 1;
submitInfo.pCommandBuffers = &drawCmdBuffers[currentBuffer];
VulkanDevice::CheckVkResult(vkEndCommandBuffer(drawCmdBuffers[currentBuffer]));
VulkanDevice::CheckVkResult(vkQueueSubmit(VulkanDevice::queue, 1, &submitInfo, VK_NULL_HANDLE));
VulkanDevice::CheckVkResult(vkQueueWaitIdle(VulkanDevice::queue));
}
void WindowVulkan::Render() {
// Acquire the next image from the swap chain
VulkanDevice::CheckVkResult(vkAcquireNextImageKHR(VulkanDevice::device, swapChain, UINT64_MAX, semaphores.presentComplete, (VkFence)nullptr, &currentBuffer));
submitInfo.commandBufferCount = 1;
submitInfo.pCommandBuffers = &drawCmdBuffers[currentBuffer];
VkCommandBufferBeginInfo cmdBufInfo {};
cmdBufInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
VulkanDevice::CheckVkResult(vkBeginCommandBuffer(drawCmdBuffers[currentBuffer], &cmdBufInfo));
VkImageSubresourceRange range{};
range.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
range.baseMipLevel = 0;
range.levelCount = VK_REMAINING_MIP_LEVELS;
range.baseArrayLayer = 0;
range.layerCount = VK_REMAINING_ARRAY_LAYERS;
VkImageMemoryBarrier image_memory_barrier {
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
.srcAccessMask = 0,
.dstAccessMask = VK_ACCESS_SHADER_WRITE_BIT,
.oldLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR,
.newLayout = VK_IMAGE_LAYOUT_GENERAL,
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.image = images[currentBuffer],
.subresourceRange = range
};
vkCmdPipelineBarrier(drawCmdBuffers[currentBuffer], VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, VK_PIPELINE_STAGE_RAY_TRACING_SHADER_BIT_KHR, 0, 0, nullptr, 0, nullptr, 1, &image_memory_barrier);
onRender.Invoke();
vkCmdBindPipeline(drawCmdBuffers[currentBuffer], VK_PIPELINE_BIND_POINT_RAY_TRACING_KHR, rtPipeline);
VkBindDescriptorSetsInfo bindDescriptorSetsInfo{
.sType = VK_STRUCTURE_TYPE_BIND_DESCRIPTOR_SETS_INFO,
.stageFlags = VK_SHADER_STAGE_ALL,
.layout = rtPipelineLayout,
.firstSet = 0,
.descriptorSetCount = static_cast<std::uint32_t>(descriptorsRt.size()),
.pDescriptorSets = descriptorsRt.data()
};
vkCmdBindDescriptorSets2(drawCmdBuffers[currentBuffer], &bindDescriptorSetsInfo);
VulkanDevice::vkCmdTraceRaysKHR(drawCmdBuffers[currentBuffer], &raygenRegion, &missRegion, &hitRegion, &callableRegion, width, height, 1);
VkImageMemoryBarrier image_memory_barrier2 {
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
.srcAccessMask = VK_ACCESS_SHADER_WRITE_BIT,
.dstAccessMask = 0,
.oldLayout = VK_IMAGE_LAYOUT_GENERAL,
.newLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR,
.srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED,
.image = images[currentBuffer],
.subresourceRange = range
};
vkCmdPipelineBarrier(drawCmdBuffers[currentBuffer], VK_PIPELINE_STAGE_RAY_TRACING_SHADER_BIT_KHR, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, 0, 0, nullptr, 0, nullptr, 1, &image_memory_barrier2);
VulkanDevice::CheckVkResult(vkEndCommandBuffer(drawCmdBuffers[currentBuffer]));
VulkanDevice::CheckVkResult(vkQueueSubmit(VulkanDevice::queue, 1, &submitInfo, VK_NULL_HANDLE));
VkPresentInfoKHR presentInfo = {};
presentInfo.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;
presentInfo.pNext = NULL;
presentInfo.swapchainCount = 1;
presentInfo.pSwapchains = &swapChain;
presentInfo.pImageIndices = &currentBuffer;
// Check if a wait semaphore has been specified to wait for before presenting the image
if (semaphores.renderComplete != VK_NULL_HANDLE)
{
presentInfo.pWaitSemaphores = &semaphores.renderComplete;
presentInfo.waitSemaphoreCount = 1;
}
VkResult result = vkQueuePresentKHR(VulkanDevice::queue, &presentInfo);
if(result == VK_SUBOPTIMAL_KHR) {
CreateSwapchain();
} else {
VulkanDevice::CheckVkResult(result);
}
VulkanDevice::CheckVkResult(vkQueueWaitIdle(VulkanDevice::queue));
}