From d661c88ee2c615ca2d68d9bbfcf29ecf10dfb0b0 Mon Sep 17 00:00:00 2001 From: Jorijn van der Graaf Date: Mon, 9 Mar 2026 20:10:19 +0100 Subject: [PATCH] cleaned up renderer --- examples/HelloDrawing/main.cpp | 21 +- examples/HelloDrawing/project.json | 5 +- examples/HelloUI/main.cpp | 33 +- examples/HelloWindow/main.cpp | 3 +- examples/HelloWindow/project.json | 5 +- examples/VulkanAnimation/main.cpp | 28 +- examples/VulkanTriangle/README.md | 2 +- examples/VulkanTriangle/main.cpp | 16 +- examples/VulkanTriangle/project.json | 4 +- examples/VulkanTriangleRuntime/README.md | 2 +- examples/VulkanTriangleRuntime/main.cpp | 14 +- examples/VulkanWindow/README.md | 25 - examples/VulkanWindow/main.cpp | 29 - examples/VulkanWindow/project.json | 15 - implementations/Crafter.Graphics-Device.cpp | 700 +++++++++++++ implementations/Crafter.Graphics-Font.cpp | 2 +- .../Crafter.Graphics-GridElement.cpp | 22 +- implementations/Crafter.Graphics-Image.cpp | 63 -- ...h_vulkan.cpp => Crafter.Graphics-Mesh.cpp} | 12 +- .../Crafter.Graphics-MouseElement.cpp | 27 +- ...> Crafter.Graphics-RenderingElement3D.cpp} | 16 +- .../Crafter.Graphics-Rendertarget.cpp | 249 +++++ implementations/Crafter.Graphics-Shm.cpp | 2 +- .../Crafter.Graphics-Transform.cpp | 51 - .../Crafter.Graphics-Transform2D.cpp | 70 ++ .../Crafter.Graphics-VulkanDevice.cpp | 337 ------ implementations/Crafter.Graphics-Window.cpp | 978 ++++++++++++++++-- .../Crafter.Graphics-Window_vulkan.cpp | 860 --------------- ...Crafter.Graphics-Window_vulkan_windows.cpp | 682 ------------ .../Crafter.Graphics-Window_wayland.cpp | 829 --------------- interfaces/Crafter.Graphics-Animation.cppm | 2 +- ...Crafter.Graphics-DescriptorPoolVulkan.cppm | 12 +- ...er.Graphics-DescriptorSetLayoutVulkan.cppm | 10 +- interfaces/Crafter.Graphics-Device.cppm | 125 +++ interfaces/Crafter.Graphics-Font.cppm | 2 +- interfaces/Crafter.Graphics-GridElement.cppm | 14 +- interfaces/Crafter.Graphics-Image.cppm | 34 - interfaces/Crafter.Graphics-ImageVulkan.cppm | 16 +- interfaces/Crafter.Graphics-Mesh.cppm | 5 +- interfaces/Crafter.Graphics-MouseElement.cppm | 37 +- .../Crafter.Graphics-PipelineRTVulkan.cppm | 58 +- .../Crafter.Graphics-RenderingElement.cppm | 652 ------------ .../Crafter.Graphics-RenderingElement2D.cppm | 508 +++++++++ ... Crafter.Graphics-RenderingElement3D.cppm} | 11 +- interfaces/Crafter.Graphics-Rendertarget.cppm | 43 + .../Crafter.Graphics-SamplerVulkan.cppm | 6 +- ...ter.Graphics-ShaderBindingTableVulkan.cppm | 6 +- interfaces/Crafter.Graphics-ShaderVulkan.cppm | 10 +- interfaces/Crafter.Graphics-Shm.cppm | 33 - interfaces/Crafter.Graphics-Transform.cppm | 51 - interfaces/Crafter.Graphics-Transform2D.cppm | 51 + interfaces/Crafter.Graphics-Types.cppm | 284 +---- interfaces/Crafter.Graphics-VulkanBuffer.cppm | 32 +- interfaces/Crafter.Graphics-VulkanDevice.cppm | 59 -- .../Crafter.Graphics-VulkanTransition.cppm | 4 +- interfaces/Crafter.Graphics-Window.cppm | 427 ++------ interfaces/Crafter.Graphics.cppm | 18 +- project.json | 142 +-- 58 files changed, 3031 insertions(+), 4723 deletions(-) delete mode 100644 examples/VulkanWindow/README.md delete mode 100644 examples/VulkanWindow/main.cpp delete mode 100644 examples/VulkanWindow/project.json create mode 100644 implementations/Crafter.Graphics-Device.cpp delete mode 100644 implementations/Crafter.Graphics-Image.cpp rename implementations/{Crafter.Graphics-Mesh_vulkan.cpp => Crafter.Graphics-Mesh.cpp} (91%) rename implementations/{Crafter.Graphics-RenderingElement3DVulkan.cpp => Crafter.Graphics-RenderingElement3D.cpp} (87%) create mode 100644 implementations/Crafter.Graphics-Rendertarget.cpp delete mode 100644 implementations/Crafter.Graphics-Transform.cpp create mode 100644 implementations/Crafter.Graphics-Transform2D.cpp delete mode 100644 implementations/Crafter.Graphics-VulkanDevice.cpp delete mode 100644 implementations/Crafter.Graphics-Window_vulkan.cpp delete mode 100644 implementations/Crafter.Graphics-Window_vulkan_windows.cpp delete mode 100644 implementations/Crafter.Graphics-Window_wayland.cpp create mode 100644 interfaces/Crafter.Graphics-Device.cppm delete mode 100644 interfaces/Crafter.Graphics-Image.cppm delete mode 100644 interfaces/Crafter.Graphics-RenderingElement.cppm create mode 100644 interfaces/Crafter.Graphics-RenderingElement2D.cppm rename interfaces/{Crafter.Graphics-RenderingElement3DVulkan.cppm => Crafter.Graphics-RenderingElement3D.cppm} (86%) create mode 100644 interfaces/Crafter.Graphics-Rendertarget.cppm delete mode 100644 interfaces/Crafter.Graphics-Shm.cppm delete mode 100644 interfaces/Crafter.Graphics-Transform.cppm create mode 100644 interfaces/Crafter.Graphics-Transform2D.cppm delete mode 100644 interfaces/Crafter.Graphics-VulkanDevice.cppm diff --git a/examples/HelloDrawing/main.cpp b/examples/HelloDrawing/main.cpp index 857baf3..61a96e6 100644 --- a/examples/HelloDrawing/main.cpp +++ b/examples/HelloDrawing/main.cpp @@ -2,18 +2,19 @@ import Crafter.Graphics; import std; using namespace Crafter; +constexpr std::uint32_t width = 1280; +constexpr std::uint32_t height = 720; + int main() { - constexpr std::uint_fast32_t width = 1280; - constexpr std::uint_fast32_t height = 720; + Device::Initialize(); + Window window(width, height, "Hello Drawing!"); - WindowWayland window(width, height, "Hello Drawing!"); - - for(std::uint_fast32_t x = 0; x < width; x++) { - for(std::uint_fast32_t y = 0; y < height; y++) { - window.framebuffer[x*height+y].r = 255; - window.framebuffer[x*height+y].g = 0; - window.framebuffer[x*height+y].b = 0; - window.framebuffer[x*height+y].a = 255; + for(std::uint32_t x = 0; x < width; x++) { + for(std::uint32_t y = 0; y < height; y++) { + window.renderer.buffer[x*height+y].r = 255; + window.renderer.buffer[x*height+y].g = 0; + window.renderer.buffer[x*height+y].b = 0; + window.renderer.buffer[x*height+y].a = 255; } } diff --git a/examples/HelloDrawing/project.json b/examples/HelloDrawing/project.json index 70e3d7a..976af27 100644 --- a/examples/HelloDrawing/project.json +++ b/examples/HelloDrawing/project.json @@ -7,9 +7,10 @@ "dependencies": [ { "path":"../../project.json", - "configuration":"lib-wayland" + "configuration":"lib-wayland-debug" } - ] + ], + "debug": true } ] } diff --git a/examples/HelloUI/main.cpp b/examples/HelloUI/main.cpp index 1b3f315..3d19610 100644 --- a/examples/HelloUI/main.cpp +++ b/examples/HelloUI/main.cpp @@ -4,16 +4,17 @@ import std; using namespace Crafter; int main() { - WindowWayland window(1280, 720, "Hello Input!"); + Device::Initialize(); + Window window(1280, 720, "Hello Drawing!"); - RenderingElement element( + RenderingElement2D element( { - FractionalToMapped(0.5), //anchorX: relative position where this elements x anchor (top-left) is placed to its parent x anchor - FractionalToMapped(0.5), //anchorY: relative position where this elements y anchor (top-left) is placed to its parent y anchor - FractionalToMapped(0.5), //relativeSizeX: the relative x size this element should be scaled to compared to its parent - FractionalToMapped(0.5), //relativeSizeY: the relative y size this element should be scaled to compared to its parent - FractionalToMapped(0.5), //anchorOffsetX: the amount this element's anchor should be offset from the top left corner (0.5 to in the middle) - FractionalToMapped(0.5), //anchorOffsetY: the amount this element's anchor should be offset from the top left corner (0.5 to place it in the middle) + 0.5, //anchorX: relative position where this elements x anchor (top-left) is placed to its parent x anchor + 0.5, //anchorY: relative position where this elements y anchor (top-left) is placed to its parent y anchor + 0.5, //relativeSizeX: the relative x size this element should be scaled to compared to its parent + 0.5, //relativeSizeY: the relative y size this element should be scaled to compared to its parent + 0.5, //anchorOffsetX: the amount this element's anchor should be offset from the top left corner (0.5 to in the middle) + 0.5, //anchorOffsetY: the amount this element's anchor should be offset from the top left corner (0.5 to place it in the middle) 0 //z: this elements Z position }, OpaqueType::FullyOpaque, @@ -23,20 +24,14 @@ int main() { MouseElement mouse(window); element.children.push_back(&mouse); - window.elements.push_back(&element); + window.renderer.elements.push_back(&element); element.scalingBuffer = {{255, 0, 0 ,255}, {0, 255, 0 ,255}}; - element.UpdatePosition(window); + element.UpdatePosition(window.renderer); - EventListener clickListener(&mouse.onMouseLeftClick, [&mouse, &window](MousePoint point) { - // Print the coordinates where the user clicked relative to the element's top left corner. - std::println("Clicked on Mapped X:{} Y:{}!\nClicked on Fraction X:{} Y:{}!\nClicked on Screen X:{} Y:{}!\n", - //Mapped space - point.x, point.y, - // Fraction space - MappedToFractionalBoundless(point.x), MappedToFractionalBoundless(point.y), - // Screen space - MappedToAbsoluteBoundless(point.x, MappedToAbsoluteBoundless(mouse.mouseScaled.width, window.width)), MappedToAbsoluteBoundless(point.y, MappedToAbsoluteBoundless(mouse.mouseScaled.width, window.height)) + EventListener clickListener(&mouse.onMouseLeftClick, [&window]() { + std::println("Clicked on X:{} Y:{}!", + window.currentMousePos.x, window.currentMousePos.y ); }); diff --git a/examples/HelloWindow/main.cpp b/examples/HelloWindow/main.cpp index eb754a7..1a8c42a 100644 --- a/examples/HelloWindow/main.cpp +++ b/examples/HelloWindow/main.cpp @@ -3,6 +3,7 @@ import std; using namespace Crafter; int main() { - WindowWayland window(1280, 720, "Hello Window!"); + Device::Initialize(); + Window window(1280, 720, "Hello Window!"); window.StartSync(); } diff --git a/examples/HelloWindow/project.json b/examples/HelloWindow/project.json index 70e3d7a..976af27 100644 --- a/examples/HelloWindow/project.json +++ b/examples/HelloWindow/project.json @@ -7,9 +7,10 @@ "dependencies": [ { "path":"../../project.json", - "configuration":"lib-wayland" + "configuration":"lib-wayland-debug" } - ] + ], + "debug": true } ] } diff --git a/examples/VulkanAnimation/main.cpp b/examples/VulkanAnimation/main.cpp index 7baded2..8bd1b9d 100644 --- a/examples/VulkanAnimation/main.cpp +++ b/examples/VulkanAnimation/main.cpp @@ -34,7 +34,7 @@ typedef DescriptorSetLayoutVulkanConst<1, {{ }}> descriptorSetLayoutImage; int main() { - VulkanDevice::CreateDevice(); + Device::CreateDevice(); WindowVulkan window(1280, 720, "HelloVulkan"); VkCommandBuffer cmd = window.StartInit(); @@ -60,7 +60,7 @@ int main() { std::array index {{2,1,0}}; triangleMesh.Build(verts, index, cmd); - RenderingElement3DVulkan renderer = { + RenderingElement3D renderer = { .instance = { .instanceCustomIndex = 0, .mask = 0xFF, @@ -70,16 +70,16 @@ int main() { } }; - RenderingElement3DVulkan::elements.emplace_back(&renderer); + RenderingElement3D::elements.emplace_back(&renderer); MatrixRowMajor transform = MatrixRowMajor::Identity(); std::memcpy(renderer.instance.transform.matrix, transform.m, sizeof(transform.m)); - RenderingElement3DVulkan::tlases.resize(WindowVulkan::numFrames); - RenderingElement3DVulkan::BuildTLAS(cmd, 0); - RenderingElement3DVulkan::BuildTLAS(cmd, 1); - RenderingElement3DVulkan::BuildTLAS(cmd, 2); + RenderingElement3D::tlases.resize(Window::numFrames); + RenderingElement3D::BuildTLAS(cmd, 0); + RenderingElement3D::BuildTLAS(cmd, 1); + RenderingElement3D::BuildTLAS(cmd, 2); - VkDescriptorImageInfo imageInfo[WindowVulkan::numFrames] = { + VkDescriptorImageInfo imageInfo[Window::numFrames] = { { .imageView = window.imageViews[0], .imageLayout = VK_IMAGE_LAYOUT_GENERAL @@ -97,7 +97,7 @@ int main() { VkWriteDescriptorSetAccelerationStructureKHR writeDescriptorSetAccelerationStructure { .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET_ACCELERATION_STRUCTURE_KHR, .accelerationStructureCount = 1, - .pAccelerationStructures = &RenderingElement3DVulkan::tlases[0].accelerationStructure + .pAccelerationStructures = &RenderingElement3D::tlases[0].accelerationStructure }; VkWriteDescriptorSet write[4]; @@ -112,7 +112,7 @@ int main() { .descriptorType = VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR, }; - for(std::uint32_t i = 0; i < WindowVulkan::numFrames; i++) { + for(std::uint32_t i = 0; i < Window::numFrames; i++) { write[i+1] = { .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, .dstSet = pool.sets[i+1], @@ -123,7 +123,7 @@ int main() { .pImageInfo = &imageInfo[i] }; } - vkUpdateDescriptorSets(VulkanDevice::device, 4, write, 0, nullptr); + vkUpdateDescriptorSets(Device::device, 4, write, 0, nullptr); window.descriptorsRt = {pool.sets[0], pool.sets[1]}; /* @@ -147,12 +147,12 @@ int main() { MatrixRowMajor transform = MatrixRowMajor::Translation(value, 0, 0); std::memcpy(renderer.instance.transform.matrix, transform.m, sizeof(transform.m)); - RenderingElement3DVulkan::BuildTLAS(window.drawCmdBuffers[window.currentBuffer], window.currentBuffer); + RenderingElement3D::BuildTLAS(window.drawCmdBuffers[window.currentBuffer], window.currentBuffer); VkWriteDescriptorSetAccelerationStructureKHR writeDescriptorSetAccelerationStructure { .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET_ACCELERATION_STRUCTURE_KHR, .accelerationStructureCount = 1, - .pAccelerationStructures = &RenderingElement3DVulkan::tlases[window.currentBuffer].accelerationStructure + .pAccelerationStructures = &RenderingElement3D::tlases[window.currentBuffer].accelerationStructure }; VkWriteDescriptorSet write = { @@ -164,7 +164,7 @@ int main() { .descriptorCount = 1, .descriptorType = VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR, }; - vkUpdateDescriptorSets(VulkanDevice::device, 1, &write, 0, nullptr); + vkUpdateDescriptorSets(Device::device, 1, &write, 0, nullptr); window.descriptorsRt[1] = pool.sets[window.currentBuffer+1]; }); diff --git a/examples/VulkanTriangle/README.md b/examples/VulkanTriangle/README.md index 7d58bea..6da7987 100644 --- a/examples/VulkanTriangle/README.md +++ b/examples/VulkanTriangle/README.md @@ -14,7 +14,7 @@ A blue tinted vulkan window with a white triangle in the center. EventListener listener(&window.onDraw, [&descriptors, &meshShader](VkCommandBuffer cmd){ vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, Pipeline::pipelineLayout, 0, 2, &descriptors.set[0], 0, NULL); vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, Pipeline::pipeline); - VulkanDevice::vkCmdDrawMeshTasksEXTProc(cmd, meshShader.threadCount, 1, 1); + Device::vkCmdDrawMeshTasksEXTProc(cmd, meshShader.threadCount, 1, 1); }); ``` diff --git a/examples/VulkanTriangle/main.cpp b/examples/VulkanTriangle/main.cpp index 79a3d2f..74406f8 100644 --- a/examples/VulkanTriangle/main.cpp +++ b/examples/VulkanTriangle/main.cpp @@ -38,8 +38,8 @@ typedef DescriptorSetLayoutVulkanConst<3, {{ }}> descriptorSetLayout; int main() { - VulkanDevice::CreateDevice(); - WindowVulkan window(1280, 720, "HelloVulkan"); + Device::Initialize(); + Window window(1280, 720, "HelloVulkan"); VkCommandBuffer cmd = window.StartInit(); Raygenspv::CreateShader(); @@ -61,7 +61,7 @@ int main() { std::array index {{2,1,0}}; triangleMesh.Build(verts, index, cmd); - RenderingElement3DVulkan renderer = { + RenderingElement3D renderer = { .instance = { .instanceCustomIndex = 0, .mask = 0xFF, @@ -71,12 +71,12 @@ int main() { } }; - RenderingElement3DVulkan::elements.emplace_back(&renderer); + RenderingElement3D::elements.emplace_back(&renderer); MatrixRowMajor transform = MatrixRowMajor::Identity(); std::memcpy(renderer.instance.transform.matrix, transform.m, sizeof(transform.m)); - RenderingElement3DVulkan::tlases.resize(1); - RenderingElement3DVulkan::BuildTLAS(cmd, 0); + RenderingElement3D::tlases.resize(1); + RenderingElement3D::BuildTLAS(cmd, 0); VkDescriptorImageInfo imageInfo = { .imageView = window.imageViews[0], @@ -86,7 +86,7 @@ int main() { VkWriteDescriptorSetAccelerationStructureKHR writeDescriptorSetAccelerationStructure { .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET_ACCELERATION_STRUCTURE_KHR, .accelerationStructureCount = 1, - .pAccelerationStructures = &RenderingElement3DVulkan::tlases[0].accelerationStructure + .pAccelerationStructures = &RenderingElement3D::tlases[0].accelerationStructure }; VulkanBuffer lightBuffer; @@ -121,7 +121,7 @@ int main() { .pBufferInfo = &lightBuffer.descriptor } }; - vkUpdateDescriptorSets(VulkanDevice::device, 3, write, 0, nullptr); + vkUpdateDescriptorSets(Device::device, 3, write, 0, nullptr); window.SetPipelineRT(); diff --git a/examples/VulkanTriangle/project.json b/examples/VulkanTriangle/project.json index 61eba87..df14df2 100644 --- a/examples/VulkanTriangle/project.json +++ b/examples/VulkanTriangle/project.json @@ -7,9 +7,11 @@ "dependencies": [ { "path":"../../project.json", - "configuration":"lib-vulkan" + "configuration":"lib-win32-vulkan-debug" } ], + "target": "x86_64-w64-mingw32", + "debug": true, "shaders": [ { "path":"raygen.glsl", diff --git a/examples/VulkanTriangleRuntime/README.md b/examples/VulkanTriangleRuntime/README.md index 7d58bea..6da7987 100644 --- a/examples/VulkanTriangleRuntime/README.md +++ b/examples/VulkanTriangleRuntime/README.md @@ -14,7 +14,7 @@ A blue tinted vulkan window with a white triangle in the center. EventListener listener(&window.onDraw, [&descriptors, &meshShader](VkCommandBuffer cmd){ vkCmdBindDescriptorSets(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, Pipeline::pipelineLayout, 0, 2, &descriptors.set[0], 0, NULL); vkCmdBindPipeline(cmd, VK_PIPELINE_BIND_POINT_GRAPHICS, Pipeline::pipeline); - VulkanDevice::vkCmdDrawMeshTasksEXTProc(cmd, meshShader.threadCount, 1, 1); + Device::vkCmdDrawMeshTasksEXTProc(cmd, meshShader.threadCount, 1, 1); }); ``` diff --git a/examples/VulkanTriangleRuntime/main.cpp b/examples/VulkanTriangleRuntime/main.cpp index 07b8661..242971a 100644 --- a/examples/VulkanTriangleRuntime/main.cpp +++ b/examples/VulkanTriangleRuntime/main.cpp @@ -8,7 +8,7 @@ import Crafter.Math; int main() { - VulkanDevice::CreateDevice(); + Device::CreateDevice(); WindowVulkan window(1280, 720, "HelloVulkan"); VkCommandBuffer cmd = window.StartInit(); @@ -81,7 +81,7 @@ int main() { 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 renderer = { + RenderingElement3D renderer = { .instance = { .instanceCustomIndex = 0, .mask = 0xFF, @@ -92,9 +92,9 @@ int main() { }; MatrixRowMajor transform = MatrixRowMajor::Identity(); std::memcpy(renderer.instance.transform.matrix, transform.m, sizeof(transform.m)); - RenderingElement3DVulkan::tlases.resize(1); - RenderingElement3DVulkan::elements.push_back(&renderer); - RenderingElement3DVulkan::BuildTLAS(cmd, 0); + RenderingElement3D::tlases.resize(1); + RenderingElement3D::elements.push_back(&renderer); + RenderingElement3D::BuildTLAS(cmd, 0); VkDescriptorImageInfo imageInfo = { .imageView = window.imageViews[0], @@ -104,7 +104,7 @@ int main() { VkWriteDescriptorSetAccelerationStructureKHR writeDescriptorSetAccelerationStructure { .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET_ACCELERATION_STRUCTURE_KHR, .accelerationStructureCount = 1, - .pAccelerationStructures = &RenderingElement3DVulkan::tlases[0].accelerationStructure + .pAccelerationStructures = &RenderingElement3D::tlases[0].accelerationStructure }; VkWriteDescriptorSet write[2] = { @@ -127,7 +127,7 @@ int main() { .pImageInfo = &imageInfo } }; - vkUpdateDescriptorSets(VulkanDevice::device, 2, write, 0, nullptr); + vkUpdateDescriptorSets(Device::device, 2, write, 0, nullptr); window.SetPipelineRT(pipeline); window.descriptorsRt = pool.sets; diff --git a/examples/VulkanWindow/README.md b/examples/VulkanWindow/README.md deleted file mode 100644 index 3443992..0000000 --- a/examples/VulkanWindow/README.md +++ /dev/null @@ -1,25 +0,0 @@ -# HelloWindow Example - -## Description - -This example demonstrates the minimal code needed to create a vulkan window and show it on the screen. - -## Expected Result - -A blue tinted vulkan window with the title "HelloWindow" shows onscreen, with debug info in the console. - -## Highlighted Code Snippet - -```cpp -VulkanDevice::CreateDevice(); -WindowWaylandVulkan window("HelloWindow", 1280, 720); -VkCommandBuffer cmd = window.StartInit(); -window.FinishInit(); -window.StartSync(); -``` - -## How to Run - -```bash -crafter-build build executable -r -``` \ No newline at end of file diff --git a/examples/VulkanWindow/main.cpp b/examples/VulkanWindow/main.cpp deleted file mode 100644 index db636fe..0000000 --- a/examples/VulkanWindow/main.cpp +++ /dev/null @@ -1,29 +0,0 @@ -#include "vulkan/vulkan.h" - -import Crafter.Graphics; -using namespace Crafter; - -int main() { - /* - This sets up all necessary things and creates the vulkan device. - This must be called before any vulkan related things. - Things like VkDevice are static members of the VulkanDevice class. - */ - VulkanDevice::CreateDevice(); - WindowVulkan window(1280, 720, "HelloVulkan"); - - // /* - // StartInit gives you a VkCommandBuffer to use before the event loop starts - // Use this for inititializing things like textures. - // */ - // VkCommandBuffer cmd = window.StartInit(); - - // /* - // FinishInit executes all commands recorded to StartInit. - // This must be called before the the event loops starts if you called StartInit before. - // */ - // window.FinishInit(); - - window.Render(); - window.StartSync(); -} diff --git a/examples/VulkanWindow/project.json b/examples/VulkanWindow/project.json deleted file mode 100644 index a4b0e06..0000000 --- a/examples/VulkanWindow/project.json +++ /dev/null @@ -1,15 +0,0 @@ -{ - "name": "crafter-graphics", - "configurations": [ - { - "name": "executable", - "implementations": ["main"], - "dependencies": [ - { - "path":"../../project.json", - "configuration":"lib-vulkan" - } - ] - } - ] -} diff --git a/implementations/Crafter.Graphics-Device.cpp b/implementations/Crafter.Graphics-Device.cpp new file mode 100644 index 0000000..c6d3a63 --- /dev/null +++ b/implementations/Crafter.Graphics-Device.cpp @@ -0,0 +1,700 @@ +/* +Crafter®.Graphics +Copyright (C) 2026 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; + +#ifdef CRAFTER_GRAPHICS_RENDERER_VULKAN +#include "vulkan/vulkan.h" +#include "vulkan/vk_enum_string_helper.h" +#define GET_EXTENSION_FUNCTION(_id) ((PFN_##_id)(vkGetInstanceProcAddr(instance, #_id))) +#endif + +#ifdef CRAFTER_GRAPHICS_WINDOW_WAYLAND +#include +#include +#include +#include "../lib/xdg-shell-client-protocol.h" +#include "../lib/wayland-xdg-decoration-unstable-v1-client-protocol.h" +#include "../lib/fractional-scale-v1.h" +#include "../lib/viewporter.h" +#include +#include +#include +#include +#include +#include +#endif + +module Crafter.Graphics:Device_impl; +import :Device; +import :Window; +import :MouseElement; +import :Types; +import std; +using namespace Crafter; + +#ifdef CRAFTER_GRAPHICS_RENDERER_VULKAN +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", + "VK_KHR_spirv_1_4", + "VK_EXT_mesh_shader", + "VK_KHR_shader_float_controls", + "VK_KHR_dynamic_rendering", + "VK_KHR_acceleration_structure", + "VK_KHR_deferred_host_operations", + "VK_KHR_ray_tracing_pipeline", + "VK_KHR_ray_tracing_position_fetch" +}; +const char* const layerNames[] = { + "VK_LAYER_KHRONOS_validation" +}; + + +void Device::CheckVkResult(VkResult result) { + if (result != VK_SUCCESS) + { + throw std::runtime_error(string_VkResult(result)); + } +} + +VkBool32 onError(VkDebugUtilsMessageSeverityFlagBitsEXT severity, VkDebugUtilsMessageTypeFlagsEXT type, const VkDebugUtilsMessengerCallbackDataEXT* callbackData, void* userData) +{ + printf("Vulkan "); + + switch (type) + { + case VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT : + printf("general "); + break; + case VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT : + printf("validation "); + break; + case VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT : + printf("performance "); + break; + } + + switch (severity) + { + case VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT : + printf("(verbose): "); + break; + default : + case VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT : + printf("(info): "); + break; + case VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT : + printf("(warning): "); + break; + case VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT : + printf("(error): "); + break; + } + + printf("%s\n", callbackData->pMessage); + + return 0; +} +#endif + + +#ifdef CRAFTER_GRAPHICS_WINDOW_WAYLAND +constexpr CrafterKeys keysym_to_crafter_key(xkb_keysym_t sym) +{ + switch (sym) + { + // Alphabet + case XKB_KEY_a: return CrafterKeys::A; + case XKB_KEY_b: return CrafterKeys::B; + case XKB_KEY_c: return CrafterKeys::C; + case XKB_KEY_d: return CrafterKeys::D; + case XKB_KEY_e: return CrafterKeys::E; + case XKB_KEY_f: return CrafterKeys::F; + case XKB_KEY_g: return CrafterKeys::G; + case XKB_KEY_h: return CrafterKeys::H; + case XKB_KEY_i: return CrafterKeys::I; + case XKB_KEY_j: return CrafterKeys::J; + case XKB_KEY_k: return CrafterKeys::K; + case XKB_KEY_l: return CrafterKeys::L; + case XKB_KEY_m: return CrafterKeys::M; + case XKB_KEY_n: return CrafterKeys::N; + case XKB_KEY_o: return CrafterKeys::O; + case XKB_KEY_p: return CrafterKeys::P; + case XKB_KEY_q: return CrafterKeys::Q; + case XKB_KEY_r: return CrafterKeys::R; + case XKB_KEY_s: return CrafterKeys::S; + case XKB_KEY_t: return CrafterKeys::T; + case XKB_KEY_u: return CrafterKeys::U; + case XKB_KEY_v: return CrafterKeys::V; + case XKB_KEY_w: return CrafterKeys::W; + case XKB_KEY_x: return CrafterKeys::X; + case XKB_KEY_y: return CrafterKeys::Y; + case XKB_KEY_z: return CrafterKeys::Z; + + // Numbers + case XKB_KEY_0: return CrafterKeys::_0; + case XKB_KEY_1: return CrafterKeys::_1; + case XKB_KEY_2: return CrafterKeys::_2; + case XKB_KEY_3: return CrafterKeys::_3; + case XKB_KEY_4: return CrafterKeys::_4; + case XKB_KEY_5: return CrafterKeys::_5; + case XKB_KEY_6: return CrafterKeys::_6; + case XKB_KEY_7: return CrafterKeys::_7; + case XKB_KEY_8: return CrafterKeys::_8; + case XKB_KEY_9: return CrafterKeys::_9; + + // Function keys + case XKB_KEY_F1: return CrafterKeys::F1; + case XKB_KEY_F2: return CrafterKeys::F2; + case XKB_KEY_F3: return CrafterKeys::F3; + case XKB_KEY_F4: return CrafterKeys::F4; + case XKB_KEY_F5: return CrafterKeys::F5; + case XKB_KEY_F6: return CrafterKeys::F6; + case XKB_KEY_F7: return CrafterKeys::F7; + case XKB_KEY_F8: return CrafterKeys::F8; + case XKB_KEY_F9: return CrafterKeys::F9; + case XKB_KEY_F10: return CrafterKeys::F10; + case XKB_KEY_F11: return CrafterKeys::F11; + case XKB_KEY_F12: return CrafterKeys::F12; + + // Control keys + case XKB_KEY_Escape: return CrafterKeys::Escape; + case XKB_KEY_Tab: return CrafterKeys::Tab; + case XKB_KEY_Return: return CrafterKeys::Enter; + case XKB_KEY_space: return CrafterKeys::Space; + case XKB_KEY_BackSpace: return CrafterKeys::Backspace; + case XKB_KEY_Delete: return CrafterKeys::Delete; + case XKB_KEY_Insert: return CrafterKeys::Insert; + case XKB_KEY_Home: return CrafterKeys::Home; + case XKB_KEY_End: return CrafterKeys::End; + case XKB_KEY_Page_Up: return CrafterKeys::PageUp; + case XKB_KEY_Page_Down: return CrafterKeys::PageDown; + case XKB_KEY_Caps_Lock: return CrafterKeys::CapsLock; + case XKB_KEY_Num_Lock: return CrafterKeys::NumLock; + case XKB_KEY_Scroll_Lock:return CrafterKeys::ScrollLock; + + // Modifiers + case XKB_KEY_Shift_L: return CrafterKeys::LeftShift; + case XKB_KEY_Shift_R: return CrafterKeys::RightShift; + case XKB_KEY_Control_L: return CrafterKeys::LeftCtrl; + case XKB_KEY_Control_R: return CrafterKeys::RightCtrl; + case XKB_KEY_Alt_L: return CrafterKeys::LeftAlt; + case XKB_KEY_Alt_R: return CrafterKeys::RightAlt; + case XKB_KEY_Super_L: return CrafterKeys::LeftSuper; + case XKB_KEY_Super_R: return CrafterKeys::RightSuper; + + // Arrows + case XKB_KEY_Up: return CrafterKeys::Up; + case XKB_KEY_Down: return CrafterKeys::Down; + case XKB_KEY_Left: return CrafterKeys::Left; + case XKB_KEY_Right: return CrafterKeys::Right; + + // Keypad + case XKB_KEY_KP_0: return CrafterKeys::keypad_0; + case XKB_KEY_KP_1: return CrafterKeys::keypad_1; + case XKB_KEY_KP_2: return CrafterKeys::keypad_2; + case XKB_KEY_KP_3: return CrafterKeys::keypad_3; + case XKB_KEY_KP_4: return CrafterKeys::keypad_4; + case XKB_KEY_KP_5: return CrafterKeys::keypad_5; + case XKB_KEY_KP_6: return CrafterKeys::keypad_6; + case XKB_KEY_KP_7: return CrafterKeys::keypad_7; + case XKB_KEY_KP_8: return CrafterKeys::keypad_8; + case XKB_KEY_KP_9: return CrafterKeys::keypad_9; + case XKB_KEY_KP_Enter: return CrafterKeys::keypad_enter; + case XKB_KEY_KP_Add: return CrafterKeys::keypad_plus; + case XKB_KEY_KP_Subtract: return CrafterKeys::keypad_minus; + case XKB_KEY_KP_Multiply: return CrafterKeys::keypad_multiply; + case XKB_KEY_KP_Divide: return CrafterKeys::keypad_divide; + case XKB_KEY_KP_Decimal: return CrafterKeys::keypad_decimal; + + // Punctuation + case XKB_KEY_grave: return CrafterKeys::grave; + case XKB_KEY_minus: return CrafterKeys::minus; + case XKB_KEY_equal: return CrafterKeys::equal; + case XKB_KEY_bracketleft: return CrafterKeys::bracket_left; + case XKB_KEY_bracketright:return CrafterKeys::bracket_right; + case XKB_KEY_backslash: return CrafterKeys::backslash; + case XKB_KEY_semicolon: return CrafterKeys::semicolon; + case XKB_KEY_apostrophe: return CrafterKeys::quote; + case XKB_KEY_comma: return CrafterKeys::comma; + case XKB_KEY_period: return CrafterKeys::period; + case XKB_KEY_slash: return CrafterKeys::slash; + + default: + throw std::runtime_error(std::format("Unkown XKB_KEY: {}", sym)); + } +} + +void Device::xdg_wm_base_handle_ping(void* data, xdg_wm_base* xdg_wm_base, std::uint32_t serial) { + xdg_wm_base_pong(xdg_wm_base, serial); +} + +void Device::handle_global(void *data, wl_registry *registry, std::uint32_t name, const char *interface, std::uint32_t version) { + if (strcmp(interface, wl_shm_interface.name) == 0) { + shm = reinterpret_cast(wl_registry_bind(registry, name, &wl_shm_interface, 1)); + } else if (strcmp(interface, wl_seat_interface.name) == 0) { + wl_seat* seat = reinterpret_cast(wl_registry_bind(registry, name, &wl_seat_interface, 1)); + wl_seat_add_listener(seat, &seat_listener, nullptr); + } else if (compositor == nullptr && strcmp(interface, wl_compositor_interface.name) == 0) { + compositor = reinterpret_cast(wl_registry_bind(registry, name, &wl_compositor_interface, 3)); + } else if (strcmp(interface, xdg_wm_base_interface.name) == 0) { + xdgWmBase = reinterpret_cast(wl_registry_bind(registry, name, &xdg_wm_base_interface, 1)); + xdg_wm_base_add_listener(xdgWmBase, &xdgWmBaseListener, nullptr); + } else if (strcmp(interface, zxdg_decoration_manager_v1_interface.name) == 0) { + manager = reinterpret_cast(wl_registry_bind(registry, name, &zxdg_decoration_manager_v1_interface, 1)); + } else if (strcmp(interface, wp_viewporter_interface.name) == 0) { + wpViewporter = reinterpret_cast(wl_registry_bind(registry, name, &wp_viewporter_interface, 1)); + } else if (strcmp(interface, wp_fractional_scale_manager_v1_interface.name) == 0) { + fractionalScaleManager = reinterpret_cast(wl_registry_bind(registry, name, &wp_fractional_scale_manager_v1_interface, 1)); + } +} + +void Device::handle_global_remove(void* data, wl_registry* registry, uint32_t name) { + +} + +void Device::pointer_handle_button(void* data, wl_pointer* pointer, std::uint32_t serial, std::uint32_t time, std::uint32_t button, std::uint32_t state) { + if (button == BTN_LEFT) { + if(state == WL_POINTER_BUTTON_STATE_PRESSED) { + Device::focusedWindow->mouseLeftHeld = true; + Device::focusedWindow->onMouseLeftClick.Invoke(); + for(MouseElement* element : Device::focusedWindow->mouseElements) { + if(element) { + if(Device::focusedWindow->currentMousePos.x >= element->scaled.position.x && Device::focusedWindow->currentMousePos.x <= element->scaled.position.x+element->scaled.size.x && Device::focusedWindow->currentMousePos.y > element->scaled.position.y && Device::focusedWindow->currentMousePos.y < element->scaled.position.y+element->scaled.size.y) { + element->onMouseLeftClick.Invoke(); + } + } + } + } else { + Device::focusedWindow->mouseLeftHeld = false; + Device::focusedWindow->onMouseLeftRelease.Invoke(); + for(MouseElement* element : Device::focusedWindow->mouseElements) { + if(element) { + if(Device::focusedWindow->currentMousePos.x >= element->scaled.position.x && Device::focusedWindow->currentMousePos.x <= element->scaled.position.x+element->scaled.size.x && Device::focusedWindow->currentMousePos.y > element->scaled.position.y && Device::focusedWindow->currentMousePos.y < element->scaled.position.y+element->scaled.size.y) { + element->onMouseLeftRelease.Invoke(); + } + } + } + } + } else if(button == BTN_RIGHT){ + if(state == WL_POINTER_BUTTON_STATE_PRESSED) { + Device::focusedWindow->mouseRightHeld = true; + Device::focusedWindow->onMouseRightClick.Invoke(); + for(MouseElement* element : Device::focusedWindow->mouseElements) { + if(element) { + if(Device::focusedWindow->currentMousePos.x >= element->scaled.position.x && Device::focusedWindow->currentMousePos.x <= element->scaled.position.x+element->scaled.size.x && Device::focusedWindow->currentMousePos.y > element->scaled.position.y && Device::focusedWindow->currentMousePos.y < element->scaled.position.y+element->scaled.size.y) { + element->onMouseRightClick.Invoke(); + } + } + } + } else { + Device::focusedWindow->mouseRightHeld = true; + Device::focusedWindow->onMouseRightRelease.Invoke(); + for(MouseElement* element : Device::focusedWindow->mouseElements) { + if(element) { + if(Device::focusedWindow->currentMousePos.x >= element->scaled.position.x && Device::focusedWindow->currentMousePos.x <= element->scaled.position.x+element->scaled.size.x && Device::focusedWindow->currentMousePos.y > element->scaled.position.y && Device::focusedWindow->currentMousePos.y < element->scaled.position.y+element->scaled.size.y) { + element->onMouseRightRelease.Invoke(); + } + } + } + } + } + Device::focusedWindow->mouseElements.erase(std::remove(Device::focusedWindow->mouseElements.begin(), Device::focusedWindow->mouseElements.end(), static_cast(nullptr)), Device::focusedWindow->mouseElements.end()); + Device::focusedWindow->mouseElements.insert(Device::focusedWindow->mouseElements.end(), Device::focusedWindow->pendingMouseElements.begin(), Device::focusedWindow->pendingMouseElements.end()); + Device::focusedWindow->pendingMouseElements.clear(); +} + +void Device::PointerListenerHandleMotion(void* data, wl_pointer* wl_pointer, std::uint32_t time, wl_fixed_t surface_x, wl_fixed_t surface_y) { + Vector pos(wl_fixed_to_double(surface_x), wl_fixed_to_double(surface_y)); + Device::focusedWindow->lastMousePos = Device::focusedWindow->currentMousePos; + Device::focusedWindow->currentMousePos = pos * Device::focusedWindow->scale; + Device::focusedWindow->mouseDelta = {Device::focusedWindow->currentMousePos.x-Device::focusedWindow->lastMousePos.x, Device::focusedWindow->currentMousePos.y-Device::focusedWindow->lastMousePos.y}; + Device::focusedWindow->onMouseMove.Invoke(); + for(MouseElement* element : Device::focusedWindow->mouseElements) { + if(element) { + if(Device::focusedWindow->currentMousePos.x >= element->scaled.position.x && Device::focusedWindow->currentMousePos.x <= element->scaled.position.x+element->scaled.size.x && Device::focusedWindow->currentMousePos.y > element->scaled.position.y && Device::focusedWindow->currentMousePos.y < element->scaled.position.y+element->scaled.size.y) { + element->onMouseMove.Invoke(); + if(!(Device::focusedWindow->lastMousePos.x >= element->scaled.position.x && Device::focusedWindow->lastMousePos.x <= element->scaled.position.x+element->scaled.size.x && Device::focusedWindow->lastMousePos.y > element->scaled.position.y && Device::focusedWindow->lastMousePos.y < element->scaled.position.y+element->scaled.size.y)) { + element->onMouseEnter.Invoke(); + } + } else if(Device::focusedWindow->lastMousePos.x >= element->scaled.position.x && Device::focusedWindow->lastMousePos.x <= element->scaled.position.x+element->scaled.size.x && Device::focusedWindow->lastMousePos.y > element->scaled.position.y && Device::focusedWindow->lastMousePos.y < element->scaled.position.y+element->scaled.size.y) { + element->onMouseLeave.Invoke(); + } + } + } + Device::focusedWindow->mouseElements.erase(std::remove(Device::focusedWindow->mouseElements.begin(), Device::focusedWindow->mouseElements.end(), static_cast(nullptr)), Device::focusedWindow->mouseElements.end()); +} + +void Device::PointerListenerHandleEnter(void* data, wl_pointer* wl_pointer, std::uint32_t serial, wl_surface* surface, wl_fixed_t surface_x, wl_fixed_t surface_y) { + for(Window* window : windows) { + if(window->surface == surface) { + focusedWindow = window; + window->onMouseEnter.Invoke(); + return; + } + } +} + +void Device::PointerListenerHandleLeave(void* data, wl_pointer*, std::uint32_t, wl_surface*) { + Device::focusedWindow->onMouseLeave.Invoke(); + focusedWindow = nullptr; +} + +void Device::PointerListenerHandleAxis(void*, wl_pointer*, std::uint32_t, std::uint32_t, wl_fixed_t value) { + +} + + +void Device::keyboard_keymap(void *data, wl_keyboard *keyboard, uint32_t format, int fd, uint32_t size) { + if (format != WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1) { + close(fd); + fprintf(stderr, "Unsupported keymap format\n"); + return; + } + + void *map = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0); + if (map == MAP_FAILED) { + close(fd); + perror("mmap"); + return; + } + + xkb_context = xkb_context_new(XKB_CONTEXT_NO_FLAGS); + xkb_keymap = xkb_keymap_new_from_string(xkb_context, (const char *)map, XKB_KEYMAP_FORMAT_TEXT_V1,XKB_KEYMAP_COMPILE_NO_FLAGS); + munmap(map, size); + close(fd); + + xkb_state = xkb_state_new(xkb_keymap); +} + +void Device::keyboard_enter(void *data, wl_keyboard *keyboard, uint32_t serial, wl_surface *surface, wl_array *keys) { + +} + +void Device::keyboard_leave(void *data, wl_keyboard *keyboard, uint32_t serial, wl_surface *surface) { + +} + + + +void Device::keyboard_key(void *data, wl_keyboard *keyboard, uint32_t serial, uint32_t time, uint32_t key, uint32_t state) { + xkb_keycode_t keycode = key + 8; + xkb_keysym_t keysym = xkb_state_key_get_one_sym(xkb_state, keycode); + CrafterKeys crafterKey = keysym_to_crafter_key(keysym); + + if(state == WL_KEYBOARD_KEY_STATE_PRESSED) { + if(Device::focusedWindow->heldkeys[static_cast(crafterKey)]) { + Device::focusedWindow->onKeyHold[static_cast(crafterKey)].Invoke(); + Device::focusedWindow->onAnyKeyHold.Invoke(crafterKey); + } else{ + Device::focusedWindow->heldkeys[static_cast(crafterKey)] = true; + Device::focusedWindow->onKeyDown[static_cast(crafterKey)].Invoke(); + Device::focusedWindow->onAnyKeyDown.Invoke(crafterKey); + } + } else{ + Device::focusedWindow->heldkeys[static_cast(crafterKey)] = false; + Device::focusedWindow->onKeyUp[static_cast(crafterKey)].Invoke(); + Device::focusedWindow->onAnyKeyUp.Invoke(crafterKey); + } +} + +void Device::keyboard_modifiers(void *data, wl_keyboard *keyboard, uint32_t serial, uint32_t mods_depressed, uint32_t mods_latched, uint32_t mods_locked, uint32_t group) { + +} + +void Device::keyboard_repeat_info(void *data, wl_keyboard *keyboard, int32_t rate, int32_t delay) { + +} + +void Device::seat_handle_capabilities(void* data, wl_seat* seat, uint32_t capabilities) { + seat = seat; + if (capabilities & WL_SEAT_CAPABILITY_POINTER) { + wl_pointer* pointer = wl_seat_get_pointer(seat); + wl_pointer_add_listener(pointer, &pointer_listener, nullptr); + } + if (capabilities & WL_SEAT_CAPABILITY_KEYBOARD) { + wl_keyboard* keyboard = wl_seat_get_keyboard(seat); + wl_keyboard_add_listener(keyboard, &keyboard_listener, nullptr); + } +} + +#endif + +void Device::Initialize() { + #ifdef CRAFTER_GRAPHICS_WINDOW_WAYLAND + display = wl_display_connect(NULL); + if (display == nullptr) { + throw std::runtime_error("Could not connect to wayland display"); + } + + wl_registry* registry = wl_display_get_registry(display); + wl_registry_add_listener(registry, ®istry_listener, nullptr); + + if (wl_display_roundtrip(display) == -1) { + exit(EXIT_FAILURE); + } + + if (shm == NULL || compositor == NULL || xdgWmBase == NULL) { + throw std::runtime_error("No wl_shm, wl_compositor or xdg_wm_base support"); + } + #endif + + #ifdef CRAFTER_GRAPHICS_RENDERER_VULKAN + VkApplicationInfo app{VK_STRUCTURE_TYPE_APPLICATION_INFO}; + app.pApplicationName = ""; + app.pEngineName = "Crafter.Graphics"; + app.apiVersion = VK_MAKE_VERSION(1, 4, 0); + + VkInstanceCreateInfo instanceCreateInfo = {}; + instanceCreateInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; + instanceCreateInfo.pApplicationInfo = &app; + instanceCreateInfo.enabledExtensionCount = sizeof(instanceExtensionNames) / sizeof(const char*); + instanceCreateInfo.ppEnabledExtensionNames = instanceExtensionNames; + + size_t foundInstanceLayers = 0; + + std::uint32_t instanceLayerCount; + CheckVkResult(vkEnumerateInstanceLayerProperties(&instanceLayerCount, NULL)); + + std::vector instanceLayerProperties(instanceLayerCount); + CheckVkResult(vkEnumerateInstanceLayerProperties(&instanceLayerCount, instanceLayerProperties.data())); + + for (uint32_t i = 0; i < instanceLayerCount; i++) + { + for (size_t j = 0; j < sizeof(layerNames) / sizeof(const char*); j++) + { + if (std::strcmp(instanceLayerProperties[i].layerName, layerNames[j]) == 0) + { + foundInstanceLayers++; + } + } + } + + if (foundInstanceLayers >= sizeof(layerNames) / sizeof(const char*)) + { + instanceCreateInfo.enabledLayerCount = sizeof(layerNames) / sizeof(const char*); + instanceCreateInfo.ppEnabledLayerNames = layerNames; + } + + CheckVkResult(vkCreateInstance(&instanceCreateInfo, NULL, &instance)); + + VkDebugUtilsMessengerCreateInfoEXT debugUtilsMessengerCreateInfo = {}; + debugUtilsMessengerCreateInfo.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT; + debugUtilsMessengerCreateInfo.messageSeverity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT; + debugUtilsMessengerCreateInfo.messageType = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT; + debugUtilsMessengerCreateInfo.pfnUserCallback = onError; + CheckVkResult(GET_EXTENSION_FUNCTION(vkCreateDebugUtilsMessengerEXT)(instance, &debugUtilsMessengerCreateInfo, NULL, &debugMessenger)); + + uint32_t physDeviceCount; + vkEnumeratePhysicalDevices(instance, &physDeviceCount, NULL); + + std::vector physDevices(physDeviceCount); + vkEnumeratePhysicalDevices(instance, &physDeviceCount, physDevices.data()); + + uint32_t bestScore = 0; + + for (uint32_t i = 0; i < physDeviceCount; i++) + { + VkPhysicalDevice device = physDevices[i]; + + VkPhysicalDeviceProperties2 properties2{ + .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2, + .pNext = &rayTracingProperties + }; + vkGetPhysicalDeviceProperties2(device, &properties2); + + VkPhysicalDeviceProperties properties; + vkGetPhysicalDeviceProperties(device, &properties); + + uint32_t score; + + switch (properties.deviceType) + { + default : + continue; + case VK_PHYSICAL_DEVICE_TYPE_OTHER : + score = 1; + break; + case VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU : + score = 4; + break; + case VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU : + score = 5; + break; + case VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU : + score = 3; + break; + case VK_PHYSICAL_DEVICE_TYPE_CPU : + score = 2; + break; + } + + if (score > bestScore) + { + physDevice = device; + bestScore = score; + } + } + + + uint32_t queueFamilyCount; + vkGetPhysicalDeviceQueueFamilyProperties(physDevice, &queueFamilyCount, NULL); + + std::vector queueFamilies(queueFamilyCount); + vkGetPhysicalDeviceQueueFamilyProperties(physDevice, &queueFamilyCount, queueFamilies.data()); + + for (uint32_t i = 0; i < queueFamilyCount; i++) + { + if (queueFamilies[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) + { + queueFamilyIndex = i; + break; + } + } + + + float priority = 1; + + VkDeviceQueueCreateInfo queueCreateInfo = {}; + queueCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; + queueCreateInfo.queueFamilyIndex = queueFamilyIndex; + queueCreateInfo.queueCount = 1; + queueCreateInfo.pQueuePriorities = &priority; + + VkPhysicalDeviceVulkan12Features features12 { + .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES, + .runtimeDescriptorArray = VK_TRUE, + .bufferDeviceAddress = VK_TRUE + }; + + VkPhysicalDeviceRayTracingPositionFetchFeaturesKHR vkPhysicalDeviceRayTracingPositionFetchFeatures { + .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RAY_TRACING_POSITION_FETCH_FEATURES_KHR, + .pNext = &features12, + .rayTracingPositionFetch = VK_TRUE + }; + + VkPhysicalDeviceRayTracingPipelineFeaturesKHR physicalDeviceRayTracingPipelineFeatures{ + .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RAY_TRACING_PIPELINE_FEATURES_KHR, + .pNext = &vkPhysicalDeviceRayTracingPositionFetchFeatures, + .rayTracingPipeline = VK_TRUE + }; + + VkPhysicalDeviceAccelerationStructureFeaturesKHR deviceAccelerationStructureFeature = { + .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ACCELERATION_STRUCTURE_FEATURES_KHR, + .pNext = &physicalDeviceRayTracingPipelineFeatures, + .accelerationStructure = VK_TRUE + }; + + VkPhysicalDeviceDynamicRenderingFeaturesKHR dynamicRenderingFeature = {VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DYNAMIC_RENDERING_FEATURES_KHR}; + dynamicRenderingFeature.dynamicRendering = VK_FALSE; + dynamicRenderingFeature.pNext = &deviceAccelerationStructureFeature; + + VkPhysicalDeviceMeshShaderFeaturesEXT ext_feature = {VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MESH_SHADER_FEATURES_EXT}; + ext_feature.meshShader = VK_FALSE; + ext_feature.pNext = &dynamicRenderingFeature; + + VkPhysicalDeviceFeatures2 physical_features2 = { + .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2, + .features = { + .samplerAnisotropy = VK_TRUE + } + }; + physical_features2.pNext = &ext_feature; + + VkDeviceCreateInfo deviceCreateInfo = {}; + deviceCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; + deviceCreateInfo.queueCreateInfoCount = 1; + deviceCreateInfo.pQueueCreateInfos = &queueCreateInfo; + deviceCreateInfo.enabledExtensionCount = sizeof(deviceExtensionNames) / sizeof(const char*); + deviceCreateInfo.ppEnabledExtensionNames = deviceExtensionNames; + deviceCreateInfo.pNext = &physical_features2; + + uint32_t deviceLayerCount; + CheckVkResult(vkEnumerateDeviceLayerProperties(physDevice, &deviceLayerCount, NULL)); + + std::vector deviceLayerProperties(deviceLayerCount); + CheckVkResult(vkEnumerateDeviceLayerProperties(physDevice, &deviceLayerCount, deviceLayerProperties.data())); + + size_t foundDeviceLayers = 0; + + for (uint32_t i = 0; i < deviceLayerCount; i++) + { + for (size_t j = 0; j < sizeof(layerNames) / sizeof(const char*); j++) + { + if (std::strcmp(deviceLayerProperties[i].layerName, layerNames[j]) == 0) + { + foundDeviceLayers++; + } + } + } + + if (foundDeviceLayers >= sizeof(layerNames) / sizeof(const char*)) + { + deviceCreateInfo.enabledLayerCount = sizeof(layerNames) / sizeof(const char*); + deviceCreateInfo.ppEnabledLayerNames = layerNames; + } + + CheckVkResult(vkCreateDevice(physDevice, &deviceCreateInfo, NULL, &device)); + vkGetDeviceQueue(device, queueFamilyIndex, 0, &queue); + + VkCommandPoolCreateInfo commandPoolcreateInfo = {}; + commandPoolcreateInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; + commandPoolcreateInfo.queueFamilyIndex = queueFamilyIndex; + commandPoolcreateInfo.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT; + CheckVkResult(vkCreateCommandPool(device, &commandPoolcreateInfo, NULL, &commandPool)); + + vkGetPhysicalDeviceMemoryProperties(physDevice, &memoryProperties); + + vkGetAccelerationStructureBuildSizesKHR = reinterpret_cast(vkGetInstanceProcAddr(instance, "vkGetAccelerationStructureBuildSizesKHR")); + vkCreateAccelerationStructureKHR = reinterpret_cast(vkGetInstanceProcAddr(instance, "vkCreateAccelerationStructureKHR")); + vkCmdBuildAccelerationStructuresKHR = reinterpret_cast(vkGetInstanceProcAddr(instance, "vkCmdBuildAccelerationStructuresKHR")); + vkGetAccelerationStructureDeviceAddressKHR = reinterpret_cast(vkGetInstanceProcAddr(instance, "vkGetAccelerationStructureDeviceAddressKHR")); + vkCreateRayTracingPipelinesKHR = reinterpret_cast(vkGetInstanceProcAddr(instance, "vkCreateRayTracingPipelinesKHR")); + vkGetRayTracingShaderGroupHandlesKHR = reinterpret_cast(vkGetInstanceProcAddr(instance, "vkGetRayTracingShaderGroupHandlesKHR")); + vkCmdTraceRaysKHR = reinterpret_cast(vkGetInstanceProcAddr(instance, "vkCmdTraceRaysKHR")); + #endif +} + +#ifdef CRAFTER_GRAPHICS_RENDERER_VULKAN +std::uint32_t Device::GetMemoryType(uint32_t typeBits, VkMemoryPropertyFlags properties) { + for (uint32_t i = 0; i < memoryProperties.memoryTypeCount; i++) + { + if ((typeBits & 1) == 1) + { + if ((memoryProperties.memoryTypes[i].propertyFlags & properties) == properties) + { + return i; + } + } + typeBits >>= 1; + } + + throw std::runtime_error("Could not find a matching memory type"); +} +#endif \ No newline at end of file diff --git a/implementations/Crafter.Graphics-Font.cpp b/implementations/Crafter.Graphics-Font.cpp index cc10403..9a09e62 100644 --- a/implementations/Crafter.Graphics-Font.cpp +++ b/implementations/Crafter.Graphics-Font.cpp @@ -1,6 +1,6 @@ /* Crafter®.Graphics -Copyright (C) 2025 Catcrafts® +Copyright (C) 2026 Catcrafts® Catcrafts.net This library is free software; you can redistribute it and/or diff --git a/implementations/Crafter.Graphics-GridElement.cpp b/implementations/Crafter.Graphics-GridElement.cpp index 2b68734..edaf37e 100644 --- a/implementations/Crafter.Graphics-GridElement.cpp +++ b/implementations/Crafter.Graphics-GridElement.cpp @@ -1,6 +1,6 @@ /* Crafter®.Graphics -Copyright (C) 2025 Catcrafts® +Copyright (C) 2026 Catcrafts® Catcrafts.net This library is free software; you can redistribute it and/or @@ -20,24 +20,24 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA module Crafter.Graphics:GridElement_impl; import :GridElement; -import :Window; +import :Rendertarget; import :Types; import std; using namespace Crafter; -GridElement::GridElement(std::uint32_t columns, std::uint32_t rows, std::int32_t spacingX, std::int32_t spacingY, std::int32_t paddingX, std::int32_t paddingY, Anchor anchor) : Transform(anchor), columns(columns), rows(rows), spacingX(spacingX), spacingY(spacingY), paddingX(paddingX), paddingY(paddingY) { +GridElement::GridElement(std::uint32_t columns, std::uint32_t rows, std::int32_t spacingX, std::int32_t spacingY, std::int32_t paddingX, std::int32_t paddingY, Anchor2D anchor) : Transform2D(anchor), columns(columns), rows(rows), spacingX(spacingX), spacingY(spacingY), paddingX(paddingX), paddingY(paddingY) { } -void GridElement::UpdatePositionScaled(Window& window) { - std::int32_t cellWidth = (SCALE32 - (paddingX * 2) - (spacingX * (columns - 1))) / columns; - std::int32_t cellHeight = (SCALE32 - (paddingY * 2) - (spacingY * (rows - 1))) / rows; +void GridElement::UpdatePositionScaled(Rendertarget& window) { + std::int32_t cellWidth = (paddingX * 2) - (spacingX * (columns - 1)) / columns; + std::int32_t cellHeight = (paddingY * 2) - (spacingY * (rows - 1)) / rows; std::size_t childIndex = 0; for (std::uint32_t row = 0; row < rows && childIndex < children.size(); ++row) { for (std::uint32_t col = 0; col < columns && childIndex < children.size(); ++col) { - Transform* child = children[childIndex]; + Transform2D* child = children[childIndex]; // Calculate position for this child std::int32_t childX = (cellWidth * col) + (spacingX * col) + paddingX; @@ -57,12 +57,12 @@ void GridElement::UpdatePositionScaled(Window& window) { } } -void GridElement::UpdatePosition(Window& window) { - window.ScaleElement(*this); +void GridElement::UpdatePosition(Rendertarget& window) { + ScaleElement(window.transform); UpdatePositionScaled(window); } -void GridElement::UpdatePosition(Window& window, Transform& parent) { - window.ScaleElement(*this, parent); +void GridElement::UpdatePosition(Rendertarget& window, Transform2D& parent) { + ScaleElement(parent); UpdatePositionScaled(window); } \ No newline at end of file diff --git a/implementations/Crafter.Graphics-Image.cpp b/implementations/Crafter.Graphics-Image.cpp deleted file mode 100644 index c9e7c88..0000000 --- a/implementations/Crafter.Graphics-Image.cpp +++ /dev/null @@ -1,63 +0,0 @@ -/* -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 as published by the Free Software Foundation; either -version 3.0 of the License, or (at your option) any later version. - -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 "../lib/stb_image.h" -module Crafter.Graphics:Image_impl; -import :Image; -import std; - -using namespace Crafter; - -Image::Image(const std::string_view path) { - std::filesystem::path abs = std::filesystem::absolute(path); - int xSize; - int ySize; - unsigned char* bgData = stbi_load(abs.string().c_str(), &xSize, &ySize, nullptr, 4); - - buffer.resize(xSize*ySize); - width = xSize; - height = ySize; - - opaque = OpaqueType::FullyOpaque; - - for(std::uint_fast32_t x = 0; x < xSize; x++) { - for(std::uint_fast32_t y = 0; y < ySize; y++) { - std::uint_fast32_t idx = (x*ySize+y)*4; - buffer[x*ySize+y].r = bgData[idx]; - buffer[x*ySize+y].g = bgData[idx+1]; - buffer[x*ySize+y].b = bgData[idx+2]; - buffer[x*ySize+y].a = bgData[idx+3]; - } - } - - for(std::uint_fast32_t i = 0; i < xSize*ySize; i++) { - if(buffer[i].a != 255) { - opaque = OpaqueType::SemiOpaque; - for(std::uint_fast32_t i2 = 0; i2 < xSize*ySize; i2++) { - if(buffer[i2].a != 0 && buffer[i2].a != 255) { - opaque = OpaqueType::Transparent; - return; - } - } - return; - } - } -} \ No newline at end of file diff --git a/implementations/Crafter.Graphics-Mesh_vulkan.cpp b/implementations/Crafter.Graphics-Mesh.cpp similarity index 91% rename from implementations/Crafter.Graphics-Mesh_vulkan.cpp rename to implementations/Crafter.Graphics-Mesh.cpp index 507fc55..8242b77 100644 --- a/implementations/Crafter.Graphics-Mesh_vulkan.cpp +++ b/implementations/Crafter.Graphics-Mesh.cpp @@ -24,7 +24,7 @@ module; module Crafter.Graphics:Mesh_impl; import Crafter.Math; import :Mesh; -import :VulkanDevice; +import :Device; import :Types; import std; @@ -79,8 +79,8 @@ void Mesh::Build(std::span> verticies, std::span> verticies, std::span> verticies, std::span(0), FractionalToMapped(0), FractionalToMapped(1), FractionalToMapped(1), FractionalToMapped(0), FractionalToMapped(0), 0}) { +MouseElement::MouseElement(Window& window) : Transform2D({0, 0, 1, 1, 0, 0, 0}) { window.mouseElements.push_back(this); } -MouseElement::MouseElement() : Transform({FractionalToMapped(0), FractionalToMapped(0), FractionalToMapped(1), FractionalToMapped(1), FractionalToMapped(0), FractionalToMapped(0), 0}) { +MouseElement::MouseElement() : Transform2D({0, 0, 1, 1, 0, 0, 0}) { -} - - -void MouseElement::UpdatePosition(Window& window) { - window.ScaleMouse(*this); - window.ScaleElement(*this); - for(Transform* child : children) { - child->UpdatePosition(window, *this); - } -} - -void MouseElement::UpdatePosition(Window& window, Transform& parent) { - window.ScaleMouse(*this, parent); - window.ScaleElement(*this, parent); - for(Transform* child : children) { - child->UpdatePosition(window, *this); - } } \ No newline at end of file diff --git a/implementations/Crafter.Graphics-RenderingElement3DVulkan.cpp b/implementations/Crafter.Graphics-RenderingElement3D.cpp similarity index 87% rename from implementations/Crafter.Graphics-RenderingElement3DVulkan.cpp rename to implementations/Crafter.Graphics-RenderingElement3D.cpp index 4e57f95..7299efc 100644 --- a/implementations/Crafter.Graphics-RenderingElement3DVulkan.cpp +++ b/implementations/Crafter.Graphics-RenderingElement3D.cpp @@ -19,15 +19,15 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ module; #include -module Crafter.Graphics:RenderingElement3DVulkan_impl; -import :RenderingElement3DVulkan; +module Crafter.Graphics:RenderingElement3D_impl; +import :RenderingElement3D; import std; using namespace Crafter; -std::vector RenderingElement3DVulkan::elements; +std::vector RenderingElement3D::elements; -void RenderingElement3DVulkan::BuildTLAS(VkCommandBuffer cmd, std::uint32_t index) { +void RenderingElement3D::BuildTLAS(VkCommandBuffer cmd, std::uint32_t index) { tlases[index].instanceBuffer.Resize(VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT | VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_ACCELERATION_STRUCTURE_BUILD_INPUT_READ_ONLY_BIT_KHR, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, elements.size()); for(std::uint32_t i = 0; i < elements.size(); i++) { @@ -65,8 +65,8 @@ void RenderingElement3DVulkan::BuildTLAS(VkCommandBuffer cmd, std::uint32_t inde VkAccelerationStructureBuildSizesInfoKHR tlasBuildSizes { .sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_BUILD_SIZES_INFO_KHR }; - VulkanDevice::vkGetAccelerationStructureBuildSizesKHR( - VulkanDevice::device, + Device::vkGetAccelerationStructureBuildSizesKHR( + Device::device, VK_ACCELERATION_STRUCTURE_BUILD_TYPE_DEVICE_KHR, &tlasBuildGeometryInfo, &primitiveCount, @@ -87,7 +87,7 @@ void RenderingElement3DVulkan::BuildTLAS(VkCommandBuffer cmd, std::uint32_t inde .type = VK_ACCELERATION_STRUCTURE_TYPE_TOP_LEVEL_KHR, }; - VulkanDevice::CheckVkResult(VulkanDevice::vkCreateAccelerationStructureKHR(VulkanDevice::device, &tlasCreateInfo, nullptr, &tlases[index].accelerationStructure)); + Device::CheckVkResult(Device::vkCreateAccelerationStructureKHR(Device::device, &tlasCreateInfo, nullptr, &tlases[index].accelerationStructure)); tlasBuildGeometryInfo.dstAccelerationStructure = tlases[index].accelerationStructure; // Prepare the build range for the TLAS @@ -99,7 +99,7 @@ void RenderingElement3DVulkan::BuildTLAS(VkCommandBuffer cmd, std::uint32_t inde }; VkAccelerationStructureBuildRangeInfoKHR* tlasRangeInfoPP = &tlasRangeInfo; - VulkanDevice::vkCmdBuildAccelerationStructuresKHR(cmd, 1, &tlasBuildGeometryInfo, &tlasRangeInfoPP); + Device::vkCmdBuildAccelerationStructuresKHR(cmd, 1, &tlasBuildGeometryInfo, &tlasRangeInfoPP); vkCmdPipelineBarrier( cmd, diff --git a/implementations/Crafter.Graphics-Rendertarget.cpp b/implementations/Crafter.Graphics-Rendertarget.cpp new file mode 100644 index 0000000..b742ffd --- /dev/null +++ b/implementations/Crafter.Graphics-Rendertarget.cpp @@ -0,0 +1,249 @@ +/* +Crafter®.Graphics +Copyright (C) 2026 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 as published by the Free Software Foundation; either +version 3.0 of the License, or (at your option) any later version. + +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 Crafter.Graphics:Rendertarget_impl; +import :Window; +import :RenderingElement2D; +import std; + +using namespace Crafter; + +Rendertarget::Rendertarget(std::int32_t sizeX, std::int32_t sizeY) : transform({0, 0, static_cast(sizeX), static_cast(sizeY), 0, 0, 0}) { + transform.scaled.size.x = sizeX; + transform.scaled.size.y = sizeY; + transform.scaled.position.x = 0; + transform.scaled.position.y = 0; +} + +void Rendertarget::AddDirtyRect(ScaleData2D scale) { + ClipRect rect { + .left = std::max(scale.position.x, std::int32_t(0)), + .right = std::min(scale.position.x + scale.size.x, sizeX), + .top = std::max(scale.position.y, std::int32_t(0)), + .bottom = std::min(scale.position.y + scale.size.y, sizeY), + }; + dirtyRects.push_back(rect); +} + +inline void blend_pixel_optimized(Vector& dst, const Vector& src) { + if(src.a == 0) { + return; + } + + float srcA = src.a / 255.0f; + float dstA = dst.a / 255.0f; + + float outA = srcA + dstA * (1.0f - srcA); + if (outA > 0.0f) { + dst = Vector( + static_cast((src.b * srcA + dst.b * dstA * (1.0f - srcA)) / outA), + static_cast((src.g * srcA + dst.g * dstA * (1.0f - srcA)) / outA), + static_cast((src.r * srcA + dst.r * dstA * (1.0f - srcA)) / outA), + static_cast(outA * 255) + ); + } +} + +void Rendertarget::RenderElement(RenderingElement2DBase* element) { + #ifdef CRAFTER_TIMING + auto start = std::chrono::high_resolution_clock::now(); + #endif + + if(element->scaled.size.x < 1 || element->scaled.size.y < 1) { + return; + } + + for(ClipRect dirty : dirtyRects) { + dirty.left = std::max(element->scaled.position.x, dirty.left); + dirty.top = std::max(element->scaled.position.y, dirty.top); + dirty.right = std::min(element->scaled.position.x+element->scaled.size.x, dirty.right); + dirty.bottom = std::min(element->scaled.position.y+element->scaled.size.y, dirty.bottom); + + const Vector* src_buffer = element->buffer.data(); + std::int32_t src_width = element->scaled.size.x; + std::int32_t src_height = element->scaled.size.y; + + switch (element->opaque) { + case OpaqueType::FullyOpaque: + // For fully opaque, just copy pixels directly + for (std::int32_t y = dirty.top; y < dirty.bottom; y++) { + std::int32_t src_y = y - element->scaled.position.y; + + for (std::int32_t x = dirty.left; x < dirty.right; x++) { + std::int32_t src_x = x - element->scaled.position.x; + + buffer[y * sizeX + x] = src_buffer[src_y * src_width + src_x]; + } + } + break; + + case OpaqueType::SemiOpaque: + // For semi-opaque, we can avoid blending when alpha is 0 or 255 + for (std::int32_t y = dirty.top; y < dirty.bottom; y++) { + std::int32_t src_y = y - element->scaled.position.y; + + for (std::int32_t x = dirty.left; x < dirty.right; x++) { + std::int32_t src_x = x - element->scaled.position.x; + Vector src_pixel = src_buffer[src_y * src_width + src_x]; + + if (src_pixel.a == 0) { + continue; + } + buffer[y * sizeX + x] = src_pixel; + } + } + break; + + case OpaqueType::Transparent: + // For transparent, always perform blending + for (std::int32_t y = dirty.top; y < dirty.bottom; y++) { + std::int32_t src_y = y - element->scaled.position.y; + + for (std::int32_t x = dirty.left; x < dirty.right; x++) { + std::int32_t src_x = x - element->scaled.position.x; + blend_pixel_optimized(buffer[y * sizeX + x], src_buffer[src_y * src_width + src_x]); + } + } + break; + } + } + + #ifdef CRAFTER_TIMING + auto end = std::chrono::high_resolution_clock::now(); + renderTimings.push_back({element, element->scaled.size.x, element->scaled.size.y, end-start}); + #endif +} + +void Rendertarget::Render() { + //std::vector newClip; + + // for (std::uint32_t i = 0; i < dirtyRects.size(); i++) { + // ClipRect rect = dirtyRects[i]; + // for (std::uint32_t i2 = i + 1; i2 < dirtyRects.size(); i2++) { + // ClipRect existing = dirtyRects[i2]; + // if(rect.bottom >= existing.top && rect.top <= existing.top) { + // newClip.push_back({ + // .left = rect.left, + // .right = rect.right, + // .top = rect.top, + // .bottom = existing.top, + // }); + // //-| shape + // if(rect.right > existing.right) { + // newClip.push_back({ + // .left = existing.right, + // .right = rect.right, + // .top = existing.top, + // .bottom = existing.bottom, + // }); + // } + // //|- shape + // if(rect.left < existing.left) { + // newClip.push_back({ + // .left = rect.left, + // .right = existing.left, + // .top = existing.top, + // .bottom = existing.bottom, + // }); + // } + // //-| or |- shape where rect extends further down + // if(rect.bottom > existing.bottom) { + // newClip.push_back({ + // .left = rect.left, + // .right = rect.right, + // .top = existing.bottom, + // .bottom = rect.bottom, + // }); + // } + // goto inner; + // } + // if (rect.left <= existing.right && rect.right >= existing.left) { + // newClip.push_back({ + // .left = rect.left, + // .right = existing.left, + // .top = rect.top, + // .bottom = rect.bottom, + // }); + // if (rect.right > existing.right) { + // newClip.push_back({ + // .left = existing.right, + // .right = rect.right, + // .top = rect.top, + // .bottom = rect.bottom, + // }); + // } + // goto inner; + // } + // } + // newClip.push_back(rect); + // inner:; + // } + + //dirtyRects = std::move(newClip); + + // std::memset(buffer, 0, width*height*4); + + // std::cout << dirtyRects.size() << std::endl; + // // Color palette + // static const std::vector> colors = { + // {255, 0, 0, 255}, // red + // { 0, 255, 0, 255}, // green + // { 0, 0, 255, 255}, // blue + // {255, 255, 0, 255}, // yellow + // {255, 0, 255, 255}, // magenta + // { 0, 255, 255, 255}, // cyan + // }; + + // std::size_t rectIndex = 0; + + // for (const ClipRect& rect : dirtyRects) { + // const Vector& color = colors[rectIndex % colors.size()]; + + // std::cout << std::format( + // "ClipRect {}: [{}, {}, {}, {}] Color = RGBA({}, {}, {}, {})", + // rectIndex, + // rect.left, rect.top, rect.right, rect.bottom, + // color.r, color.g, color.b, color.a + // ) << std::endl; + + // for (std::int32_t y = rect.top; y < rect.bottom; ++y) { + // for (std::int32_t x = rect.left; x < rect.right; ++x) { + // buffer[y * width + x] = color; + // } + // } + + // ++rectIndex; + // } + + if (!dirtyRects.empty()) { + for (ClipRect rect : dirtyRects) { + for (std::int32_t y = rect.top; y < rect.bottom; y++) { + for (std::int32_t x = rect.left; x < rect.right; x++) { + buffer[y * sizeX + x] = {0, 0, 0, 0}; + } + } + } + + for(RenderingElement2DBase* child : elements) { + RenderElement(child); + } + dirtyRects.clear(); + } +} \ No newline at end of file diff --git a/implementations/Crafter.Graphics-Shm.cpp b/implementations/Crafter.Graphics-Shm.cpp index 48f6ecf..b2dcc6f 100644 --- a/implementations/Crafter.Graphics-Shm.cpp +++ b/implementations/Crafter.Graphics-Shm.cpp @@ -1,6 +1,6 @@ /* Crafter®.Graphics -Copyright (C) 2025 Catcrafts® +Copyright (C) 2026 Catcrafts® catcrafts.net This library is free software; you can redistribute it and/or diff --git a/implementations/Crafter.Graphics-Transform.cpp b/implementations/Crafter.Graphics-Transform.cpp deleted file mode 100644 index 2bcfe24..0000000 --- a/implementations/Crafter.Graphics-Transform.cpp +++ /dev/null @@ -1,51 +0,0 @@ -/* -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 as published by the Free Software Foundation; either -version 3.0 of the License, or (at your option) any later version. - -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 Crafter.Graphics:Transform_impl; -import :Transform; -import :Window; -import :Types; -import :Font; -import std; - -using namespace Crafter; - - -Anchor::Anchor(std::int32_t x, std::int32_t y, std::uint32_t width, std::uint32_t height, std::int32_t offsetX, std::int32_t offsetY, std::int32_t z, bool maintainAspectRatio): x(x), y(y), width(width), height(height), offsetX(offsetX), offsetY(offsetY), z(z), maintainAspectRatio(maintainAspectRatio) { - -} - -Transform::Transform(Anchor anchor) : anchor(anchor) { - -} - -void Transform::UpdatePosition(Window& window) { - window.ScaleElement(*this); - for(Transform* child : children) { - child->UpdatePosition(window, *this); - } -} - -void Transform::UpdatePosition(Window& window, Transform& parent) { - window.ScaleElement(*this, parent); - for(Transform* child : children) { - child->UpdatePosition(window, *this); - } -} \ No newline at end of file diff --git a/implementations/Crafter.Graphics-Transform2D.cpp b/implementations/Crafter.Graphics-Transform2D.cpp new file mode 100644 index 0000000..2394803 --- /dev/null +++ b/implementations/Crafter.Graphics-Transform2D.cpp @@ -0,0 +1,70 @@ +/* +Crafter®.Graphics +Copyright (C) 2026 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 as published by the Free Software Foundation; either +version 3.0 of the License, or (at your option) any later version. + +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 Crafter.Graphics:Transform2D_impl; +import :Transform2D; +import :Rendertarget; +import :Types; +import :Font; +import std; + +using namespace Crafter; + + +Anchor2D::Anchor2D(float x, float y, float width, float height, float offsetX, float offsetY, std::int32_t z, bool maintainAspectRatio): x(x), y(y), width(width), height(height), offsetX(offsetX), offsetY(offsetY), z(z), maintainAspectRatio(maintainAspectRatio) { + +} + +Transform2D::Transform2D(Anchor2D anchor) : anchor(anchor) { + +} + +void Transform2D::UpdatePosition(Rendertarget& window) { + ScaleElement(window.transform); + for(Transform2D* child : children) { + child->UpdatePosition(window, *this); + } +} + +void Transform2D::UpdatePosition(Rendertarget& window, Transform2D& parent) { + ScaleElement(parent); + for(Transform2D* child : children) { + child->UpdatePosition(window, *this); + } +} + +void Transform2D::ScaleElement(Transform2D& parent) { + if(anchor.maintainAspectRatio) { + if(parent.scaled.size.x > parent.scaled.size.y) { + scaled.size.x = anchor.width * parent.scaled.size.y; + scaled.size.y = anchor.height * parent.scaled.size.y; + } else { + scaled.size.x = anchor.width * parent.scaled.size.x; + scaled.size.y = anchor.height * parent.scaled.size.x; + } + } else { + + scaled.size.x = anchor.width * parent.scaled.size.x; + scaled.size.y = anchor.height * parent.scaled.size.y; + } + + scaled.position.x = parent.scaled.position.x + (anchor.x * parent.scaled.size.x - anchor.offsetX * scaled.size.x); + scaled.position.y = parent.scaled.position.y + (anchor.y * parent.scaled.size.y - anchor.offsetY * scaled.size.y); +} \ No newline at end of file diff --git a/implementations/Crafter.Graphics-VulkanDevice.cpp b/implementations/Crafter.Graphics-VulkanDevice.cpp deleted file mode 100644 index a50412d..0000000 --- a/implementations/Crafter.Graphics-VulkanDevice.cpp +++ /dev/null @@ -1,337 +0,0 @@ -/* -Crafter®.Graphics -Copyright (C) 2026 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 "vulkan/vulkan.h" -#include "vulkan/vk_enum_string_helper.h" - -#define GET_EXTENSION_FUNCTION(_id) ((PFN_##_id)(vkGetInstanceProcAddr(instance, #_id))) - -module Crafter.Graphics:VulkanDevice_impl; -import :VulkanDevice; -import std; - - -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", - "VK_KHR_spirv_1_4", - "VK_EXT_mesh_shader", - "VK_KHR_shader_float_controls", - "VK_KHR_dynamic_rendering", - "VK_KHR_acceleration_structure", - "VK_KHR_deferred_host_operations", - "VK_KHR_ray_tracing_pipeline", - "VK_KHR_ray_tracing_position_fetch" -}; -const char* const layerNames[] = { - "VK_LAYER_KHRONOS_validation" -}; - - -void VulkanDevice::CheckVkResult(VkResult result) { - if (result != VK_SUCCESS) - { - throw std::runtime_error(string_VkResult(result)); - } -} - -VkBool32 onError(VkDebugUtilsMessageSeverityFlagBitsEXT severity, VkDebugUtilsMessageTypeFlagsEXT type, const VkDebugUtilsMessengerCallbackDataEXT* callbackData, void* userData) -{ - printf("Vulkan "); - - switch (type) - { - case VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT : - printf("general "); - break; - case VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT : - printf("validation "); - break; - case VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT : - printf("performance "); - break; - } - - switch (severity) - { - case VK_DEBUG_UTILS_MESSAGE_SEVERITY_VERBOSE_BIT_EXT : - printf("(verbose): "); - break; - default : - case VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT : - printf("(info): "); - break; - case VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT : - printf("(warning): "); - break; - case VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT : - printf("(error): "); - break; - } - - printf("%s\n", callbackData->pMessage); - - return 0; -} - -void VulkanDevice::CreateDevice() { - VkApplicationInfo app{VK_STRUCTURE_TYPE_APPLICATION_INFO}; - app.pApplicationName = ""; - app.pEngineName = "Crafter.Graphics"; - app.apiVersion = VK_MAKE_VERSION(1, 4, 0); - - VkInstanceCreateInfo instanceCreateInfo = {}; - instanceCreateInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; - instanceCreateInfo.pApplicationInfo = &app; - instanceCreateInfo.enabledExtensionCount = sizeof(instanceExtensionNames) / sizeof(const char*); - instanceCreateInfo.ppEnabledExtensionNames = instanceExtensionNames; - - size_t foundInstanceLayers = 0; - - std::uint32_t instanceLayerCount; - CheckVkResult(vkEnumerateInstanceLayerProperties(&instanceLayerCount, NULL)); - - std::vector instanceLayerProperties(instanceLayerCount); - CheckVkResult(vkEnumerateInstanceLayerProperties(&instanceLayerCount, instanceLayerProperties.data())); - - for (uint32_t i = 0; i < instanceLayerCount; i++) - { - for (size_t j = 0; j < sizeof(layerNames) / sizeof(const char*); j++) - { - if (std::strcmp(instanceLayerProperties[i].layerName, layerNames[j]) == 0) - { - foundInstanceLayers++; - } - } - } - - if (foundInstanceLayers >= sizeof(layerNames) / sizeof(const char*)) - { - instanceCreateInfo.enabledLayerCount = sizeof(layerNames) / sizeof(const char*); - instanceCreateInfo.ppEnabledLayerNames = layerNames; - } - - CheckVkResult(vkCreateInstance(&instanceCreateInfo, NULL, &instance)); - - VkDebugUtilsMessengerCreateInfoEXT debugUtilsMessengerCreateInfo = {}; - debugUtilsMessengerCreateInfo.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_MESSENGER_CREATE_INFO_EXT; - debugUtilsMessengerCreateInfo.messageSeverity = VK_DEBUG_UTILS_MESSAGE_SEVERITY_INFO_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_SEVERITY_WARNING_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT; - debugUtilsMessengerCreateInfo.messageType = VK_DEBUG_UTILS_MESSAGE_TYPE_GENERAL_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_TYPE_VALIDATION_BIT_EXT | VK_DEBUG_UTILS_MESSAGE_TYPE_PERFORMANCE_BIT_EXT; - debugUtilsMessengerCreateInfo.pfnUserCallback = onError; - CheckVkResult(GET_EXTENSION_FUNCTION(vkCreateDebugUtilsMessengerEXT)(instance, &debugUtilsMessengerCreateInfo, NULL, &debugMessenger)); - - uint32_t physDeviceCount; - vkEnumeratePhysicalDevices(instance, &physDeviceCount, NULL); - - std::vector physDevices(physDeviceCount); - vkEnumeratePhysicalDevices(instance, &physDeviceCount, physDevices.data()); - - uint32_t bestScore = 0; - - for (uint32_t i = 0; i < physDeviceCount; i++) - { - VkPhysicalDevice device = physDevices[i]; - - VkPhysicalDeviceProperties2 properties2{ - .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2, - .pNext = &rayTracingProperties - }; - vkGetPhysicalDeviceProperties2(device, &properties2); - - VkPhysicalDeviceProperties properties; - vkGetPhysicalDeviceProperties(device, &properties); - - uint32_t score; - - switch (properties.deviceType) - { - default : - continue; - case VK_PHYSICAL_DEVICE_TYPE_OTHER : - score = 1; - break; - case VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU : - score = 4; - break; - case VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU : - score = 5; - break; - case VK_PHYSICAL_DEVICE_TYPE_VIRTUAL_GPU : - score = 3; - break; - case VK_PHYSICAL_DEVICE_TYPE_CPU : - score = 2; - break; - } - - if (score > bestScore) - { - physDevice = device; - bestScore = score; - } - } - - - uint32_t queueFamilyCount; - vkGetPhysicalDeviceQueueFamilyProperties(physDevice, &queueFamilyCount, NULL); - - std::vector queueFamilies(queueFamilyCount); - vkGetPhysicalDeviceQueueFamilyProperties(physDevice, &queueFamilyCount, queueFamilies.data()); - - for (uint32_t i = 0; i < queueFamilyCount; i++) - { - if (queueFamilies[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) - { - queueFamilyIndex = i; - break; - } - } - - - float priority = 1; - - VkDeviceQueueCreateInfo queueCreateInfo = {}; - queueCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; - queueCreateInfo.queueFamilyIndex = queueFamilyIndex; - queueCreateInfo.queueCount = 1; - queueCreateInfo.pQueuePriorities = &priority; - - VkPhysicalDeviceVulkan12Features features12 { - .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES, - .runtimeDescriptorArray = VK_TRUE, - .bufferDeviceAddress = VK_TRUE - }; - - VkPhysicalDeviceRayTracingPositionFetchFeaturesKHR vkPhysicalDeviceRayTracingPositionFetchFeatures { - .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RAY_TRACING_POSITION_FETCH_FEATURES_KHR, - .pNext = &features12, - .rayTracingPositionFetch = VK_TRUE - }; - - VkPhysicalDeviceRayTracingPipelineFeaturesKHR physicalDeviceRayTracingPipelineFeatures{ - .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RAY_TRACING_PIPELINE_FEATURES_KHR, - .pNext = &vkPhysicalDeviceRayTracingPositionFetchFeatures, - .rayTracingPipeline = VK_TRUE - }; - - VkPhysicalDeviceAccelerationStructureFeaturesKHR deviceAccelerationStructureFeature = { - .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ACCELERATION_STRUCTURE_FEATURES_KHR, - .pNext = &physicalDeviceRayTracingPipelineFeatures, - .accelerationStructure = VK_TRUE - }; - - VkPhysicalDeviceDynamicRenderingFeaturesKHR dynamicRenderingFeature = {VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DYNAMIC_RENDERING_FEATURES_KHR}; - dynamicRenderingFeature.dynamicRendering = VK_FALSE; - dynamicRenderingFeature.pNext = &deviceAccelerationStructureFeature; - - VkPhysicalDeviceMeshShaderFeaturesEXT ext_feature = {VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MESH_SHADER_FEATURES_EXT}; - ext_feature.meshShader = VK_FALSE; - ext_feature.pNext = &dynamicRenderingFeature; - - VkPhysicalDeviceFeatures2 physical_features2 = { - .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2, - .features = { - .samplerAnisotropy = VK_TRUE - } - }; - physical_features2.pNext = &ext_feature; - - VkDeviceCreateInfo deviceCreateInfo = {}; - deviceCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; - deviceCreateInfo.queueCreateInfoCount = 1; - deviceCreateInfo.pQueueCreateInfos = &queueCreateInfo; - deviceCreateInfo.enabledExtensionCount = sizeof(deviceExtensionNames) / sizeof(const char*); - deviceCreateInfo.ppEnabledExtensionNames = deviceExtensionNames; - deviceCreateInfo.pNext = &physical_features2; - - uint32_t deviceLayerCount; - CheckVkResult(vkEnumerateDeviceLayerProperties(physDevice, &deviceLayerCount, NULL)); - - std::vector deviceLayerProperties(deviceLayerCount); - CheckVkResult(vkEnumerateDeviceLayerProperties(physDevice, &deviceLayerCount, deviceLayerProperties.data())); - - size_t foundDeviceLayers = 0; - - for (uint32_t i = 0; i < deviceLayerCount; i++) - { - for (size_t j = 0; j < sizeof(layerNames) / sizeof(const char*); j++) - { - if (std::strcmp(deviceLayerProperties[i].layerName, layerNames[j]) == 0) - { - foundDeviceLayers++; - } - } - } - - if (foundDeviceLayers >= sizeof(layerNames) / sizeof(const char*)) - { - deviceCreateInfo.enabledLayerCount = sizeof(layerNames) / sizeof(const char*); - deviceCreateInfo.ppEnabledLayerNames = layerNames; - } - - CheckVkResult(vkCreateDevice(physDevice, &deviceCreateInfo, NULL, &device)); - vkGetDeviceQueue(device, queueFamilyIndex, 0, &queue); - - VkCommandPoolCreateInfo commandPoolcreateInfo = {}; - commandPoolcreateInfo.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; - commandPoolcreateInfo.queueFamilyIndex = queueFamilyIndex; - commandPoolcreateInfo.flags = VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT; - CheckVkResult(vkCreateCommandPool(device, &commandPoolcreateInfo, NULL, &commandPool)); - - vkGetPhysicalDeviceMemoryProperties(physDevice, &memoryProperties); - - // 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")); - vkGetAccelerationStructureDeviceAddressKHR = reinterpret_cast(vkGetInstanceProcAddr(instance, "vkGetAccelerationStructureDeviceAddressKHR")); - vkCreateRayTracingPipelinesKHR = reinterpret_cast(vkGetInstanceProcAddr(instance, "vkCreateRayTracingPipelinesKHR")); - vkGetRayTracingShaderGroupHandlesKHR = reinterpret_cast(vkGetInstanceProcAddr(instance, "vkGetRayTracingShaderGroupHandlesKHR")); - vkCmdTraceRaysKHR = reinterpret_cast(vkGetInstanceProcAddr(instance, "vkCmdTraceRaysKHR")); -} - -std::uint32_t VulkanDevice::GetMemoryType(uint32_t typeBits, VkMemoryPropertyFlags properties) { - for (uint32_t i = 0; i < memoryProperties.memoryTypeCount; i++) - { - if ((typeBits & 1) == 1) - { - if ((memoryProperties.memoryTypes[i].propertyFlags & properties) == properties) - { - return i; - } - } - typeBits >>= 1; - } - - throw std::runtime_error("Could not find a matching memory type"); -} \ No newline at end of file diff --git a/implementations/Crafter.Graphics-Window.cpp b/implementations/Crafter.Graphics-Window.cpp index 1cc850c..05eef25 100644 --- a/implementations/Crafter.Graphics-Window.cpp +++ b/implementations/Crafter.Graphics-Window.cpp @@ -1,6 +1,6 @@ /* Crafter®.Graphics -Copyright (C) 2025 Catcrafts® +Copyright (C) 2026 Catcrafts® catcrafts.net This library is free software; you can redistribute it and/or @@ -16,67 +16,713 @@ 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; +#ifdef CRAFTER_GRAPHICS_WINDOW_WAYLAND +#include +#include +#include +#include +#include "../lib/xdg-shell-client-protocol.h" +#include "../lib/wayland-xdg-decoration-unstable-v1-client-protocol.h" +#include "../lib/fractional-scale-v1.h" +#include "../lib/viewporter.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#ifdef CRAFTER_GRAPHICS_RENDERER_SOFTWARE +#include +#include +#endif +#endif +#ifdef CRAFTER_GRAPHICS_WINDOW_WIN32 +#include +#include +#endif +#ifdef CRAFTER_GRAPHICS_RENDERER_VULKAN +#include "vulkan/vulkan.h" +#ifdef CRAFTER_GRAPHICS_WINDOW_WAYLAND +#include "vulkan/vulkan_wayland.h" +#endif +#ifdef CRAFTER_GRAPHICS_WINDOW_WIN32 +#include "vulkan/vulkan_win32.h" +#endif +#endif module Crafter.Graphics:Window_impl; import :Window; -import :Transform; +import :Transform2D; import :MouseElement; +import :Device; +#ifdef CRAFTER_GRAPHICS_RENDERER_VULKAN +import :VulkanTransition; +#endif import std; using namespace Crafter; -Window::Window(std::int32_t width, std::int32_t height) : width(width), height(height) { +#ifdef CRAFTER_GRAPHICS_WINDOW_WAYLAND +#ifdef CRAFTER_GRAPHICS_RENDERER_SOFTWARE +void randname(char *buf) { + struct timespec ts; + clock_gettime(CLOCK_REALTIME, &ts); + long r = ts.tv_nsec; + for (int i = 0; i < 6; ++i) { + buf[i] = 'A'+(r&15)+(r&16)*2; + r >>= 5; + } } -void Window::ScaleElement(Transform& element) { - if(element.anchor.maintainAspectRatio) { - if(width > height) { - element.scaled.width = MappedToAbsolute(element.anchor.width, height); - element.scaled.height = MappedToAbsolute(element.anchor.height, height); - } else { - element.scaled.width = MappedToAbsolute(element.anchor.width, width); - element.scaled.height = MappedToAbsolute(element.anchor.height, width); - } - } else { - element.scaled.width = MappedToAbsolute(element.anchor.width, width); - element.scaled.height = MappedToAbsolute(element.anchor.height, height); +int anonymous_shm_open(void) { + char name[] = "/hello-wayland-XXXXXX"; + int retries = 100; + + do { + randname(name + strlen(name) - 6); + + --retries; + // shm_open guarantees that O_CLOEXEC is set + int fd = shm_open(name, O_RDWR | O_CREAT | O_EXCL, 0600); + if (fd >= 0) { + shm_unlink(name); + return fd; + } + } while (retries > 0 && errno == EEXIST); + + return -1; +} + +int create_shm_file(off_t size) { + int fd = anonymous_shm_open(); + if (fd < 0) { + return fd; + } + + if (ftruncate(fd, size) < 0) { + close(fd); + return -1; + } + + return fd; +} +#endif +#endif + +#ifdef CRAFTER_GRAPHICS_WINDOW_WIN32 +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: throw std::runtime_error(std::format("Unkown VK {}", vk)); + } +} + +// 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) { + + Window* 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) + ); } - element.scaled.x = MappedToAbsolute(element.anchor.x, width) - MappedToAbsolute(element.anchor.offsetX, element.scaled.width); - element.scaled.y = MappedToAbsolute(element.anchor.y, height) - MappedToAbsolute(element.anchor.offsetY, element.scaled.height); + 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); + + Vector pos(x, y); + window->currentMousePos = pos; + window->onMouseMove.Invoke(); + for(MouseElement* element : window->mouseElements) { + if(element) { + if(window->currentMousePos.x >= element->scaled.position.x && window->currentMousePos.x <= element->scaled.position.x+element->scaled.size.x && window->currentMousePos.y > element->scaled.position.y && window->currentMousePos.y < element->scaled.position.y+element->scaled.size.y) { + element->onMouseMove.Invoke(); + if(!(window->lastMousePos.x >= element->scaled.position.x && window->lastMousePos.x <= element->scaled.position.x+element->scaled.size.x && window->lastMousePos.y > element->scaled.position.y && window->lastMousePos.y < element->scaled.position.y+element->scaled.size.y)) { + element->onMouseEnter.Invoke(); + } + } else if(window->lastMousePos.x >= element->scaled.position.x && window->lastMousePos.x <= element->scaled.position.x+element->scaled.size.x && window->lastMousePos.y > element->scaled.position.y && window->lastMousePos.y < element->scaled.position.y+element->scaled.size.y) { + element->onMouseLeave.Invoke(); + } + } + } + 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(); + for(MouseElement* element : window->mouseElements) { + if(element) { + if(window->currentMousePos.x >= element->scaled.position.x && window->currentMousePos.x <= element->scaled.position.x+element->scaled.size.x && window->currentMousePos.y > element->scaled.position.y && window->currentMousePos.y < element->scaled.position.y+element->scaled.size.y) { + element->onMouseLeftClick.Invoke(); + } + } + } + break; + } + + case WM_LBUTTONUP: { + window->mouseLeftHeld = false; + window->onMouseLeftRelease.Invoke(); + for(MouseElement* element : window->mouseElements) { + if(element) { + if(window->currentMousePos.x >= element->scaled.position.x && window->currentMousePos.x <= element->scaled.position.x+element->scaled.size.x && window->currentMousePos.y > element->scaled.position.y && window->currentMousePos.y < element->scaled.position.y+element->scaled.size.y) { + element->onMouseLeftRelease.Invoke(); + } + } + } + break; + } + + case WM_RBUTTONDOWN: { + window->mouseRightHeld = true; + window->onMouseRightClick.Invoke(); + for(MouseElement* element : window->mouseElements) { + if(element) { + if(window->currentMousePos.x >= element->scaled.position.x && window->currentMousePos.x <= element->scaled.position.x+element->scaled.size.x && window->currentMousePos.y > element->scaled.position.y && window->currentMousePos.y < element->scaled.position.y+element->scaled.size.y) { + element->onMouseRightClick.Invoke(); + } + } + } + break; + } + + case WM_RBUTTONUP: { + window->mouseRightHeld = false; + window->onMouseRightRelease.Invoke(); + for(MouseElement* element : window->mouseElements) { + if(element) { + if(window->currentMousePos.x >= element->scaled.position.x && window->currentMousePos.x <= element->scaled.position.x+element->scaled.size.x && window->currentMousePos.y > element->scaled.position.y && window->currentMousePos.y < element->scaled.position.y+element->scaled.size.y) { + element->onMouseRightRelease.Invoke(); + } + } + } + break; + } + + default: return DefWindowProc(hwnd, msg, wParam, lParam); + } + return 0; } -void Window::ScaleElement(Transform& element, Transform& parent) { - element.scaled.width = MappedToAbsolute(element.anchor.width, parent.scaled.width); - element.scaled.height = MappedToAbsolute(element.anchor.height, parent.scaled.height); - element.scaled.x = MappedToAbsolute(element.anchor.x, parent.scaled.width) - MappedToAbsolute(element.anchor.offsetX, element.scaled.width) + parent.scaled.x; - element.scaled.y = MappedToAbsolute(element.anchor.y, parent.scaled.height) - MappedToAbsolute(element.anchor.offsetY, element.scaled.height) + parent.scaled.y; +#endif + + +Window::Window(std::uint32_t width, std::uint32_t height, const std::string_view title) : Window(width, height) { + SetTitle(title); } -void Window::ScaleMouse(MouseElement& element, Transform& parent) { - // element.scaled.width = MappedToAbsoluteBoundless(BoundToBoundless(element.anchor.width), parent.scaled.width); - // element.scaled.height = MappedToAbsoluteBoundless(BoundToBoundless(element.anchor.height), parent.scaled.height); - // element.scaled.x = MappedToAbsoluteBoundless(BoundToBoundless(element.anchor.x), parent.scaled.width) - MappedToAbsoluteBoundless(BoundToBoundless(element.anchor.offsetX), element.scaled.width) + BoundToBoundless(parent.scaled.x); - // element.scaled.y = MappedToAbsoluteBoundless(BoundToBoundless(element.anchor.y), parent.scaled.height) - MappedToAbsoluteBoundless(BoundToBoundless(element.anchor.offsetY), element.scaled.height) + BoundToBoundless(parent.scaled.y); - - std::uint32_t mappedParentWidth = AbsoluteToMappedBoundless(parent.scaled.width, width); - std::uint32_t mappedParentHeight = AbsoluteToMappedBoundless(parent.scaled.height, height); - std::uint32_t mappedParentX = AbsoluteToMappedBoundless(parent.scaled.x, width); - std::uint32_t mappedParentY = AbsoluteToMappedBoundless(parent.scaled.y, height); - element.mouseScaled.width = MappedToAbsoluteBoundless(BoundToBoundless(element.anchor.width), mappedParentWidth); - element.mouseScaled.height = MappedToAbsoluteBoundless(BoundToBoundless(element.anchor.height), mappedParentHeight); - element.mouseScaled.x = MappedToAbsoluteBoundless(BoundToBoundless(element.anchor.x), mappedParentWidth) - MappedToAbsoluteBoundless(BoundToBoundless(element.anchor.offsetX), element.mouseScaled.width) + mappedParentX; - element.mouseScaled.y = MappedToAbsoluteBoundless(BoundToBoundless(element.anchor.y), mappedParentHeight) - MappedToAbsoluteBoundless(BoundToBoundless(element.anchor.offsetY), element.mouseScaled.height) + mappedParentY; +#ifdef CRAFTER_GRAPHICS_RENDERER_SOFTWARE +Window::Window(std::uint32_t width, std::uint32_t height) : width(width), height(height) : Rendertarget(width, height) { +#else +Window::Window(std::uint32_t width, std::uint32_t height) : width(width), height(height) { +#endif + #ifdef CRAFTER_GRAPHICS_WINDOW_WAYLAND + Device::windows.push_back(this); + surface = wl_compositor_create_surface(Device::compositor); + xdgSurface = xdg_wm_base_get_xdg_surface(Device::xdgWmBase, surface); + xdgToplevel = xdg_surface_get_toplevel(xdgSurface); + + xdg_surface_add_listener(xdgSurface, &xdg_surface_listener, this); + xdg_toplevel_add_listener(xdgToplevel, &xdg_toplevel_listener, this); + wl_surface_commit(surface); + + wp_scale = wp_fractional_scale_manager_v1_get_fractional_scale(Device::fractionalScaleManager, surface); + wp_fractional_scale_v1_add_listener(wp_scale, &wp_fractional_scale_v1_listener, this); + + while (wl_display_dispatch(Device::display) != -1 && !configured) {} + + wl_surface_commit(surface); + + zxdg_toplevel_decoration_v1* decoration = zxdg_decoration_manager_v1_get_toplevel_decoration(Device::manager, xdgToplevel); + zxdg_toplevel_decoration_v1_set_mode(decoration, ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE); + + wpViewport = wp_viewporter_get_viewport(Device::wpViewporter, surface); + wp_viewport_set_destination(wpViewport, std::ceil(width/scale), std::ceil(height/scale)); + + wl_surface_commit(surface); + #ifdef CRAFTER_GRAPHICS_RENDERER_SOFTWARE + // Create a wl_buffer, attach it to the surface and commit the surface + int stride = width * 4; + int size = stride * height; + + // Allocate a shared memory file with the right size + int fd = create_shm_file(size); + if (fd < 0) { + throw std::runtime_error(std::format("creating a buffer file for {}B failed", size)); + } + + // Map the shared memory file + renderer.buffer = reinterpret_cast*>(mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0)); + renderer.sizeX = width; + renderer.sizeY = height; + if (renderer.buffer == MAP_FAILED) { + throw std::runtime_error("mmap failed"); + } + + wl_shm_pool *pool = wl_shm_create_pool(Device::shm, fd, size); + buffer = wl_shm_pool_create_buffer(pool, 0, width, height, stride, WL_SHM_FORMAT_ARGB8888); + wl_shm_pool_destroy(pool); + + close(fd); + + if (buffer == nullptr) { + throw std::runtime_error("wl_buffer creation failed"); + } + + wl_surface_attach(surface, buffer, 0, 0); + wl_surface_commit(surface); + #endif + #endif + + #ifdef CRAFTER_GRAPHICS_RENDERER_VULKAN + + #ifdef CRAFTER_GRAPHICS_WINDOW_WIN32 + // 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, + "", + 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; + Device::CheckVkResult(vkCreateWin32SurfaceKHR(Device::instance, &createInfo, NULL, &vulkanSurface)); + #endif + + #ifdef CRAFTER_GRAPHICS_WINDOW_WAYLAND + VkWaylandSurfaceCreateInfoKHR createInfo = {}; + createInfo.sType = VK_STRUCTURE_TYPE_WAYLAND_SURFACE_CREATE_INFO_KHR; + createInfo.display = Device::display; + createInfo.surface = surface; + Device::CheckVkResult(vkCreateWaylandSurfaceKHR(Device::instance, &createInfo, NULL, &vulkanSurface)); + #endif + + // Get list of supported surface formats + std::uint32_t formatCount; + Device::CheckVkResult(vkGetPhysicalDeviceSurfaceFormatsKHR(Device::physDevice, vulkanSurface, &formatCount, NULL)); + assert(formatCount > 0); + + std::vector surfaceFormats(formatCount); + Device::CheckVkResult(vkGetPhysicalDeviceSurfaceFormatsKHR(Device::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 = Device::commandPool; + cmdBufAllocateInfo.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; + cmdBufAllocateInfo.commandBufferCount = numFrames; + Device::CheckVkResult(vkAllocateCommandBuffers(Device::device, &cmdBufAllocateInfo, drawCmdBuffers)); + + VkSemaphoreCreateInfo semaphoreCreateInfo {}; + semaphoreCreateInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO; + Device::CheckVkResult(vkCreateSemaphore(Device::device, &semaphoreCreateInfo, nullptr, &semaphores.presentComplete)); + Device::CheckVkResult(vkCreateSemaphore(Device::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; + #endif } -void Window::ScaleMouse(MouseElement& element) { -// std::int32_t boundlessWidth = PixelToMappedBoundless(parent.scaled.width, width); -// std::int32_t boundlessHeight = PixelToMappedBoundless(parent.scaled.height, height); -// element.scaled.width = BoundToBoundless(MappedToPixel(element.anchor.width, width)); -// element.scaled.height = BoundToBoundless(MappedToPixel(element.anchor.height, height)); -// element.scaled.x = MappedToPixelBoundless(element.anchor.x, boundlessWidth) - MappedToPixelBoundless(element.anchor.offsetX, element.scaled.width) + PixelToMappedBoundless(parent.scaled.x, width); -// element.scaled.y = MappedToPixelBoundless(element.anchor.y, boundlessHeight) - MappedToPixelBoundless(element.anchor.offsetY, element.scaled.height) + PixelToMappedBoundless(parent.scaled.y, height); +void Window::SetTitle(const std::string_view title) { + #ifdef CRAFTER_GRAPHICS_WINDOW_WAYLAND + xdg_toplevel_set_title(xdgToplevel, title.data()); + #endif +} + +void Window::StartSync() { + #ifdef CRAFTER_GRAPHICS_WINDOW_WAYLAND + while (open && wl_display_dispatch(Device::display) != -1) { + + } + #endif + #ifdef CRAFTER_GRAHPICS_WINDOW_WIN32 + while(open) { + MSG msg; + while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + if(updating) { + Update(); + } + } + #endif +} + +void Window::StartUpdate() { + lastFrameBegin = std::chrono::high_resolution_clock::now(); + updating = true; + #ifdef CRAFTER_GRAPHICS_WINDOW_WAYLAND + cb = wl_surface_frame(surface); + wl_callback_add_listener(cb, &wl_callback_listener, this); + #endif +} + +void Window::StopUpdate() { + updating = false; +} + +void Window::Update() { + auto start = std::chrono::high_resolution_clock::now(); + #ifdef CRAFTER_TIMING + vblank = duration_cast(start - frameEnd); + #endif + + onUpdate.Invoke({start, start-lastFrameBegin}); + #ifdef CRAFTER_TIMING + totalUpdate = std::chrono::nanoseconds(0); + updateTimings.clear(); + for (const std::pair*, std::chrono::nanoseconds>& entry : onUpdate.listenerTimes) { + updateTimings.push_back(entry); + totalUpdate += entry.second; + } + #endif + + #ifdef CRAFTER_TIMING + auto renderStart = std::chrono::high_resolution_clock::now(); + renderTimings.clear(); + #endif + Render(); + #ifdef CRAFTER_TIMING + auto renderEnd = std::chrono::high_resolution_clock::now(); + totalRender = renderEnd - renderStart; + #endif + + #ifdef CRAFTER_TIMING + frameEnd = std::chrono::high_resolution_clock::now(); + + frameTimes.push_back(totalUpdate+totalRender); + + // Keep only the last 100 frame times + if (frameTimes.size() > 100) { + frameTimes.erase(frameTimes.begin()); + } + #endif + lastFrameBegin = start; +} + +void Window::Render() { + #ifdef CRAFTER_GRAPHICS_RENDERER_SOFTWARE + // elements.erase(std::remove(elements.begin(), elements.end(), static_cast(nullptr)), elements.end()); + // std::sort(elements.begin(), elements.end(), [](Transform* a, Transform* b){ return a->anchor.z < b->anchor.z; }); + renderer.Render(); + #endif + #ifdef CRAFTER_GRAPHICS_WINDOW_WAYLAND + wl_surface_attach(surface, buffer, 0, 0); + wl_surface_commit(surface); + wl_surface_damage(surface, 0, 0, 10000, 100000); + #endif + #ifdef CRAFTER_GRAPHICS_RENDERER_VULKAN + // Acquire the next image from the swap chain + Device::CheckVkResult(vkAcquireNextImageKHR(Device::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; + + Device::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); + + 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); + Device::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); + + Device::CheckVkResult(vkEndCommandBuffer(drawCmdBuffers[currentBuffer])); + + Device::CheckVkResult(vkQueueSubmit(Device::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(Device::queue, &presentInfo); + if(result == VK_SUBOPTIMAL_KHR) { + CreateSwapchain(); + } else { + Device::CheckVkResult(result); + } + #endif } #ifdef CRAFTER_TIMING @@ -86,7 +732,7 @@ void Window::LogTiming() { std::cout << std::format("\t{} {}", reinterpret_cast(entry.first), duration_cast(entry.second)) << std::endl; } std::cout << std::format("Render: {}", duration_cast(totalRender)) << std::endl; - for (const std::tuple& entry : renderTimings) { + for (const std::tuple& entry : renderer.renderTimings) { std::cout << std::format("\t{} {}x{} {}", reinterpret_cast(std::get<0>(entry)), std::get<1>(entry), std::get<2>(entry), duration_cast(std::get<3>(entry))) << std::endl; } std::cout << std::format("Total: {}", duration_cast(totalUpdate+totalRender)) << std::endl; @@ -117,40 +763,220 @@ void Window::LogTiming() { } #endif +#ifdef CRAFTER_GRAPHICS_RENDERER_VULKAN +void Window::CreateSwapchain() +{ + // Store the current swap chain handle so we can use it later on to ease up recreation + VkSwapchainKHR oldSwapchain = swapChain; -void Window::AddDirtyRect(ScaleData scale) { - ClipRect rect { - .left = std::max(scale.x, std::int32_t(0)), - .right = std::min(scale.x + scale.width, width), - .top = std::max(scale.y, std::int32_t(0)), - .bottom = std::min(scale.y + scale.height, height), - }; + // Get physical device surface properties and formats + VkSurfaceCapabilitiesKHR surfCaps; + Device::CheckVkResult(vkGetPhysicalDeviceSurfaceCapabilitiesKHR(Device::physDevice, vulkanSurface, &surfCaps)); - // if (rect.left >= rect.right || rect.top >= rect.bottom) { - // return; - // } + 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; + } - // for(ClipRect existing : dirtyRects) { - // //fully enclosed - // if(rect.left >= existing.left && rect.right <= existing.right && rect.top >= existing.top && rect.bottom <= existing.bottom) { - // return; - // } - // //horizontal line - // if(rect.top == existing.top && rect.bottom == existing.bottom) { - // existing.left = std::min(rect.left, existing.left); - // existing.right = std::max(rect.right, existing.right); - // return; - // } + // Select a present mode for the swapchain + uint32_t presentModeCount; + Device::CheckVkResult(vkGetPhysicalDeviceSurfacePresentModesKHR(Device::physDevice, vulkanSurface, &presentModeCount, NULL)); + assert(presentModeCount > 0); - // //vertical line - // if(rect.left == existing.left && rect.right == existing.right) { - // existing.top = std::min(rect.top, existing.top); - // existing.bottom = std::max(rect.bottom, existing.bottom); - // return; - // } - // } + std::vector presentModes(presentModeCount); + Device::CheckVkResult(vkGetPhysicalDeviceSurfacePresentModesKHR(Device::physDevice, vulkanSurface, &presentModeCount, presentModes.data())); - //no overlap - dirtyRects.push_back(rect); -} \ No newline at end of file + // 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; + + Device::CheckVkResult(vkCreateSwapchainKHR(Device::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(Device::device, imageViews[i], nullptr); + } + vkDestroySwapchainKHR(Device::device, oldSwapchain, nullptr); + } + uint32_t imageCount{ 0 }; + Device::CheckVkResult(vkGetSwapchainImagesKHR(Device::device, swapChain, &imageCount, nullptr)); + + // Get the swap chain images + Device::CheckVkResult(vkGetSwapchainImagesKHR(Device::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]; + Device::CheckVkResult(vkCreateImageView(Device::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; + } +} + +VkCommandBuffer Window::StartInit() { + VkCommandBufferBeginInfo cmdBufInfo {}; + cmdBufInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; + Device::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 Window::FinishInit() { + VkSubmitInfo submitInfo{}; + submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; + submitInfo.commandBufferCount = 1; + submitInfo.pCommandBuffers = &drawCmdBuffers[currentBuffer]; + Device::CheckVkResult(vkEndCommandBuffer(drawCmdBuffers[currentBuffer])); + Device::CheckVkResult(vkQueueSubmit(Device::queue, 1, &submitInfo, VK_NULL_HANDLE)); + Device::CheckVkResult(vkQueueWaitIdle(Device::queue)); +} + +#endif + +#ifdef CRAFTER_GRAPHICS_WINDOW_WAYLAND +void Window::wl_surface_frame_done(void* data, struct wl_callback *cb, uint32_t time) { + wl_callback_destroy(cb); + cb = nullptr; + Window* window = reinterpret_cast(data); + + if(window->updating) { + window->Update(); + cb = wl_surface_frame(window->surface); + wl_callback_add_listener(cb, &Window::wl_callback_listener, window); + } +} + + +void Window::xdg_toplevel_configure(void*, xdg_toplevel*, std::int32_t, std::int32_t, wl_array*){ + +} + +void Window::xdg_toplevel_handle_close(void* data, xdg_toplevel*) { + Window* window = reinterpret_cast(data); + window->onClose.Invoke(); + window->open = false; +} + +void Window::xdg_surface_handle_configure(void* data, xdg_surface* xdg_surface, std::uint32_t serial) { + Window* window = reinterpret_cast(data); + // The compositor configures our surface, acknowledge the configure event + xdg_surface_ack_configure(xdg_surface, serial); + + if (window->configured) { + // If this isn't the first configure event we've received, we already + // have a buffer attached, so no need to do anything. Commit the + // surface to apply the configure acknowledgement. + wl_surface_commit(window->surface); + } + + window->configured = true; +} + +void Window::xdg_surface_handle_preferred_scale(void* data, wp_fractional_scale_v1*, std::uint32_t scale) { + Window* window = reinterpret_cast(data); + window->scale = scale / 120.0f; +} + +#endif \ No newline at end of file diff --git a/implementations/Crafter.Graphics-Window_vulkan.cpp b/implementations/Crafter.Graphics-Window_vulkan.cpp deleted file mode 100644 index 270b048..0000000 --- a/implementations/Crafter.Graphics-Window_vulkan.cpp +++ /dev/null @@ -1,860 +0,0 @@ -/* -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 -#include -#include "../lib/xdg-shell-client-protocol.h" -#include "../lib/wayland-xdg-decoration-unstable-v1-client-protocol.h" -#include "../lib/fractional-scale-v1.h" -#include "../lib/viewporter.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "vulkan/vulkan.h" -#include "vulkan/vulkan_wayland.h" - -module Crafter.Graphics:Window_vulkan_impl; -import :Window; -import :MouseElement; -import std; -import :Types; -import :Shm; -import :VulkanDevice; -import :VulkanTransition; -import Crafter.Event; - -using namespace Crafter; - - -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])); - - 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; - } -} - -WindowVulkan::WindowVulkan(std::uint32_t width, std::uint32_t height) : Window(width, height) { - - display = wl_display_connect(NULL); - if (display == NULL) { - std::cerr << "failed to create display" << std::endl; - } - - wl_registry* registry = wl_display_get_registry(display); - wl_registry_add_listener(registry, ®istry_listener, this); - if (wl_display_roundtrip(display) == -1) { - exit(EXIT_FAILURE); - } - if (shm == NULL || compositor == NULL || xdgWmBase == NULL) { - std::cerr << "no wl_shm, wl_compositor or xdg_wm_base support" << std::endl; - exit(EXIT_FAILURE); - } - - surface = wl_compositor_create_surface(compositor); - xdgSurface = xdg_wm_base_get_xdg_surface(xdgWmBase, surface); - xdgToplevel = xdg_surface_get_toplevel(xdgSurface); - - xdg_surface_add_listener(xdgSurface, &xdg_surface_listener, this); - xdg_toplevel_add_listener(xdgToplevel, &xdg_toplevel_listener, this); - wl_surface_commit(surface); - - wp_scale = wp_fractional_scale_manager_v1_get_fractional_scale(fractionalScaleManager, surface); - wp_fractional_scale_v1_add_listener(wp_scale, &wp_fractional_scale_v1_listener, this); - - while (wl_display_dispatch(display) != -1 && !configured) {} - - wl_surface_commit(surface); - - zxdg_toplevel_decoration_v1* decoration = zxdg_decoration_manager_v1_get_toplevel_decoration(manager, xdgToplevel); - zxdg_toplevel_decoration_v1_set_mode(decoration, ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE); - - wpViewport = wp_viewporter_get_viewport(wpViewporter, surface); - wp_viewport_set_destination(wpViewport, std::ceil(width/scale), std::ceil(height/scale)); - - wl_surface_commit(surface); - - VkWaylandSurfaceCreateInfoKHR createInfo = {}; - createInfo.sType = VK_STRUCTURE_TYPE_WAYLAND_SURFACE_CREATE_INFO_KHR; - createInfo.display = display; - createInfo.surface = surface; - VulkanDevice::CheckVkResult(vkCreateWaylandSurfaceKHR(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, - }; - - 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(std::uint32_t width, std::uint32_t height, const std::string_view title) : WindowVulkan(width, height) { - xdg_toplevel_set_title(xdgToplevel, title.data()); -} - -WindowVulkan::~WindowVulkan() { - xdg_toplevel_destroy(xdgToplevel); - xdg_surface_destroy(xdgSurface); - wl_surface_destroy(surface); -} - -void WindowVulkan::StartSync() { - while (open && wl_display_dispatch(display) != -1) { - - } -} - -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)); -} - -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::QueueRender() { - if(cb == nullptr) { - cb = wl_surface_frame(surface); - wl_callback_add_listener(cb, &wl_callback_listener, this); - } -} - -void WindowVulkan::StartUpdate() { - lastFrameBegin = std::chrono::high_resolution_clock::now(); - cb = wl_surface_frame(surface); - wl_callback_add_listener(cb, &wl_callback_listener, this); - updating = true; -} - -void WindowVulkan::StopUpdate() { - updating = false; -} - -void WindowVulkan::SetTitle(const std::string_view title) { - xdg_toplevel_set_title(xdgToplevel, title.data()); -} - -void WindowVulkan::Resize(std::uint32_t width, std::uint32_t height) { - -} - -void WindowVulkan::xdg_wm_base_handle_ping(void* data, xdg_wm_base* xdg_wm_base, std::uint32_t serial) { - xdg_wm_base_pong(xdg_wm_base, serial); -} - -#ifdef CRAFTER_TIMING -std::chrono::time_point framEnd; -#endif - -void WindowVulkan::wl_surface_frame_done(void* data, struct wl_callback *cb, uint32_t time) -{ - auto start = std::chrono::high_resolution_clock::now(); - wl_callback_destroy(cb); - cb = nullptr; - WindowVulkan* window = reinterpret_cast(data); - #ifdef CRAFTER_TIMING - window->vblank = duration_cast(start - window->frameEnd); - #endif - - if(window->updating) { - window->mouseDelta = {std::int64_t(window->currentMousePos.x)-window->lastMousePos.x, std::int64_t(window->currentMousePos.y)-window->lastMousePos.y}; - cb = wl_surface_frame(window->surface); - wl_callback_add_listener(cb, &WindowVulkan::wl_callback_listener, window); - window->currentFrameTime = {start, start-window->lastFrameBegin}; - window->onUpdate.Invoke({start, start-window->lastFrameBegin}); - window->lastMousePos = window->currentMousePos; - #ifdef CRAFTER_TIMING - window->totalUpdate = std::chrono::nanoseconds(0); - window->updateTimings.clear(); - for (const std::pair*, std::chrono::nanoseconds>& entry : window->onUpdate.listenerTimes) { - window->updateTimings.push_back(entry); - window->totalUpdate += entry.second; - } - #endif - } - - #ifdef CRAFTER_TIMING - auto renderStart = std::chrono::high_resolution_clock::now(); - window->renderTimings.clear(); - #endif - window->Render(); - #ifdef CRAFTER_TIMING - auto renderEnd = std::chrono::high_resolution_clock::now(); - window->totalRender = renderEnd - renderStart; - #endif - - #ifdef CRAFTER_TIMING - window->frameEnd = std::chrono::high_resolution_clock::now(); - - window->frameTimes.push_back(window->totalUpdate+window->totalRender); - - // Keep only the last 100 frame times - if (window->frameTimes.size() > 100) { - window->frameTimes.erase(window->frameTimes.begin()); - } - #endif - window->lastFrameBegin = start; -} - -void WindowVulkan::pointer_handle_button(void* data, wl_pointer* pointer, std::uint32_t serial, std::uint32_t time, std::uint32_t button, std::uint32_t state) { - WindowVulkan* window = reinterpret_cast(data); - - if (button == BTN_LEFT) { - if(state == WL_POINTER_BUTTON_STATE_PRESSED) { - 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)}); - } - } - } - } else { - 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)}); - } - } - } - } - } else if(button == BTN_RIGHT){ - if(state == WL_POINTER_BUTTON_STATE_PRESSED) { - 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)}); - } - } - } - } else { - 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)}); - } - } - } - } - } - window->mouseElements.erase(std::remove(window->mouseElements.begin(), window->mouseElements.end(), static_cast(nullptr)), window->mouseElements.end()); - window->mouseElements.insert(window->mouseElements.end(), window->pendingMouseElements.begin(), window->pendingMouseElements.end()); - window->pendingMouseElements.clear(); -} - -void WindowVulkan::PointerListenerHandleMotion(void* data, wl_pointer* wl_pointer, uint time, wl_fixed_t surface_x, wl_fixed_t surface_y) { - WindowVulkan* window = reinterpret_cast(data); - MousePoint pos = {FractionalToMappedBoundless((wl_fixed_to_double(surface_x) * window->scale) / window->width), FractionalToMappedBoundless((wl_fixed_to_double(surface_y) * window->scale) / 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()); -} - -void WindowVulkan::PointerListenerHandleEnter(void* data, wl_pointer* wl_pointer, uint serial, wl_surface* surface, wl_fixed_t surface_x, wl_fixed_t surface_y) { - WindowVulkan* window = reinterpret_cast(data); - window->onMouseEnter.Invoke({window->lastMousePos, window->currentMousePos, window->mouseDelta}); -} - -void WindowVulkan::PointerListenerHandleLeave(void* data, wl_pointer*, std::uint32_t, wl_surface*) { - WindowVulkan* window = reinterpret_cast(data); - window->onMouseLeave.Invoke({window->lastMousePos, window->currentMousePos, window->mouseDelta}); -} - -void WindowVulkan::PointerListenerHandleAxis(void*, wl_pointer*, std::uint32_t, std::uint32_t, wl_fixed_t value) { - -} - - -void WindowVulkan::keyboard_keymap(void *data, wl_keyboard *keyboard, uint32_t format, int fd, uint32_t size) { - WindowVulkan* window = reinterpret_cast(data); - - if (format != WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1) { - close(fd); - fprintf(stderr, "Unsupported keymap format\n"); - return; - } - - void *map = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0); - if (map == MAP_FAILED) { - close(fd); - perror("mmap"); - return; - } - - window->xkb_context = xkb_context_new(XKB_CONTEXT_NO_FLAGS); - window->xkb_keymap = xkb_keymap_new_from_string(window->xkb_context, (const char *)map, XKB_KEYMAP_FORMAT_TEXT_V1,XKB_KEYMAP_COMPILE_NO_FLAGS); - munmap(map, size); - close(fd); - - window->xkb_state = xkb_state_new(window->xkb_keymap); -} - -void WindowVulkan::keyboard_enter(void *data, wl_keyboard *keyboard, uint32_t serial, wl_surface *surface, wl_array *keys) { - -} - -void WindowVulkan::keyboard_leave(void *data, wl_keyboard *keyboard, uint32_t serial, wl_surface *surface) { - -} - -CrafterKeys keysym_to_crafter_key(xkb_keysym_t sym) -{ - switch (sym) - { - // Alphabet - case XKB_KEY_a: return CrafterKeys::A; - case XKB_KEY_b: return CrafterKeys::B; - case XKB_KEY_c: return CrafterKeys::C; - case XKB_KEY_d: return CrafterKeys::D; - case XKB_KEY_e: return CrafterKeys::E; - case XKB_KEY_f: return CrafterKeys::F; - case XKB_KEY_g: return CrafterKeys::G; - case XKB_KEY_h: return CrafterKeys::H; - case XKB_KEY_i: return CrafterKeys::I; - case XKB_KEY_j: return CrafterKeys::J; - case XKB_KEY_k: return CrafterKeys::K; - case XKB_KEY_l: return CrafterKeys::L; - case XKB_KEY_m: return CrafterKeys::M; - case XKB_KEY_n: return CrafterKeys::N; - case XKB_KEY_o: return CrafterKeys::O; - case XKB_KEY_p: return CrafterKeys::P; - case XKB_KEY_q: return CrafterKeys::Q; - case XKB_KEY_r: return CrafterKeys::R; - case XKB_KEY_s: return CrafterKeys::S; - case XKB_KEY_t: return CrafterKeys::T; - case XKB_KEY_u: return CrafterKeys::U; - case XKB_KEY_v: return CrafterKeys::V; - case XKB_KEY_w: return CrafterKeys::W; - case XKB_KEY_x: return CrafterKeys::X; - case XKB_KEY_y: return CrafterKeys::Y; - case XKB_KEY_z: return CrafterKeys::Z; - - // Numbers - case XKB_KEY_0: return CrafterKeys::_0; - case XKB_KEY_1: return CrafterKeys::_1; - case XKB_KEY_2: return CrafterKeys::_2; - case XKB_KEY_3: return CrafterKeys::_3; - case XKB_KEY_4: return CrafterKeys::_4; - case XKB_KEY_5: return CrafterKeys::_5; - case XKB_KEY_6: return CrafterKeys::_6; - case XKB_KEY_7: return CrafterKeys::_7; - case XKB_KEY_8: return CrafterKeys::_8; - case XKB_KEY_9: return CrafterKeys::_9; - - // Function keys - case XKB_KEY_F1: return CrafterKeys::F1; - case XKB_KEY_F2: return CrafterKeys::F2; - case XKB_KEY_F3: return CrafterKeys::F3; - case XKB_KEY_F4: return CrafterKeys::F4; - case XKB_KEY_F5: return CrafterKeys::F5; - case XKB_KEY_F6: return CrafterKeys::F6; - case XKB_KEY_F7: return CrafterKeys::F7; - case XKB_KEY_F8: return CrafterKeys::F8; - case XKB_KEY_F9: return CrafterKeys::F9; - case XKB_KEY_F10: return CrafterKeys::F10; - case XKB_KEY_F11: return CrafterKeys::F11; - case XKB_KEY_F12: return CrafterKeys::F12; - - // Control keys - case XKB_KEY_Escape: return CrafterKeys::Escape; - case XKB_KEY_Tab: return CrafterKeys::Tab; - case XKB_KEY_Return: return CrafterKeys::Enter; - case XKB_KEY_space: return CrafterKeys::Space; - case XKB_KEY_BackSpace: return CrafterKeys::Backspace; - case XKB_KEY_Delete: return CrafterKeys::Delete; - case XKB_KEY_Insert: return CrafterKeys::Insert; - case XKB_KEY_Home: return CrafterKeys::Home; - case XKB_KEY_End: return CrafterKeys::End; - case XKB_KEY_Page_Up: return CrafterKeys::PageUp; - case XKB_KEY_Page_Down: return CrafterKeys::PageDown; - case XKB_KEY_Caps_Lock: return CrafterKeys::CapsLock; - case XKB_KEY_Num_Lock: return CrafterKeys::NumLock; - case XKB_KEY_Scroll_Lock:return CrafterKeys::ScrollLock; - - // Modifiers - case XKB_KEY_Shift_L: return CrafterKeys::LeftShift; - case XKB_KEY_Shift_R: return CrafterKeys::RightShift; - case XKB_KEY_Control_L: return CrafterKeys::LeftCtrl; - case XKB_KEY_Control_R: return CrafterKeys::RightCtrl; - case XKB_KEY_Alt_L: return CrafterKeys::LeftAlt; - case XKB_KEY_Alt_R: return CrafterKeys::RightAlt; - case XKB_KEY_Super_L: return CrafterKeys::LeftSuper; - case XKB_KEY_Super_R: return CrafterKeys::RightSuper; - - // Arrows - case XKB_KEY_Up: return CrafterKeys::Up; - case XKB_KEY_Down: return CrafterKeys::Down; - case XKB_KEY_Left: return CrafterKeys::Left; - case XKB_KEY_Right: return CrafterKeys::Right; - - // Keypad - case XKB_KEY_KP_0: return CrafterKeys::keypad_0; - case XKB_KEY_KP_1: return CrafterKeys::keypad_1; - case XKB_KEY_KP_2: return CrafterKeys::keypad_2; - case XKB_KEY_KP_3: return CrafterKeys::keypad_3; - case XKB_KEY_KP_4: return CrafterKeys::keypad_4; - case XKB_KEY_KP_5: return CrafterKeys::keypad_5; - case XKB_KEY_KP_6: return CrafterKeys::keypad_6; - case XKB_KEY_KP_7: return CrafterKeys::keypad_7; - case XKB_KEY_KP_8: return CrafterKeys::keypad_8; - case XKB_KEY_KP_9: return CrafterKeys::keypad_9; - case XKB_KEY_KP_Enter: return CrafterKeys::keypad_enter; - case XKB_KEY_KP_Add: return CrafterKeys::keypad_plus; - case XKB_KEY_KP_Subtract: return CrafterKeys::keypad_minus; - case XKB_KEY_KP_Multiply: return CrafterKeys::keypad_multiply; - case XKB_KEY_KP_Divide: return CrafterKeys::keypad_divide; - case XKB_KEY_KP_Decimal: return CrafterKeys::keypad_decimal; - - // Punctuation - case XKB_KEY_grave: return CrafterKeys::grave; - case XKB_KEY_minus: return CrafterKeys::minus; - case XKB_KEY_equal: return CrafterKeys::equal; - case XKB_KEY_bracketleft: return CrafterKeys::bracket_left; - case XKB_KEY_bracketright:return CrafterKeys::bracket_right; - case XKB_KEY_backslash: return CrafterKeys::backslash; - case XKB_KEY_semicolon: return CrafterKeys::semicolon; - case XKB_KEY_apostrophe: return CrafterKeys::quote; - case XKB_KEY_comma: return CrafterKeys::comma; - case XKB_KEY_period: return CrafterKeys::period; - case XKB_KEY_slash: return CrafterKeys::slash; - - default: - return CrafterKeys::CrafterKeysMax; - } -} - -void WindowVulkan::keyboard_key(void *data, wl_keyboard *keyboard, uint32_t serial, uint32_t time, uint32_t key, uint32_t state) { - WindowVulkan* window = reinterpret_cast(data); - - if (!window->xkb_state) { - return; - } - - xkb_keycode_t keycode = key + 8; - xkb_keysym_t keysym = xkb_state_key_get_one_sym(window->xkb_state, keycode); - CrafterKeys crafterKey = keysym_to_crafter_key(keysym); - - if(state == WL_KEYBOARD_KEY_STATE_PRESSED) { - 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); - } - } else{ - window->heldkeys[static_cast(crafterKey)] = false; - window->onKeyUp[static_cast(crafterKey)].Invoke(); - window->onAnyKeyUp.Invoke(crafterKey); - } -} - -void WindowVulkan::keyboard_modifiers(void *data, wl_keyboard *keyboard, uint32_t serial, uint32_t mods_depressed, uint32_t mods_latched, uint32_t mods_locked, uint32_t group) { - -} - -void WindowVulkan::keyboard_repeat_info(void *data, wl_keyboard *keyboard, int32_t rate, int32_t delay) { - -} - -void WindowVulkan::seat_handle_capabilities(void* data, wl_seat* seat, uint32_t capabilities) { - WindowVulkan* window = reinterpret_cast(data); - window->seat = seat; - if (capabilities & WL_SEAT_CAPABILITY_POINTER) { - wl_pointer* pointer = wl_seat_get_pointer(seat); - wl_pointer_add_listener(pointer, &pointer_listener, window); - } - if (capabilities & WL_SEAT_CAPABILITY_KEYBOARD) { - wl_keyboard* keyboard = wl_seat_get_keyboard(seat); - wl_keyboard_add_listener(keyboard, &keyboard_listener, window); - } -} - -void WindowVulkan::handle_global(void *data, wl_registry *registry, std::uint32_t name, const char *interface, std::uint32_t version) { - WindowVulkan* window = reinterpret_cast(data); - if (strcmp(interface, wl_shm_interface.name) == 0) { - window->shm = reinterpret_cast(wl_registry_bind(registry, name, &wl_shm_interface, 1)); - } else if (strcmp(interface, wl_seat_interface.name) == 0) { - wl_seat* seat = reinterpret_cast(wl_registry_bind(registry, name, &wl_seat_interface, 1)); - wl_seat_add_listener(seat, &seat_listener, window); - } else if (compositor == NULL && strcmp(interface, wl_compositor_interface.name) == 0) { - compositor = reinterpret_cast(wl_registry_bind(registry, name, &wl_compositor_interface, 3)); - } else if (strcmp(interface, xdg_wm_base_interface.name) == 0) { - window->xdgWmBase = reinterpret_cast(wl_registry_bind(registry, name, &xdg_wm_base_interface, 1)); - xdg_wm_base_add_listener(window->xdgWmBase, &xdgWmBaseListener, NULL); - } else if (strcmp(interface, zxdg_decoration_manager_v1_interface.name) == 0) { - window->manager = reinterpret_cast(wl_registry_bind(registry, name, &zxdg_decoration_manager_v1_interface, 1)); - } else if (strcmp(interface, wp_viewporter_interface.name) == 0) { - window->wpViewporter = reinterpret_cast(wl_registry_bind(registry, name, &wp_viewporter_interface, 1)); - } else if (strcmp(interface, wp_fractional_scale_manager_v1_interface.name) == 0) { - window->fractionalScaleManager = reinterpret_cast(wl_registry_bind(registry, name, &wp_fractional_scale_manager_v1_interface, 1)); - } -} - -void WindowVulkan::handle_global_remove(void* data, wl_registry* registry, uint32_t name) { - - -} - -void WindowVulkan::xdg_toplevel_configure(void*, xdg_toplevel*, std::int32_t, std::int32_t, wl_array*){ - -} - -void WindowVulkan::xdg_toplevel_handle_close(void* data, xdg_toplevel*) { - WindowVulkan* window = reinterpret_cast(data); - window->onClose.Invoke(); - window->open = false; -} - -void WindowVulkan::xdg_surface_handle_configure(void* data, xdg_surface* xdg_surface, std::uint32_t serial) { - WindowVulkan* window = reinterpret_cast(data); - // The compositor configures our surface, acknowledge the configure event - xdg_surface_ack_configure(xdg_surface, serial); - - if (window->configured) { - // If this isn't the first configure event we've received, we already - // have a buffer attached, so no need to do anything. Commit the - // surface to apply the configure acknowledgement. - wl_surface_commit(window->surface); - } - - window->configured = true; -} - -void WindowVulkan::xdg_surface_handle_preferred_scale(void* data, wp_fractional_scale_v1*, std::uint32_t scale) { - WindowVulkan* window = reinterpret_cast(data); - - window->scale = scale / 120.0f; -} \ No newline at end of file diff --git a/implementations/Crafter.Graphics-Window_vulkan_windows.cpp b/implementations/Crafter.Graphics-Window_vulkan_windows.cpp deleted file mode 100644 index 581c69c..0000000 --- a/implementations/Crafter.Graphics-Window_vulkan_windows.cpp +++ /dev/null @@ -1,682 +0,0 @@ -/* -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)); -} diff --git a/implementations/Crafter.Graphics-Window_wayland.cpp b/implementations/Crafter.Graphics-Window_wayland.cpp deleted file mode 100644 index 63fc117..0000000 --- a/implementations/Crafter.Graphics-Window_wayland.cpp +++ /dev/null @@ -1,829 +0,0 @@ -/* -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 -#include "../lib/xdg-shell-client-protocol.h" -#include "../lib/wayland-xdg-decoration-unstable-v1-client-protocol.h" -#include "../lib/fractional-scale-v1.h" -#include "../lib/viewporter.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -module Crafter.Graphics:Window_wayland_impl; -import :Window; -import :RenderingElement; -import :MouseElement; -import std; -import :Types; -import :Shm; -import Crafter.Event; - -using namespace Crafter; - -WindowFramebuffer::WindowFramebuffer(std::uint32_t width, std::uint32_t height) : Window(width, height) { - -} - -WindowWayland::WindowWayland(std::uint32_t width, std::uint32_t height) : WindowFramebuffer(width, height) { - display = wl_display_connect(NULL); - if (display == NULL) { - std::cerr << "failed to create display" << std::endl; - } - - wl_registry* registry = wl_display_get_registry(display); - wl_registry_add_listener(registry, ®istry_listener, this); - if (wl_display_roundtrip(display) == -1) { - exit(EXIT_FAILURE); - } - if (shm == NULL || compositor == NULL || xdgWmBase == NULL) { - std::cerr << "no wl_shm, wl_compositor or xdg_wm_base support" << std::endl; - exit(EXIT_FAILURE); - } - - surface = wl_compositor_create_surface(compositor); - xdgSurface = xdg_wm_base_get_xdg_surface(xdgWmBase, surface); - xdgToplevel = xdg_surface_get_toplevel(xdgSurface); - - xdg_surface_add_listener(xdgSurface, &xdg_surface_listener, this); - xdg_toplevel_add_listener(xdgToplevel, &xdg_toplevel_listener, this); - wl_surface_commit(surface); - - wp_scale = wp_fractional_scale_manager_v1_get_fractional_scale(fractionalScaleManager, surface); - wp_fractional_scale_v1_add_listener(wp_scale, &wp_fractional_scale_v1_listener, this); - - while (wl_display_dispatch(display) != -1 && !configured) {} - - wl_surface_commit(surface); - - zxdg_toplevel_decoration_v1* decoration = zxdg_decoration_manager_v1_get_toplevel_decoration(manager, xdgToplevel); - zxdg_toplevel_decoration_v1_set_mode(decoration, ZXDG_TOPLEVEL_DECORATION_V1_MODE_SERVER_SIDE); - - wpViewport = wp_viewporter_get_viewport(wpViewporter, surface); - wp_viewport_set_destination(wpViewport, std::ceil(width/scale), std::ceil(height/scale)); - - wl_surface_commit(surface); - - - // Create a wl_buffer, attach it to the surface and commit the surface - int stride = width * 4; - int size = stride * height; - - // Allocate a shared memory file with the right size - int fd = create_shm_file(size); - if (fd < 0) { - fprintf(stderr, "creating a buffer file for %d B failed: %m\n", size); - } - - // Map the shared memory file - framebuffer = reinterpret_cast(mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0)); - if (framebuffer == MAP_FAILED) { - fprintf(stderr, "mmap failed: %m\n"); - close(fd); - } - - // Create a wl_buffer from our shared memory file descriptor - wl_shm_pool *pool = wl_shm_create_pool(shm, fd, size); - buffer = wl_shm_pool_create_buffer(pool, 0, width, height, stride, WL_SHM_FORMAT_ARGB8888); - wl_shm_pool_destroy(pool); - - // Now that we've mapped the file and created the wl_buffer, we no longer - // need to keep file descriptor opened - close(fd); - - if (buffer == NULL) { - exit(EXIT_FAILURE); - } - - wl_surface_attach(surface, buffer, 0, 0); - wl_surface_commit(surface); -} - -WindowWayland::WindowWayland(std::uint32_t width, std::uint32_t height, const std::string_view title) : WindowWayland(width, height) { - xdg_toplevel_set_title(xdgToplevel, title.data()); -} - -WindowWayland::~WindowWayland() { - xdg_toplevel_destroy(xdgToplevel); - xdg_surface_destroy(xdgSurface); - wl_surface_destroy(surface); - wl_buffer_destroy(buffer); -} - -void WindowWayland::StartSync() { - while (open && wl_display_dispatch(display) != -1) { - - } -} - -// Optimized pixel blending function using SIMD-like operations -inline void blend_pixel_optimized(Pixel_BU8_GU8_RU8_AU8& dst, const Pixel_BU8_GU8_RU8_AU8& src) { - if(src.a == 0) { - return; - } - - float srcA = src.a / 255.0f; - float dstA = dst.a / 255.0f; - - float outA = srcA + dstA * (1.0f - srcA); - if (outA > 0.0f) { - dst = { - static_cast((src.b * srcA + dst.b * dstA * (1.0f - srcA)) / outA), - static_cast((src.g * srcA + dst.g * dstA * (1.0f - srcA)) / outA), - static_cast((src.r * srcA + dst.r * dstA * (1.0f - srcA)) / outA), - static_cast(outA * 255) - }; - } -} - -void WindowWayland::RenderElement(Transform* transform) { - RenderingElementBase* element = dynamic_cast(transform); - if(element) { - #ifdef CRAFTER_TIMING - auto start = std::chrono::high_resolution_clock::now(); - #endif - - if(element->scaled.width < 1 || element->scaled.height < 1) { - return; - } - - for(ClipRect dirty : dirtyRects) { - dirty.left = std::max(element->scaled.x, dirty.left); - dirty.top = std::max(element->scaled.y, dirty.top); - dirty.right = std::min(element->scaled.x+element->scaled.width, dirty.right); - dirty.bottom = std::min(element->scaled.y+element->scaled.height, dirty.bottom); - - const Pixel_BU8_GU8_RU8_AU8* src_buffer = element->buffer.data(); - std::int32_t src_width = element->scaled.width; - std::int32_t src_height = element->scaled.height; - - switch (element->opaque) { - case OpaqueType::FullyOpaque: - // For fully opaque, just copy pixels directly - for (std::int32_t y = dirty.top; y < dirty.bottom; y++) { - std::int32_t src_y = y - element->scaled.y; - - for (std::int32_t x = dirty.left; x < dirty.right; x++) { - std::int32_t src_x = x - element->scaled.x; - - framebuffer[y * width + x] = src_buffer[src_y * src_width + src_x]; - } - } - break; - - case OpaqueType::SemiOpaque: - // For semi-opaque, we can avoid blending when alpha is 0 or 255 - for (std::int32_t y = dirty.top; y < dirty.bottom; y++) { - std::int32_t src_y = y - element->scaled.y; - - for (std::int32_t x = dirty.left; x < dirty.right; x++) { - std::int32_t src_x = x - element->scaled.x; - Pixel_BU8_GU8_RU8_AU8 src_pixel = src_buffer[src_y * src_width + src_x]; - - if (src_pixel.a == 0) { - continue; - } - framebuffer[y * width + x] = src_pixel; - } - } - break; - - case OpaqueType::Transparent: - // For transparent, always perform blending - for (std::int32_t y = dirty.top; y < dirty.bottom; y++) { - std::int32_t src_y = y - element->scaled.y; - - for (std::int32_t x = dirty.left; x < dirty.right; x++) { - std::int32_t src_x = x - element->scaled.x; - blend_pixel_optimized(framebuffer[y * width + x], src_buffer[src_y * src_width + src_x]); - } - } - break; - } - } - - #ifdef CRAFTER_TIMING - auto end = std::chrono::high_resolution_clock::now(); - renderTimings.push_back({element, element->scaled.width, element->scaled.height, end-start}); - #endif - } - - std::sort(transform->children.begin(), transform->children.end(), [](Transform* a, Transform* b){ return a->anchor.z < b->anchor.z; }); - for(Transform* child : transform->children) { - this->RenderElement(child); - } -} - -void WindowWayland::Render() { - elements.erase(std::remove(elements.begin(), elements.end(), static_cast(nullptr)), elements.end()); - std::sort(elements.begin(), elements.end(), [](Transform* a, Transform* b){ return a->anchor.z < b->anchor.z; }); - - //std::vector newClip; - - // for (std::uint32_t i = 0; i < dirtyRects.size(); i++) { - // ClipRect rect = dirtyRects[i]; - // for (std::uint32_t i2 = i + 1; i2 < dirtyRects.size(); i2++) { - // ClipRect existing = dirtyRects[i2]; - // if(rect.bottom >= existing.top && rect.top <= existing.top) { - // newClip.push_back({ - // .left = rect.left, - // .right = rect.right, - // .top = rect.top, - // .bottom = existing.top, - // }); - // //-| shape - // if(rect.right > existing.right) { - // newClip.push_back({ - // .left = existing.right, - // .right = rect.right, - // .top = existing.top, - // .bottom = existing.bottom, - // }); - // } - // //|- shape - // if(rect.left < existing.left) { - // newClip.push_back({ - // .left = rect.left, - // .right = existing.left, - // .top = existing.top, - // .bottom = existing.bottom, - // }); - // } - // //-| or |- shape where rect extends further down - // if(rect.bottom > existing.bottom) { - // newClip.push_back({ - // .left = rect.left, - // .right = rect.right, - // .top = existing.bottom, - // .bottom = rect.bottom, - // }); - // } - // goto inner; - // } - // if (rect.left <= existing.right && rect.right >= existing.left) { - // newClip.push_back({ - // .left = rect.left, - // .right = existing.left, - // .top = rect.top, - // .bottom = rect.bottom, - // }); - // if (rect.right > existing.right) { - // newClip.push_back({ - // .left = existing.right, - // .right = rect.right, - // .top = rect.top, - // .bottom = rect.bottom, - // }); - // } - // goto inner; - // } - // } - // newClip.push_back(rect); - // inner:; - // } - - //dirtyRects = std::move(newClip); - - // std::memset(framebuffer, 0, width*height*4); - - // std::cout << dirtyRects.size() << std::endl; - // // Color palette - // static const std::vector colors = { - // {255, 0, 0, 255}, // red - // { 0, 255, 0, 255}, // green - // { 0, 0, 255, 255}, // blue - // {255, 255, 0, 255}, // yellow - // {255, 0, 255, 255}, // magenta - // { 0, 255, 255, 255}, // cyan - // }; - - // std::size_t rectIndex = 0; - - // for (const ClipRect& rect : dirtyRects) { - // const Pixel_BU8_GU8_RU8_AU8& color = colors[rectIndex % colors.size()]; - - // std::cout << std::format( - // "ClipRect {}: [{}, {}, {}, {}] Color = RGBA({}, {}, {}, {})", - // rectIndex, - // rect.left, rect.top, rect.right, rect.bottom, - // color.r, color.g, color.b, color.a - // ) << std::endl; - - // for (std::int32_t y = rect.top; y < rect.bottom; ++y) { - // for (std::int32_t x = rect.left; x < rect.right; ++x) { - // framebuffer[y * width + x] = color; - // } - // } - - // ++rectIndex; - // } - - if (!dirtyRects.empty()) { - for (ClipRect rect : dirtyRects) { - for (std::int32_t y = rect.top; y < rect.bottom; y++) { - for (std::int32_t x = rect.left; x < rect.right; x++) { - framebuffer[y * width + x] = {0, 0, 0, 0}; - } - } - } - - for(Transform* child : elements) { - RenderElement(child); - } - - for (ClipRect rect : dirtyRects) { - wl_surface_damage(surface, rect.left, rect.top, rect.right-rect.left, rect.bottom-rect.top); - } - dirtyRects.clear(); - } - - wl_surface_attach(surface, buffer, 0, 0); - wl_surface_commit(surface); - wl_surface_damage(surface, 0, 0, 10000, 100000); -} - -void WindowWayland::QueueRender() { - if(cb == nullptr) { - cb = wl_surface_frame(surface); - wl_callback_add_listener(cb, &wl_callback_listener, this); - } -} - -void WindowWayland::StartUpdate() { - lastFrameBegin = std::chrono::high_resolution_clock::now(); - cb = wl_surface_frame(surface); - wl_callback_add_listener(cb, &wl_callback_listener, this); - updating = true; -} - -void WindowWayland::StopUpdate() { - updating = false; -} - -void WindowWayland::SetTitle(const std::string_view title) { - xdg_toplevel_set_title(xdgToplevel, title.data()); -} - -void WindowWayland::Resize(std::uint32_t width, std::uint32_t height) { - -} - -void WindowWayland::Write(Pixel_BU8_GU8_RU8_AU8* pixels) { - std::memcpy(framebuffer, pixels, width*height*sizeof(Pixel_BU8_GU8_RU8_AU8)); -} - -void WindowWayland::Write(std::uint32_t x, std::uint32_t y, Pixel_BU8_GU8_RU8_AU8 pixel) { - framebuffer[y * width + x] = pixel; -} - -Pixel_BU8_GU8_RU8_AU8 WindowWayland::Read(std::uint32_t x, std::uint32_t y) const{ - return framebuffer[y * width + x]; -} - -const Pixel_BU8_GU8_RU8_AU8* WindowWayland::Read() const{ - return framebuffer; -} - -Pixel_BU8_GU8_RU8_AU8* WindowWayland::Get() { - return framebuffer; -} - -void WindowWayland::Store() { - -} - -void WindowWayland::xdg_wm_base_handle_ping(void* data, xdg_wm_base* xdg_wm_base, std::uint32_t serial) { - xdg_wm_base_pong(xdg_wm_base, serial); -} - -#ifdef CRAFTER_TIMING -std::chrono::time_point framEnd; -#endif - -void WindowWayland::wl_surface_frame_done(void* data, struct wl_callback *cb, uint32_t time) -{ - auto start = std::chrono::high_resolution_clock::now(); - wl_callback_destroy(cb); - cb = nullptr; - WindowWayland* window = reinterpret_cast(data); - #ifdef CRAFTER_TIMING - window->vblank = duration_cast(start - window->frameEnd); - #endif - - if(window->updating) { - cb = wl_surface_frame(window->surface); - wl_callback_add_listener(cb, &WindowWayland::wl_callback_listener, window); - window->onUpdate.Invoke({start, start-window->lastFrameBegin}); - #ifdef CRAFTER_TIMING - window->totalUpdate = std::chrono::nanoseconds(0); - window->updateTimings.clear(); - for (const std::pair*, std::chrono::nanoseconds>& entry : window->onUpdate.listenerTimes) { - window->updateTimings.push_back(entry); - window->totalUpdate += entry.second; - } - #endif - } - - #ifdef CRAFTER_TIMING - auto renderStart = std::chrono::high_resolution_clock::now(); - window->renderTimings.clear(); - #endif - window->Render(); - #ifdef CRAFTER_TIMING - auto renderEnd = std::chrono::high_resolution_clock::now(); - window->totalRender = renderEnd - renderStart; - #endif - - #ifdef CRAFTER_TIMING - window->frameEnd = std::chrono::high_resolution_clock::now(); - - window->frameTimes.push_back(window->totalUpdate+window->totalRender); - - // Keep only the last 100 frame times - if (window->frameTimes.size() > 100) { - window->frameTimes.erase(window->frameTimes.begin()); - } - #endif - window->lastFrameBegin = start; -} - -void WindowWayland::pointer_handle_button(void* data, wl_pointer* pointer, std::uint32_t serial, std::uint32_t time, std::uint32_t button, std::uint32_t state) { - WindowWayland* window = reinterpret_cast(data); - - if (button == BTN_LEFT) { - if(state == WL_POINTER_BUTTON_STATE_PRESSED) { - 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)}); - } - } - } - } else { - 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)}); - } - } - } - } - } else if(button == BTN_RIGHT){ - if(state == WL_POINTER_BUTTON_STATE_PRESSED) { - 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)}); - } - } - } - } else { - window->mouseRightHeld = true; - 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)}); - } - } - } - } - } - window->mouseElements.erase(std::remove(window->mouseElements.begin(), window->mouseElements.end(), static_cast(nullptr)), window->mouseElements.end()); - window->mouseElements.insert(window->mouseElements.end(), window->pendingMouseElements.begin(), window->pendingMouseElements.end()); - window->pendingMouseElements.clear(); -} - -void WindowWayland::PointerListenerHandleMotion(void* data, wl_pointer* wl_pointer, uint time, wl_fixed_t surface_x, wl_fixed_t surface_y) { - WindowWayland* window = reinterpret_cast(data); - MousePoint pos = {FractionalToMappedBoundless((wl_fixed_to_double(surface_x) * window->scale) / window->width), FractionalToMappedBoundless((wl_fixed_to_double(surface_y) * window->scale) / window->height)}; - window->lastMousePos = window->currentMousePos; - window->currentMousePos = pos; - window->mouseDelta = {window->currentMousePos.x-window->lastMousePos.x, window->currentMousePos.y-window->lastMousePos.y}; - 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()); -} - -void WindowWayland::PointerListenerHandleEnter(void* data, wl_pointer* wl_pointer, uint serial, wl_surface* surface, wl_fixed_t surface_x, wl_fixed_t surface_y) { - WindowWayland* window = reinterpret_cast(data); - window->onMouseEnter.Invoke({window->lastMousePos, window->currentMousePos, window->mouseDelta}); -} - -void WindowWayland::PointerListenerHandleLeave(void* data, wl_pointer*, std::uint32_t, wl_surface*) { - WindowWayland* window = reinterpret_cast(data); - window->onMouseLeave.Invoke({window->lastMousePos, window->currentMousePos, window->mouseDelta}); -} - -void WindowWayland::PointerListenerHandleAxis(void*, wl_pointer*, std::uint32_t, std::uint32_t, wl_fixed_t value) { - -} - - -void WindowWayland::keyboard_keymap(void *data, wl_keyboard *keyboard, uint32_t format, int fd, uint32_t size) { - WindowWayland* window = reinterpret_cast(data); - - if (format != WL_KEYBOARD_KEYMAP_FORMAT_XKB_V1) { - close(fd); - fprintf(stderr, "Unsupported keymap format\n"); - return; - } - - void *map = mmap(NULL, size, PROT_READ, MAP_SHARED, fd, 0); - if (map == MAP_FAILED) { - close(fd); - perror("mmap"); - return; - } - - window->xkb_context = xkb_context_new(XKB_CONTEXT_NO_FLAGS); - window->xkb_keymap = xkb_keymap_new_from_string(window->xkb_context, (const char *)map, XKB_KEYMAP_FORMAT_TEXT_V1,XKB_KEYMAP_COMPILE_NO_FLAGS); - munmap(map, size); - close(fd); - - window->xkb_state = xkb_state_new(window->xkb_keymap); -} - -void WindowWayland::keyboard_enter(void *data, wl_keyboard *keyboard, uint32_t serial, wl_surface *surface, wl_array *keys) { - -} - -void WindowWayland::keyboard_leave(void *data, wl_keyboard *keyboard, uint32_t serial, wl_surface *surface) { - -} - -CrafterKeys keysym_to_crafter_key(xkb_keysym_t sym) -{ - switch (sym) - { - // Alphabet - case XKB_KEY_a: return CrafterKeys::A; - case XKB_KEY_b: return CrafterKeys::B; - case XKB_KEY_c: return CrafterKeys::C; - case XKB_KEY_d: return CrafterKeys::D; - case XKB_KEY_e: return CrafterKeys::E; - case XKB_KEY_f: return CrafterKeys::F; - case XKB_KEY_g: return CrafterKeys::G; - case XKB_KEY_h: return CrafterKeys::H; - case XKB_KEY_i: return CrafterKeys::I; - case XKB_KEY_j: return CrafterKeys::J; - case XKB_KEY_k: return CrafterKeys::K; - case XKB_KEY_l: return CrafterKeys::L; - case XKB_KEY_m: return CrafterKeys::M; - case XKB_KEY_n: return CrafterKeys::N; - case XKB_KEY_o: return CrafterKeys::O; - case XKB_KEY_p: return CrafterKeys::P; - case XKB_KEY_q: return CrafterKeys::Q; - case XKB_KEY_r: return CrafterKeys::R; - case XKB_KEY_s: return CrafterKeys::S; - case XKB_KEY_t: return CrafterKeys::T; - case XKB_KEY_u: return CrafterKeys::U; - case XKB_KEY_v: return CrafterKeys::V; - case XKB_KEY_w: return CrafterKeys::W; - case XKB_KEY_x: return CrafterKeys::X; - case XKB_KEY_y: return CrafterKeys::Y; - case XKB_KEY_z: return CrafterKeys::Z; - - // Numbers - case XKB_KEY_0: return CrafterKeys::_0; - case XKB_KEY_1: return CrafterKeys::_1; - case XKB_KEY_2: return CrafterKeys::_2; - case XKB_KEY_3: return CrafterKeys::_3; - case XKB_KEY_4: return CrafterKeys::_4; - case XKB_KEY_5: return CrafterKeys::_5; - case XKB_KEY_6: return CrafterKeys::_6; - case XKB_KEY_7: return CrafterKeys::_7; - case XKB_KEY_8: return CrafterKeys::_8; - case XKB_KEY_9: return CrafterKeys::_9; - - // Function keys - case XKB_KEY_F1: return CrafterKeys::F1; - case XKB_KEY_F2: return CrafterKeys::F2; - case XKB_KEY_F3: return CrafterKeys::F3; - case XKB_KEY_F4: return CrafterKeys::F4; - case XKB_KEY_F5: return CrafterKeys::F5; - case XKB_KEY_F6: return CrafterKeys::F6; - case XKB_KEY_F7: return CrafterKeys::F7; - case XKB_KEY_F8: return CrafterKeys::F8; - case XKB_KEY_F9: return CrafterKeys::F9; - case XKB_KEY_F10: return CrafterKeys::F10; - case XKB_KEY_F11: return CrafterKeys::F11; - case XKB_KEY_F12: return CrafterKeys::F12; - - // Control keys - case XKB_KEY_Escape: return CrafterKeys::Escape; - case XKB_KEY_Tab: return CrafterKeys::Tab; - case XKB_KEY_Return: return CrafterKeys::Enter; - case XKB_KEY_space: return CrafterKeys::Space; - case XKB_KEY_BackSpace: return CrafterKeys::Backspace; - case XKB_KEY_Delete: return CrafterKeys::Delete; - case XKB_KEY_Insert: return CrafterKeys::Insert; - case XKB_KEY_Home: return CrafterKeys::Home; - case XKB_KEY_End: return CrafterKeys::End; - case XKB_KEY_Page_Up: return CrafterKeys::PageUp; - case XKB_KEY_Page_Down: return CrafterKeys::PageDown; - case XKB_KEY_Caps_Lock: return CrafterKeys::CapsLock; - case XKB_KEY_Num_Lock: return CrafterKeys::NumLock; - case XKB_KEY_Scroll_Lock:return CrafterKeys::ScrollLock; - - // Modifiers - case XKB_KEY_Shift_L: return CrafterKeys::LeftShift; - case XKB_KEY_Shift_R: return CrafterKeys::RightShift; - case XKB_KEY_Control_L: return CrafterKeys::LeftCtrl; - case XKB_KEY_Control_R: return CrafterKeys::RightCtrl; - case XKB_KEY_Alt_L: return CrafterKeys::LeftAlt; - case XKB_KEY_Alt_R: return CrafterKeys::RightAlt; - case XKB_KEY_Super_L: return CrafterKeys::LeftSuper; - case XKB_KEY_Super_R: return CrafterKeys::RightSuper; - - // Arrows - case XKB_KEY_Up: return CrafterKeys::Up; - case XKB_KEY_Down: return CrafterKeys::Down; - case XKB_KEY_Left: return CrafterKeys::Left; - case XKB_KEY_Right: return CrafterKeys::Right; - - // Keypad - case XKB_KEY_KP_0: return CrafterKeys::keypad_0; - case XKB_KEY_KP_1: return CrafterKeys::keypad_1; - case XKB_KEY_KP_2: return CrafterKeys::keypad_2; - case XKB_KEY_KP_3: return CrafterKeys::keypad_3; - case XKB_KEY_KP_4: return CrafterKeys::keypad_4; - case XKB_KEY_KP_5: return CrafterKeys::keypad_5; - case XKB_KEY_KP_6: return CrafterKeys::keypad_6; - case XKB_KEY_KP_7: return CrafterKeys::keypad_7; - case XKB_KEY_KP_8: return CrafterKeys::keypad_8; - case XKB_KEY_KP_9: return CrafterKeys::keypad_9; - case XKB_KEY_KP_Enter: return CrafterKeys::keypad_enter; - case XKB_KEY_KP_Add: return CrafterKeys::keypad_plus; - case XKB_KEY_KP_Subtract: return CrafterKeys::keypad_minus; - case XKB_KEY_KP_Multiply: return CrafterKeys::keypad_multiply; - case XKB_KEY_KP_Divide: return CrafterKeys::keypad_divide; - case XKB_KEY_KP_Decimal: return CrafterKeys::keypad_decimal; - - // Punctuation - case XKB_KEY_grave: return CrafterKeys::grave; - case XKB_KEY_minus: return CrafterKeys::minus; - case XKB_KEY_equal: return CrafterKeys::equal; - case XKB_KEY_bracketleft: return CrafterKeys::bracket_left; - case XKB_KEY_bracketright:return CrafterKeys::bracket_right; - case XKB_KEY_backslash: return CrafterKeys::backslash; - case XKB_KEY_semicolon: return CrafterKeys::semicolon; - case XKB_KEY_apostrophe: return CrafterKeys::quote; - case XKB_KEY_comma: return CrafterKeys::comma; - case XKB_KEY_period: return CrafterKeys::period; - case XKB_KEY_slash: return CrafterKeys::slash; - - default: - return CrafterKeys::CrafterKeysMax; - } -} - -void WindowWayland::keyboard_key(void *data, wl_keyboard *keyboard, uint32_t serial, uint32_t time, uint32_t key, uint32_t state) { - WindowWayland* window = reinterpret_cast(data); - - if (!window->xkb_state) { - return; - } - - xkb_keycode_t keycode = key + 8; - xkb_keysym_t keysym = xkb_state_key_get_one_sym(window->xkb_state, keycode); - CrafterKeys crafterKey = keysym_to_crafter_key(keysym); - - if(state == WL_KEYBOARD_KEY_STATE_PRESSED) { - 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); - } - } else{ - window->heldkeys[static_cast(crafterKey)] = false; - window->onKeyUp[static_cast(crafterKey)].Invoke(); - window->onAnyKeyUp.Invoke(crafterKey); - } -} - -void WindowWayland::keyboard_modifiers(void *data, wl_keyboard *keyboard, uint32_t serial, uint32_t mods_depressed, uint32_t mods_latched, uint32_t mods_locked, uint32_t group) { - -} - -void WindowWayland::keyboard_repeat_info(void *data, wl_keyboard *keyboard, int32_t rate, int32_t delay) { - -} - -void WindowWayland::seat_handle_capabilities(void* data, wl_seat* seat, uint32_t capabilities) { - WindowWayland* window = reinterpret_cast(data); - window->seat = seat; - if (capabilities & WL_SEAT_CAPABILITY_POINTER) { - wl_pointer* pointer = wl_seat_get_pointer(seat); - wl_pointer_add_listener(pointer, &pointer_listener, window); - } - if (capabilities & WL_SEAT_CAPABILITY_KEYBOARD) { - wl_keyboard* keyboard = wl_seat_get_keyboard(seat); - wl_keyboard_add_listener(keyboard, &keyboard_listener, window); - } -} - -void WindowWayland::handle_global(void *data, wl_registry *registry, std::uint32_t name, const char *interface, std::uint32_t version) { - WindowWayland* window = reinterpret_cast(data); - if (strcmp(interface, wl_shm_interface.name) == 0) { - window->shm = reinterpret_cast(wl_registry_bind(registry, name, &wl_shm_interface, 1)); - } else if (strcmp(interface, wl_seat_interface.name) == 0) { - wl_seat* seat = reinterpret_cast(wl_registry_bind(registry, name, &wl_seat_interface, 1)); - wl_seat_add_listener(seat, &seat_listener, window); - } else if (compositor == NULL && strcmp(interface, wl_compositor_interface.name) == 0) { - compositor = reinterpret_cast(wl_registry_bind(registry, name, &wl_compositor_interface, 3)); - } else if (strcmp(interface, xdg_wm_base_interface.name) == 0) { - window->xdgWmBase = reinterpret_cast(wl_registry_bind(registry, name, &xdg_wm_base_interface, 1)); - xdg_wm_base_add_listener(window->xdgWmBase, &xdgWmBaseListener, NULL); - } else if (strcmp(interface, zxdg_decoration_manager_v1_interface.name) == 0) { - window->manager = reinterpret_cast(wl_registry_bind(registry, name, &zxdg_decoration_manager_v1_interface, 1)); - } else if (strcmp(interface, wp_viewporter_interface.name) == 0) { - window->wpViewporter = reinterpret_cast(wl_registry_bind(registry, name, &wp_viewporter_interface, 1)); - } else if (strcmp(interface, wp_fractional_scale_manager_v1_interface.name) == 0) { - window->fractionalScaleManager = reinterpret_cast(wl_registry_bind(registry, name, &wp_fractional_scale_manager_v1_interface, 1)); - } -} - -void WindowWayland::handle_global_remove(void* data, wl_registry* registry, uint32_t name) { - - -} - -void WindowWayland::xdg_toplevel_configure(void*, xdg_toplevel*, std::int32_t, std::int32_t, wl_array*){ - -} - -void WindowWayland::xdg_toplevel_handle_close(void* data, xdg_toplevel*) { - WindowWayland* window = reinterpret_cast(data); - window->onClose.Invoke(); - window->open = false; -} - -void WindowWayland::xdg_surface_handle_configure(void* data, xdg_surface* xdg_surface, std::uint32_t serial) { - WindowWayland* window = reinterpret_cast(data); - // The compositor configures our surface, acknowledge the configure event - xdg_surface_ack_configure(xdg_surface, serial); - - if (window->configured) { - // If this isn't the first configure event we've received, we already - // have a buffer attached, so no need to do anything. Commit the - // surface to apply the configure acknowledgement. - wl_surface_commit(window->surface); - } - - window->configured = true; -} - -void WindowWayland::xdg_surface_handle_preferred_scale(void* data, wp_fractional_scale_v1*, std::uint32_t scale) { - WindowWayland* window = reinterpret_cast(data); - - window->scale = scale / 120.0f; -} \ No newline at end of file diff --git a/interfaces/Crafter.Graphics-Animation.cppm b/interfaces/Crafter.Graphics-Animation.cppm index c432b80..5f1a9a0 100644 --- a/interfaces/Crafter.Graphics-Animation.cppm +++ b/interfaces/Crafter.Graphics-Animation.cppm @@ -1,6 +1,6 @@ /* Crafter®.Graphics -Copyright (C) 2025 Catcrafts® +Copyright (C) 2026 Catcrafts® Catcrafts.net This library is free software; you can redistribute it and/or diff --git a/interfaces/Crafter.Graphics-DescriptorPoolVulkan.cppm b/interfaces/Crafter.Graphics-DescriptorPoolVulkan.cppm index f50f6c9..7cee35a 100644 --- a/interfaces/Crafter.Graphics-DescriptorPoolVulkan.cppm +++ b/interfaces/Crafter.Graphics-DescriptorPoolVulkan.cppm @@ -18,13 +18,13 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ module; -#ifdef CRAFTER_GRAPHICS_VULKAN +#ifdef CRAFTER_GRAPHICS_RENDERER_VULKAN #include "vulkan/vulkan.h" #endif export module Crafter.Graphics:DescriptorPoolVulkan; -#ifdef CRAFTER_GRAPHICS_VULKAN +#ifdef CRAFTER_GRAPHICS_RENDERER_VULKAN import std; -import :VulkanDevice; +import :Device; import :Types; import :DescriptorSetLayoutVulkan; import Crafter.Event; @@ -155,7 +155,7 @@ export namespace Crafter { public: void BuildPool(std::span poolSizes, std::span setLayouts) { if(descriptorPool != VK_NULL_HANDLE) { - vkDestroyDescriptorPool(VulkanDevice::device, descriptorPool, nullptr); + vkDestroyDescriptorPool(Device::device, descriptorPool, nullptr); } @@ -166,7 +166,7 @@ export namespace Crafter { .pPoolSizes = poolSizes.data() }; - VulkanDevice::CheckVkResult(vkCreateDescriptorPool(VulkanDevice::device, &descriptorPoolInfo, nullptr, &descriptorPool)); + Device::CheckVkResult(vkCreateDescriptorPool(Device::device, &descriptorPoolInfo, nullptr, &descriptorPool)); VkDescriptorSetAllocateInfo allocInfo { .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO, @@ -175,7 +175,7 @@ export namespace Crafter { .pSetLayouts = setLayouts.data(), }; - VulkanDevice::CheckVkResult(vkAllocateDescriptorSets(VulkanDevice::device, &allocInfo, sets.data())); + Device::CheckVkResult(vkAllocateDescriptorSets(Device::device, &allocInfo, sets.data())); } }; } diff --git a/interfaces/Crafter.Graphics-DescriptorSetLayoutVulkan.cppm b/interfaces/Crafter.Graphics-DescriptorSetLayoutVulkan.cppm index cff916f..c499eff 100644 --- a/interfaces/Crafter.Graphics-DescriptorSetLayoutVulkan.cppm +++ b/interfaces/Crafter.Graphics-DescriptorSetLayoutVulkan.cppm @@ -18,13 +18,13 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ module; -#ifdef CRAFTER_GRAPHICS_VULKAN +#ifdef CRAFTER_GRAPHICS_RENDERER_VULKAN #include "vulkan/vulkan.h" #endif export module Crafter.Graphics:DescriptorSetLayoutVulkan; -#ifdef CRAFTER_GRAPHICS_VULKAN +#ifdef CRAFTER_GRAPHICS_RENDERER_VULKAN import std; -import :VulkanDevice; +import :Device; import :Types; import Crafter.Event; @@ -39,7 +39,7 @@ export namespace Crafter { .bindingCount = static_cast(descriptors.size()), .pBindings = descriptors.data() }; - VulkanDevice::CheckVkResult(vkCreateDescriptorSetLayout(VulkanDevice::device, &descriptorLayoutInfoMesh, nullptr, &layout)); + Device::CheckVkResult(vkCreateDescriptorSetLayout(Device::device, &descriptorLayoutInfoMesh, nullptr, &layout)); } }; @@ -54,7 +54,7 @@ export namespace Crafter { .bindingCount = DescriptorCount, .pBindings = Descriptors.data() }; - VulkanDevice::CheckVkResult(vkCreateDescriptorSetLayout(VulkanDevice::device, &descriptorLayoutInfoMesh, nullptr, &layout)); + Device::CheckVkResult(vkCreateDescriptorSetLayout(Device::device, &descriptorLayoutInfoMesh, nullptr, &layout)); } }; } diff --git a/interfaces/Crafter.Graphics-Device.cppm b/interfaces/Crafter.Graphics-Device.cppm new file mode 100644 index 0000000..84ee9a0 --- /dev/null +++ b/interfaces/Crafter.Graphics-Device.cppm @@ -0,0 +1,125 @@ +/* +Crafter®.Graphics +Copyright (C) 2026 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; +#ifdef CRAFTER_GRAPHICS_RENDERER_VULKAN +#include "vulkan/vulkan.h" +#endif +#ifdef CRAFTER_GRAPHICS_WINDOW_WAYLAND +#include +#include +#include "../lib/xdg-shell-client-protocol.h" +#include "../lib/wayland-xdg-decoration-unstable-v1-client-protocol.h" +#include "../lib/fractional-scale-v1.h" +#include "../lib/viewporter.h" +#include +#endif +export module Crafter.Graphics:Device; +import std; + +export namespace Crafter { + struct Window; + struct Device { + static void Initialize(); + + #ifdef CRAFTER_GRAPHICS_WINDOW_WAYLAND + inline static wl_display* display = nullptr; + inline static wl_seat* seat = nullptr; + inline static xdg_wm_base* xdgWmBase = nullptr; + inline static wp_fractional_scale_manager_v1* fractionalScaleManager = nullptr; + inline static zxdg_decoration_manager_v1* manager = nullptr; + inline static xkb_keymap* xkb_keymap; + inline static wl_compositor* compositor = nullptr; + inline static Window* focusedWindow = nullptr; + inline static wl_shm* shm = nullptr; + inline static wp_viewporter* wpViewporter = nullptr; + inline static xkb_context* xkb_context = xkb_context_new(XKB_CONTEXT_NO_FLAGS); + inline static xkb_state* xkb_state; + inline static std::vector windows; + + static void seat_handle_capabilities(void* data, wl_seat* seat, uint32_t capabilities); + static void xdg_surface_handle_preferred_scale(void* data, wp_fractional_scale_v1*, std::uint32_t scale); + static void xdg_wm_base_handle_ping(void* data, xdg_wm_base* xdg_wm_base, std::uint32_t serial); + static void handle_global(void* data, wl_registry* registry, std::uint32_t name, const char* interface, std::uint32_t version); + static void handle_global_remove(void* data, wl_registry* registry, uint32_t name); + static void keyboard_keymap(void* data, wl_keyboard* keyboard, uint32_t format, int fd, uint32_t size); + static void keyboard_enter(void *data, wl_keyboard *keyboard, uint32_t serial, wl_surface *surface, wl_array *keys); + static void keyboard_leave(void *data, wl_keyboard *keyboard, uint32_t serial, wl_surface *surface); + static void keyboard_key(void *data, wl_keyboard *keyboard, uint32_t serial, uint32_t time, uint32_t key, uint32_t state); + static void keyboard_modifiers(void *data, wl_keyboard *keyboard, uint32_t serial, uint32_t mods_depressed, uint32_t mods_latched, uint32_t mods_locked, uint32_t group); + static void keyboard_repeat_info(void *data, wl_keyboard *keyboard, int32_t rate, int32_t delay); + static void pointer_handle_button(void* data, wl_pointer* pointer, std::uint32_t serial, std::uint32_t time, std::uint32_t button, std::uint32_t state); + static void PointerListenerHandleMotion(void* data, wl_pointer* wl_pointer, std::uint32_t time, wl_fixed_t surface_x, wl_fixed_t surface_y); + static void PointerListenerHandleAxis(void*, wl_pointer*, std::uint32_t, std::uint32_t, wl_fixed_t value); + static void PointerListenerHandleEnter(void* data, wl_pointer* wl_pointer, std::uint32_t serial, wl_surface* surface, wl_fixed_t surface_x, wl_fixed_t surface_y); + static void PointerListenerHandleLeave(void*, wl_pointer*, std::uint32_t, wl_surface*); + + constexpr static wl_pointer_listener pointer_listener = { + .enter = PointerListenerHandleEnter, + .leave = PointerListenerHandleLeave, + .motion = PointerListenerHandleMotion, + .button = pointer_handle_button, + .axis = PointerListenerHandleAxis, + }; + constexpr static wl_keyboard_listener keyboard_listener = { + .keymap = keyboard_keymap, + .enter = keyboard_enter, + .leave = keyboard_leave, + .key = keyboard_key, + .modifiers = keyboard_modifiers, + .repeat_info = keyboard_repeat_info, + }; + constexpr static wl_seat_listener seat_listener = { + .capabilities = seat_handle_capabilities, + }; + constexpr static wl_registry_listener registry_listener = { + .global = handle_global, + .global_remove = handle_global_remove, + }; + constexpr static xdg_wm_base_listener xdgWmBaseListener = { + .ping = xdg_wm_base_handle_ping, + }; + #endif + + #ifdef CRAFTER_GRAPHICS_RENDERER_VULKAN + inline static VkInstance instance = VK_NULL_HANDLE; + inline static VkDebugUtilsMessengerEXT debugMessenger = VK_NULL_HANDLE; + inline static VkPhysicalDevice physDevice = VK_NULL_HANDLE; + inline static VkDevice device = VK_NULL_HANDLE; + inline static std::uint32_t queueFamilyIndex = 0; + inline static VkQueue queue = VK_NULL_HANDLE; + inline static VkCommandPool commandPool = VK_NULL_HANDLE; + inline static VkSwapchainKHR swapchain = VK_NULL_HANDLE; + inline static PFN_vkGetAccelerationStructureBuildSizesKHR vkGetAccelerationStructureBuildSizesKHR; + inline static PFN_vkCreateAccelerationStructureKHR vkCreateAccelerationStructureKHR; + inline static PFN_vkCmdBuildAccelerationStructuresKHR vkCmdBuildAccelerationStructuresKHR; + inline static PFN_vkGetAccelerationStructureDeviceAddressKHR vkGetAccelerationStructureDeviceAddressKHR; + inline static PFN_vkCreateRayTracingPipelinesKHR vkCreateRayTracingPipelinesKHR; + inline static PFN_vkGetRayTracingShaderGroupHandlesKHR vkGetRayTracingShaderGroupHandlesKHR; + inline static PFN_vkCmdTraceRaysKHR vkCmdTraceRaysKHR; + inline static VkPhysicalDeviceMemoryProperties memoryProperties; + inline static VkPhysicalDeviceRayTracingPipelinePropertiesKHR rayTracingProperties = { + .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RAY_TRACING_PIPELINE_PROPERTIES_KHR + }; + + static void CheckVkResult(VkResult result); + static std::uint32_t GetMemoryType(std::uint32_t typeBits, VkMemoryPropertyFlags properties); + #endif + }; +} \ No newline at end of file diff --git a/interfaces/Crafter.Graphics-Font.cppm b/interfaces/Crafter.Graphics-Font.cppm index d736133..77d14dd 100644 --- a/interfaces/Crafter.Graphics-Font.cppm +++ b/interfaces/Crafter.Graphics-Font.cppm @@ -1,6 +1,6 @@ /* Crafter®.Graphics -Copyright (C) 2025 Catcrafts® +Copyright (C) 2026 Catcrafts® Catcrafts.net This library is free software; you can redistribute it and/or diff --git a/interfaces/Crafter.Graphics-GridElement.cppm b/interfaces/Crafter.Graphics-GridElement.cppm index 6433c9a..b8db891 100644 --- a/interfaces/Crafter.Graphics-GridElement.cppm +++ b/interfaces/Crafter.Graphics-GridElement.cppm @@ -1,6 +1,6 @@ /* Crafter®.Graphics -Copyright (C) 2025 Catcrafts® +Copyright (C) 2026 Catcrafts® catcrafts.net This library is free software; you can redistribute it and/or @@ -19,11 +19,11 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA export module Crafter.Graphics:GridElement; import std; -import :Transform; +import :Transform2D; import :Types; export namespace Crafter { - class GridElement : public Transform { + class GridElement : public Transform2D { public: std::uint32_t columns; std::uint32_t rows; @@ -31,9 +31,9 @@ export namespace Crafter { std::int32_t spacingY; std::int32_t paddingX; std::int32_t paddingY; - GridElement(std::uint32_t columns, std::uint32_t rows, std::int32_t spacingX, std::int32_t spacingY, std::int32_t paddingX, std::int32_t paddingY, Anchor anchor); - void UpdatePositionScaled(Window& window); - void UpdatePosition(Window& window) override; - void UpdatePosition(Window& window, Transform& parent) override; + GridElement(std::uint32_t columns, std::uint32_t rows, std::int32_t spacingX, std::int32_t spacingY, std::int32_t paddingX, std::int32_t paddingY, Anchor2D anchor); + void UpdatePositionScaled(Rendertarget& window); + void UpdatePosition(Rendertarget& window) override; + void UpdatePosition(Rendertarget& window, Transform2D& parent) override; }; } \ No newline at end of file diff --git a/interfaces/Crafter.Graphics-Image.cppm b/interfaces/Crafter.Graphics-Image.cppm deleted file mode 100644 index c8afc7d..0000000 --- a/interfaces/Crafter.Graphics-Image.cppm +++ /dev/null @@ -1,34 +0,0 @@ -/* -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 as published by the Free Software Foundation; either -version 3.0 of the License, or (at your option) any later version. - -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 -*/ - -export module Crafter.Graphics:Image; -import std; - -import :Types; - -namespace Crafter { - export struct Image { - std::vector buffer; - std::uint_fast32_t width; - std::uint_fast32_t height; - OpaqueType opaque; - Image(const std::string_view path); - }; -} \ No newline at end of file diff --git a/interfaces/Crafter.Graphics-ImageVulkan.cppm b/interfaces/Crafter.Graphics-ImageVulkan.cppm index 22efd2a..9a51331 100644 --- a/interfaces/Crafter.Graphics-ImageVulkan.cppm +++ b/interfaces/Crafter.Graphics-ImageVulkan.cppm @@ -19,7 +19,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 0215-1301 USA module; -#ifdef CRAFTER_GRAPHICS_VULKAN +#ifdef CRAFTER_GRAPHICS_RENDERER_VULKAN #include "vulkan/vulkan.h" #endif @@ -28,7 +28,7 @@ import std; import :VulkanBuffer; export namespace Crafter { - #ifdef CRAFTER_GRAPHICS_VULKAN + #ifdef CRAFTER_GRAPHICS_RENDERER_VULKAN template class ImageVulkan { public: @@ -61,18 +61,18 @@ export namespace Crafter { imageInfo.samples = VK_SAMPLE_COUNT_1_BIT; imageInfo.sharingMode = VK_SHARING_MODE_EXCLUSIVE; - VulkanDevice::CheckVkResult(vkCreateImage(VulkanDevice::device, &imageInfo, nullptr, &image)); + Device::CheckVkResult(vkCreateImage(Device::device, &imageInfo, nullptr, &image)); VkMemoryRequirements memRequirements; - vkGetImageMemoryRequirements(VulkanDevice::device, image, &memRequirements); + vkGetImageMemoryRequirements(Device::device, image, &memRequirements); VkMemoryAllocateInfo allocInfo = {}; allocInfo.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; allocInfo.allocationSize = memRequirements.size; - allocInfo.memoryTypeIndex = VulkanDevice::GetMemoryType(memRequirements.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); + allocInfo.memoryTypeIndex = Device::GetMemoryType(memRequirements.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT); - VulkanDevice::CheckVkResult(vkAllocateMemory(VulkanDevice::device, &allocInfo, nullptr, &imageMemory)); - vkBindImageMemory(VulkanDevice::device, image, imageMemory, 0); + Device::CheckVkResult(vkAllocateMemory(Device::device, &allocInfo, nullptr, &imageMemory)); + vkBindImageMemory(Device::device, image, imageMemory, 0); VkImageViewCreateInfo viewInfo = {}; viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; @@ -85,7 +85,7 @@ export namespace Crafter { viewInfo.subresourceRange.baseArrayLayer = 0; viewInfo.subresourceRange.layerCount = 1; - VulkanDevice::CheckVkResult(vkCreateImageView(VulkanDevice::device, &viewInfo, nullptr, &imageView)); + Device::CheckVkResult(vkCreateImageView(Device::device, &viewInfo, nullptr, &imageView)); // Final transition to shader read-only layout TransitionImageLayout(cmd, image, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL, 0, 5); diff --git a/interfaces/Crafter.Graphics-Mesh.cppm b/interfaces/Crafter.Graphics-Mesh.cppm index bfee5e4..76720b2 100644 --- a/interfaces/Crafter.Graphics-Mesh.cppm +++ b/interfaces/Crafter.Graphics-Mesh.cppm @@ -19,7 +19,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA module; -#ifdef CRAFTER_GRAPHICS_VULKAN +#ifdef CRAFTER_GRAPHICS_RENDERER_VULKAN #include "vulkan/vulkan.h" #endif @@ -29,8 +29,7 @@ import Crafter.Math; import :VulkanBuffer; export namespace Crafter { - - #ifdef CRAFTER_GRAPHICS_VULKAN + #ifdef CRAFTER_GRAPHICS_RENDERER_VULKAN class Mesh { public: VulkanBuffer scratchBuffer; diff --git a/interfaces/Crafter.Graphics-MouseElement.cppm b/interfaces/Crafter.Graphics-MouseElement.cppm index 97f3220..cf4f17c 100644 --- a/interfaces/Crafter.Graphics-MouseElement.cppm +++ b/interfaces/Crafter.Graphics-MouseElement.cppm @@ -1,6 +1,6 @@ /* Crafter®.Graphics -Copyright (C) 2025 Catcrafts® +Copyright (C) 2026 Catcrafts® catcrafts.net This library is free software; you can redistribute it and/or @@ -21,29 +21,24 @@ export module Crafter.Graphics:MouseElement; import std; import Crafter.Event; import :Types; -import :Transform; +import :Transform2D; export namespace Crafter { class Window; - class WindowMouse; - class MouseElement : public Transform { - public: - Event onMouseMove; - Event onMouseEnter; - Event onMouseLeave; - Event onMouseRightClick; - Event onMouseLeftClick; - Event onMouseRightHold; - Event onMouseLeftHold; - Event onMouseRightRelease; - Event onMouseLeftRelease; - ScaleDataBoundless mouseScaled; - + struct MouseElement : Transform2D { + Event onMouseMove; + Event onMouseEnter; + Event onMouseLeave; + Event onMouseRightClick; + Event onMouseLeftClick; + Event onMouseRightHold; + Event onMouseLeftHold; + Event onMouseRightRelease; + Event onMouseLeftRelease; + MouseElement(); - MouseElement(WindowMouse& window); - MouseElement(Anchor anchor); - MouseElement(Anchor anchor, WindowMouse& window); - void UpdatePosition(Window& window) override; - void UpdatePosition(Window& window, Transform& parent) override; + MouseElement(Window& window); + MouseElement(Anchor2D anchor); + MouseElement(Anchor2D anchor, Window& window); }; } \ No newline at end of file diff --git a/interfaces/Crafter.Graphics-PipelineRTVulkan.cppm b/interfaces/Crafter.Graphics-PipelineRTVulkan.cppm index d2cec11..2b312a8 100644 --- a/interfaces/Crafter.Graphics-PipelineRTVulkan.cppm +++ b/interfaces/Crafter.Graphics-PipelineRTVulkan.cppm @@ -18,13 +18,13 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ module; -#ifdef CRAFTER_GRAPHICS_VULKAN +#ifdef CRAFTER_GRAPHICS_RENDERER_VULKAN #include "vulkan/vulkan.h" #endif export module Crafter.Graphics:PipelineRTVulkan; -#ifdef CRAFTER_GRAPHICS_VULKAN +#ifdef CRAFTER_GRAPHICS_RENDERER_VULKAN import std; -import :VulkanDevice; +import :Device; import :VulkanBuffer; import :ShaderBindingTableVulkan; import :Types; @@ -48,7 +48,7 @@ export namespace Crafter { .pSetLayouts = setLayouts.data() }; - VulkanDevice::CheckVkResult(vkCreatePipelineLayout(VulkanDevice::device, &pipelineLayoutInfo, nullptr, &pipelineLayout)); + Device::CheckVkResult(vkCreatePipelineLayout(Device::device, &pipelineLayoutInfo, nullptr, &pipelineLayout)); std::vector groups; groups.reserve(raygenGroups.size() + missGroups.size() + hitGroups.size()); @@ -67,24 +67,24 @@ export namespace Crafter { .layout = pipelineLayout }; - VulkanDevice::CheckVkResult(VulkanDevice::vkCreateRayTracingPipelinesKHR(VulkanDevice::device, {}, {}, 1, &rtPipelineInfo, nullptr, &pipeline)); + Device::CheckVkResult(Device::vkCreateRayTracingPipelinesKHR(Device::device, {}, {}, 1, &rtPipelineInfo, nullptr, &pipeline)); - std::size_t dataSize = VulkanDevice::rayTracingProperties.shaderGroupHandleSize * rtPipelineInfo.groupCount; + std::size_t dataSize = Device::rayTracingProperties.shaderGroupHandleSize * rtPipelineInfo.groupCount; shaderHandles.resize(dataSize); - VulkanDevice::CheckVkResult(VulkanDevice::vkGetRayTracingShaderGroupHandlesKHR(VulkanDevice::device, pipeline, 0, rtPipelineInfo.groupCount, dataSize, shaderHandles.data())); + Device::CheckVkResult(Device::vkGetRayTracingShaderGroupHandlesKHR(Device::device, pipeline, 0, rtPipelineInfo.groupCount, dataSize, shaderHandles.data())); - std::uint32_t sbtStride = AlignUp(VulkanDevice::rayTracingProperties.shaderGroupHandleSize, VulkanDevice::rayTracingProperties.shaderGroupHandleAlignment); + std::uint32_t sbtStride = AlignUp(Device::rayTracingProperties.shaderGroupHandleSize, Device::rayTracingProperties.shaderGroupHandleAlignment); raygenRegion.stride = sbtStride; raygenRegion.deviceAddress = 0; raygenRegion.size = raygenGroups.size() * sbtStride; missRegion.stride = sbtStride; - missRegion.deviceAddress = AlignUp(raygenRegion.size, VulkanDevice::rayTracingProperties.shaderGroupBaseAlignment); + missRegion.deviceAddress = AlignUp(raygenRegion.size, Device::rayTracingProperties.shaderGroupBaseAlignment); missRegion.size = missGroups.size() * sbtStride; hitRegion.stride = sbtStride; - hitRegion.deviceAddress = AlignUp(missRegion.deviceAddress + missRegion.size, VulkanDevice::rayTracingProperties.shaderGroupBaseAlignment); + hitRegion.deviceAddress = AlignUp(missRegion.deviceAddress + missRegion.size, Device::rayTracingProperties.shaderGroupBaseAlignment); hitRegion.size = hitGroups.size() * sbtStride; std::size_t bufferSize = hitRegion.deviceAddress + hitRegion.size; @@ -93,15 +93,15 @@ export namespace Crafter { std::uint8_t* offset = sbtBuffer.value; std::uint8_t* handleOffset = shaderHandles.data(); - std::memcpy(offset, handleOffset, raygenGroups.size() * VulkanDevice::rayTracingProperties.shaderGroupHandleSize); - offset += AlignUp(raygenRegion.size, VulkanDevice::rayTracingProperties.shaderGroupBaseAlignment); - handleOffset += raygenGroups.size() * VulkanDevice::rayTracingProperties.shaderGroupHandleSize; + std::memcpy(offset, handleOffset, raygenGroups.size() * Device::rayTracingProperties.shaderGroupHandleSize); + offset += AlignUp(raygenRegion.size, Device::rayTracingProperties.shaderGroupBaseAlignment); + handleOffset += raygenGroups.size() * Device::rayTracingProperties.shaderGroupHandleSize; - std::memcpy(offset, handleOffset, missGroups.size() * VulkanDevice::rayTracingProperties.shaderGroupHandleSize); - offset += AlignUp(missRegion.size, VulkanDevice::rayTracingProperties.shaderGroupBaseAlignment); - handleOffset += missGroups.size() * VulkanDevice::rayTracingProperties.shaderGroupHandleSize; + std::memcpy(offset, handleOffset, missGroups.size() * Device::rayTracingProperties.shaderGroupHandleSize); + offset += AlignUp(missRegion.size, Device::rayTracingProperties.shaderGroupBaseAlignment); + handleOffset += missGroups.size() * Device::rayTracingProperties.shaderGroupHandleSize; - std::memcpy(offset, handleOffset, hitGroups.size() * VulkanDevice::rayTracingProperties.shaderGroupHandleSize); + std::memcpy(offset, handleOffset, hitGroups.size() * Device::rayTracingProperties.shaderGroupHandleSize); sbtBuffer.FlushDevice(cmd, VK_ACCESS_MEMORY_READ_BIT, VK_PIPELINE_STAGE_RAY_TRACING_SHADER_BIT_KHR); @@ -142,7 +142,7 @@ export namespace Crafter { .pSetLayouts = setLayouts.data() }; - VulkanDevice::CheckVkResult(vkCreatePipelineLayout(VulkanDevice::device, &pipelineLayoutInfo, nullptr, &pipelineLayout)); + Device::CheckVkResult(vkCreatePipelineLayout(Device::device, &pipelineLayoutInfo, nullptr, &pipelineLayout)); constexpr auto groupIndexSeq = std::make_index_sequence>{}; @@ -158,24 +158,24 @@ export namespace Crafter { .layout = pipelineLayout }; - VulkanDevice::CheckVkResult(VulkanDevice::vkCreateRayTracingPipelinesKHR(VulkanDevice::device, {}, {}, 1, &rtPipelineInfo, nullptr, &pipeline)); + Device::CheckVkResult(Device::vkCreateRayTracingPipelinesKHR(Device::device, {}, {}, 1, &rtPipelineInfo, nullptr, &pipeline)); - std::size_t dataSize = VulkanDevice::rayTracingProperties.shaderGroupHandleSize * rtPipelineInfo.groupCount; + std::size_t dataSize = Device::rayTracingProperties.shaderGroupHandleSize * rtPipelineInfo.groupCount; shaderHandles.resize(dataSize); - VulkanDevice::CheckVkResult(VulkanDevice::vkGetRayTracingShaderGroupHandlesKHR(VulkanDevice::device, pipeline, 0, rtPipelineInfo.groupCount, dataSize, shaderHandles.data())); + Device::CheckVkResult(Device::vkGetRayTracingShaderGroupHandlesKHR(Device::device, pipeline, 0, rtPipelineInfo.groupCount, dataSize, shaderHandles.data())); - std::uint32_t sbtStride = AlignUp(VulkanDevice::rayTracingProperties.shaderGroupHandleSize, VulkanDevice::rayTracingProperties.shaderGroupHandleAlignment); + std::uint32_t sbtStride = AlignUp(Device::rayTracingProperties.shaderGroupHandleSize, Device::rayTracingProperties.shaderGroupHandleAlignment); raygenRegion.stride = sbtStride; raygenRegion.deviceAddress = 0; raygenRegion.size = GetGroupCount(groupIndexSeq) * sbtStride; missRegion.stride = sbtStride; - missRegion.deviceAddress = AlignUp(raygenRegion.size, VulkanDevice::rayTracingProperties.shaderGroupBaseAlignment); + missRegion.deviceAddress = AlignUp(raygenRegion.size, Device::rayTracingProperties.shaderGroupBaseAlignment); missRegion.size = GetGroupCount(groupIndexSeq) * sbtStride; hitRegion.stride = sbtStride; - hitRegion.deviceAddress = AlignUp(missRegion.deviceAddress + missRegion.size, VulkanDevice::rayTracingProperties.shaderGroupBaseAlignment); + hitRegion.deviceAddress = AlignUp(missRegion.deviceAddress + missRegion.size, Device::rayTracingProperties.shaderGroupBaseAlignment); hitRegion.size = (GetGroupCount(groupIndexSeq) * sbtStride) + (GetGroupCount(groupIndexSeq) * sbtStride); std::size_t bufferSize = hitRegion.deviceAddress + hitRegion.size; @@ -263,19 +263,19 @@ export namespace Crafter { if constexpr(groupTemplate::generalShader != 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); + std::memcpy(sbtBuffer.value + offset, shaderHandles.data() + index * Device::rayTracingProperties.shaderGroupHandleSize, Device::rayTracingProperties.shaderGroupHandleSize); offset += sbtStride; } } else if constexpr(groupTemplate::closestHitShader != 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); + std::memcpy(sbtBuffer.value + offset, shaderHandles.data() + index * Device::rayTracingProperties.shaderGroupHandleSize, Device::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); + std::memcpy(sbtBuffer.value + offset, shaderHandles.data() + index * Device::rayTracingProperties.shaderGroupHandleSize, Device::rayTracingProperties.shaderGroupHandleSize); offset += sbtStride; } } else { @@ -290,9 +290,9 @@ export namespace Crafter { static void AddShaderGroupsToBuffer(std::uint32_t sbtStride, std::index_sequence) { std::uint32_t offset = 0; (AddShaderGroupToBuffer(sbtStride, offset), ...); - offset = AlignUp(offset, VulkanDevice::rayTracingProperties.shaderGroupBaseAlignment); + offset = AlignUp(offset, Device::rayTracingProperties.shaderGroupBaseAlignment); (AddShaderGroupToBuffer(sbtStride, offset), ...); - offset = AlignUp(offset, VulkanDevice::rayTracingProperties.shaderGroupBaseAlignment); + offset = AlignUp(offset, Device::rayTracingProperties.shaderGroupBaseAlignment); (AddShaderGroupToBuffer(sbtStride, offset), ...); (AddShaderGroupToBuffer(sbtStride, offset), ...); } diff --git a/interfaces/Crafter.Graphics-RenderingElement.cppm b/interfaces/Crafter.Graphics-RenderingElement.cppm deleted file mode 100644 index f6ccf19..0000000 --- a/interfaces/Crafter.Graphics-RenderingElement.cppm +++ /dev/null @@ -1,652 +0,0 @@ -/* -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 "../lib/stb_image.h" -#include "../lib/stb_truetype.h" -export module Crafter.Graphics:RenderingElement; -import std; -import :Transform; -import :Font; -import :Types; -import :Image; -import :Window; - -export namespace Crafter { - enum class TextAlignment { - Left, - Center, - Right - }; - - enum class TextOverflowMode { - Clip, - Wrap - }; - - enum class TextScaleMode { - None, - Font, - Element, - Buffer - }; - - struct RenderElementScalingOwning { - std::vector scalingBuffer; - std::uint32_t bufferWidth; - std::uint32_t bufferHeight; - bool bufferUpdated = true; - RenderElementScalingOwning() = default; - RenderElementScalingOwning(std::uint32_t bufferWidth, std::uint32_t bufferHeight) : scalingBuffer(bufferWidth*bufferHeight), bufferWidth(bufferWidth), bufferHeight(bufferHeight) { - - } - }; - - struct RenderElementScalingNonOwning { - Pixel_BU8_GU8_RU8_AU8* scalingBuffer; - std::uint32_t bufferWidth; - std::uint32_t bufferHeight; - bool bufferUpdated = true; - RenderElementScalingNonOwning() = default; - RenderElementScalingNonOwning(Pixel_BU8_GU8_RU8_AU8* scalingBuffer, std::uint32_t bufferWidth, std::uint32_t bufferHeight) : scalingBuffer(scalingBuffer), bufferWidth(bufferWidth), bufferHeight(bufferHeight) { - - } - }; - - struct RenderElementRotating { - std::uint32_t rotation; - bool rotationUpdated = true; - RenderElementRotating() = default; - RenderElementRotating(std::uint32_t rotation) : rotation(rotation) { - - } - }; - - - struct EmptyScalingBase {}; - struct EmptyRotatingBase {}; - - template - using ScalingBase = - std::conditional_t< - Scaling, - std::conditional_t, - EmptyScalingBase - >; - - template - using RotatingBase = - std::conditional_t< - Rotating, - RenderElementRotating, - EmptyRotatingBase - >; - - class RenderingElementBase : public Transform { - public: - std::vector buffer; - OpaqueType opaque; - RenderingElementBase(Anchor anchor) : Transform(anchor) { - scaled.width = 0; - } - RenderingElementBase(Anchor anchor, OpaqueType opaque) : Transform(anchor), opaque(opaque) { - scaled.width = 0; - } - }; - - template requires ((!Rotating || Scaling) && (!Owning || Scaling)) - class RenderingElement : public RenderingElementBase, public ScalingBase, public RotatingBase { - public: - RenderingElement() = default; - RenderingElement(Anchor anchor, OpaqueType opaque) : RenderingElementBase(anchor, opaque) { - - } - RenderingElement(Anchor anchor, OpaqueType opaque, std::uint32_t rotation) requires(Rotating) : RenderingElementBase(anchor, opaque), RotatingBase(rotation) { - - } - - RenderingElement(Anchor anchor, const std::string_view imagePath) : RenderingElementBase(anchor) { - LoadImage(imagePath); - } - RenderingElement(Anchor anchor, const std::string_view imagePath, std::uint32_t rotation) requires(Rotating) : RenderingElementBase(anchor), RotatingBase(rotation) { - LoadImage(imagePath); - } - - RenderingElement(Anchor anchor, const std::string_view imagePath, OpaqueType opaque) : RenderingElementBase(anchor, opaque) { - LoadImageNoOpaqueCheck(imagePath); - } - RenderingElement(Anchor anchor, const std::string_view imagePath, OpaqueType opaque, std::uint32_t rotation) requires(Rotating) : RenderingElementBase(anchor, opaque), RotatingBase(rotation) { - LoadImageNoOpaqueCheck(imagePath); - } - - RenderingElement(Anchor anchor, OpaqueType opaque, std::uint32_t bufferWidth, std::uint32_t bufferHeight, Pixel_BU8_GU8_RU8_AU8* scalingBuffer) requires(Scaling && !Owning) : RenderingElementBase(anchor, opaque), ScalingBase(bufferWidth, bufferHeight, scalingBuffer) { - - } - RenderingElement(Anchor anchor, OpaqueType opaque, std::uint32_t bufferWidth, std::uint32_t bufferHeight, Pixel_BU8_GU8_RU8_AU8* scalingBuffer, std::uint32_t rotation) requires(Scaling && !Owning && Rotating) : RenderingElementBase(anchor, opaque), ScalingBase(bufferWidth, bufferHeight, scalingBuffer), RotatingBase(rotation) { - - } - - RenderingElement(Anchor anchor, OpaqueType opaque, Image& image) requires(Scaling && !Owning) : RenderingElementBase(anchor, opaque), ScalingBase(image.buffer.data(), image.width, image.height) { - - } - RenderingElement(Anchor anchor, OpaqueType opaque, Image& image, std::uint32_t rotation) requires(Scaling && !Owning && Rotating) : RenderingElementBase(anchor, opaque), ScalingBase(image.buffer.data(), image.width, image.height), RotatingBase(rotation) { - - } - - RenderingElement(Anchor anchor, Image& image) requires(Scaling && !Owning) : RenderingElementBase(anchor, image.opaque), ScalingBase(image.buffer.data(), image.width, image.height) { - - } - RenderingElement(Anchor anchor, Image& image, std::uint32_t rotation) requires(Scaling && !Owning && Rotating) : RenderingElementBase(anchor, image.opaque), ScalingBase(image.buffer.data(), image.width, image.height), RotatingBase(rotation) { - - } - - RenderingElement(Anchor anchor, OpaqueType opaque, std::uint32_t bufferWidth, std::uint32_t bufferHeight) requires(Owning) : RenderingElementBase(anchor, opaque), ScalingBase(bufferWidth, bufferHeight) { - - } - RenderingElement(Anchor anchor, OpaqueType opaque, std::uint32_t bufferWidth, std::uint32_t bufferHeight, std::uint32_t rotation) requires(Owning && Rotating) : RenderingElementBase(anchor, opaque), ScalingBase(bufferWidth, bufferHeight) , RotatingBase(rotation) { - - } - - RenderingElement(RenderingElement&) = delete; - RenderingElement& operator=(RenderingElement&) = delete; - - void ScaleNearestNeighbor() requires(Scaling) { - for (std::uint32_t y = 0; y < scaled.height; y++) { - std::uint32_t srcY = y * ScalingBase::bufferHeight / scaled.height; - for (std::uint32_t x = 0; x < scaled.width; x++) { - std::uint32_t srcX = x * ScalingBase::bufferWidth / scaled.width; - buffer[y * scaled.width + x] = ScalingBase::scalingBuffer[srcY * ScalingBase::bufferWidth + srcX]; - } - } - } - - void ScaleRotating() requires(Scaling) { - const double rad = (static_cast(RotatingBase::rotation) / static_cast(std::numeric_limits::max())) * 2.0 * std::numbers::pi; - - const std::uint32_t dstWidth = scaled.width; - const std::uint32_t dstHeight = scaled.height; - - const double c2 = std::abs(std::cos(rad)); - const double s2 = std::abs(std::sin(rad)); - - const double rotatedWidth = dstWidth * c2 + dstHeight * s2; - const double rotatedHeight = dstWidth * s2 + dstHeight * c2; - - const std::uint32_t diffX = static_cast(std::ceil((rotatedWidth - dstWidth) * 0.5)); - const std::uint32_t diffY = static_cast(std::ceil((rotatedHeight - dstHeight) * 0.5)); - - scaled.width += diffX + diffX; - scaled.height += diffY + diffY; - - scaled.x -= diffX; - scaled.y -= diffY; - - buffer.clear(); - buffer.resize(scaled.width * scaled.height); - - // Destination center - const double dstCx = (static_cast(scaled.width) - 1.0) * 0.5; - const double dstCy = (static_cast(scaled.height) - 1.0) * 0.5; - - // Source center - const double srcCx = (static_cast(ScalingBase::bufferWidth) - 1.0) * 0.5; - const double srcCy = (static_cast(ScalingBase::bufferHeight) - 1.0) * 0.5; - - const double c = std::cos(rad); - const double s = std::sin(rad); - - // Scale factors (destination → source) - const double scaleX = static_cast(ScalingBase::bufferWidth) / dstWidth; - const double scaleY = static_cast(ScalingBase::bufferHeight) / dstHeight; - - for (std::uint32_t yB = 0; yB < scaled.height; ++yB) { - for (std::uint32_t xB = 0; xB < scaled.width; ++xB) { - - // ---- Destination pixel relative to center ---- - const double dx = (static_cast(xB) - dstCx) * scaleX; - const double dy = (static_cast(yB) - dstCy) * scaleY; - - // ---- Inverse rotation ---- - const double sx = (c * dx - s * dy) + srcCx; - const double sy = (s * dx + c * dy) + srcCy; - - // ---- Nearest neighbour sampling ---- - const std::int32_t srcX = static_cast(std::round(sx)); - const std::int32_t srcY = static_cast(std::round(sy)); - - if (srcX >= 0 && srcX < ScalingBase::bufferWidth && srcY >= 0 && srcY < ScalingBase::bufferHeight) { - buffer[yB * scaled.width + xB] = ScalingBase::scalingBuffer[srcY * ScalingBase::bufferWidth + srcX]; - } - } - } - } - - void UpdatePosition(Window& window, ScaleData oldScale) { - if constexpr(Scaling && !Rotating) { - if(oldScale.width != scaled.width || oldScale.height != scaled.height) { - buffer.resize(scaled.width * scaled.height); - ScaleNearestNeighbor(); - window.AddDirtyRect(oldScale); - window.AddDirtyRect(scaled); - } else if(oldScale.x != scaled.x || oldScale.y != scaled.y) { - window.AddDirtyRect(oldScale); - window.AddDirtyRect(scaled); - if(ScalingBase::bufferUpdated) { - ScaleNearestNeighbor(); - ScalingBase::bufferUpdated = false; - } - } else if(ScalingBase::bufferUpdated) { - ScaleNearestNeighbor(); - ScalingBase::bufferUpdated = false; - } - } else if constexpr(Rotating) { - if(oldScale.width != scaled.width || oldScale.height != scaled.height) { - buffer.resize(scaled.width * scaled.height); - ScaleRotating(); - window.AddDirtyRect(oldScale); - window.AddDirtyRect(scaled); - } else if(oldScale.x != scaled.x || oldScale.y != scaled.y) { - window.AddDirtyRect(oldScale); - window.AddDirtyRect(scaled); - if(ScalingBase::bufferUpdated || RotatingBase::rotationUpdated) { - ScaleRotating(); - ScalingBase::bufferUpdated = false; - RotatingBase::rotationUpdated = false; - } - } else if(ScalingBase::bufferUpdated || RotatingBase::rotationUpdated) { - ScaleRotating(); - ScalingBase::bufferUpdated = false; - RotatingBase::rotationUpdated = false; - } - } else { - if(oldScale.width != scaled.width || oldScale.height != scaled.height) { - buffer.resize(scaled.width * scaled.height); - window.AddDirtyRect(oldScale); - window.AddDirtyRect(scaled); - } - if(oldScale.x != scaled.x || oldScale.y != scaled.y) { - window.AddDirtyRect(oldScale); - window.AddDirtyRect(scaled); - } - } - } - - void UpdatePosition(Window& window) override { - ScaleData oldScale = scaled; - window.ScaleElement(*this); - UpdatePosition(window, oldScale); - for(Transform* child : children) { - child->UpdatePosition(window, *this); - } - } - - void UpdatePosition(Window& window, Transform& parent) override { - ScaleData oldScale = scaled; - window.ScaleElement(*this, parent); - UpdatePosition(window, oldScale); - for(Transform* child : children) { - child->UpdatePosition(window, *this); - } -} - - void LoadImage(const std::string_view imagePath) { - std::filesystem::path abs = std::filesystem::absolute(imagePath); - int xSize; - int ySize; - unsigned char* bgData = stbi_load(abs.string().c_str(), &xSize, &ySize, nullptr, 4); - - if constexpr(Scaling && !Owning) { - ScalingBase::bufferUpdated = true; - } else if constexpr(Scaling && Owning) { - ScalingBase::bufferWidth = xSize; - ScalingBase::bufferHeight = ySize; - ScalingBase::bufferUpdated = true; - ScalingBase::scalingBuffer.resize(xSize*ySize); - } else { - buffer.resize(xSize*ySize); - } - - opaque = OpaqueType::FullyOpaque; - - if constexpr(Scaling) { - for(std::uint32_t x = 0; x < xSize; x++) { - for(std::uint32_t y = 0; y < ySize; y++) { - std::uint32_t idx = (x*ySize+y)*4; - ScalingBase::scalingBuffer[x*ySize+y].r = bgData[idx]; - ScalingBase::scalingBuffer[x*ySize+y].g = bgData[idx+1]; - ScalingBase::scalingBuffer[x*ySize+y].b = bgData[idx+2]; - ScalingBase::scalingBuffer[x*ySize+y].a = bgData[idx+3]; - } - } - - for(std::uint32_t i = 0; i < xSize*ySize; i++) { - if(ScalingBase::scalingBuffer[i].a != 255) { - opaque = OpaqueType::SemiOpaque; - for(std::uint32_t i2 = 0; i2 < xSize*ySize; i2++) { - if(ScalingBase::scalingBuffer[i2].a != 0 && ScalingBase::scalingBuffer[i2].a != 255) { - opaque = OpaqueType::Transparent; - return; - } - } - return; - } - } - } else { - for(std::uint32_t x = 0; x < xSize; x++) { - for(std::uint32_t y = 0; y < ySize; y++) { - std::uint32_t idx = (x*ySize+y)*4; - buffer[x*ySize+y].r = bgData[idx]; - buffer[x*ySize+y].g = bgData[idx+1]; - buffer[x*ySize+y].b = bgData[idx+2]; - buffer[x*ySize+y].a = bgData[idx+3]; - } - } - - for(std::uint32_t i = 0; i < xSize*ySize; i++) { - if(buffer[i].a != 255) { - opaque = OpaqueType::SemiOpaque; - for(std::uint32_t i2 = 0; i2 < xSize*ySize; i2++) { - if(buffer[i2].a != 0 && buffer[i2].a != 255) { - opaque = OpaqueType::Transparent; - return; - } - } - return; - } - } - } - } - void LoadImageNoOpaqueCheck(const std::string_view imagePath) { - std::filesystem::path abs = std::filesystem::absolute(imagePath); - int xSize; - int ySize; - unsigned char* bgData = stbi_load(abs.string().c_str(), &xSize, &ySize, nullptr, 4); - - - if constexpr(Scaling && !Owning) { - ScalingBase::bufferUpdated = true; - } else if constexpr(Scaling && Owning) { - ScalingBase::bufferWidth = xSize; - ScalingBase::bufferHeight = ySize; - ScalingBase::bufferUpdated = true; - ScalingBase::scalingBuffer.resize(xSize*ySize); - } else { - buffer.resize(xSize*ySize); - } - - - if constexpr(Scaling) { - for(std::uint32_t x = 0; x < xSize; x++) { - for(std::uint32_t y = 0; y < ySize; y++) { - std::uint32_t idx = (x*ySize+y)*4; - ScalingBase::scalingBuffer[x*ySize+y].r = bgData[idx]; - ScalingBase::scalingBuffer[x*ySize+y].g = bgData[idx+1]; - ScalingBase::scalingBuffer[x*ySize+y].b = bgData[idx+2]; - ScalingBase::scalingBuffer[x*ySize+y].a = bgData[idx+3]; - } - } - } else { - for(std::uint32_t x = 0; x < xSize; x++) { - for(std::uint32_t y = 0; y < ySize; y++) { - std::uint32_t idx = (x*ySize+y)*4; - buffer[x*ySize+y].r = bgData[idx]; - buffer[x*ySize+y].g = bgData[idx+1]; - buffer[x*ySize+y].b = bgData[idx+2]; - buffer[x*ySize+y].a = bgData[idx+3]; - } - } - } - } - std::vector ResizeText(Window& window, const std::string_view text, float size, Font& font, TextOverflowMode overflowMode = TextOverflowMode::Clip, TextScaleMode scaleMode = TextScaleMode::None, Transform* parent = nullptr) { - float scale = stbtt_ScaleForPixelHeight(&font.font, size); - int baseline = (int)(font.ascent * scale); - - std::vector lines; - std::string_view remaining = text; - - std::uint32_t lineHeight = (font.ascent - font.descent) * scale; - - if(overflowMode == TextOverflowMode::Clip) { - while (!remaining.empty()) { - // Find next newline or end of string - auto newlinePos = remaining.find('\n'); - if (newlinePos != std::string_view::npos) { - lines.emplace_back(remaining.substr(0, newlinePos)); - remaining = remaining.substr(newlinePos + 1); - } else { - lines.emplace_back(remaining); - break; - } - } - std::uint32_t maxWidth = 0; - - for(const std::string_view line: lines) { - std::uint32_t lineWidth = 0; - for (const char c : line) { - int advance, lsb; - stbtt_GetCodepointHMetrics(&font.font, c, &advance, &lsb); - lineWidth += (int)(advance * scale); - } - if(lineWidth > maxWidth) { - maxWidth = lineWidth; - } - } - - - if(scaleMode == TextScaleMode::Element) { - std::int32_t logicalPerPixelY = anchor.height / scaled.height; - std::int32_t oldHeight = anchor.height; - std::int32_t logicalPerPixelX = anchor.width / scaled.width; - std::int32_t oldwidth = anchor.width; - anchor.height = lineHeight * logicalPerPixelY; - anchor.width = maxWidth * logicalPerPixelX; - if(oldHeight != anchor.height || oldwidth != anchor.width) { - if(parent) { - UpdatePosition(window, *parent); - } else { - UpdatePosition(window); - } - } - } else if(scaleMode == TextScaleMode::Font) { - //todo - } else if(scaleMode == TextScaleMode::Buffer) { - if constexpr(Scaling && Owning) { - std::uint32_t neededHeight = lines.size() * lineHeight; - if(neededHeight != ScalingBase::bufferHeight || maxWidth != ScalingBase::bufferWidth) { - ScalingBase::bufferHeight = neededHeight; - ScalingBase::bufferWidth = maxWidth; - ScalingBase::bufferUpdated = true; - ScalingBase::scalingBuffer.resize(neededHeight*maxWidth); - } - } - } else { - if constexpr(Scaling) { - lines.resize(ScalingBase::bufferHeight / lines.size()); - } else { - lines.resize(scaled.height / lines.size()); - } - } - } else { - while (!remaining.empty()) { - std::string_view line; - auto newlinePos = remaining.find('\n'); - if (newlinePos != std::string_view::npos) { - line = remaining.substr(0, newlinePos); - remaining = remaining.substr(newlinePos + 1); - } else { - line = remaining; - remaining = ""; - } - - std::uint32_t lineWidth = 0; - std::size_t lastWrapPos = 0; // position of last space that can be used to wrap - std::size_t startPos = 0; - - for (std::size_t i = 0; i < line.size(); ++i) { - char c = line[i]; - - // get width of this character - int advance, lsb; - stbtt_GetCodepointHMetrics(&font.font, c, &advance, &lsb); - lineWidth += (std::uint32_t)(advance * scale); - - // remember last space for wrapping - if (c == ' ') { - lastWrapPos = i; - } - - // if line exceeds width, wrap - if (lineWidth > scaled.width) { - std::size_t wrapPos; - if (lastWrapPos > startPos) { - wrapPos = lastWrapPos; // wrap at last space - } else { - wrapPos = i; // no space, hard wrap - } - - // push the line up to wrapPos - lines.push_back(line.substr(startPos, wrapPos - startPos)); - - // skip any spaces at the beginning of next line - startPos = wrapPos; - while (startPos < line.size() && line[startPos] == ' ') { - ++startPos; - } - - // reset width and i - lineWidth = 0; - i = startPos - 1; // -1 because loop will increment i - } - } - - // add the remaining part of the line - if (startPos < line.size()) { - lines.push_back(line.substr(startPos)); - } - } - - if(scaleMode == TextScaleMode::Element) { - std::int32_t logicalPerPixelY = anchor.height / scaled.height; - std::int32_t oldHeight = anchor.height; - anchor.height = lineHeight * logicalPerPixelY; - if(oldHeight != anchor.height) { - if(parent) { - UpdatePosition(window, *parent); - } else { - UpdatePosition(window); - } - } - } else if(scaleMode == TextScaleMode::Font) { - //todo - } else if(scaleMode == TextScaleMode::Buffer) { - if constexpr(Scaling && Owning) { - std::uint32_t neededHeight = lines.size() * lineHeight; - if(neededHeight != ScalingBase::bufferHeight) { - ScalingBase::bufferHeight = neededHeight; - ScalingBase::bufferUpdated = true; - ScalingBase::scalingBuffer.resize(neededHeight*ScalingBase::bufferWidth); - } - } - } else { - if constexpr(Scaling) { - lines.resize(ScalingBase::bufferHeight / lines.size()); - } else { - lines.resize(scaled.height / lines.size()); - } - } - } - - return lines; - } - void RenderText(Window& window, std::span lines, float size, Pixel_BU8_GU8_RU8_AU8 color, Font& font, TextAlignment alignment = TextAlignment::Left, std::uint32_t offsetX = 0, std::uint32_t offsetY = 0) { - float scale = stbtt_ScaleForPixelHeight(&font.font, size); - int baseline = (int)(font.ascent * scale); - std::uint32_t lineHeight = (font.ascent - font.descent) * scale; - std::uint32_t currentY = baseline; - for(std::string_view line : lines) { - - std::uint32_t lineWidth = 0; - for (const char c : line) { - int advance, lsb; - stbtt_GetCodepointHMetrics(&font.font, c, &advance, &lsb); - lineWidth += (int)(advance * scale); - } - - std::uint32_t startX = 0; - switch (alignment) { - case TextAlignment::Left: - startX = 0; - break; - case TextAlignment::Center: - startX = (scaled.width - lineWidth) / 2; - break; - case TextAlignment::Right: - startX = scaled.width - lineWidth; - break; - } - std::uint32_t x = startX; - - for (std::size_t i = 0; i < line.size(); ++i) { - int codepoint = line[i]; - - int ax; - int lsb; - stbtt_GetCodepointHMetrics(&font.font, codepoint, &ax, &lsb); - - int c_x1, c_y1, c_x2, c_y2; - stbtt_GetCodepointBitmapBox(&font.font, codepoint, scale, scale, &c_x1, &c_y1, &c_x2, &c_y2); - - int w = c_x2 - c_x1; - int h = c_y2 - c_y1; - - std::vector bitmap(w * h); - stbtt_MakeCodepointBitmap(&font.font, bitmap.data(), w, h, w, scale, scale, codepoint); - - // Only render characters that fit within the scaled bounds - for (int j = 0; j < h; j++) { - for (int i = 0; i < w; i++) { - int bufferX = x + i + c_x1 + offsetX; - int bufferY = currentY + j + c_y1 + offsetY; - - // Only draw pixels that are within our scaled buffer bounds - if constexpr(Scaling) { - if (bufferX >= 0 && bufferX < ScalingBase::bufferWidth && bufferY >= 0 && bufferY < ScalingBase::bufferHeight) { - ScalingBase::scalingBuffer[bufferY * ScalingBase::bufferWidth + bufferX] = {color.r, color.g, color.b, bitmap[j * w + i]}; - } - } else { - if (bufferX >= 0 && bufferX < (int)scaled.width && bufferY >= 0 && bufferY < (int)scaled.height) { - buffer[bufferY * scaled.width + bufferX] = {color.r, color.g, color.b, bitmap[j * w + i]}; - } - } - } - } - - x += (int)(ax * scale); - - if (i + 1 < line.size()) { - x += (int)stbtt_GetCodepointKernAdvance(&font.font, codepoint, line[i+1]); - } - } - currentY += lineHeight; - } - } - }; -} \ No newline at end of file diff --git a/interfaces/Crafter.Graphics-RenderingElement2D.cppm b/interfaces/Crafter.Graphics-RenderingElement2D.cppm new file mode 100644 index 0000000..1c6b522 --- /dev/null +++ b/interfaces/Crafter.Graphics-RenderingElement2D.cppm @@ -0,0 +1,508 @@ +/* +Crafter®.Graphics +Copyright (C) 2026 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 "../lib/stb_truetype.h" +export module Crafter.Graphics:RenderingElement2D; +import std; +import :Transform2D; +import :Font; +import :Types; +import :Window; + +export namespace Crafter { + enum class TextAlignment { + Left, + Center, + Right + }; + + enum class TextOverflowMode { + Clip, + Wrap + }; + + enum class TextScaleMode { + None, + Font, + Element, + Buffer + }; + + struct RenderElement2DScalingOwning { + std::vector> scalingBuffer; + std::uint32_t bufferWidth; + std::uint32_t bufferHeight; + bool bufferUpdated = true; + RenderElement2DScalingOwning() = default; + RenderElement2DScalingOwning(std::uint32_t bufferWidth, std::uint32_t bufferHeight) : scalingBuffer(bufferWidth*bufferHeight), bufferWidth(bufferWidth), bufferHeight(bufferHeight) { + + } + }; + + struct RenderElement2DScalingNonOwning { + Vector* scalingBuffer; + std::uint32_t bufferWidth; + std::uint32_t bufferHeight; + bool bufferUpdated = true; + RenderElement2DScalingNonOwning() = default; + RenderElement2DScalingNonOwning(Vector* scalingBuffer, std::uint32_t bufferWidth, std::uint32_t bufferHeight) : scalingBuffer(scalingBuffer), bufferWidth(bufferWidth), bufferHeight(bufferHeight) { + + } + }; + + struct RenderElement2DRotating { + float rotation; + bool rotationUpdated = true; + RenderElement2DRotating() = default; + RenderElement2DRotating(float rotation) : rotation(rotation) { + + } + }; + + + struct EmptyScalingBase {}; + struct EmptyRotatingBase {}; + + template + using ScalingBase = + std::conditional_t< + Scaling, + std::conditional_t, + EmptyScalingBase + >; + + template + using RotatingBase = + std::conditional_t< + Rotating, + RenderElement2DRotating, + EmptyRotatingBase + >; + + struct RenderingElement2DBase : Transform2D { + std::vector> buffer; + OpaqueType opaque; + RenderingElement2DBase(Anchor2D anchor) : Transform2D(anchor) { + scaled.size.x = 0; + } + RenderingElement2DBase(Anchor2D anchor, OpaqueType opaque) : Transform2D(anchor), opaque(opaque) { + scaled.size.x = 0; + } + }; + + template requires ((!Rotating || Scaling) && (!Owning || Scaling)) + struct RenderingElement2D : RenderingElement2DBase, ScalingBase, RotatingBase { + RenderingElement2D() = default; + RenderingElement2D(Anchor2D anchor, OpaqueType opaque) : RenderingElement2DBase(anchor, opaque) { + + } + RenderingElement2D(Anchor2D anchor, OpaqueType opaque, std::uint32_t rotation) requires(Rotating) : RenderingElement2DBase(anchor, opaque), RotatingBase(rotation) { + + } + RenderingElement2D(Anchor2D anchor, OpaqueType opaque, std::uint32_t bufferWidth, std::uint32_t bufferHeight, Vector* scalingBuffer) requires(Scaling && !Owning) : RenderingElement2DBase(anchor, opaque), ScalingBase(bufferWidth, bufferHeight, scalingBuffer) { + + } + RenderingElement2D(Anchor2D anchor, OpaqueType opaque, std::uint32_t bufferWidth, std::uint32_t bufferHeight, Vector* scalingBuffer, std::uint32_t rotation) requires(Scaling && !Owning && Rotating) : RenderingElement2DBase(anchor, opaque), ScalingBase(bufferWidth, bufferHeight, scalingBuffer), RotatingBase(rotation) { + + } + RenderingElement2D(Anchor2D anchor, OpaqueType opaque, std::uint32_t bufferWidth, std::uint32_t bufferHeight) requires(Owning) : RenderingElement2DBase(anchor, opaque), ScalingBase(bufferWidth, bufferHeight) { + + } + RenderingElement2D(Anchor2D anchor, OpaqueType opaque, std::uint32_t bufferWidth, std::uint32_t bufferHeight, std::uint32_t rotation) requires(Owning && Rotating) : RenderingElement2DBase(anchor, opaque), ScalingBase(bufferWidth, bufferHeight) , RotatingBase(rotation) { + + } + + RenderingElement2D(RenderingElement2D&) = delete; + RenderingElement2D& operator=(RenderingElement2D&) = delete; + + void ScaleNearestNeighbor() requires(Scaling) { + for (std::uint32_t y = 0; y < scaled.size.y; y++) { + std::uint32_t srcY = y * ScalingBase::bufferHeight / scaled.size.y; + for (std::uint32_t x = 0; x < scaled.size.x; x++) { + std::uint32_t srcX = x * ScalingBase::bufferWidth / scaled.size.x; + buffer[y * scaled.size.x + x] = ScalingBase::scalingBuffer[srcY * ScalingBase::bufferWidth + srcX]; + } + } + } + + void ScaleRotating() requires(Scaling) { + const std::uint32_t dstWidth = scaled.size.x; + const std::uint32_t dstHeight = scaled.size.y; + + const float c2 = std::abs(std::cos(RotatingBase::rotation)); + const float s2 = std::abs(std::sin(RotatingBase::rotation)); + + const float rotatedWidth = dstWidth * c2 + dstHeight * s2; + const float rotatedHeight = dstWidth * s2 + dstHeight * c2; + + const std::uint32_t diffX = static_cast(std::ceil((rotatedWidth - dstWidth) * 0.5)); + const std::uint32_t diffY = static_cast(std::ceil((rotatedHeight - dstHeight) * 0.5)); + + scaled.size.x += diffX + diffX; + scaled.size.y += diffY + diffY; + + scaled.position.x -= diffX; + scaled.position.y -= diffY; + + buffer.clear(); + buffer.resize(scaled.size.x * scaled.size.y); + + // Destination center + const float dstCx = (static_cast(scaled.size.x) - 1.0) * 0.5; + const float dstCy = (static_cast(scaled.size.y) - 1.0) * 0.5; + + // Source center + const float srcCx = (static_cast(ScalingBase::bufferWidth) - 1.0) * 0.5; + const float srcCy = (static_cast(ScalingBase::bufferHeight) - 1.0) * 0.5; + + const float c = std::cos(RotatingBase::rotation); + const float s = std::sin(RotatingBase::rotation); + + // Scale factors (destination → source) + const float scaleX = static_cast(ScalingBase::bufferWidth) / dstWidth; + const float scaleY = static_cast(ScalingBase::bufferHeight) / dstHeight; + + for (std::uint32_t yB = 0; yB < scaled.size.y; ++yB) { + for (std::uint32_t xB = 0; xB < scaled.size.x; ++xB) { + + // ---- Destination pixel relative to center ---- + const float dx = (static_cast(xB) - dstCx) * scaleX; + const float dy = (static_cast(yB) - dstCy) * scaleY; + + // ---- Inverse rotation ---- + const float sx = (c * dx - s * dy) + srcCx; + const float sy = (s * dx + c * dy) + srcCy; + + // ---- Nearest neighbour sampling ---- + const std::int32_t srcX = static_cast(std::round(sx)); + const std::int32_t srcY = static_cast(std::round(sy)); + + if (srcX >= 0 && srcX < ScalingBase::bufferWidth && srcY >= 0 && srcY < ScalingBase::bufferHeight) { + buffer[yB * scaled.size.x + xB] = ScalingBase::scalingBuffer[srcY * ScalingBase::bufferWidth + srcX]; + } + } + } + } + + void UpdatePosition(Rendertarget& renderer, ScaleData2D oldScale) { + if constexpr(Scaling && !Rotating) { + if(oldScale.size.x != scaled.size.x || oldScale.size.y != scaled.size.y) { + buffer.resize(scaled.size.x * scaled.size.y); + ScaleNearestNeighbor(); + renderer.AddDirtyRect(oldScale); + renderer.AddDirtyRect(scaled); + } else if(oldScale.position.x != scaled.position.x || oldScale.position.y != scaled.position.y) { + renderer.AddDirtyRect(oldScale); + renderer.AddDirtyRect(scaled); + if(ScalingBase::bufferUpdated) { + ScaleNearestNeighbor(); + ScalingBase::bufferUpdated = false; + } + } else if(ScalingBase::bufferUpdated) { + ScaleNearestNeighbor(); + ScalingBase::bufferUpdated = false; + } + } else if constexpr(Rotating) { + if(oldScale.size.x != scaled.size.x || oldScale.size.y != scaled.size.y) { + buffer.resize(scaled.size.x * scaled.size.y); + ScaleRotating(); + renderer.AddDirtyRect(oldScale); + renderer.AddDirtyRect(scaled); + } else if(oldScale.position.x != scaled.position.x || oldScale.position.y != scaled.position.y) { + renderer.AddDirtyRect(oldScale); + renderer.AddDirtyRect(scaled); + if(ScalingBase::bufferUpdated || RotatingBase::rotationUpdated) { + ScaleRotating(); + ScalingBase::bufferUpdated = false; + RotatingBase::rotationUpdated = false; + } + } else if(ScalingBase::bufferUpdated || RotatingBase::rotationUpdated) { + ScaleRotating(); + ScalingBase::bufferUpdated = false; + RotatingBase::rotationUpdated = false; + } + } else { + if(oldScale.size.x != scaled.size.x || oldScale.size.y != scaled.size.y) { + buffer.resize(scaled.size.x * scaled.size.y); + renderer.AddDirtyRect(oldScale); + renderer.AddDirtyRect(scaled); + } + if(oldScale.position.x != scaled.position.x || oldScale.position.y != scaled.position.y) { + renderer.AddDirtyRect(oldScale); + renderer.AddDirtyRect(scaled); + } + } + } + + void UpdatePosition(Rendertarget& window) override { + ScaleData2D oldScale = scaled; + ScaleElement(window.transform); + UpdatePosition(window, oldScale); + for(Transform2D* child : children) { + child->UpdatePosition(window, *this); + } + } + + void UpdatePosition(Rendertarget& window, Transform2D& parent) override { + ScaleData2D oldScale = scaled; + ScaleElement(parent); + UpdatePosition(window, oldScale); + for(Transform2D* child : children) { + child->UpdatePosition(window, *this); + } + } + + std::vector ResizeText(Window& window, const std::string_view text, float size, Font& font, TextOverflowMode overflowMode = TextOverflowMode::Clip, TextScaleMode scaleMode = TextScaleMode::None, Transform2D* parent = nullptr) { + float scale = stbtt_ScaleForPixelHeight(&font.font, size); + int baseline = (int)(font.ascent * scale); + + std::vector lines; + std::string_view remaining = text; + + std::uint32_t lineHeight = (font.ascent - font.descent) * scale; + + if(overflowMode == TextOverflowMode::Clip) { + while (!remaining.empty()) { + // Find next newline or end of string + auto newlinePos = remaining.find('\n'); + if (newlinePos != std::string_view::npos) { + lines.emplace_back(remaining.substr(0, newlinePos)); + remaining = remaining.substr(newlinePos + 1); + } else { + lines.emplace_back(remaining); + break; + } + } + std::uint32_t maxWidth = 0; + + for(const std::string_view line: lines) { + std::uint32_t lineWidth = 0; + for (const char c : line) { + int advance, lsb; + stbtt_GetCodepointHMetrics(&font.font, c, &advance, &lsb); + lineWidth += (int)(advance * scale); + } + if(lineWidth > maxWidth) { + maxWidth = lineWidth; + } + } + + + if(scaleMode == TextScaleMode::Element) { + std::int32_t logicalPerPixelY = anchor.height / scaled.size.y; + std::int32_t oldHeight = anchor.height; + std::int32_t logicalPerPixelX = anchor.width / scaled.size.x; + std::int32_t oldwidth = anchor.width; + anchor.height = lineHeight * logicalPerPixelY; + anchor.width = maxWidth * logicalPerPixelX; + if(oldHeight != anchor.height || oldwidth != anchor.width) { + if(parent) { + UpdatePosition(window, *parent); + } else { + UpdatePosition(window); + } + } + } else if(scaleMode == TextScaleMode::Font) { + //todo + } else if(scaleMode == TextScaleMode::Buffer) { + if constexpr(Scaling && Owning) { + std::uint32_t neededHeight = lines.size() * lineHeight; + if(neededHeight != ScalingBase::bufferHeight || maxWidth != ScalingBase::bufferWidth) { + ScalingBase::bufferHeight = neededHeight; + ScalingBase::bufferWidth = maxWidth; + ScalingBase::bufferUpdated = true; + ScalingBase::scalingBuffer.resize(neededHeight*maxWidth); + } + } + } else { + if constexpr(Scaling) { + lines.resize(ScalingBase::bufferHeight / lines.size()); + } else { + lines.resize(scaled.size.y / lines.size()); + } + } + } else { + while (!remaining.empty()) { + std::string_view line; + auto newlinePos = remaining.find('\n'); + if (newlinePos != std::string_view::npos) { + line = remaining.substr(0, newlinePos); + remaining = remaining.substr(newlinePos + 1); + } else { + line = remaining; + remaining = ""; + } + + std::uint32_t lineWidth = 0; + std::size_t lastWrapPos = 0; // position of last space that can be used to wrap + std::size_t startPos = 0; + + for (std::size_t i = 0; i < line.size(); ++i) { + char c = line[i]; + + // get width of this character + int advance, lsb; + stbtt_GetCodepointHMetrics(&font.font, c, &advance, &lsb); + lineWidth += (std::uint32_t)(advance * scale); + + // remember last space for wrapping + if (c == ' ') { + lastWrapPos = i; + } + + // if line exceeds width, wrap + if (lineWidth > scaled.size.x) { + std::size_t wrapPos; + if (lastWrapPos > startPos) { + wrapPos = lastWrapPos; // wrap at last space + } else { + wrapPos = i; // no space, hard wrap + } + + // push the line up to wrapPos + lines.push_back(line.substr(startPos, wrapPos - startPos)); + + // skip any spaces at the beginning of next line + startPos = wrapPos; + while (startPos < line.size() && line[startPos] == ' ') { + ++startPos; + } + + // reset width and i + lineWidth = 0; + i = startPos - 1; // -1 because loop will increment i + } + } + + // add the remaining part of the line + if (startPos < line.size()) { + lines.push_back(line.substr(startPos)); + } + } + + if(scaleMode == TextScaleMode::Element) { + float logicalPerPixelY = anchor.height / scaled.size.y; + float oldHeight = anchor.height; + anchor.height = lineHeight * logicalPerPixelY; + if(oldHeight != anchor.height) { + if(parent) { + UpdatePosition(window, *parent); + } else { + UpdatePosition(window); + } + } + } else if(scaleMode == TextScaleMode::Font) { + //todo + } else if(scaleMode == TextScaleMode::Buffer) { + if constexpr(Scaling && Owning) { + float neededHeight = lines.size() * lineHeight; + if(neededHeight != ScalingBase::bufferHeight) { + ScalingBase::bufferHeight = neededHeight; + ScalingBase::bufferUpdated = true; + ScalingBase::scalingBuffer.resize(neededHeight*ScalingBase::bufferWidth); + } + } + } else { + if constexpr(Scaling) { + lines.resize(ScalingBase::bufferHeight / lines.size()); + } else { + lines.resize(scaled.size.y / lines.size()); + } + } + } + + return lines; + } + void RenderText(Window& window, std::span lines, float size, Vector color, Font& font, TextAlignment alignment = TextAlignment::Left, std::uint32_t offsetX = 0, std::uint32_t offsetY = 0) { + float scale = stbtt_ScaleForPixelHeight(&font.font, size); + int baseline = (int)(font.ascent * scale); + std::uint32_t lineHeight = (font.ascent - font.descent) * scale; + std::uint32_t currentY = baseline; + for(std::string_view line : lines) { + + std::uint32_t lineWidth = 0; + for (const char c : line) { + int advance, lsb; + stbtt_GetCodepointHMetrics(&font.font, c, &advance, &lsb); + lineWidth += (int)(advance * scale); + } + + std::uint32_t startX = 0; + switch (alignment) { + case TextAlignment::Left: + startX = 0; + break; + case TextAlignment::Center: + startX = (scaled.size.x - lineWidth) / 2; + break; + case TextAlignment::Right: + startX = scaled.size.x - lineWidth; + break; + } + std::uint32_t x = startX; + + for (std::size_t i = 0; i < line.size(); ++i) { + int codepoint = line[i]; + + int ax; + int lsb; + stbtt_GetCodepointHMetrics(&font.font, codepoint, &ax, &lsb); + + int c_x1, c_y1, c_x2, c_y2; + stbtt_GetCodepointBitmapBox(&font.font, codepoint, scale, scale, &c_x1, &c_y1, &c_x2, &c_y2); + + int w = c_x2 - c_x1; + int h = c_y2 - c_y1; + + std::vector bitmap(w * h); + stbtt_MakeCodepointBitmap(&font.font, bitmap.data(), w, h, w, scale, scale, codepoint); + + // Only render characters that fit within the scaled bounds + for (int j = 0; j < h; j++) { + for (int i = 0; i < w; i++) { + int bufferX = x + i + c_x1 + offsetX; + int bufferY = currentY + j + c_y1 + offsetY; + + // Only draw pixels that are within our scaled buffer bounds + if constexpr(Scaling) { + if (bufferX >= 0 && bufferX < ScalingBase::bufferWidth && bufferY >= 0 && bufferY < ScalingBase::bufferHeight) { + ScalingBase::scalingBuffer[bufferY * ScalingBase::bufferWidth + bufferX] = {color.r, color.g, color.b, bitmap[j * w + i]}; + } + } else { + if (bufferX >= 0 && bufferX < (int)scaled.size.x && bufferY >= 0 && bufferY < (int)scaled.size.y) { + buffer[bufferY * scaled.size.x + bufferX] = {color.r, color.g, color.b, bitmap[j * w + i]}; + } + } + } + } + + x += (int)(ax * scale); + + if (i + 1 < line.size()) { + x += (int)stbtt_GetCodepointKernAdvance(&font.font, codepoint, line[i+1]); + } + } + currentY += lineHeight; + } + } + }; +} \ No newline at end of file diff --git a/interfaces/Crafter.Graphics-RenderingElement3DVulkan.cppm b/interfaces/Crafter.Graphics-RenderingElement3D.cppm similarity index 86% rename from interfaces/Crafter.Graphics-RenderingElement3DVulkan.cppm rename to interfaces/Crafter.Graphics-RenderingElement3D.cppm index eade599..2931cc4 100644 --- a/interfaces/Crafter.Graphics-RenderingElement3DVulkan.cppm +++ b/interfaces/Crafter.Graphics-RenderingElement3D.cppm @@ -18,28 +18,27 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ module; -#ifdef CRAFTER_GRAPHICS_VULKAN +#ifdef CRAFTER_GRAPHICS_RENDERER_VULKAN #include "vulkan/vulkan.h" #endif -export module Crafter.Graphics:RenderingElement3DVulkan; -#ifdef CRAFTER_GRAPHICS_VULKAN +export module Crafter.Graphics:RenderingElement3D; +#ifdef CRAFTER_GRAPHICS_RENDERER_VULKAN import std; import :Mesh; import :VulkanBuffer; import Crafter.Math; export namespace Crafter { - struct TlasWithBuffer { VulkanBuffer buffer; VkAccelerationStructureKHR accelerationStructure; VulkanBuffer instanceBuffer; }; - class RenderingElement3DVulkan { + class RenderingElement3D { public: VkAccelerationStructureInstanceKHR instance; - static std::vector elements; + static std::vector elements; inline static VulkanBuffer scratchBuffer; inline static std::vector tlases; static void BuildTLAS(VkCommandBuffer cmd, std::uint32_t index); diff --git a/interfaces/Crafter.Graphics-Rendertarget.cppm b/interfaces/Crafter.Graphics-Rendertarget.cppm new file mode 100644 index 0000000..d07c03e --- /dev/null +++ b/interfaces/Crafter.Graphics-Rendertarget.cppm @@ -0,0 +1,43 @@ +/* +Crafter®.Graphics +Copyright (C) 2026 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 +*/ +export module Crafter.Graphics:Rendertarget; +import Crafter.Math; +import std; +import :Types; +import :Transform2D; + +export namespace Crafter { + struct RenderingElement2DBase; + struct Window; + struct Rendertarget { + Vector* buffer; + #ifdef CRAFTER_TIMING + std::vector> renderTimings; + #endif + Transform2D transform; + std::int32_t sizeX; + std::int32_t sizeY; + std::vector elements; + std::vector dirtyRects; + Rendertarget(std::int32_t sizeX, std::int32_t sizeY); + void RenderElement(RenderingElement2DBase* transform); + void AddDirtyRect(ScaleData2D rect); + void Render(); + }; +} \ No newline at end of file diff --git a/interfaces/Crafter.Graphics-SamplerVulkan.cppm b/interfaces/Crafter.Graphics-SamplerVulkan.cppm index 42a0a1d..ea688f1 100644 --- a/interfaces/Crafter.Graphics-SamplerVulkan.cppm +++ b/interfaces/Crafter.Graphics-SamplerVulkan.cppm @@ -19,7 +19,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA module; -#ifdef CRAFTER_GRAPHICS_VULKAN +#ifdef CRAFTER_GRAPHICS_RENDERER_VULKAN #include "vulkan/vulkan.h" #endif @@ -29,7 +29,7 @@ import :VulkanBuffer; import :ImageVulkan; export namespace Crafter { - #ifdef CRAFTER_GRAPHICS_VULKAN + #ifdef CRAFTER_GRAPHICS_RENDERER_VULKAN template class SamplerVulkan { public: @@ -53,7 +53,7 @@ export namespace Crafter { samplerInfo.mipLodBias = 0.0f; samplerInfo.minLod = 0.0f; samplerInfo.maxLod = VK_LOD_CLAMP_NONE; - VulkanDevice::CheckVkResult(vkCreateSampler(VulkanDevice::device, &samplerInfo, nullptr, &textureSampler)); + Device::CheckVkResult(vkCreateSampler(Device::device, &samplerInfo, nullptr, &textureSampler)); imageInfo = { .sampler = textureSampler diff --git a/interfaces/Crafter.Graphics-ShaderBindingTableVulkan.cppm b/interfaces/Crafter.Graphics-ShaderBindingTableVulkan.cppm index af0edaa..54b9720 100644 --- a/interfaces/Crafter.Graphics-ShaderBindingTableVulkan.cppm +++ b/interfaces/Crafter.Graphics-ShaderBindingTableVulkan.cppm @@ -18,13 +18,13 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ module; -#ifdef CRAFTER_GRAPHICS_VULKAN +#ifdef CRAFTER_GRAPHICS_RENDERER_VULKAN #include "vulkan/vulkan.h" #endif export module Crafter.Graphics:ShaderBindingTableVulkan; -#ifdef CRAFTER_GRAPHICS_VULKAN +#ifdef CRAFTER_GRAPHICS_RENDERER_VULKAN import std; -import :VulkanDevice; +import :Device; import :VulkanBuffer; import :ShaderVulkan; import :Types; diff --git a/interfaces/Crafter.Graphics-ShaderVulkan.cppm b/interfaces/Crafter.Graphics-ShaderVulkan.cppm index 9dc2899..a571ded 100644 --- a/interfaces/Crafter.Graphics-ShaderVulkan.cppm +++ b/interfaces/Crafter.Graphics-ShaderVulkan.cppm @@ -18,13 +18,13 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ module; -#ifdef CRAFTER_GRAPHICS_VULKAN +#ifdef CRAFTER_GRAPHICS_RENDERER_VULKAN #include "vulkan/vulkan.h" #endif export module Crafter.Graphics:ShaderVulkan; -#ifdef CRAFTER_GRAPHICS_VULKAN +#ifdef CRAFTER_GRAPHICS_RENDERER_VULKAN import std; -import :VulkanDevice; +import :Device; import :Types; export namespace Crafter { @@ -66,7 +66,7 @@ export namespace Crafter { module_info.codeSize = spirv.size() * sizeof(uint32_t); module_info.pCode = spirv.data(); - VulkanDevice::CheckVkResult(vkCreateShaderModule(VulkanDevice::device, &module_info, nullptr, &shader)); + Device::CheckVkResult(vkCreateShaderModule(Device::device, &module_info, nullptr, &shader)); } }; @@ -104,7 +104,7 @@ export namespace Crafter { module_info.codeSize = spirv.size() * sizeof(uint32_t); module_info.pCode = spirv.data(); - VulkanDevice::CheckVkResult(vkCreateShaderModule(VulkanDevice::device, &module_info, nullptr, &shader)); + Device::CheckVkResult(vkCreateShaderModule(Device::device, &module_info, nullptr, &shader)); } }; } diff --git a/interfaces/Crafter.Graphics-Shm.cppm b/interfaces/Crafter.Graphics-Shm.cppm deleted file mode 100644 index 002b26f..0000000 --- a/interfaces/Crafter.Graphics-Shm.cppm +++ /dev/null @@ -1,33 +0,0 @@ -/* -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 - -export module Crafter.Graphics:Shm; -import std; - -export namespace Crafter { - #ifdef CRAFTER_BUILD_CONFIGURATION_TARGET_x86_64_pc_linux_gnu - void randname(char *buf); - int anonymous_shm_open(); - int create_shm_file(off_t size); - #endif -} \ No newline at end of file diff --git a/interfaces/Crafter.Graphics-Transform.cppm b/interfaces/Crafter.Graphics-Transform.cppm deleted file mode 100644 index fc8fc8f..0000000 --- a/interfaces/Crafter.Graphics-Transform.cppm +++ /dev/null @@ -1,51 +0,0 @@ -/* -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 -*/ - -export module Crafter.Graphics:Transform; -import std; -import :Types; - -export namespace Crafter { - class Window; - struct Anchor { - std::int32_t x; - std::int32_t y; - std::uint32_t width; - std::uint32_t height; - std::int32_t offsetX; - std::int32_t offsetY; - std::int32_t z; - bool maintainAspectRatio; - Anchor() = default; - Anchor(std::int32_t x, std::int32_t y, std::uint32_t width, std::uint32_t height, std::int32_t offsetX, std::int32_t offsetY, std::int32_t z, bool maintainAspectRatio = false); - }; - class Transform { - public: - Anchor anchor; - ScaleData scaled; - std::vector children; - Transform(Anchor anchor); - Transform(Transform&) = delete; - Transform(Transform&&) = delete; - Transform& operator=(Transform&) = delete; - virtual ~Transform() = default; - virtual void UpdatePosition(Window& window); - virtual void UpdatePosition(Window& window, Transform& parent); - }; -} \ No newline at end of file diff --git a/interfaces/Crafter.Graphics-Transform2D.cppm b/interfaces/Crafter.Graphics-Transform2D.cppm new file mode 100644 index 0000000..86e5abf --- /dev/null +++ b/interfaces/Crafter.Graphics-Transform2D.cppm @@ -0,0 +1,51 @@ +/* +Crafter®.Graphics +Copyright (C) 2026 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 +*/ + +export module Crafter.Graphics:Transform2D; +import std; +import :Types; + +export namespace Crafter { + struct Rendertarget; + struct Anchor2D { + float x; + float y; + float width; + float height; + float offsetX; + float offsetY; + std::int32_t z; + bool maintainAspectRatio; + Anchor2D() = default; + Anchor2D(float x, float y, float width, float height, float offsetX, float offsetY, std::int32_t z, bool maintainAspectRatio = false); + }; + struct Transform2D { + Anchor2D anchor; + ScaleData2D scaled; + std::vector children; + Transform2D(Anchor2D anchor); + Transform2D(Transform2D&) = delete; + Transform2D(Transform2D&&) = delete; + Transform2D& operator=(Transform2D&) = delete; + virtual ~Transform2D() = default; + void ScaleElement(Transform2D& parent); + virtual void UpdatePosition(Rendertarget& window); + virtual void UpdatePosition(Rendertarget& window, Transform2D& parent); + }; +} \ No newline at end of file diff --git a/interfaces/Crafter.Graphics-Types.cppm b/interfaces/Crafter.Graphics-Types.cppm index 7ea1669..6cd1002 100644 --- a/interfaces/Crafter.Graphics-Types.cppm +++ b/interfaces/Crafter.Graphics-Types.cppm @@ -1,6 +1,6 @@ /* Crafter®.Graphics -Copyright (C) 2025 Catcrafts® +Copyright (C) 2026 Catcrafts® Catcrafts.net This library is free software; you can redistribute it and/or @@ -18,41 +18,23 @@ 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; -#ifdef CRAFTER_GRAPHICS_VULKAN +#ifdef CRAFTER_GRAPHICS_RENDERER_VULKAN #include "vulkan/vulkan.h" #endif export module Crafter.Graphics:Types; import std; +import Crafter.Math; export namespace Crafter { - struct MousePoint { - std::uint32_t x; - std::uint32_t y; - }; - - struct MouseDelta { - std::int64_t x; - std::int64_t y; - }; - struct MouseMoveEvent { - MousePoint lastMousePos; - MousePoint currentMousePos; - MouseDelta mouseDelta; + Vector lastMousePos; + Vector currentMousePos; + Vector mouseDelta; }; - struct ScaleData { - std::int32_t x; - std::int32_t y; - std::int32_t width; - std::int32_t height; - }; - - struct ScaleDataBoundless { - std::uint32_t x; - std::uint32_t y; - std::uint32_t width; - std::uint32_t height; + struct ScaleData2D { + Vector position; + Vector size; }; struct ClipRect { @@ -62,67 +44,6 @@ export namespace Crafter { std::int32_t bottom; }; - struct __attribute__((packed)) Pixel_BU8_GU8_RU8_AU8 { - std::uint8_t b; - std::uint8_t g; - std::uint8_t r; - std::uint8_t a; - }; - - struct __attribute__((packed)) Pixel_RU8_GU8_BU8_AU8 { - std::uint8_t r; - std::uint8_t g; - std::uint8_t b; - std::uint8_t a; - }; - - struct __attribute__((packed)) Pixel_RU8_GU8_BU8 { - std::uint8_t r; - std::uint8_t g; - std::uint8_t b; - }; - - // struct __attribute__((packed)) Vertex { - // float x; - // float y; - // float z; - // float w; - // }; - // struct __attribute__((packed)) VertexUV { - // float x; - // float y; - // float z; - // float w; - - // float u; - // float v; - - // float pad[2]; - // }; - - // struct __attribute__((packed)) VertexRGBA { - // float x; - // float y; - // float z; - // float w; - - // float r; - // float g; - // float b; - // float a; - // }; - - // struct __attribute__((packed)) HeightRGBA { - // float height; - - // float pad[3]; - - // float r; - // float g; - // float b; - // float a; - // }; - struct FrameTime { std::chrono::time_point now; std::chrono::duration delta; @@ -134,191 +55,6 @@ export namespace Crafter { Transparent // Color blending is used }; - - constexpr std::int8_t BOUND8 = 9; - constexpr std::int8_t SCALE8 = std::numeric_limits::max() / BOUND8; - constexpr std::uint8_t SCALEBOUNDLESS8 = std::numeric_limits::max(); - - constexpr double SCALEDOUBLE8 = static_cast(std::numeric_limits::max() / BOUND8); - constexpr double SCALEBOUNDLESSDOUBLE8 = static_cast(std::numeric_limits::max()); - - constexpr std::int16_t BOUND16 = 9; - constexpr std::int16_t SCALE16 = std::numeric_limits::max() / BOUND16; - constexpr std::uint16_t SCALEBOUNDLESS16 = std::numeric_limits::max(); - - constexpr double SCALEDOUBLE16 = static_cast(std::numeric_limits::max() / BOUND16); - constexpr double SCALEBOUNDLESSDOUBLE16 = static_cast(std::numeric_limits::max()); - - constexpr std::int32_t BOUND32 = 9; - constexpr std::int32_t SCALE32 = std::numeric_limits::max() / BOUND32; - constexpr std::uint32_t SCALEBOUNDLESS32 = std::numeric_limits::max(); - - constexpr double SCALEDOUBLE32 = static_cast(std::numeric_limits::max() / BOUND32); - constexpr double SCALEBOUNDLESSDOUBLE32 = static_cast(std::numeric_limits::max()); - - constexpr std::int64_t BOUND64 = 9; - constexpr std::int64_t SCALE64 = std::numeric_limits::max() / BOUND64; - constexpr std::uint64_t SCALEBOUNDLESS64 = std::numeric_limits::max(); - - constexpr double SCALEDOUBLE64 = static_cast(std::numeric_limits::max() / BOUND64); - constexpr double SCALEBOUNDLESSDOUBLE64 = static_cast(std::numeric_limits::max()); - - template - constexpr T FractionalToMapped(double f) requires(std::is_integral_v) { - if constexpr (std::is_same_v || std::is_same_v) { - return T(f * SCALEDOUBLE8); - } else if constexpr (std::is_same_v || std::is_same_v) { - return T(f * SCALEDOUBLE16); - } else if constexpr (std::is_same_v || std::is_same_v) { - return T(f * SCALEDOUBLE32); - } else { - return T(f * SCALEDOUBLE64); - } - } - - template - constexpr T FractionalToMappedBoundless(double f) requires(std::is_integral_v) { - if constexpr (std::is_same_v || std::is_same_v) { - return T(f * SCALEBOUNDLESSDOUBLE8); - } else if constexpr (std::is_same_v || std::is_same_v) { - return T(f * SCALEBOUNDLESSDOUBLE16); - } else if constexpr (std::is_same_v || std::is_same_v) { - return T(f * SCALEBOUNDLESSDOUBLE32); - } else { - return T(f * SCALEBOUNDLESSDOUBLE64); - } - } - - template - constexpr double MappedToFractional(T mapped) requires(std::is_integral_v) { - if constexpr (std::is_same_v || std::is_same_v) { - return mapped / SCALEDOUBLE8; - } else if constexpr (std::is_same_v || std::is_same_v) { - return mapped / SCALEDOUBLE16; - } else if constexpr (std::is_same_v || std::is_same_v) { - return mapped / SCALEDOUBLE32; - } else { - return mapped / SCALEDOUBLE64; - } - } - - template - constexpr double MappedToFractionalBoundless(T mapped) requires(std::is_integral_v) { - if constexpr (std::is_same_v || std::is_same_v) { - return mapped / SCALEBOUNDLESSDOUBLE8; - } else if constexpr (std::is_same_v || std::is_same_v) { - return mapped / SCALEBOUNDLESSDOUBLE16; - } else if constexpr (std::is_same_v || std::is_same_v) { - return mapped / SCALEBOUNDLESSDOUBLE32; - } else { - return mapped / SCALEBOUNDLESSDOUBLE64; - } - } - - template - constexpr double MappedToNDCBoundless(T mapped, std::uint32_t size) requires(std::is_integral_v) { - if constexpr (std::is_same_v || std::is_same_v) { - return mapped / SCALEBOUNDLESSDOUBLE8 - 0.5; - } else if constexpr (std::is_same_v || std::is_same_v) { - return mapped / SCALEBOUNDLESSDOUBLE16 - 0.5; - } else if constexpr (std::is_same_v || std::is_same_v) { - return mapped / SCALEBOUNDLESSDOUBLE32 - 0.5; - } else { - return mapped / SCALEBOUNDLESSDOUBLE64 - 0.5; - } - } - - template - constexpr T MappedToAbsolute(T mapped, T2 absolute) requires(std::is_integral_v) { - if constexpr (std::is_same_v || std::is_same_v) { - return static_cast(mapped) * absolute / SCALE8; - } else if constexpr (std::is_same_v || std::is_same_v) { - return static_cast(mapped) * absolute / SCALE16; - } else if constexpr (std::is_same_v || std::is_same_v) { - return static_cast(mapped) * absolute / SCALE32; - } else { - return static_cast<__int128>(mapped) * absolute / SCALE64; - } - } - - template - constexpr T MappedToAbsoluteBoundless(T mapped, T2 absolute) requires(std::is_integral_v) { - if constexpr (std::is_same_v || std::is_same_v) { - return static_cast(mapped) * absolute / SCALEBOUNDLESS8; - } else if constexpr (std::is_same_v || std::is_same_v) { - return static_cast(mapped) * absolute / SCALEBOUNDLESS16; - } else if constexpr (std::is_same_v || std::is_same_v) { - return static_cast(mapped) * absolute / SCALEBOUNDLESS32; - } else { - return static_cast(mapped) * absolute / SCALEBOUNDLESS64; - } - } - - // template - // constexpr T PixelToMappedBoundless(T pixel, T2 screen) requires(std::is_integral_v) { - // if constexpr (std::is_same_v) { - // return (static_cast(relative) * SCALE8) / (static_cast(absolute) * SCALE8); - // } else if constexpr (std::is_same_v) { - // return (static_cast(relative) * SCALE16) / (static_cast(absolute) * SCALE16); - // } else if constexpr (std::is_same_v) { - // return (static_cast(relative) * SCALE32) / (static_cast(absolute) * SCALE32); - // } else { - // return (static_cast(relative) * SCALE32) / (static_cast(absolute) * SCALE32); - // } - // } - - template - constexpr T AbsoluteToMapped(T absolute, T2 mapped) { - if constexpr (std::is_same_v || std::is_same_v) { - return static_cast(absolute) * SCALE8 / mapped; - } else if constexpr (std::is_same_v || std::is_same_v ) { - return static_cast(absolute) * SCALE16 / mapped; - } else if constexpr (std::is_same_v|| std::is_same_v) { - return static_cast(absolute) * SCALE32 / mapped; - } else { - return static_cast<__int128>(absolute) * SCALE64 / mapped; - } - } - - template - constexpr T AbsoluteToMappedBoundless(T absolute, T2 mapped) { - if constexpr (std::is_same_v || std::is_same_v ) { - return static_cast(absolute) * SCALEBOUNDLESS8 / mapped; - } else if constexpr (std::is_same_v || std::is_same_v) { - return static_cast(absolute) * SCALEBOUNDLESS16 / mapped; - } else if constexpr (std::is_same_v || std::is_same_v) { - return static_cast(absolute) * SCALEBOUNDLESS32 / mapped; - } else { - return static_cast(absolute) * SCALEBOUNDLESS64 / mapped; - } - } - - template - constexpr T BoundToBoundless(T mapped) { - if constexpr (std::is_same_v || std::is_same_v) { - return mapped * BOUND8 * 2; - } else if constexpr (std::is_same_v || std::is_same_v) { - return mapped * BOUND16 * 2; - } else if constexpr (std::is_same_v || std::is_same_v) { - return mapped * BOUND32 * 2; - } else { - return mapped * BOUND64 * 2; - } - } - - template - constexpr T BoundlessToBound(T mapped) { - if constexpr (std::is_same_v || std::is_same_v ) { - return mapped / 2; - } else if constexpr (std::is_same_v || std::is_same_v) { - return mapped / 2; - } else if constexpr (std::is_same_v || std::is_same_v) { - return mapped / 2; - } else { - return mapped / 2; - } - } - enum class CrafterKeys { // Alphabetic keys A, B, C, D, E, F, G, H, I, J, K, L, M, @@ -372,7 +108,7 @@ export namespace Crafter { return std::tan(fov * std::numbers::pi / 360.0); } - #ifdef CRAFTER_GRAPHICS_VULKAN + #ifdef CRAFTER_GRAPHICS_RENDERER_VULKAN struct DescriptorBinding { VkDescriptorType type; std::uint32_t slot; diff --git a/interfaces/Crafter.Graphics-VulkanBuffer.cppm b/interfaces/Crafter.Graphics-VulkanBuffer.cppm index 96ae436..fdaef60 100644 --- a/interfaces/Crafter.Graphics-VulkanBuffer.cppm +++ b/interfaces/Crafter.Graphics-VulkanBuffer.cppm @@ -19,14 +19,14 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA module; -#ifdef CRAFTER_GRAPHICS_VULKAN +#ifdef CRAFTER_GRAPHICS_RENDERER_VULKAN #include "vulkan/vulkan.h" #endif export module Crafter.Graphics:VulkanBuffer; -#ifdef CRAFTER_GRAPHICS_VULKAN +#ifdef CRAFTER_GRAPHICS_RENDERER_VULKAN import std; -import :VulkanDevice; +import :Device; namespace Crafter { export class VulkanBufferBase { @@ -95,14 +95,14 @@ namespace Crafter { bufferCreateInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; bufferCreateInfo.usage = usageFlags; bufferCreateInfo.size = sizeof(T)*count; - VulkanDevice::CheckVkResult(vkCreateBuffer(VulkanDevice::device, &bufferCreateInfo, nullptr, &buffer)); + Device::CheckVkResult(vkCreateBuffer(Device::device, &bufferCreateInfo, nullptr, &buffer)); VkMemoryRequirements memReqs; - vkGetBufferMemoryRequirements(VulkanDevice::device, buffer, &memReqs); + vkGetBufferMemoryRequirements(Device::device, buffer, &memReqs); VkMemoryAllocateInfo memAlloc { .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, .allocationSize = memReqs.size, - .memoryTypeIndex = VulkanDevice::GetMemoryType(memReqs.memoryTypeBits, memoryPropertyFlags) + .memoryTypeIndex = Device::GetMemoryType(memReqs.memoryTypeBits, memoryPropertyFlags) }; if constexpr(Adressable) { VkMemoryAllocateFlagsInfoKHR allocFlagsInfo { @@ -110,36 +110,36 @@ namespace Crafter { .flags = VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT_KHR, }; memAlloc.pNext = &allocFlagsInfo; - VulkanDevice::CheckVkResult(vkAllocateMemory(VulkanDevice::device, &memAlloc, nullptr, &memory)); + Device::CheckVkResult(vkAllocateMemory(Device::device, &memAlloc, nullptr, &memory)); } else { - VulkanDevice::CheckVkResult(vkAllocateMemory(VulkanDevice::device, &memAlloc, nullptr, &memory)); + Device::CheckVkResult(vkAllocateMemory(Device::device, &memAlloc, nullptr, &memory)); } descriptor.offset = 0; descriptor.buffer = buffer; descriptor.range = VK_WHOLE_SIZE; - VulkanDevice::CheckVkResult(vkBindBufferMemory(VulkanDevice::device, buffer, memory, 0)); + Device::CheckVkResult(vkBindBufferMemory(Device::device, buffer, memory, 0)); if constexpr(Adressable) { VkBufferDeviceAddressInfo addressInfo = { .sType = VK_STRUCTURE_TYPE_BUFFER_DEVICE_ADDRESS_INFO, .buffer = buffer }; - VulkanBufferAdressableConditional::address = vkGetBufferDeviceAddress(VulkanDevice::device, &addressInfo); + VulkanBufferAdressableConditional::address = vkGetBufferDeviceAddress(Device::device, &addressInfo); } if constexpr(Mapped) { - VulkanDevice::CheckVkResult(vkMapMemory(VulkanDevice::device, memory, 0, memReqs.size, 0, reinterpret_cast(&(VulkanBufferMappedConditional::value)))); + Device::CheckVkResult(vkMapMemory(Device::device, memory, 0, memReqs.size, 0, reinterpret_cast(&(VulkanBufferMappedConditional::value)))); } } void Clear() { if constexpr(Mapped) { - vkUnmapMemory(VulkanDevice::device, memory); + vkUnmapMemory(Device::device, memory); } - vkDestroyBuffer(VulkanDevice::device, buffer, nullptr); - vkFreeMemory(VulkanDevice::device, memory, nullptr); + vkDestroyBuffer(Device::device, buffer, nullptr); + vkFreeMemory(Device::device, memory, nullptr); buffer = VK_NULL_HANDLE; if constexpr(Staged) { delete VulkanBufferMappedConditional::stagingBuffer; @@ -201,7 +201,7 @@ namespace Crafter { .offset = 0, .size = VK_WHOLE_SIZE }; - vkFlushMappedMemoryRanges(VulkanDevice::device, 1, &range); + vkFlushMappedMemoryRanges(Device::device, 1, &range); } void FlushDevice(VkCommandBuffer cmd, VkAccessFlags dstAccessMask, VkPipelineStageFlags dstStageMask) requires(Mapped) { @@ -235,7 +235,7 @@ namespace Crafter { .offset = 0, .size = VK_WHOLE_SIZE }; - vkInvalidateMappedMemoryRanges(VulkanDevice::device, 1, &range); + vkInvalidateMappedMemoryRanges(Device::device, 1, &range); } void FlushDevice(VkCommandBuffer cmd) requires(Staged) { diff --git a/interfaces/Crafter.Graphics-VulkanDevice.cppm b/interfaces/Crafter.Graphics-VulkanDevice.cppm deleted file mode 100644 index df87abd..0000000 --- a/interfaces/Crafter.Graphics-VulkanDevice.cppm +++ /dev/null @@ -1,59 +0,0 @@ -/* -Crafter®.Graphics -Copyright (C) 2026 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; -#ifdef CRAFTER_GRAPHICS_VULKAN -#include "vulkan/vulkan.h" -#endif -export module Crafter.Graphics:VulkanDevice; -#ifdef CRAFTER_GRAPHICS_VULKAN -import std; - -export namespace Crafter { - class VulkanDevice { - public: - inline static VkInstance instance = VK_NULL_HANDLE; - inline static VkDebugUtilsMessengerEXT debugMessenger = VK_NULL_HANDLE; - inline static VkPhysicalDevice physDevice = VK_NULL_HANDLE; - inline static VkDevice device = VK_NULL_HANDLE; - inline static std::uint32_t queueFamilyIndex = 0; - inline static VkQueue queue = VK_NULL_HANDLE; - inline static VkCommandPool commandPool = VK_NULL_HANDLE; - inline static VkSwapchainKHR swapchain = VK_NULL_HANDLE; - inline static PFN_vkCmdDrawMeshTasksEXT vkCmdDrawMeshTasksEXTProc; - inline static PFN_vkCmdBeginRenderingKHR vkCmdBeginRenderingKHRProc; - inline static PFN_vkCmdEndRenderingKHR vkCmdEndRenderingKHRProc; - inline static PFN_vkGetAccelerationStructureBuildSizesKHR vkGetAccelerationStructureBuildSizesKHR; - inline static PFN_vkCreateAccelerationStructureKHR vkCreateAccelerationStructureKHR; - inline static PFN_vkCmdBuildAccelerationStructuresKHR vkCmdBuildAccelerationStructuresKHR; - inline static PFN_vkGetAccelerationStructureDeviceAddressKHR vkGetAccelerationStructureDeviceAddressKHR; - inline static PFN_vkCreateRayTracingPipelinesKHR vkCreateRayTracingPipelinesKHR; - inline static PFN_vkGetRayTracingShaderGroupHandlesKHR vkGetRayTracingShaderGroupHandlesKHR; - inline static PFN_vkCmdTraceRaysKHR vkCmdTraceRaysKHR; - inline static VkPhysicalDeviceMemoryProperties memoryProperties; - inline static VkPhysicalDeviceRayTracingPipelinePropertiesKHR rayTracingProperties = { - .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RAY_TRACING_PIPELINE_PROPERTIES_KHR - }; - static void CreateDevice(); - static void CheckVkResult(VkResult result); - static std::uint32_t GetMemoryType(std::uint32_t typeBits, VkMemoryPropertyFlags properties); - }; -} - -#endif \ No newline at end of file diff --git a/interfaces/Crafter.Graphics-VulkanTransition.cppm b/interfaces/Crafter.Graphics-VulkanTransition.cppm index 82971d6..4ce05c2 100644 --- a/interfaces/Crafter.Graphics-VulkanTransition.cppm +++ b/interfaces/Crafter.Graphics-VulkanTransition.cppm @@ -18,12 +18,12 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ module; -#ifdef CRAFTER_GRAPHICS_VULKAN +#ifdef CRAFTER_GRAPHICS_RENDERER_VULKAN #include "vulkan/vulkan.h" #include #endif export module Crafter.Graphics:VulkanTransition; -#ifdef CRAFTER_GRAPHICS_VULKAN +#ifdef CRAFTER_GRAPHICS_RENDERER_VULKAN import std; export namespace Crafter { diff --git a/interfaces/Crafter.Graphics-Window.cppm b/interfaces/Crafter.Graphics-Window.cppm index f0d1a4b..65b3a1f 100644 --- a/interfaces/Crafter.Graphics-Window.cppm +++ b/interfaces/Crafter.Graphics-Window.cppm @@ -1,6 +1,6 @@ /* Crafter®.Graphics -Copyright (C) 2025 Catcrafts® +Copyright (C) 2026 Catcrafts® catcrafts.net This library is free software; you can redistribute it and/or @@ -18,7 +18,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ module; -#ifdef CRAFTER_GRAPHICS_WAYLAND +#ifdef CRAFTER_GRAPHICS_WINDOW_WAYLAND #include #include #include @@ -32,95 +32,46 @@ module; #include #include #include -#include #include #include #include #include #include #endif -#ifdef CRAFTER_GRAPHICS_VULKAN -#ifndef CRAFTER_GRAPHICS_WINDOWS -#include -#include -#include -#include -#include -#include "../lib/xdg-shell-client-protocol.h" -#include "../lib/wayland-xdg-decoration-unstable-v1-client-protocol.h" -#include "../lib/fractional-scale-v1.h" -#include "../lib/viewporter.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include +#ifdef CRAFTER_GRAPHICS_RENDERER_VULKAN #include "vulkan/vulkan.h" -#include "vulkan/vulkan_wayland.h" -#else -#include "vulkan/vulkan.h" -#endif #endif export module Crafter.Graphics:Window; import std; import :Types; +import :Rendertarget; +import :Transform2D; import Crafter.Event; -#ifdef CRAFTER_GRAPHICS_VULKAN +#ifdef CRAFTER_GRAPHICS_RENDERER_VULKAN import :PipelineRTVulkan; #endif export namespace Crafter { - class Transform; - class MouseElement; - class Window { - public: + #ifdef CRAFTER_GRAPHICS_RENDERER_VULKAN + struct Semaphores { + // Swap chain image presentation + VkSemaphore presentComplete; + // Command buffer submission and execution + VkSemaphore renderComplete; + }; + #endif + + struct MouseElement; + struct Window { FrameTime currentFrameTime; - std::int32_t width; - std::int32_t height; + std::uint32_t width; + std::uint32_t height; std::chrono::time_point lastFrameBegin; - std::vector elements; Event onClose; Event onUpdate; bool open = true; bool updating = false; - std::vector dirtyRects; - float scale; - - Window() = default; - Window(std::int32_t width, std::int32_t height); - Window(Window&) = delete; - Window(Window&&) = delete; - virtual ~Window() = default; - Window& operator=(const Window&) = delete; - void AddDirtyRect(ScaleData rect); - virtual void StartSync() = 0; - virtual void StartUpdate() = 0; - virtual void StopUpdate() = 0; - void ScaleElement(Transform& element, Transform& parent); - void ScaleElement(Transform& element); - void ScaleMouse(MouseElement& element, Transform& parent); - void ScaleMouse(MouseElement& element); - #ifdef CRAFTER_TIMING - std::chrono::nanoseconds totalUpdate; - std::vector*, std::chrono::nanoseconds>> updateTimings; - std::chrono::nanoseconds totalRender; - std::vector> renderTimings; - std::chrono::nanoseconds vblank; - std::chrono::nanoseconds totalFrame; - std::chrono::time_point frameEnd; - std::vector frameTimes; - void LogTiming(); - #endif - }; - - class WindowKeyboard { - public: bool heldkeys[static_cast(CrafterKeys::CrafterKeysMax)] = {}; Event onKeyDown[static_cast(CrafterKeys::CrafterKeysMax)]; Event onKeyHold[static_cast(CrafterKeys::CrafterKeysMax)]; @@ -128,134 +79,69 @@ export namespace Crafter { Event onAnyKeyDown; Event onAnyKeyHold; Event onAnyKeyUp; - }; - - class MouseElement; - class WindowMouse { - public: - Event onMouseRightClick; - Event onMouseLeftClick; - Event onMouseRightHold; - Event onMouseLeftHold; - Event onMouseRightRelease; - Event onMouseLeftRelease; - Event onMouseMove; - Event onMouseEnter; - Event onMouseLeave; + Event onMouseRightClick; + Event onMouseLeftClick; + Event onMouseRightHold; + Event onMouseLeftHold; + Event onMouseRightRelease; + Event onMouseLeftRelease; + Event onMouseMove; + Event onMouseEnter; + Event onMouseLeave; Event onMouseScroll; - MousePoint currentMousePos; - MousePoint lastMousePos; - MouseDelta mouseDelta; + Vector currentMousePos; + Vector lastMousePos; + Vector mouseDelta; bool mouseLeftHeld = false; bool mouseRightHeld = false; std::vector mouseElements; std::vector pendingMouseElements; - }; - class WindowTitle { - public: - virtual void SetTitle(const std::string_view title) = 0; - }; + Window(std::uint32_t width, std::uint32_t height); + Window(std::uint32_t width, std::uint32_t height, const std::string_view title); + Window(Window&) = delete; + Window(Window&&) = delete; + Window& operator=(const Window&) = delete; - class Transform; - class WindowFramebuffer : public Window { - public: - WindowFramebuffer() = default; - WindowFramebuffer(std::uint32_t width, std::uint32_t height); - virtual void Resize(std::uint32_t width, std::uint32_t height) = 0; - virtual void Write(Pixel_BU8_GU8_RU8_AU8* pixels) = 0; - virtual void Write(std::uint32_t x, std::uint32_t y, Pixel_BU8_GU8_RU8_AU8 pixel) = 0; - virtual Pixel_BU8_GU8_RU8_AU8 Read(std::uint32_t x, std::uint32_t y) const = 0; - virtual const Pixel_BU8_GU8_RU8_AU8* Read() const = 0; - virtual Pixel_BU8_GU8_RU8_AU8* Get() = 0; - virtual void Store() = 0; - virtual void Render() = 0; - }; + void StartSync(); + void StartUpdate(); + void StopUpdate(); + void SetTitle(const std::string_view title); + void Resize(std::uint32_t width, std::uint32_t height); + void Render(); + void Update(); - #ifdef CRAFTER_GRAPHICS_WAYLAND - class WindowWayland final : public WindowKeyboard, public WindowMouse, public WindowFramebuffer, public WindowTitle { - public: - Pixel_BU8_GU8_RU8_AU8* framebuffer = nullptr; - WindowWayland(std::uint32_t width, std::uint32_t height); - WindowWayland(std::uint32_t width, std::uint32_t height, const std::string_view title); - ~WindowWayland(); + #ifdef CRAFTER_TIMING + std::chrono::nanoseconds totalUpdate; + std::vector*, std::chrono::nanoseconds>> updateTimings; + std::chrono::nanoseconds totalRender; + std::chrono::nanoseconds vblank; + std::chrono::nanoseconds totalFrame; + std::chrono::time_point frameEnd; + std::vector frameTimes; + void LogTiming(); + #endif + + #ifdef CRAFTER_GRAPHICS_WINDOW_WAYLAND + float scale; + #ifdef CRAFTER_GRAPHICS_RENDERER_SOFTWARE + Rendertarget renderer; + #endif bool configured = false; - wl_shm* shm = nullptr; - wl_seat* seat = nullptr; - wp_fractional_scale_v1* wp_scale = nullptr; xdg_toplevel* xdgToplevel = nullptr; wp_viewport* wpViewport = nullptr; - wp_viewporter* wpViewporter = nullptr; - xdg_wm_base* xdgWmBase = nullptr; - zxdg_decoration_manager_v1* manager = nullptr; - wp_fractional_scale_manager_v1* fractionalScaleManager = nullptr; wl_surface* surface = nullptr; wl_buffer* buffer = nullptr; wl_buffer* backBuffer = nullptr; xdg_surface* xdgSurface = nullptr; - wl_display* display = nullptr; wl_callback* cb = nullptr; - xkb_keymap* xkb_keymap; - xkb_context* xkb_context = xkb_context_new(XKB_CONTEXT_NO_FLAGS); - xkb_state* xkb_state; - void RenderElement(Transform* transform); - void Render() override; - void QueueRender(); - void StartSync() override; - void StartUpdate() override; - void StopUpdate() override; - void SetTitle(const std::string_view title) override; - void Resize(std::uint32_t width, std::uint32_t height) override; - void Write(Pixel_BU8_GU8_RU8_AU8* pixels) override; - void Write(std::uint32_t x, std::uint32_t y, Pixel_BU8_GU8_RU8_AU8 pixel) override; - Pixel_BU8_GU8_RU8_AU8 Read(std::uint32_t x, std::uint32_t y) const override; - const Pixel_BU8_GU8_RU8_AU8* Read() const override; - Pixel_BU8_GU8_RU8_AU8* Get() override; - void Store() override; - inline static wl_compositor* compositor = nullptr; - static void wl_surface_frame_done(void *data, wl_callback *cb, uint32_t time); - static void PointerListenerHandleMotion(void* data, wl_pointer* wl_pointer, uint time, wl_fixed_t surface_x, wl_fixed_t surface_y); - static void PointerListenerHandleAxis(void*, wl_pointer*, std::uint32_t, std::uint32_t, wl_fixed_t value); - static void PointerListenerHandleEnter(void* data, wl_pointer* wl_pointer, uint serial, wl_surface* surface, wl_fixed_t surface_x, wl_fixed_t surface_y); - static void PointerListenerHandleLeave(void*, wl_pointer*, std::uint32_t, wl_surface*); - static void xdg_toplevel_handle_close(void* data, xdg_toplevel*); - static void handle_global(void* data, wl_registry* registry, std::uint32_t name, const char* interface, std::uint32_t version); - static void pointer_handle_button(void* data, wl_pointer* pointer, std::uint32_t serial, std::uint32_t time, std::uint32_t button, std::uint32_t state); - static void seat_handle_capabilities(void* data, wl_seat* seat, uint32_t capabilities); - static void xdg_surface_handle_configure(void* data, xdg_surface* xdg_surface, std::uint32_t serial); + static void xdg_surface_handle_preferred_scale(void* data, wp_fractional_scale_v1*, std::uint32_t scale); - static void xdg_wm_base_handle_ping(void* data, xdg_wm_base* xdg_wm_base, std::uint32_t serial); - static void keyboard_keymap(void* data, wl_keyboard* keyboard, uint32_t format, int fd, uint32_t size); - static void keyboard_enter(void *data, wl_keyboard *keyboard, uint32_t serial, wl_surface *surface, wl_array *keys); - static void keyboard_leave(void *data, wl_keyboard *keyboard, uint32_t serial, wl_surface *surface); - static void keyboard_key(void *data, wl_keyboard *keyboard, uint32_t serial, uint32_t time, uint32_t key, uint32_t state); - static void keyboard_modifiers(void *data, wl_keyboard *keyboard, uint32_t serial, uint32_t mods_depressed, uint32_t mods_latched, uint32_t mods_locked, uint32_t group); - static void keyboard_repeat_info(void *data, wl_keyboard *keyboard, int32_t rate, int32_t delay); - static void handle_global_remove(void* data, wl_registry* registry, uint32_t name); + static void wl_surface_frame_done(void *data, wl_callback *cb, uint32_t time); + static void xdg_toplevel_handle_close(void* data, xdg_toplevel*); + static void xdg_surface_handle_configure(void* data, xdg_surface* xdg_surface, std::uint32_t serial); static void xdg_toplevel_configure(void*, xdg_toplevel*, std::int32_t, std::int32_t, wl_array*); - - constexpr static wl_pointer_listener pointer_listener = { - .enter = PointerListenerHandleEnter, - .leave = PointerListenerHandleLeave, - .motion = PointerListenerHandleMotion, - .button = pointer_handle_button, - .axis = PointerListenerHandleAxis, - }; - constexpr static wl_keyboard_listener keyboard_listener = { - .keymap = keyboard_keymap, - .enter = keyboard_enter, - .leave = keyboard_leave, - .key = keyboard_key, - .modifiers = keyboard_modifiers, - .repeat_info = keyboard_repeat_info, - }; - constexpr static wl_seat_listener seat_listener = { - .capabilities = seat_handle_capabilities, - }; - constexpr static wl_registry_listener registry_listener = { - .global = handle_global, - .global_remove = handle_global_remove, - }; + constexpr static xdg_toplevel_listener xdg_toplevel_listener = { .configure = xdg_toplevel_configure, .close = xdg_toplevel_handle_close, @@ -263,60 +149,17 @@ export namespace Crafter { constexpr static wl_callback_listener wl_callback_listener = { .done = wl_surface_frame_done, }; - constexpr static xdg_wm_base_listener xdgWmBaseListener = { - .ping = xdg_wm_base_handle_ping, - }; constexpr static xdg_surface_listener xdg_surface_listener = { .configure = xdg_surface_handle_configure, }; constexpr static wp_fractional_scale_v1_listener wp_fractional_scale_v1_listener = { .preferred_scale = xdg_surface_handle_preferred_scale, }; - }; - #endif + inline static wp_fractional_scale_v1* wp_scale = nullptr; + #endif - #ifdef CRAFTER_GRAPHICS_VULKAN - struct Semaphores { - // Swap chain image presentation - VkSemaphore presentComplete; - // Command buffer submission and execution - VkSemaphore renderComplete; - }; - #ifndef CRAFTER_GRAPHICS_WINDOWS - 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(); - bool configured = false; - wl_shm* shm = nullptr; - wl_seat* seat = nullptr; - wp_fractional_scale_v1* wp_scale = nullptr; - xdg_toplevel* xdgToplevel = nullptr; - wp_viewport* wpViewport = nullptr; - wp_viewporter* wpViewporter = nullptr; - xdg_wm_base* xdgWmBase = nullptr; - zxdg_decoration_manager_v1* manager = nullptr; - wp_fractional_scale_manager_v1* fractionalScaleManager = nullptr; - wl_surface* surface = nullptr; - xdg_surface* xdgSurface = nullptr; - wl_display* display = nullptr; - wl_callback* cb = nullptr; - xkb_keymap* xkb_keymap; - xkb_context* xkb_context = xkb_context_new(XKB_CONTEXT_NO_FLAGS); - xkb_state* xkb_state; - Event onRender; + #ifdef CRAFTER_GRAPHICS_RENDERER_VULKAN 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; @@ -326,77 +169,11 @@ export namespace Crafter { 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; - } - inline static wl_compositor* compositor = nullptr; - static void wl_surface_frame_done(void *data, wl_callback *cb, uint32_t time); - static void PointerListenerHandleMotion(void* data, wl_pointer* wl_pointer, uint time, wl_fixed_t surface_x, wl_fixed_t surface_y); - static void PointerListenerHandleAxis(void*, wl_pointer*, std::uint32_t, std::uint32_t, wl_fixed_t value); - static void PointerListenerHandleEnter(void* data, wl_pointer* wl_pointer, uint serial, wl_surface* surface, wl_fixed_t surface_x, wl_fixed_t surface_y); - static void PointerListenerHandleLeave(void*, wl_pointer*, std::uint32_t, wl_surface*); - static void xdg_toplevel_handle_close(void* data, xdg_toplevel*); - static void handle_global(void* data, wl_registry* registry, std::uint32_t name, const char* interface, std::uint32_t version); - static void pointer_handle_button(void* data, wl_pointer* pointer, std::uint32_t serial, std::uint32_t time, std::uint32_t button, std::uint32_t state); - static void seat_handle_capabilities(void* data, wl_seat* seat, uint32_t capabilities); - static void xdg_surface_handle_configure(void* data, xdg_surface* xdg_surface, std::uint32_t serial); - static void xdg_surface_handle_preferred_scale(void* data, wp_fractional_scale_v1*, std::uint32_t scale); - static void xdg_wm_base_handle_ping(void* data, xdg_wm_base* xdg_wm_base, std::uint32_t serial); - static void keyboard_keymap(void* data, wl_keyboard* keyboard, uint32_t format, int fd, uint32_t size); - static void keyboard_enter(void *data, wl_keyboard *keyboard, uint32_t serial, wl_surface *surface, wl_array *keys); - static void keyboard_leave(void *data, wl_keyboard *keyboard, uint32_t serial, wl_surface *surface); - static void keyboard_key(void *data, wl_keyboard *keyboard, uint32_t serial, uint32_t time, uint32_t key, uint32_t state); - static void keyboard_modifiers(void *data, wl_keyboard *keyboard, uint32_t serial, uint32_t mods_depressed, uint32_t mods_latched, uint32_t mods_locked, uint32_t group); - static void keyboard_repeat_info(void *data, wl_keyboard *keyboard, int32_t rate, int32_t delay); - static void handle_global_remove(void* data, wl_registry* registry, uint32_t name); - static void xdg_toplevel_configure(void*, xdg_toplevel*, std::int32_t, std::int32_t, wl_array*); - - constexpr static wl_pointer_listener pointer_listener = { - .enter = PointerListenerHandleEnter, - .leave = PointerListenerHandleLeave, - .motion = PointerListenerHandleMotion, - .button = pointer_handle_button, - .axis = PointerListenerHandleAxis, - }; - constexpr static wl_keyboard_listener keyboard_listener = { - .keymap = keyboard_keymap, - .enter = keyboard_enter, - .leave = keyboard_leave, - .key = keyboard_key, - .modifiers = keyboard_modifiers, - .repeat_info = keyboard_repeat_info, - }; - constexpr static wl_seat_listener seat_listener = { - .capabilities = seat_handle_capabilities, - }; - constexpr static wl_registry_listener registry_listener = { - .global = handle_global, - .global_remove = handle_global_remove, - }; - constexpr static xdg_toplevel_listener xdg_toplevel_listener = { - .configure = xdg_toplevel_configure, - .close = xdg_toplevel_handle_close, - }; - constexpr static wl_callback_listener wl_callback_listener = { - .done = wl_surface_frame_done, - }; - constexpr static xdg_wm_base_listener xdgWmBaseListener = { - .ping = xdg_wm_base_handle_ping, - }; - constexpr static xdg_surface_listener xdg_surface_listener = { - .configure = xdg_surface_handle_configure, - }; - constexpr static wp_fractional_scale_v1_listener wp_fractional_scale_v1_listener = { - .preferred_scale = xdg_surface_handle_preferred_scale, - }; + void SetPipelineRT(PipelineRTVulkan& pipeline); + VkCommandBuffer StartInit(); + void FinishInit(); void CreateSwapchain(); - static constexpr std::uint32_t numFrames = 3; + static constexpr std::uint8_t numFrames = 3; VkSurfaceKHR vulkanSurface = VK_NULL_HANDLE; VkSwapchainKHR swapChain = VK_NULL_HANDLE; VkFormat colorFormat; @@ -407,7 +184,7 @@ export namespace Crafter { VkCommandBuffer drawCmdBuffers[numFrames]; VkSubmitInfo submitInfo; Semaphores semaphores; - uint32_t currentBuffer = 0; + std::uint32_t currentBuffer = 0; VkPipelineStageFlags submitPipelineStages = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; VkPipeline rtPipeline; VkPipelineLayout rtPipelineLayout; @@ -415,64 +192,6 @@ export namespace Crafter { VkStridedDeviceAddressRegionKHR missRegion; VkStridedDeviceAddressRegionKHR hitRegion; VkStridedDeviceAddressRegionKHR callableRegion; + #endif }; - #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/interfaces/Crafter.Graphics.cppm b/interfaces/Crafter.Graphics.cppm index 0bd48e4..2ee5caf 100644 --- a/interfaces/Crafter.Graphics.cppm +++ b/interfaces/Crafter.Graphics.cppm @@ -1,6 +1,6 @@ /* Crafter®.Graphics -Copyright (C) 2025 Catcrafts® +Copyright (C) 2026 Catcrafts® Catcrafts.net This library is free software; you can redistribute it and/or @@ -21,26 +21,26 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA export module Crafter.Graphics; export import :Window; -export import :Transform; -export import :RenderingElement; +export import :Transform2D; +export import :RenderingElement2D; export import :MouseElement; export import :GridElement; export import :Types; +export import :Device; export import :Font; -export import :Image; -export import :Shm; export import :Animation; export import :Mesh; +export import :Rendertarget; -#ifdef CRAFTER_GRAPHICS_VULKAN -export import :VulkanDevice; +#ifdef CRAFTER_GRAPHICS_RENDERER_VULKAN +export import :Device; export import :VulkanTransition; export import :VulkanBuffer; export import :DescriptorPoolVulkan; export import :ShaderVulkan; export import :ShaderBindingTableVulkan; export import :PipelineRTVulkan; -export import :RenderingElement3DVulkan; +export import :RenderingElement3D; export import :ImageVulkan; export import :SamplerVulkan; export import :DescriptorSetLayoutVulkan; @@ -55,6 +55,6 @@ export import :DescriptorSetLayoutVulkan; // export import :TextureShader; // export import :DescriptorSet; // export import :HeightmapShader; -// export import :VulkanDevice; +// export import :Device; // export import :VulkanPipeline; // export import :VulkanShader; \ No newline at end of file diff --git a/project.json b/project.json index eb93cb0..2575830 100644 --- a/project.json +++ b/project.json @@ -3,33 +3,68 @@ "configurations": [ { "name": "base", - "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"], + "implementations": [ + "implementations/Crafter.Graphics-Font", + "implementations/Crafter.Graphics-Window", + "implementations/Crafter.Graphics-MouseElement", + "implementations/Crafter.Graphics-Transform2D", + "implementations/Crafter.Graphics-GridElement", + "implementations/Crafter.Graphics-Rendertarget", + "implementations/Crafter.Graphics-Device" + ], + "interfaces": [ + "interfaces/Crafter.Graphics-Window", + "interfaces/Crafter.Graphics", + "interfaces/Crafter.Graphics-Types", + "interfaces/Crafter.Graphics-Font", + "interfaces/Crafter.Graphics-Animation", + "interfaces/Crafter.Graphics-RenderingElement2D", + "interfaces/Crafter.Graphics-MouseElement", + "interfaces/Crafter.Graphics-Transform2D", + "interfaces/Crafter.Graphics-GridElement", + "interfaces/Crafter.Graphics-Device", + "interfaces/Crafter.Graphics-VulkanTransition", + "interfaces/Crafter.Graphics-Mesh", + "interfaces/Crafter.Graphics-VulkanBuffer", + "interfaces/Crafter.Graphics-RenderingElement3D", + "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", + "interfaces/Crafter.Graphics-Rendertarget" + ], "type": "library" }, { "name": "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"], "extends": ["base"], "defines": [ { - "name": "CRAFTER_GRAPHICS_WAYLAND" + "name": "CRAFTER_GRAPHICS_WINDOW_WAYLAND" + } + ] + }, + { + "name": "win32", + "libs": ["kernel32", "user32"], + "extends": ["base"], + "defines": [ + { + "name": "CRAFTER_GRAPHICS_WINDOW_WIN32" } ] }, { "name": "vulkan", - "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"], - "extends": ["base"], + "implementations": ["implementations/Crafter.Graphics-Mesh", "implementations/Crafter.Graphics-RenderingElement3D"], "defines": [ { - "name": "CRAFTER_GRAPHICS_VULKAN" + "name": "CRAFTER_GRAPHICS_RENDERER_VULKAN" } ], "dependencies": [ @@ -44,27 +79,10 @@ ] }, { - "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": ["kernel32", "user32"], - "extends": ["base"], + "name": "software", "defines": [ { - "name": "CRAFTER_GRAPHICS_VULKAN" - }, - { - "name": "CRAFTER_GRAPHICS_WINDOWS" - } - ], - "dependencies": [ - { - "path":"https://github.com/KhronosGroup/Vulkan-Headers.git", - "type":"include" - }, - { - "path":"https://github.com/KhronosGroup/Vulkan-Utility-Libraries.git", - "type":"include" + "name": "CRAFTER_GRAPHICS_RENDERER_SOFTWARE" } ] }, @@ -92,7 +110,8 @@ "path":"https://forgejo.catcrafts.net/Catcrafts/Crafter.Math.git", "configuration":"lib" } - ] + ], + "defines": [{ "name": "CRAFTER_TIMING" }] }, { "name": "deps-debug", @@ -112,52 +131,57 @@ }, { "name": "lib-wayland", - "extends": ["wayland", "deps"], - "type": "library" + "extends": ["wayland", "software", "deps"] }, { "name": "lib-wayland-timing", - "extends": ["wayland", "deps-timing"], - "type": "library", - "defines": [{ "name": "CRAFTER_TIMING" }] + "extends": ["wayland", "software", "deps-timing"] }, { "name": "lib-wayland-debug", - "type": "library", - "extends": ["wayland", "deps-debug"] + "extends": ["wayland", "software", "deps-debug"] }, { - "name": "lib-vulkan", - "extends": ["vulkan", "deps"], - "type": "library" + "name": "lib-wayland-vulkan", + "extends": ["wayland", "vulkan", "deps"], + "libs": ["vulkan"] }, { - "name": "lib-vulkan-windows", - "extends": ["vulkan-windows", "deps"], - "type": "library" + "name": "lib-wayland-vulkan-timing", + "extends": ["wayland", "vulkan", "deps-timing"], + "libs": ["vulkan"] }, { - "name": "lib-vulkan-windows-debug", - "extends": ["vulkan-windows", "deps-debug"], - "type": "library", - "debug": true + "name": "lib-wayland-vulkan-debug", + "extends": ["wayland", "vulkan", "deps-debug"], + "libs": ["vulkan"] }, { - "name": "lib-vulkan-windows-timing", - "extends": ["vulkan-windows", "deps-timing"], - "type": "library", - "defines": [{ "name": "CRAFTER_TIMING" }] + "name": "lib-win32", + "extends": ["win32", "software", "deps"] }, { - "name": "lib-vulkan-timing", - "extends": ["vulkan", "deps-timing"], - "type": "library", - "defines": [{ "name": "CRAFTER_TIMING" }] + "name": "lib-win32-timing", + "extends": ["win32", "software", "deps-timing"] }, { - "name": "lib-vulkan-debug", - "type": "library", - "extends": ["vulkan", "deps-debug"] + "name": "lib-win32-debug", + "extends": ["win32", "software", "deps-debug"] + }, + { + "name": "lib-win32-vulkan", + "extends": ["win32", "vulkan", "deps"], + "libs": ["vulkan-1"] + }, + { + "name": "lib-win32-vulkan-timing", + "extends": ["win32", "vulkan", "deps-timing"], + "libs": ["vulkan-1"] + }, + { + "name": "lib-win32-vulkan-debug", + "extends": ["win32", "vulkan", "deps-debug"], + "libs": ["vulkan-1"] } ] }