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 ) {
}
2026-03-01 03:00:08 +01:00
# ifdef CRAFTER_TIMING
std : : chrono : : time_point < std : : chrono : : high_resolution_clock > framEnd ;
# endif
2026-02-24 02:32:37 +01:00
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 } ;
2026-03-01 03:00:08 +01:00
lastFrameBegin = start ;
2026-02-24 02:32:37 +01:00
}
}
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 ) ) ;
}