/* 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 #include "vulkan/vulkan.h" #include "vulkan/vulkan_win32.h" #include 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 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 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(lParam); window = static_cast(pCreate->lpCreateParams); SetWindowLongPtr(hwnd, GWLP_USERDATA, reinterpret_cast(window)); return TRUE; } else { window = reinterpret_cast( 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(crafterKey)]) { window->onKeyHold[static_cast(crafterKey)].Invoke(); window->onAnyKeyHold.Invoke(crafterKey); } else{ window->heldkeys[static_cast(crafterKey)] = true; window->onKeyDown[static_cast(crafterKey)].Invoke(); window->onAnyKeyDown.Invoke(crafterKey); } } break; } case WM_KEYUP: { CrafterKeys crafterKey = vk_to_crafter_key(wParam); window->heldkeys[static_cast(crafterKey)] = false; window->onKeyUp[static_cast(crafterKey)].Invoke(); window->onAnyKeyUp.Invoke(crafterKey); break; } case WM_MOUSEMOVE: { int x = LOWORD(lParam); int y = HIWORD(lParam); MousePoint pos = {FractionalToMappedBoundless(static_cast(x) / window->width), FractionalToMappedBoundless(static_cast(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(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(width), static_cast(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 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 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) { } #ifdef CRAFTER_TIMING std::chrono::time_point framEnd; #endif 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}; lastFrameBegin = start; } } 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, ¤tBuffer)); 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(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 = ¤tBuffer; // 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)); }