From b5e69c90fa90e49f6c0b373f4e13c4073c06b119 Mon Sep 17 00:00:00 2001 From: Jorijn van der Graaf Date: Tue, 24 Feb 2026 02:32:37 +0100 Subject: [PATCH] Win32 vulkan window --- examples/VulkanAnimation/main.cpp | 33 +- examples/VulkanTriangle/main.cpp | 53 +- examples/VulkanTriangle/project.json | 2 +- .../Crafter.Graphics-Mesh_vulkan.cpp | 2 +- .../Crafter.Graphics-VulkanDevice.cpp | 15 +- .../Crafter.Graphics-Window_vulkan.cpp | 109 +-- ...Crafter.Graphics-Window_vulkan_windows.cpp | 678 ++++++++++++++++++ ...Crafter.Graphics-DescriptorPoolVulkan.cppm | 34 + .../Crafter.Graphics-PipelineRTVulkan.cppm | 20 +- interfaces/Crafter.Graphics-VulkanDevice.cppm | 1 - interfaces/Crafter.Graphics-Window.cppm | 64 +- project.json | 24 +- 12 files changed, 950 insertions(+), 85 deletions(-) create mode 100644 implementations/Crafter.Graphics-Window_vulkan_windows.cpp diff --git a/examples/VulkanAnimation/main.cpp b/examples/VulkanAnimation/main.cpp index 39647a3..ede4061 100644 --- a/examples/VulkanAnimation/main.cpp +++ b/examples/VulkanAnimation/main.cpp @@ -6,17 +6,17 @@ import std; import Crafter.Event; import Crafter.Math; -typedef VulkanShader<"raygen.spv", "main", VK_SHADER_STAGE_RAYGEN_BIT_KHR> Raygenspv; -typedef VulkanShader<"closesthit.spv", "main", VK_SHADER_STAGE_CLOSEST_HIT_BIT_KHR> Closesthitspv; -typedef VulkanShader<"miss.spv", "main", VK_SHADER_STAGE_MISS_BIT_KHR> Misspv; +typedef VulkanShaderConst<"raygen.spv", "main", VK_SHADER_STAGE_RAYGEN_BIT_KHR> Raygenspv; +typedef VulkanShaderConst<"closesthit.spv", "main", VK_SHADER_STAGE_CLOSEST_HIT_BIT_KHR> Closesthitspv; +typedef VulkanShaderConst<"miss.spv", "main", VK_SHADER_STAGE_MISS_BIT_KHR> Misspv; typedef std::tuple AllShaders; typedef std::tuple< ShaderGroup<0, VK_SHADER_UNUSED_KHR, VK_SHADER_UNUSED_KHR, VK_SHADER_UNUSED_KHR>, ShaderGroup<1, VK_SHADER_UNUSED_KHR, VK_SHADER_UNUSED_KHR, VK_SHADER_UNUSED_KHR>, ShaderGroup > ShaderGroups; -typedef PipelineRTVulkan Pipeline; -typedef DescriptorSetLayoutVulkan<1, {{ +typedef PipelineRTVulkanConst Pipeline; +typedef DescriptorSetLayoutVulkanConst<1, {{ { .binding = 0, .descriptorType = VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR, @@ -24,7 +24,7 @@ typedef DescriptorSetLayoutVulkan<1, {{ .stageFlags = VK_SHADER_STAGE_RAYGEN_BIT_KHR, }, }}> descriptorSetLayoutTlas; -typedef DescriptorSetLayoutVulkan<1, {{ +typedef DescriptorSetLayoutVulkanConst<1, {{ { .binding = 0, .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, @@ -42,7 +42,7 @@ int main() { Closesthitspv::CreateShader(); Misspv::CreateShader(); - ShaderBindingTableVulkan::Init(); + ShaderBindingTableVulkanConst::Init(); descriptorSetLayoutTlas::Init(); descriptorSetLayoutImage::Init(); @@ -56,12 +56,23 @@ int main() { window.SetPipelineRT(); Mesh triangleMesh; - std::array verts {{{-150, -150, 100}, {0, 150, 100}, {150, -150, 100}}}; + std::array, 3> verts {{{-150, -150, 100}, {0, 150, 100}, {150, -150, 100}}}; std::array index {{2,1,0}}; triangleMesh.Build(verts, index, cmd); - RenderingElement3DVulkan& el = RenderingElement3DVulkan::elements.emplace_back(triangleMesh); + + RenderingElement3DVulkan renderer = { + .instance = { + .instanceCustomIndex = 0, + .mask = 0xFF, + .instanceShaderBindingTableRecordOffset = 0, + .flags = VK_GEOMETRY_INSTANCE_FORCE_OPAQUE_BIT_KHR, + .accelerationStructureReference = triangleMesh.blasAddr + } + }; + + RenderingElement3DVulkan::elements.emplace_back(&renderer); MatrixRowMajor transform = MatrixRowMajor::Identity(); - std::memcpy(el.instance.transform.matrix, transform.m, sizeof(transform.m)); + std::memcpy(renderer.instance.transform.matrix, transform.m, sizeof(transform.m)); RenderingElement3DVulkan::tlases.resize(WindowVulkan::numFrames); RenderingElement3DVulkan::BuildTLAS(cmd, 0); @@ -135,7 +146,7 @@ int main() { } MatrixRowMajor transform = MatrixRowMajor::Translation(value, 0, 0); - std::memcpy(el.instance.transform.matrix, transform.m, sizeof(transform.m)); + std::memcpy(renderer.instance.transform.matrix, transform.m, sizeof(transform.m)); RenderingElement3DVulkan::BuildTLAS(window.drawCmdBuffers[window.currentBuffer], window.currentBuffer); VkWriteDescriptorSetAccelerationStructureKHR writeDescriptorSetAccelerationStructure { diff --git a/examples/VulkanTriangle/main.cpp b/examples/VulkanTriangle/main.cpp index a35c950..029a248 100644 --- a/examples/VulkanTriangle/main.cpp +++ b/examples/VulkanTriangle/main.cpp @@ -6,17 +6,17 @@ import std; import Crafter.Event; import Crafter.Math; -typedef VulkanShader<"raygen.spv", "main", VK_SHADER_STAGE_RAYGEN_BIT_KHR> Raygenspv; -typedef VulkanShader<"closesthit.spv", "main", VK_SHADER_STAGE_CLOSEST_HIT_BIT_KHR> Closesthitspv; -typedef VulkanShader<"miss.spv", "main", VK_SHADER_STAGE_MISS_BIT_KHR> Misspv; +typedef VulkanShaderConst<"raygen.spv", "main", VK_SHADER_STAGE_RAYGEN_BIT_KHR> Raygenspv; +typedef VulkanShaderConst<"closesthit.spv", "main", VK_SHADER_STAGE_CLOSEST_HIT_BIT_KHR> Closesthitspv; +typedef VulkanShaderConst<"miss.spv", "main", VK_SHADER_STAGE_MISS_BIT_KHR> Misspv; typedef std::tuple AllShaders; typedef std::tuple< ShaderGroup<0, VK_SHADER_UNUSED_KHR, VK_SHADER_UNUSED_KHR, VK_SHADER_UNUSED_KHR>, ShaderGroup<1, VK_SHADER_UNUSED_KHR, VK_SHADER_UNUSED_KHR, VK_SHADER_UNUSED_KHR>, ShaderGroup > ShaderGroups; -typedef PipelineRTVulkan Pipeline; -typedef DescriptorSetLayoutVulkan<2, {{ +typedef PipelineRTVulkanConst Pipeline; +typedef DescriptorSetLayoutVulkanConst<3, {{ { .binding = 0, .descriptorType = VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR, @@ -28,6 +28,12 @@ typedef DescriptorSetLayoutVulkan<2, {{ .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, .descriptorCount = 1, .stageFlags = VK_SHADER_STAGE_RAYGEN_BIT_KHR, + }, + { + .binding = 2, + .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, + .descriptorCount = 1, + .stageFlags = VK_SHADER_STAGE_RAYGEN_BIT_KHR, } }}> descriptorSetLayout; @@ -39,7 +45,7 @@ int main() { Raygenspv::CreateShader(); Closesthitspv::CreateShader(); Misspv::CreateShader(); - ShaderBindingTableVulkan::Init(); + ShaderBindingTableVulkanConst::Init(); descriptorSetLayout::Init(); std::array layouts {{descriptorSetLayout::layout}}; @@ -51,12 +57,24 @@ int main() { Pipeline::Init(cmd, layouts); Mesh triangleMesh; - std::array verts {{{-150, -150, 100}, {0, 150, 100}, {150, -150, 100}}}; + std::array, 3> verts {{{-150, -150, 100}, {0, 150, 100}, {150, -150, 100}}}; std::array index {{2,1,0}}; triangleMesh.Build(verts, index, cmd); - RenderingElement3DVulkan& el = RenderingElement3DVulkan::elements.emplace_back(triangleMesh); + + RenderingElement3DVulkan renderer = { + .instance = { + .instanceCustomIndex = 0, + .mask = 0xFF, + .instanceShaderBindingTableRecordOffset = 0, + .flags = VK_GEOMETRY_INSTANCE_FORCE_OPAQUE_BIT_KHR, + .accelerationStructureReference = triangleMesh.blasAddr + } + }; + + RenderingElement3DVulkan::elements.emplace_back(&renderer); + MatrixRowMajor transform = MatrixRowMajor::Identity(); - std::memcpy(el.instance.transform.matrix, transform.m, sizeof(transform.m)); + std::memcpy(renderer.instance.transform.matrix, transform.m, sizeof(transform.m)); RenderingElement3DVulkan::tlases.resize(1); RenderingElement3DVulkan::BuildTLAS(cmd, 0); @@ -71,7 +89,10 @@ int main() { .pAccelerationStructures = &RenderingElement3DVulkan::tlases[0].accelerationStructure }; - VkWriteDescriptorSet write[2] = { + VulkanBuffer lightBuffer; + lightBuffer.Create(VK_BUFFER_USAGE_STORAGE_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, 1); + + VkWriteDescriptorSet write[3] = { { .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, .pNext = &writeDescriptorSetAccelerationStructure, @@ -89,9 +110,19 @@ int main() { .descriptorCount = 1, .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, .pImageInfo = &imageInfo + }, + { + .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, + .dstSet = pool.sets[0], + .dstBinding = 2, + .dstArrayElement = 0, + .descriptorCount = 1, + .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, + .pBufferInfo = &lightBuffer.descriptor } }; - vkUpdateDescriptorSets(VulkanDevice::device, 2, write, 0, nullptr); + vkUpdateDescriptorSets(VulkanDevice::device, 3, write, 0, nullptr); + window.SetPipelineRT(); window.descriptorsRt = pool.sets; diff --git a/examples/VulkanTriangle/project.json b/examples/VulkanTriangle/project.json index b30faa8..61eba87 100644 --- a/examples/VulkanTriangle/project.json +++ b/examples/VulkanTriangle/project.json @@ -7,7 +7,7 @@ "dependencies": [ { "path":"../../project.json", - "configuration":"lib-vulkan-debug" + "configuration":"lib-vulkan" } ], "shaders": [ diff --git a/implementations/Crafter.Graphics-Mesh_vulkan.cpp b/implementations/Crafter.Graphics-Mesh_vulkan.cpp index fb48192..472ff8a 100644 --- a/implementations/Crafter.Graphics-Mesh_vulkan.cpp +++ b/implementations/Crafter.Graphics-Mesh_vulkan.cpp @@ -32,7 +32,7 @@ using namespace Crafter; void Mesh::Build(std::span> verticies, std::span indicies, VkCommandBuffer cmd) { vertexBuffer.Resize(VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT | VK_BUFFER_USAGE_2_ACCELERATION_STRUCTURE_BUILD_INPUT_READ_ONLY_BIT_KHR, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, verticies.size()); - indexBuffer.Resize(VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT | VK_BUFFER_USAGE_2_ACCELERATION_STRUCTURE_BUILD_INPUT_READ_ONLY_BIT_KHR, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, indicies.size()); + indexBuffer.Resize(VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT | VK_BUFFER_USAGE_2_ACCELERATION_STRUCTURE_BUILD_INPUT_READ_ONLY_BIT_KHR | VK_BUFFER_USAGE_STORAGE_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, indicies.size()); std::memcpy(vertexBuffer.value, verticies.data(), verticies.size() * sizeof(Vector)); std::memcpy(indexBuffer.value, indicies.data(), indicies.size() * sizeof(std::uint32_t)); diff --git a/implementations/Crafter.Graphics-VulkanDevice.cpp b/implementations/Crafter.Graphics-VulkanDevice.cpp index c17971f..af110ba 100644 --- a/implementations/Crafter.Graphics-VulkanDevice.cpp +++ b/implementations/Crafter.Graphics-VulkanDevice.cpp @@ -21,7 +21,6 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA module; #include -#include #include #define GET_EXTENSION_FUNCTION(_id) ((PFN_##_id)(vkGetInstanceProcAddr(instance, #_id))) @@ -35,7 +34,11 @@ using namespace Crafter; const char* const instanceExtensionNames[] = { "VK_EXT_debug_utils", "VK_KHR_surface", + #ifdef CRAFTER_BUILD_CONFIGURATION_TARGET_x86_64_w64_mingw32 + "VK_KHR_win32_surface" + #else "VK_KHR_wayland_surface" + #endif }; const char* const deviceExtensionNames[] = { "VK_KHR_swapchain", @@ -246,11 +249,11 @@ void VulkanDevice::CreateDevice() { }; VkPhysicalDeviceDynamicRenderingFeaturesKHR dynamicRenderingFeature = {VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DYNAMIC_RENDERING_FEATURES_KHR}; - dynamicRenderingFeature.dynamicRendering = VK_TRUE; + dynamicRenderingFeature.dynamicRendering = VK_FALSE; dynamicRenderingFeature.pNext = &deviceAccelerationStructureFeature; VkPhysicalDeviceMeshShaderFeaturesEXT ext_feature = {VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MESH_SHADER_FEATURES_EXT}; - ext_feature.meshShader = VK_TRUE; + ext_feature.meshShader = VK_FALSE; ext_feature.pNext = &dynamicRenderingFeature; VkPhysicalDeviceFeatures2 physical_features2 = { @@ -305,9 +308,9 @@ void VulkanDevice::CreateDevice() { vkGetPhysicalDeviceMemoryProperties(physDevice, &memoryProperties); - vkCmdDrawMeshTasksEXTProc = reinterpret_cast(vkGetDeviceProcAddr(device, "vkCmdDrawMeshTasksEXT")); - vkCmdBeginRenderingKHRProc = reinterpret_cast(vkGetInstanceProcAddr(instance, "vkCmdBeginRenderingKHR")); - vkCmdEndRenderingKHRProc = reinterpret_cast(vkGetInstanceProcAddr(instance, "vkCmdEndRenderingKHR")); + // vkCmdDrawMeshTasksEXTProc = reinterpret_cast(vkGetDeviceProcAddr(device, "vkCmdDrawMeshTasksEXT")); + // vkCmdBeginRenderingKHRProc = reinterpret_cast(vkGetInstanceProcAddr(instance, "vkCmdBeginRenderingKHR")); + // vkCmdEndRenderingKHRProc = reinterpret_cast(vkGetInstanceProcAddr(instance, "vkCmdEndRenderingKHR")); vkGetAccelerationStructureBuildSizesKHR = reinterpret_cast(vkGetInstanceProcAddr(instance, "vkGetAccelerationStructureBuildSizesKHR")); vkCreateAccelerationStructureKHR = reinterpret_cast(vkGetInstanceProcAddr(instance, "vkCreateAccelerationStructureKHR")); vkCmdBuildAccelerationStructuresKHR = reinterpret_cast(vkGetInstanceProcAddr(instance, "vkCmdBuildAccelerationStructuresKHR")); diff --git a/implementations/Crafter.Graphics-Window_vulkan.cpp b/implementations/Crafter.Graphics-Window_vulkan.cpp index f2fb871..952c40f 100644 --- a/implementations/Crafter.Graphics-Window_vulkan.cpp +++ b/implementations/Crafter.Graphics-Window_vulkan.cpp @@ -127,7 +127,7 @@ void WindowVulkan::CreateSwapchain() swapchainCI.imageFormat = colorFormat; swapchainCI.imageColorSpace = colorSpace; swapchainCI.imageExtent = { swapchainExtent.width, swapchainExtent.height }; - swapchainCI.imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT; + swapchainCI.imageUsage = VK_IMAGE_USAGE_STORAGE_BIT; swapchainCI.preTransform = (VkSurfaceTransformFlagBitsKHR)preTransform; swapchainCI.imageArrayLayers = 1; swapchainCI.imageSharingMode = VK_SHARING_MODE_EXCLUSIVE; @@ -138,8 +138,6 @@ void WindowVulkan::CreateSwapchain() // Setting clipped to VK_TRUE allows the implementation to discard rendering outside of the surface area swapchainCI.clipped = VK_TRUE; swapchainCI.compositeAlpha = compositeAlpha; - swapchainCI.imageUsage |= VK_IMAGE_USAGE_TRANSFER_SRC_BIT | VK_IMAGE_USAGE_STORAGE_BIT; - swapchainCI.imageUsage |= VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_STORAGE_BIT; VulkanDevice::CheckVkResult(vkCreateSwapchainKHR(VulkanDevice::device, &swapchainCI, nullptr, &swapChain)); @@ -177,6 +175,13 @@ void WindowVulkan::CreateSwapchain() colorAttachmentView.flags = 0; colorAttachmentView.image = images[i]; VulkanDevice::CheckVkResult(vkCreateImageView(VulkanDevice::device, &colorAttachmentView, nullptr, &imageViews[i])); + + 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; } } @@ -253,32 +258,6 @@ WindowVulkan::WindowVulkan(std::uint32_t width, std::uint32_t height) : Window(w CreateSwapchain(); - std::array attachments = {}; - // Color attachment - attachments[0].format = colorFormat; - attachments[0].samples = VK_SAMPLE_COUNT_1_BIT; - attachments[0].loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR; - attachments[0].storeOp = VK_ATTACHMENT_STORE_OP_STORE; - attachments[0].stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; - attachments[0].stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; - attachments[0].initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; - attachments[0].finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; - - VkAttachmentReference colorReference = {}; - colorReference.attachment = 0; - colorReference.layout = VK_IMAGE_LAYOUT_GENERAL; - - VkSubpassDescription subpassDescription = {}; - subpassDescription.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; - subpassDescription.colorAttachmentCount = 1; - subpassDescription.pColorAttachments = &colorReference; - subpassDescription.pDepthStencilAttachment = nullptr; - subpassDescription.inputAttachmentCount = 0; - subpassDescription.pInputAttachments = nullptr; - subpassDescription.preserveAttachmentCount = 0; - subpassDescription.pPreserveAttachments = nullptr; - subpassDescription.pResolveAttachments = nullptr; - VkCommandBufferAllocateInfo cmdBufAllocateInfo {}; cmdBufAllocateInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; cmdBufAllocateInfo.commandPool = VulkanDevice::commandPool; @@ -337,16 +316,19 @@ void WindowVulkan::Render() { range.baseArrayLayer = 0; range.layerCount = VK_REMAINING_ARRAY_LAYERS; - image_layout_transition(drawCmdBuffers[currentBuffer], - images[currentBuffer], - VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, - VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, - 0, - VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT, - VK_IMAGE_LAYOUT_UNDEFINED, - VK_IMAGE_LAYOUT_GENERAL, - range - ); + 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(); @@ -363,12 +345,20 @@ void WindowVulkan::Render() { vkCmdBindDescriptorSets2(drawCmdBuffers[currentBuffer], &bindDescriptorSetsInfo); VulkanDevice::vkCmdTraceRaysKHR(drawCmdBuffers[currentBuffer], &raygenRegion, &missRegion, &hitRegion, &callableRegion, width, height, 1); - image_layout_transition(drawCmdBuffers[currentBuffer], - images[currentBuffer], - VK_IMAGE_LAYOUT_GENERAL, - VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, - range - ); + + 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])); @@ -385,7 +375,13 @@ void WindowVulkan::Render() { presentInfo.pWaitSemaphores = &semaphores.renderComplete; presentInfo.waitSemaphoreCount = 1; } - VulkanDevice::CheckVkResult(vkQueuePresentKHR(VulkanDevice::queue, &presentInfo)); + + VkResult result = vkQueuePresentKHR(VulkanDevice::queue, &presentInfo); + if(result == VK_SUBOPTIMAL_KHR) { + CreateSwapchain(); + } else { + VulkanDevice::CheckVkResult(result); + } VulkanDevice::CheckVkResult(vkQueueWaitIdle(VulkanDevice::queue)); } @@ -393,6 +389,27 @@ 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]; } diff --git a/implementations/Crafter.Graphics-Window_vulkan_windows.cpp b/implementations/Crafter.Graphics-Window_vulkan_windows.cpp new file mode 100644 index 0000000..a0b97a8 --- /dev/null +++ b/implementations/Crafter.Graphics-Window_vulkan_windows.cpp @@ -0,0 +1,678 @@ +/* +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 +#include +#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) { + +} + + +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, ¤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)); +} diff --git a/interfaces/Crafter.Graphics-DescriptorPoolVulkan.cppm b/interfaces/Crafter.Graphics-DescriptorPoolVulkan.cppm index 3ed0129..562fc36 100644 --- a/interfaces/Crafter.Graphics-DescriptorPoolVulkan.cppm +++ b/interfaces/Crafter.Graphics-DescriptorPoolVulkan.cppm @@ -117,6 +117,40 @@ export namespace Crafter { return types; } + + template + constexpr static std::vector GetPoolSizesCombined(const std::span shaders) { + std::vector types; + + for(const DescriptorSetLayoutVulkan& shader : shaders) { + for (const VkDescriptorSetLayoutBinding& binding : shader.descriptors) { + for(VkDescriptorPoolSize& type : types) { + if(type.type == binding.descriptorType) { + type.descriptorCount += binding.descriptorCount; + goto inner; + } + } + types.emplace_back(binding.descriptorType, binding.descriptorCount); + inner:; + } + } + + ([&] { + for (const VkDescriptorSetLayoutBinding& binding : Shaders::descriptors) { + for(VkDescriptorPoolSize& type : types) { + if(type.type == binding.descriptorType) { + type.descriptorCount += binding.descriptorCount; + goto inner2; + } + } + types.emplace_back(binding.descriptorType, binding.descriptorCount); + inner2:; + } + }(), + ...); + + return types; + } public: void BuildPool(std::span poolSizes, std::span setLayouts) { diff --git a/interfaces/Crafter.Graphics-PipelineRTVulkan.cppm b/interfaces/Crafter.Graphics-PipelineRTVulkan.cppm index 2b8aa52..151bfaf 100644 --- a/interfaces/Crafter.Graphics-PipelineRTVulkan.cppm +++ b/interfaces/Crafter.Graphics-PipelineRTVulkan.cppm @@ -115,7 +115,7 @@ export namespace Crafter { } }; - template + template struct ShaderGroup { static constexpr std::uint32_t generalShader = GeneralShader; static constexpr std::uint32_t closestHitShader = ClosestHitShader; @@ -135,7 +135,7 @@ export namespace Crafter { inline static VkStridedDeviceAddressRegionKHR hitRegion; inline static VkStridedDeviceAddressRegionKHR callableRegion; - static void Init(VkCommandBuffer cmd, std::span setLayouts) { + static void Init(VkCommandBuffer cmd, const std::span setLayouts) { VkPipelineLayoutCreateInfo pipelineLayoutInfo { .sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, .setLayoutCount = static_cast(setLayouts.size()), @@ -176,7 +176,7 @@ export namespace Crafter { hitRegion.stride = sbtStride; hitRegion.deviceAddress = AlignUp(missRegion.deviceAddress + missRegion.size, VulkanDevice::rayTracingProperties.shaderGroupBaseAlignment); - hitRegion.size = GetGroupCount(groupIndexSeq) * sbtStride; + hitRegion.size = (GetGroupCount(groupIndexSeq) * sbtStride) + (GetGroupCount(groupIndexSeq) * sbtStride); std::size_t bufferSize = hitRegion.deviceAddress + hitRegion.size; sbtBuffer.Create(VK_BUFFER_USAGE_2_SHADER_BINDING_TABLE_BIT_KHR | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, bufferSize); @@ -199,7 +199,7 @@ export namespace Crafter { VkRayTracingShaderGroupTypeKHR groupType; if constexpr(groupTemplate::generalShader != VK_SHADER_UNUSED_KHR) { groupType = VK_RAY_TRACING_SHADER_GROUP_TYPE_GENERAL_KHR; - } else if constexpr(groupTemplate::closestHitShader != VK_SHADER_UNUSED_KHR) { + } else if constexpr(groupTemplate::closestHitShader != VK_SHADER_UNUSED_KHR || groupTemplate::anyHitShader != VK_SHADER_UNUSED_KHR) { groupType = VK_RAY_TRACING_SHADER_GROUP_TYPE_TRIANGLES_HIT_GROUP_KHR; } else { static_assert( @@ -237,6 +237,11 @@ export namespace Crafter { if constexpr(shaderTemplate::_stage == stage) { count++; } + } else if constexpr(groupTemplate::anyHitShader != VK_SHADER_UNUSED_KHR) { + using shaderTemplate = std::tuple_element_t; + if constexpr(shaderTemplate::_stage == stage) { + count++; + } } else { static_assert( groupTemplate::generalShader != VK_SHADER_UNUSED_KHR || @@ -267,6 +272,12 @@ export namespace Crafter { std::memcpy(sbtBuffer.value + offset, shaderHandles.data() + index * VulkanDevice::rayTracingProperties.shaderGroupHandleSize, VulkanDevice::rayTracingProperties.shaderGroupHandleSize); offset += sbtStride; } + } else if constexpr(groupTemplate::anyHitShader != VK_SHADER_UNUSED_KHR) { + using shaderTemplate = std::tuple_element_t; + if constexpr(shaderTemplate::_stage == stage) { + std::memcpy(sbtBuffer.value + offset, shaderHandles.data() + index * VulkanDevice::rayTracingProperties.shaderGroupHandleSize, VulkanDevice::rayTracingProperties.shaderGroupHandleSize); + offset += sbtStride; + } } else { static_assert( groupTemplate::generalShader != VK_SHADER_UNUSED_KHR || @@ -283,6 +294,7 @@ export namespace Crafter { (AddShaderGroupToBuffer(sbtStride, offset), ...); offset = AlignUp(offset, VulkanDevice::rayTracingProperties.shaderGroupBaseAlignment); (AddShaderGroupToBuffer(sbtStride, offset), ...); + (AddShaderGroupToBuffer(sbtStride, offset), ...); } }; } diff --git a/interfaces/Crafter.Graphics-VulkanDevice.cppm b/interfaces/Crafter.Graphics-VulkanDevice.cppm index 2d60704..7d7c176 100644 --- a/interfaces/Crafter.Graphics-VulkanDevice.cppm +++ b/interfaces/Crafter.Graphics-VulkanDevice.cppm @@ -20,7 +20,6 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA module; #ifdef CRAFTER_GRAPHICS_VULKAN #include -#include #endif export module Crafter.Graphics:VulkanDevice; #ifdef CRAFTER_GRAPHICS_VULKAN diff --git a/interfaces/Crafter.Graphics-Window.cppm b/interfaces/Crafter.Graphics-Window.cppm index bd39133..b277371 100644 --- a/interfaces/Crafter.Graphics-Window.cppm +++ b/interfaces/Crafter.Graphics-Window.cppm @@ -40,6 +40,7 @@ module; #include #endif #ifdef CRAFTER_GRAPHICS_VULKAN +#ifndef CRAFTER_BUILD_CONFIGURATION_TARGET_x86_64_w64_mingw32 #include #include #include @@ -61,6 +62,9 @@ module; #include #include #include +#else +#include +#endif #endif export module Crafter.Graphics:Window; @@ -278,7 +282,7 @@ export namespace Crafter { // Command buffer submission and execution VkSemaphore renderComplete; }; - + #ifndef CRAFTER_BUILD_CONFIGURATION_TARGET_x86_64_w64_mingw32 class WindowVulkan final : public Window, public WindowKeyboard, public WindowMouse, public WindowTitle { public: WindowVulkan(std::uint32_t width, std::uint32_t height); @@ -412,5 +416,63 @@ export namespace Crafter { VkStridedDeviceAddressRegionKHR hitRegion; VkStridedDeviceAddressRegionKHR callableRegion; }; + #else + class WindowVulkan final : public Window, public WindowKeyboard, public WindowMouse, public WindowTitle { + public: + WindowVulkan(std::uint32_t width, std::uint32_t height); + WindowVulkan(std::uint32_t width, std::uint32_t height, const std::string_view title); + ~WindowVulkan(); + Event onRender; + std::vector descriptorsRt; + void Render(); + void QueueRender(); + void Resize(std::uint32_t width, std::uint32_t height); + void StartSync() override; + void StartUpdate() override; + void StopUpdate() override; + void SetTitle(const std::string_view title) override; + VkCommandBuffer StartInit(); + void FinishInit(); + + template + void SetPipelineRT() { + rtPipeline = Pipeline::pipeline; + rtPipelineLayout = Pipeline::pipelineLayout; + raygenRegion = Pipeline::raygenRegion; + missRegion = Pipeline::missRegion; + hitRegion = Pipeline::hitRegion; + callableRegion = Pipeline::callableRegion; + } + + void SetPipelineRT(PipelineRTVulkan& pipeline) { + rtPipeline = pipeline.pipeline; + rtPipelineLayout = pipeline.pipelineLayout; + raygenRegion = pipeline.raygenRegion; + missRegion = pipeline.missRegion; + hitRegion = pipeline.hitRegion; + callableRegion = pipeline.callableRegion; + } + void CreateSwapchain(); + static constexpr std::uint32_t numFrames = 3; + VkSurfaceKHR vulkanSurface = VK_NULL_HANDLE; + VkSwapchainKHR swapChain = VK_NULL_HANDLE; + VkFormat colorFormat; + VkColorSpaceKHR colorSpace; + VkImage images[numFrames]; + VkImageView imageViews[numFrames]; + std::thread thread; + VkCommandBuffer drawCmdBuffers[numFrames]; + VkSubmitInfo submitInfo; + Semaphores semaphores; + uint32_t currentBuffer = 0; + VkPipelineStageFlags submitPipelineStages = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; + VkPipeline rtPipeline; + VkPipelineLayout rtPipelineLayout; + VkStridedDeviceAddressRegionKHR raygenRegion; + VkStridedDeviceAddressRegionKHR missRegion; + VkStridedDeviceAddressRegionKHR hitRegion; + VkStridedDeviceAddressRegionKHR callableRegion; + }; + #endif #endif } \ No newline at end of file diff --git a/project.json b/project.json index dc0f8e7..d094fec 100644 --- a/project.json +++ b/project.json @@ -3,13 +3,13 @@ "configurations": [ { "name": "base", - "implementations": ["implementations/Crafter.Graphics-Font", "implementations/Crafter.Graphics-Shm", "implementations/Crafter.Graphics-Window", "implementations/Crafter.Graphics-MouseElement", "implementations/Crafter.Graphics-Transform", "implementations/Crafter.Graphics-GridElement", "implementations/Crafter.Graphics-Image"], + "implementations": ["implementations/Crafter.Graphics-Font", "implementations/Crafter.Graphics-Window", "implementations/Crafter.Graphics-MouseElement", "implementations/Crafter.Graphics-Transform", "implementations/Crafter.Graphics-GridElement", "implementations/Crafter.Graphics-Image"], "interfaces": ["interfaces/Crafter.Graphics-Window", "interfaces/Crafter.Graphics", "interfaces/Crafter.Graphics-Types", "interfaces/Crafter.Graphics-Font", "interfaces/Crafter.Graphics-Image", "interfaces/Crafter.Graphics-Shm", "interfaces/Crafter.Graphics-Animation", "interfaces/Crafter.Graphics-RenderingElement", "interfaces/Crafter.Graphics-MouseElement", "interfaces/Crafter.Graphics-Transform", "interfaces/Crafter.Graphics-GridElement", "interfaces/Crafter.Graphics-VulkanDevice", "interfaces/Crafter.Graphics-VulkanTransition", "interfaces/Crafter.Graphics-Mesh", "interfaces/Crafter.Graphics-VulkanBuffer", "interfaces/Crafter.Graphics-RenderingElement3DVulkan", "interfaces/Crafter.Graphics-DescriptorPoolVulkan", "interfaces/Crafter.Graphics-ShaderVulkan", "interfaces/Crafter.Graphics-PipelineRTVulkan", "interfaces/Crafter.Graphics-ShaderBindingTableVulkan", "interfaces/Crafter.Graphics-ImageVulkan", "interfaces/Crafter.Graphics-SamplerVulkan", "interfaces/Crafter.Graphics-DescriptorSetLayoutVulkan"], "type": "library" }, { "name": "wayland", - "implementations": ["implementations/Crafter.Graphics-Window_wayland"], + "implementations": ["implementations/Crafter.Graphics-Window_wayland", "implementations/Crafter.Graphics-Shm"], "interfaces": [], "libs": ["wayland-client", "xkbcommon"], "c_files": ["lib/xdg-shell-protocol", "lib/wayland-xdg-decoration-unstable-v1-client-protocol", "lib/fractional-scale-v1", "lib/viewporter"], @@ -22,7 +22,7 @@ }, { "name": "vulkan", - "implementations": ["implementations/Crafter.Graphics-VulkanDevice", "implementations/Crafter.Graphics-Window_vulkan", "implementations/Crafter.Graphics-Mesh_vulkan", "implementations/Crafter.Graphics-RenderingElement3DVulkan"], + "implementations": ["implementations/Crafter.Graphics-VulkanDevice", "implementations/Crafter.Graphics-Shm", "implementations/Crafter.Graphics-Window_vulkan", "implementations/Crafter.Graphics-Mesh_vulkan", "implementations/Crafter.Graphics-RenderingElement3DVulkan"], "interfaces": [], "libs": ["wayland-client", "xkbcommon", "vulkan"], "c_files": ["lib/xdg-shell-protocol", "lib/wayland-xdg-decoration-unstable-v1-client-protocol", "lib/fractional-scale-v1", "lib/viewporter"], @@ -33,6 +33,18 @@ } ] }, + { + "name": "vulkan-windows", + "implementations": ["implementations/Crafter.Graphics-VulkanDevice", "implementations/Crafter.Graphics-Window_vulkan_windows", "implementations/Crafter.Graphics-Mesh_vulkan", "implementations/Crafter.Graphics-RenderingElement3DVulkan"], + "interfaces": [], + "libs": ["vulkan-1"], + "extends": ["base"], + "defines": [ + { + "name": "CRAFTER_GRAPHICS_VULKAN" + } + ] + }, { "name": "deps", "dependencies": [ @@ -94,6 +106,12 @@ "extends": ["vulkan", "deps"], "type": "library" }, + { + "name": "lib-vulkan-windows", + "extends": ["vulkan-windows", "deps"], + "type": "library", + "target": "x86_64-w64-mingw32" + }, { "name": "lib-vulkan-timing", "extends": ["vulkan", "deps-timing"],