Compare commits
22 commits
| Author | SHA1 | Date | |
|---|---|---|---|
| c9ebd448f9 | |||
| ef8d623525 | |||
| 5ffe1404fc | |||
| 4c93c5535e | |||
| ea18f32300 | |||
| 1c1a142f52 | |||
| 8b12dc39b3 | |||
| f4a48b20c6 | |||
| 3fcea6a3d7 | |||
| 3f4ad87746 | |||
| 9d43f2e44b | |||
| 92dfe16dac | |||
| 5427867fff | |||
| 177f873639 | |||
| f8e142fb06 | |||
| b4bd0c03c5 | |||
| 22b8af7bfc | |||
| 477b7dd087 | |||
| c895c266fb | |||
| 80bb04f84a | |||
| 44a2960415 | |||
| 7fdab4f62b |
40 changed files with 1863 additions and 1272 deletions
|
|
@ -1,4 +1,5 @@
|
|||
#include "vulkan/vulkan.h"
|
||||
#include <cassert>
|
||||
|
||||
import Crafter.Graphics;
|
||||
using namespace Crafter;
|
||||
|
|
@ -6,55 +7,69 @@ import std;
|
|||
import Crafter.Event;
|
||||
import Crafter.Math;
|
||||
|
||||
typedef VulkanShaderConst<"raygen.spv", "main", VK_SHADER_STAGE_RAYGEN_BIT_KHR> Raygenspv;
|
||||
typedef VulkanShaderConst<"closesthit.spv", "main", VK_SHADER_STAGE_CLOSEST_HIT_BIT_KHR> Closesthitspv;
|
||||
typedef VulkanShaderConst<"miss.spv", "main", VK_SHADER_STAGE_MISS_BIT_KHR> Misspv;
|
||||
typedef std::tuple<Raygenspv, Misspv, Closesthitspv> AllShaders;
|
||||
typedef std::tuple<
|
||||
ShaderGroup<0, VK_SHADER_UNUSED_KHR, VK_SHADER_UNUSED_KHR, VK_SHADER_UNUSED_KHR>,
|
||||
ShaderGroup<1, VK_SHADER_UNUSED_KHR, VK_SHADER_UNUSED_KHR, VK_SHADER_UNUSED_KHR>,
|
||||
ShaderGroup<VK_SHADER_UNUSED_KHR, 2, VK_SHADER_UNUSED_KHR, VK_SHADER_UNUSED_KHR>
|
||||
> ShaderGroups;
|
||||
typedef PipelineRTVulkanConst<AllShaders, ShaderGroups> Pipeline;
|
||||
typedef DescriptorSetLayoutVulkanConst<3, {{
|
||||
{
|
||||
.binding = 0,
|
||||
.descriptorType = VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR,
|
||||
.descriptorCount = 1,
|
||||
.stageFlags = VK_SHADER_STAGE_RAYGEN_BIT_KHR,
|
||||
},
|
||||
{
|
||||
.binding = 1,
|
||||
.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE,
|
||||
.descriptorCount = 1,
|
||||
.stageFlags = VK_SHADER_STAGE_RAYGEN_BIT_KHR,
|
||||
},
|
||||
{
|
||||
.binding = 2,
|
||||
.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
|
||||
.descriptorCount = 1,
|
||||
.stageFlags = VK_SHADER_STAGE_RAYGEN_BIT_KHR,
|
||||
}
|
||||
}}> descriptorSetLayout;
|
||||
|
||||
int main() {
|
||||
Device::Initialize();
|
||||
Window window(1280, 720, "HelloVulkan");
|
||||
VkCommandBuffer cmd = window.StartInit();
|
||||
DescriptorHeapVulkan descriptorHeap;
|
||||
descriptorHeap.Initialize(1,1,0);
|
||||
|
||||
Raygenspv::CreateShader();
|
||||
Closesthitspv::CreateShader();
|
||||
Misspv::CreateShader();
|
||||
ShaderBindingTableVulkanConst<AllShaders>::Init();
|
||||
VkSpecializationMapEntry entry = {
|
||||
.constantID = 0,
|
||||
.offset = 0,
|
||||
.size = sizeof(uint16_t)
|
||||
};
|
||||
|
||||
descriptorSetLayout::Init();
|
||||
std::array<VkDescriptorSetLayout, 1> layouts {{descriptorSetLayout::layout}};
|
||||
VkSpecializationInfo specilizationInfo = {
|
||||
.mapEntryCount = 1,
|
||||
.pMapEntries = &entry,
|
||||
.dataSize = sizeof(uint16_t),
|
||||
.pData = &descriptorHeap.bufferStartElement
|
||||
};
|
||||
|
||||
DescriptorPool pool;
|
||||
pool.sets.resize(1);
|
||||
pool.BuildPool(DescriptorPool::GetPoolSizes<descriptorSetLayout>(), layouts);
|
||||
std::array<VulkanShader, 3> shaders{{
|
||||
{"raygen.spv", "main", VK_SHADER_STAGE_RAYGEN_BIT_KHR, &specilizationInfo},
|
||||
{"miss.spv", "main", VK_SHADER_STAGE_MISS_BIT_KHR, nullptr},
|
||||
{"closesthit.spv", "main", VK_SHADER_STAGE_CLOSEST_HIT_BIT_KHR, nullptr}
|
||||
}};
|
||||
|
||||
Pipeline::Init(cmd, layouts);
|
||||
ShaderBindingTableVulkan shaderTable;
|
||||
shaderTable.Init(shaders);
|
||||
|
||||
std::array<VkRayTracingShaderGroupCreateInfoKHR, 1> raygenGroups {{
|
||||
{
|
||||
.sType = VK_STRUCTURE_TYPE_RAY_TRACING_SHADER_GROUP_CREATE_INFO_KHR,
|
||||
.type = VK_RAY_TRACING_SHADER_GROUP_TYPE_GENERAL_KHR,
|
||||
.generalShader = 0,
|
||||
.closestHitShader = VK_SHADER_UNUSED_KHR,
|
||||
.anyHitShader = VK_SHADER_UNUSED_KHR,
|
||||
.intersectionShader = VK_SHADER_UNUSED_KHR,
|
||||
},
|
||||
}};
|
||||
std::array<VkRayTracingShaderGroupCreateInfoKHR, 1> missGroups {{
|
||||
{
|
||||
.sType = VK_STRUCTURE_TYPE_RAY_TRACING_SHADER_GROUP_CREATE_INFO_KHR,
|
||||
.type = VK_RAY_TRACING_SHADER_GROUP_TYPE_GENERAL_KHR,
|
||||
.generalShader = 1,
|
||||
.closestHitShader = VK_SHADER_UNUSED_KHR,
|
||||
.anyHitShader = VK_SHADER_UNUSED_KHR,
|
||||
.intersectionShader = VK_SHADER_UNUSED_KHR,
|
||||
},
|
||||
}};
|
||||
std::array<VkRayTracingShaderGroupCreateInfoKHR, 1> hitGroups {{
|
||||
{
|
||||
.sType = VK_STRUCTURE_TYPE_RAY_TRACING_SHADER_GROUP_CREATE_INFO_KHR,
|
||||
.type = VK_RAY_TRACING_SHADER_GROUP_TYPE_TRIANGLES_HIT_GROUP_KHR,
|
||||
.generalShader = VK_SHADER_UNUSED_KHR,
|
||||
.closestHitShader = 2,
|
||||
.anyHitShader = VK_SHADER_UNUSED_KHR,
|
||||
.intersectionShader = VK_SHADER_UNUSED_KHR,
|
||||
},
|
||||
}};
|
||||
|
||||
PipelineRTVulkan pipeline;
|
||||
pipeline.Init(cmd, raygenGroups, missGroups, hitGroups, shaderTable);
|
||||
|
||||
Mesh triangleMesh;
|
||||
std::array<Vector<float, 3, 3>, 3> verts {{{-150, -150, 100}, {0, 150, 100}, {150, -150, 100}}};
|
||||
|
|
@ -75,59 +90,111 @@ int main() {
|
|||
|
||||
MatrixRowMajor<float, 4, 3, 1> transform = MatrixRowMajor<float, 4, 3, 1>::Identity();
|
||||
std::memcpy(renderer.instance.transform.matrix, transform.m, sizeof(transform.m));
|
||||
RenderingElement3D::tlases.resize(1);
|
||||
RenderingElement3D::BuildTLAS(cmd, 0);
|
||||
|
||||
VkDescriptorImageInfo imageInfo = {
|
||||
.imageView = window.imageViews[0],
|
||||
.imageLayout = VK_IMAGE_LAYOUT_GENERAL
|
||||
};
|
||||
|
||||
VkWriteDescriptorSetAccelerationStructureKHR writeDescriptorSetAccelerationStructure {
|
||||
.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET_ACCELERATION_STRUCTURE_KHR,
|
||||
.accelerationStructureCount = 1,
|
||||
.pAccelerationStructures = &RenderingElement3D::tlases[0].accelerationStructure
|
||||
};
|
||||
|
||||
VulkanBuffer<std::uint32_t, true, false, false> lightBuffer;
|
||||
lightBuffer.Create(VK_BUFFER_USAGE_STORAGE_BUFFER_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, 1);
|
||||
|
||||
VkWriteDescriptorSet write[3] = {
|
||||
{
|
||||
.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
|
||||
.pNext = &writeDescriptorSetAccelerationStructure,
|
||||
.dstSet = pool.sets[0],
|
||||
.dstBinding = 0,
|
||||
.dstArrayElement = 0,
|
||||
.descriptorCount = 1,
|
||||
.descriptorType = VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR,
|
||||
},
|
||||
{
|
||||
.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
|
||||
.dstSet = pool.sets[0],
|
||||
.dstBinding = 1,
|
||||
.dstArrayElement = 0,
|
||||
.descriptorCount = 1,
|
||||
.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE,
|
||||
.pImageInfo = &imageInfo
|
||||
},
|
||||
{
|
||||
.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
|
||||
.dstSet = pool.sets[0],
|
||||
.dstBinding = 2,
|
||||
.dstArrayElement = 0,
|
||||
.descriptorCount = 1,
|
||||
.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
|
||||
.pBufferInfo = &lightBuffer.descriptor
|
||||
}
|
||||
};
|
||||
vkUpdateDescriptorSets(Device::device, 3, write, 0, nullptr);
|
||||
|
||||
|
||||
window.SetPipelineRT<Pipeline>();
|
||||
window.descriptorsRt = pool.sets;
|
||||
RenderingElement3D::BuildTLAS(cmd, 1);
|
||||
RenderingElement3D::BuildTLAS(cmd, 2);
|
||||
|
||||
window.FinishInit();
|
||||
|
||||
VkDeviceAddressRangeKHR tlasRange0 = {
|
||||
.address = RenderingElement3D::tlases[0].address,
|
||||
};
|
||||
|
||||
VkDeviceAddressRangeKHR tlasRange1 = {
|
||||
.address = RenderingElement3D::tlases[1].address,
|
||||
};
|
||||
|
||||
VkDeviceAddressRangeKHR tlasRange2 = {
|
||||
.address = RenderingElement3D::tlases[2].address,
|
||||
};
|
||||
|
||||
VkImageDescriptorInfoEXT imageInfo0 = {
|
||||
.sType = VK_STRUCTURE_TYPE_IMAGE_DESCRIPTOR_INFO_EXT,
|
||||
.pView = &window.imageViews[0],
|
||||
.layout = VK_IMAGE_LAYOUT_GENERAL
|
||||
};
|
||||
|
||||
VkImageDescriptorInfoEXT imageInfo1 = {
|
||||
.sType = VK_STRUCTURE_TYPE_IMAGE_DESCRIPTOR_INFO_EXT,
|
||||
.pView = &window.imageViews[1],
|
||||
.layout = VK_IMAGE_LAYOUT_GENERAL
|
||||
};
|
||||
|
||||
VkImageDescriptorInfoEXT imageInfo2 = {
|
||||
.sType = VK_STRUCTURE_TYPE_IMAGE_DESCRIPTOR_INFO_EXT,
|
||||
.pView = &window.imageViews[2],
|
||||
.layout = VK_IMAGE_LAYOUT_GENERAL
|
||||
};
|
||||
|
||||
VkResourceDescriptorInfoEXT resources[6] = {
|
||||
{
|
||||
.sType = VK_STRUCTURE_TYPE_RESOURCE_DESCRIPTOR_INFO_EXT,
|
||||
.type = VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR,
|
||||
.data = { .pAddressRange = &tlasRange0}
|
||||
},
|
||||
{
|
||||
.sType = VK_STRUCTURE_TYPE_RESOURCE_DESCRIPTOR_INFO_EXT,
|
||||
.type = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE,
|
||||
.data = { .pImage = &imageInfo0 }
|
||||
},
|
||||
{
|
||||
.sType = VK_STRUCTURE_TYPE_RESOURCE_DESCRIPTOR_INFO_EXT,
|
||||
.type = VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR,
|
||||
.data = { .pAddressRange = &tlasRange1}
|
||||
},
|
||||
{
|
||||
.sType = VK_STRUCTURE_TYPE_RESOURCE_DESCRIPTOR_INFO_EXT,
|
||||
.type = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE,
|
||||
.data = { .pImage = &imageInfo1 }
|
||||
},
|
||||
{
|
||||
.sType = VK_STRUCTURE_TYPE_RESOURCE_DESCRIPTOR_INFO_EXT,
|
||||
.type = VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR,
|
||||
.data = { .pAddressRange = &tlasRange2}
|
||||
},
|
||||
{
|
||||
.sType = VK_STRUCTURE_TYPE_RESOURCE_DESCRIPTOR_INFO_EXT,
|
||||
.type = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE,
|
||||
.data = { .pImage = &imageInfo2 }
|
||||
},
|
||||
};
|
||||
|
||||
VkHostAddressRangeEXT destinations[6] = {
|
||||
{
|
||||
.address = descriptorHeap.resourceHeap[0].value + descriptorHeap.bufferStartOffset,
|
||||
.size = Device::descriptorHeapProperties.bufferDescriptorSize
|
||||
},
|
||||
{
|
||||
.address = descriptorHeap.resourceHeap[0].value,
|
||||
.size = Device::descriptorHeapProperties.imageDescriptorSize
|
||||
},
|
||||
{
|
||||
.address = descriptorHeap.resourceHeap[1].value + descriptorHeap.bufferStartOffset,
|
||||
.size = Device::descriptorHeapProperties.bufferDescriptorSize
|
||||
},
|
||||
{
|
||||
.address = descriptorHeap.resourceHeap[1].value,
|
||||
.size = Device::descriptorHeapProperties.imageDescriptorSize
|
||||
},
|
||||
{
|
||||
.address = descriptorHeap.resourceHeap[2].value + descriptorHeap.bufferStartOffset,
|
||||
.size = Device::descriptorHeapProperties.bufferDescriptorSize
|
||||
},
|
||||
{
|
||||
.address = descriptorHeap.resourceHeap[2].value,
|
||||
.size = Device::descriptorHeapProperties.imageDescriptorSize
|
||||
},
|
||||
};
|
||||
|
||||
Device::vkWriteResourceDescriptorsEXT(Device::device, 6, resources, destinations);
|
||||
|
||||
descriptorHeap.resourceHeap[0].FlushDevice();
|
||||
descriptorHeap.resourceHeap[1].FlushDevice();
|
||||
descriptorHeap.resourceHeap[2].FlushDevice();
|
||||
|
||||
window.pipeline = &pipeline;
|
||||
window.descriptorHeap = &descriptorHeap;
|
||||
|
||||
window.Render();
|
||||
window.StartSync();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,11 +7,9 @@
|
|||
"dependencies": [
|
||||
{
|
||||
"path":"../../project.json",
|
||||
"configuration":"lib-win32-vulkan-debug"
|
||||
"configuration":"lib-wayland-vulkan-debug"
|
||||
}
|
||||
],
|
||||
"target": "x86_64-w64-mingw32",
|
||||
"debug": true,
|
||||
"shaders": [
|
||||
{
|
||||
"path":"raygen.glsl",
|
||||
|
|
|
|||
|
|
@ -1,14 +1,17 @@
|
|||
#version 460
|
||||
#extension GL_EXT_ray_tracing : enable
|
||||
#extension GL_EXT_shader_image_load_formatted : enable
|
||||
#extension GL_EXT_shader_explicit_arithmetic_types_int16 : enable
|
||||
#extension GL_EXT_descriptor_heap : enable
|
||||
#extension GL_EXT_nonuniform_qualifier : enable
|
||||
|
||||
layout(binding = 0, set = 0) uniform accelerationStructureEXT topLevelAS;
|
||||
layout(binding = 1, set = 0, rgba8) uniform writeonly image2D image;
|
||||
layout(constant_id = 0) const uint16_t bufferStart = 0us;
|
||||
layout(descriptor_heap) uniform accelerationStructureEXT topLevelAS[];
|
||||
layout(descriptor_heap) uniform writeonly image2D image[];
|
||||
|
||||
layout(location = 0) rayPayloadEXT vec3 hitValue;
|
||||
|
||||
void main()
|
||||
{
|
||||
void main() {
|
||||
// Pixel coordinates
|
||||
uvec2 pixel = gl_LaunchIDEXT.xy;
|
||||
uvec2 resolution = gl_LaunchSizeEXT.xy;
|
||||
|
|
@ -32,7 +35,7 @@ void main()
|
|||
));
|
||||
|
||||
traceRayEXT(
|
||||
topLevelAS,
|
||||
topLevelAS[bufferStart],
|
||||
gl_RayFlagsNoneEXT,
|
||||
0xff,
|
||||
0, 0, 0,
|
||||
|
|
@ -43,5 +46,5 @@ void main()
|
|||
0
|
||||
);
|
||||
|
||||
imageStore(image, ivec2(pixel), vec4(hitValue, 1.0));
|
||||
imageStore(image[0], ivec2(pixel), vec4(hitValue, 1));
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,25 +0,0 @@
|
|||
# HelloWindow Example
|
||||
|
||||
## Description
|
||||
|
||||
This example demonstrates how to load shaders and render a triangle.
|
||||
|
||||
## Expected Result
|
||||
|
||||
A blue tinted vulkan window with a white triangle in the center.
|
||||
|
||||
## Highlighted Code Snippet
|
||||
|
||||
```cpp
|
||||
EventListener<VkCommandBuffer> 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);
|
||||
Device::vkCmdDrawMeshTasksEXTProc(cmd, meshShader.threadCount, 1, 1);
|
||||
});
|
||||
```
|
||||
|
||||
## How to Run
|
||||
|
||||
```bash
|
||||
crafter-build build executable -r
|
||||
```
|
||||
|
|
@ -1,12 +0,0 @@
|
|||
#version 460
|
||||
#extension GL_EXT_ray_tracing : enable
|
||||
#extension GL_EXT_nonuniform_qualifier : enable
|
||||
|
||||
layout(location = 0) rayPayloadInEXT vec3 hitValue;
|
||||
hitAttributeEXT vec2 attribs;
|
||||
|
||||
void main()
|
||||
{
|
||||
const vec3 barycentricCoords = vec3(1.0f - attribs.x - attribs.y, attribs.x, attribs.y);
|
||||
hitValue = barycentricCoords;
|
||||
}
|
||||
|
|
@ -1,138 +0,0 @@
|
|||
#include "vulkan/vulkan.h"
|
||||
|
||||
import Crafter.Graphics;
|
||||
using namespace Crafter;
|
||||
import std;
|
||||
import Crafter.Event;
|
||||
import Crafter.Math;
|
||||
|
||||
|
||||
int main() {
|
||||
Device::CreateDevice();
|
||||
WindowVulkan window(1280, 720, "HelloVulkan");
|
||||
VkCommandBuffer cmd = window.StartInit();
|
||||
|
||||
std::vector<VkDescriptorSetLayoutBinding> bindings {
|
||||
{
|
||||
.binding = 0,
|
||||
.descriptorType = VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR,
|
||||
.descriptorCount = 1,
|
||||
.stageFlags = VK_SHADER_STAGE_RAYGEN_BIT_KHR,
|
||||
},
|
||||
{
|
||||
.binding = 1,
|
||||
.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE,
|
||||
.descriptorCount = 1,
|
||||
.stageFlags = VK_SHADER_STAGE_RAYGEN_BIT_KHR,
|
||||
}
|
||||
};
|
||||
DescriptorSetLayoutVulkan layout(std::move(bindings));
|
||||
|
||||
std::array<VkDescriptorSetLayout, 1> layouts {{layout.layout}};
|
||||
|
||||
DescriptorPool pool;
|
||||
pool.sets.resize(1);
|
||||
pool.BuildPool(DescriptorPool::GetPoolSizes({layout}), layouts);
|
||||
|
||||
std::array<VulkanShader, 3> shaders{{
|
||||
{"raygen.spv", "main", VK_SHADER_STAGE_RAYGEN_BIT_KHR},
|
||||
{"miss.spv", "main", VK_SHADER_STAGE_MISS_BIT_KHR},
|
||||
{"closesthit.spv", "main", VK_SHADER_STAGE_CLOSEST_HIT_BIT_KHR}
|
||||
}};
|
||||
|
||||
ShaderBindingTableVulkan shaderTable;
|
||||
shaderTable.Init(shaders);
|
||||
|
||||
std::array<VkRayTracingShaderGroupCreateInfoKHR, 1> raygenGroups {{
|
||||
{
|
||||
.sType = VK_STRUCTURE_TYPE_RAY_TRACING_SHADER_GROUP_CREATE_INFO_KHR,
|
||||
.type = VK_RAY_TRACING_SHADER_GROUP_TYPE_GENERAL_KHR,
|
||||
.generalShader = 0,
|
||||
.closestHitShader = VK_SHADER_UNUSED_KHR,
|
||||
.anyHitShader = VK_SHADER_UNUSED_KHR,
|
||||
.intersectionShader = VK_SHADER_UNUSED_KHR,
|
||||
},
|
||||
}};
|
||||
std::array<VkRayTracingShaderGroupCreateInfoKHR, 1> missGroups {{
|
||||
{
|
||||
.sType = VK_STRUCTURE_TYPE_RAY_TRACING_SHADER_GROUP_CREATE_INFO_KHR,
|
||||
.type = VK_RAY_TRACING_SHADER_GROUP_TYPE_GENERAL_KHR,
|
||||
.generalShader = 1,
|
||||
.closestHitShader = VK_SHADER_UNUSED_KHR,
|
||||
.anyHitShader = VK_SHADER_UNUSED_KHR,
|
||||
.intersectionShader = VK_SHADER_UNUSED_KHR,
|
||||
},
|
||||
}};
|
||||
std::array<VkRayTracingShaderGroupCreateInfoKHR, 1> hitGroups {{
|
||||
{
|
||||
.sType = VK_STRUCTURE_TYPE_RAY_TRACING_SHADER_GROUP_CREATE_INFO_KHR,
|
||||
.type = VK_RAY_TRACING_SHADER_GROUP_TYPE_TRIANGLES_HIT_GROUP_KHR,
|
||||
.generalShader = VK_SHADER_UNUSED_KHR,
|
||||
.closestHitShader = 2,
|
||||
.anyHitShader = VK_SHADER_UNUSED_KHR,
|
||||
.intersectionShader = VK_SHADER_UNUSED_KHR,
|
||||
},
|
||||
}};
|
||||
|
||||
PipelineRTVulkan pipeline;
|
||||
pipeline.Init(cmd, layouts, raygenGroups, missGroups, hitGroups, shaderTable);
|
||||
|
||||
Mesh triangleMesh;
|
||||
std::array<Vector<float, 3, 3>, 3> verts {{{-150, -150, 100}, {0, 150, 100}, {150, -150, 100}}};
|
||||
std::array<std::uint32_t, 3> index {{2,1,0}};
|
||||
triangleMesh.Build(verts, index, cmd);
|
||||
RenderingElement3D renderer = {
|
||||
.instance = {
|
||||
.instanceCustomIndex = 0,
|
||||
.mask = 0xFF,
|
||||
.instanceShaderBindingTableRecordOffset = 0,
|
||||
.flags = VK_GEOMETRY_INSTANCE_FORCE_OPAQUE_BIT_KHR,
|
||||
.accelerationStructureReference = triangleMesh.blasAddr
|
||||
}
|
||||
};
|
||||
MatrixRowMajor<float, 4, 3, 1> transform = MatrixRowMajor<float, 4, 3, 1>::Identity();
|
||||
std::memcpy(renderer.instance.transform.matrix, transform.m, sizeof(transform.m));
|
||||
RenderingElement3D::tlases.resize(1);
|
||||
RenderingElement3D::elements.push_back(&renderer);
|
||||
RenderingElement3D::BuildTLAS(cmd, 0);
|
||||
|
||||
VkDescriptorImageInfo imageInfo = {
|
||||
.imageView = window.imageViews[0],
|
||||
.imageLayout = VK_IMAGE_LAYOUT_GENERAL
|
||||
};
|
||||
|
||||
VkWriteDescriptorSetAccelerationStructureKHR writeDescriptorSetAccelerationStructure {
|
||||
.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET_ACCELERATION_STRUCTURE_KHR,
|
||||
.accelerationStructureCount = 1,
|
||||
.pAccelerationStructures = &RenderingElement3D::tlases[0].accelerationStructure
|
||||
};
|
||||
|
||||
VkWriteDescriptorSet write[2] = {
|
||||
{
|
||||
.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
|
||||
.pNext = &writeDescriptorSetAccelerationStructure,
|
||||
.dstSet = pool.sets[0],
|
||||
.dstBinding = 0,
|
||||
.dstArrayElement = 0,
|
||||
.descriptorCount = 1,
|
||||
.descriptorType = VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR,
|
||||
},
|
||||
{
|
||||
.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET,
|
||||
.dstSet = pool.sets[0],
|
||||
.dstBinding = 1,
|
||||
.dstArrayElement = 0,
|
||||
.descriptorCount = 1,
|
||||
.descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE,
|
||||
.pImageInfo = &imageInfo
|
||||
}
|
||||
};
|
||||
vkUpdateDescriptorSets(Device::device, 2, write, 0, nullptr);
|
||||
|
||||
window.SetPipelineRT(pipeline);
|
||||
window.descriptorsRt = pool.sets;
|
||||
|
||||
window.FinishInit();
|
||||
window.Render();
|
||||
window.StartSync();
|
||||
}
|
||||
|
|
@ -1,9 +0,0 @@
|
|||
#version 460
|
||||
#extension GL_EXT_ray_tracing : enable
|
||||
|
||||
layout(location = 0) rayPayloadInEXT vec3 hitValue;
|
||||
|
||||
void main()
|
||||
{
|
||||
hitValue = vec3(1, 1, 1);
|
||||
}
|
||||
|
|
@ -1,47 +0,0 @@
|
|||
#version 460
|
||||
#extension GL_EXT_ray_tracing : enable
|
||||
#extension GL_EXT_shader_image_load_formatted : enable
|
||||
|
||||
layout(binding = 0, set = 0) uniform accelerationStructureEXT topLevelAS;
|
||||
layout(binding = 1, set = 0, rgba8) uniform writeonly image2D image;
|
||||
|
||||
layout(location = 0) rayPayloadEXT vec3 hitValue;
|
||||
|
||||
void main()
|
||||
{
|
||||
// Pixel coordinates
|
||||
uvec2 pixel = gl_LaunchIDEXT.xy;
|
||||
uvec2 resolution = gl_LaunchSizeEXT.xy;
|
||||
|
||||
// Normalized coordinates in range [-1, 1]
|
||||
vec2 uv = (vec2(pixel) + 0.5) / vec2(resolution);
|
||||
vec2 ndc = uv * 2.0 - 1.0;
|
||||
|
||||
// Camera parameters
|
||||
vec3 origin = vec3(0.0, 0.0, -300.0);
|
||||
|
||||
float aspect = float(resolution.x) / float(resolution.y);
|
||||
float fov = radians(60.0);
|
||||
float tanHalfFov = tan(fov * 0.5);
|
||||
|
||||
// Simple pinhole camera facing +Z
|
||||
vec3 direction = normalize(vec3(
|
||||
ndc.x * aspect * tanHalfFov,
|
||||
-ndc.y * tanHalfFov,
|
||||
1.0
|
||||
));
|
||||
|
||||
traceRayEXT(
|
||||
topLevelAS,
|
||||
gl_RayFlagsNoneEXT,
|
||||
0xff,
|
||||
0, 0, 0,
|
||||
origin,
|
||||
0.001,
|
||||
direction,
|
||||
10000.0,
|
||||
0
|
||||
);
|
||||
|
||||
imageStore(image, ivec2(pixel), vec4(hitValue, 1.0));
|
||||
}
|
||||
133
examples/VulkanUI/main.cpp
Normal file
133
examples/VulkanUI/main.cpp
Normal file
|
|
@ -0,0 +1,133 @@
|
|||
#include "vulkan/vulkan.h"
|
||||
#include <cassert>
|
||||
|
||||
import Crafter.Graphics;
|
||||
using namespace Crafter;
|
||||
import std;
|
||||
import Crafter.Event;
|
||||
import Crafter.Math;
|
||||
|
||||
|
||||
int main() {
|
||||
Device::Initialize();
|
||||
Window window(1280, 720, "HelloVulkan");
|
||||
VkCommandBuffer cmd = window.StartInit();
|
||||
DescriptorHeapVulkan descriptorHeap;
|
||||
descriptorHeap.Initialize(1,2,0);
|
||||
|
||||
VkSpecializationMapEntry entry = {
|
||||
.constantID = 0,
|
||||
.offset = 0,
|
||||
.size = sizeof(uint16_t)
|
||||
};
|
||||
|
||||
VkSpecializationInfo specilizationInfo = {
|
||||
.mapEntryCount = 1,
|
||||
.pMapEntries = &entry,
|
||||
.dataSize = sizeof(uint16_t),
|
||||
.pData = &descriptorHeap.bufferStartElement
|
||||
};
|
||||
|
||||
std::array<VulkanShader, 1> shaders{{
|
||||
{"raygen.spv", "main", VK_SHADER_STAGE_RAYGEN_BIT_KHR, &specilizationInfo}
|
||||
}};
|
||||
|
||||
ShaderBindingTableVulkan shaderTable;
|
||||
shaderTable.Init(shaders);
|
||||
|
||||
std::array<VkRayTracingShaderGroupCreateInfoKHR, 1> raygenGroups {{
|
||||
{
|
||||
.sType = VK_STRUCTURE_TYPE_RAY_TRACING_SHADER_GROUP_CREATE_INFO_KHR,
|
||||
.type = VK_RAY_TRACING_SHADER_GROUP_TYPE_GENERAL_KHR,
|
||||
.generalShader = 0,
|
||||
.closestHitShader = VK_SHADER_UNUSED_KHR,
|
||||
.anyHitShader = VK_SHADER_UNUSED_KHR,
|
||||
.intersectionShader = VK_SHADER_UNUSED_KHR,
|
||||
},
|
||||
}};
|
||||
std::array<VkRayTracingShaderGroupCreateInfoKHR, 0> missGroups;
|
||||
std::array<VkRayTracingShaderGroupCreateInfoKHR, 0> hitGroups;
|
||||
|
||||
|
||||
PipelineRTVulkan pipeline;
|
||||
pipeline.Init(cmd, raygenGroups, missGroups, hitGroups, shaderTable);
|
||||
|
||||
window.FinishInit();
|
||||
|
||||
RenderingElement2DVulkan<true, true> element(
|
||||
{
|
||||
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
|
||||
},
|
||||
2,
|
||||
1
|
||||
);
|
||||
for(std::uint8_t i = 0; i < Window::numFrames; i++) {
|
||||
reinterpret_cast<VulkanBuffer<Vector<_Float16, 4, 4>, true>*>(element.buffers[i])->value[0] = {1, 0, 0, 1};
|
||||
reinterpret_cast<VulkanBuffer<Vector<_Float16, 4, 4>, true>*>(element.buffers[i])->value[1] = {0, 1, 0, 1};
|
||||
reinterpret_cast<VulkanBuffer<Vector<_Float16, 4, 4>, true>*>(element.buffers[i])->FlushDevice();
|
||||
}
|
||||
RendertargetVulkan rendertarget(1280, 720, {&element});
|
||||
|
||||
VkImageDescriptorInfoEXT imageInfo0 = {
|
||||
.sType = VK_STRUCTURE_TYPE_IMAGE_DESCRIPTOR_INFO_EXT,
|
||||
.pView = &window.imageViews[0],
|
||||
.layout = VK_IMAGE_LAYOUT_GENERAL
|
||||
};
|
||||
|
||||
VkImageDescriptorInfoEXT imageInfo1 = {
|
||||
.sType = VK_STRUCTURE_TYPE_IMAGE_DESCRIPTOR_INFO_EXT,
|
||||
.pView = &window.imageViews[1],
|
||||
.layout = VK_IMAGE_LAYOUT_GENERAL
|
||||
};
|
||||
|
||||
VkImageDescriptorInfoEXT imageInfo2 = {
|
||||
.sType = VK_STRUCTURE_TYPE_IMAGE_DESCRIPTOR_INFO_EXT,
|
||||
.pView = &window.imageViews[2],
|
||||
.layout = VK_IMAGE_LAYOUT_GENERAL
|
||||
};
|
||||
|
||||
std::array<VkResourceDescriptorInfoEXT, 9> infos;
|
||||
infos[0] = {
|
||||
.sType = VK_STRUCTURE_TYPE_RESOURCE_DESCRIPTOR_INFO_EXT,
|
||||
.type = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE,
|
||||
.data = { .pImage = &imageInfo0 }
|
||||
};
|
||||
infos[1] = {
|
||||
.sType = VK_STRUCTURE_TYPE_RESOURCE_DESCRIPTOR_INFO_EXT,
|
||||
.type = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE,
|
||||
.data = { .pImage = &imageInfo1 }
|
||||
};
|
||||
infos[2] = {
|
||||
.sType = VK_STRUCTURE_TYPE_RESOURCE_DESCRIPTOR_INFO_EXT,
|
||||
.type = VK_DESCRIPTOR_TYPE_STORAGE_IMAGE,
|
||||
.data = { .pImage = &imageInfo2 }
|
||||
};
|
||||
|
||||
std::array<VkHostAddressRangeEXT, 9> ranges;
|
||||
ranges[0] = {
|
||||
.address = descriptorHeap.resourceHeap[0].value,
|
||||
.size = Device::descriptorHeapProperties.imageDescriptorSize
|
||||
};
|
||||
ranges[1] = {
|
||||
.address = descriptorHeap.resourceHeap[1].value,
|
||||
.size = Device::descriptorHeapProperties.imageDescriptorSize
|
||||
},
|
||||
ranges[2] = {
|
||||
.address = descriptorHeap.resourceHeap[2].value,
|
||||
.size = Device::descriptorHeapProperties.imageDescriptorSize
|
||||
},
|
||||
|
||||
rendertarget.WriteDescriptors(infos, ranges, 3, descriptorHeap.bufferStartOffset, descriptorHeap);
|
||||
|
||||
window.pipeline = &pipeline;
|
||||
window.descriptorHeap = &descriptorHeap;
|
||||
|
||||
window.Render();
|
||||
window.StartSync();
|
||||
}
|
||||
|
|
@ -7,24 +7,15 @@
|
|||
"dependencies": [
|
||||
{
|
||||
"path":"../../project.json",
|
||||
"configuration":"lib-vulkan-debug"
|
||||
"configuration":"lib-wayland-vulkan-debug"
|
||||
}
|
||||
],
|
||||
"debug": true,
|
||||
"shaders": [
|
||||
{
|
||||
"path":"raygen.glsl",
|
||||
"type": 6,
|
||||
"entrypoint":"main"
|
||||
},
|
||||
{
|
||||
"path":"closesthit.glsl",
|
||||
"type": 9,
|
||||
"entrypoint":"main"
|
||||
},
|
||||
{
|
||||
"path":"miss.glsl",
|
||||
"type": 10,
|
||||
"entrypoint":"main"
|
||||
}
|
||||
]
|
||||
}
|
||||
48
examples/VulkanUI/raygen.glsl
Normal file
48
examples/VulkanUI/raygen.glsl
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
#version 460
|
||||
#extension GL_EXT_ray_tracing : enable
|
||||
#extension GL_EXT_shader_image_load_formatted : enable
|
||||
#extension GL_EXT_shader_explicit_arithmetic_types_int16 : enable
|
||||
#extension GL_EXT_descriptor_heap : enable
|
||||
#extension GL_EXT_nonuniform_qualifier : enable
|
||||
#extension GL_EXT_shader_explicit_arithmetic_types_float16 : enable
|
||||
|
||||
|
||||
struct UIScaledData{
|
||||
int16_t x;
|
||||
int16_t y;
|
||||
int16_t sizeX;
|
||||
int16_t sizeY;
|
||||
uint16_t bufferX;
|
||||
uint16_t bufferY;
|
||||
};
|
||||
|
||||
layout(std430, descriptor_heap) buffer UIScaledDataBuffer {
|
||||
uint16_t count;
|
||||
uint16_t pad[5];
|
||||
UIScaledData data[];
|
||||
} UITransformBuffer[];
|
||||
|
||||
layout(std430, descriptor_heap) buffer UIPixelBufferr {
|
||||
f16vec4 pixels[];
|
||||
} UIPixelBuffer[];
|
||||
|
||||
layout(constant_id = 0) const uint16_t bufferStart = 0us;
|
||||
layout(descriptor_heap) uniform writeonly image2D image[];
|
||||
|
||||
void main()
|
||||
{
|
||||
uvec2 pixel = gl_LaunchIDEXT.xy;
|
||||
uvec2 resolution = gl_LaunchSizeEXT.xy;
|
||||
|
||||
vec4 hitValue = vec4(0);
|
||||
|
||||
for (uint16_t i = 1us; i < UITransformBuffer[bufferStart].count+1; i++) {
|
||||
if(pixel.x > UITransformBuffer[bufferStart].data[i].x && pixel.x < UITransformBuffer[bufferStart].data[i].x + UITransformBuffer[bufferStart].data[i].sizeX && pixel.y > UITransformBuffer[bufferStart].data[i].y && pixel.y < UITransformBuffer[bufferStart].data[i].y + UITransformBuffer[bufferStart].data[i].sizeY) {
|
||||
int16_t srcX = int16_t(float(pixel.x - UITransformBuffer[bufferStart].data[i].x) * float(UITransformBuffer[bufferStart].data[i].bufferX) / float(UITransformBuffer[bufferStart].data[i].sizeX));
|
||||
int16_t srcY = int16_t(float(pixel.y - UITransformBuffer[bufferStart].data[i].y) * float(UITransformBuffer[bufferStart].data[i].bufferY) / float(UITransformBuffer[bufferStart].data[i].sizeY));
|
||||
hitValue = vec4(UIPixelBuffer[bufferStart + 1].pixels[srcY * UITransformBuffer[bufferStart].data[i].bufferX + srcX]);
|
||||
}
|
||||
}
|
||||
|
||||
imageStore(image[0], ivec2(pixel), hitValue);
|
||||
}
|
||||
|
|
@ -65,11 +65,13 @@ const char* const deviceExtensionNames[] = {
|
|||
"VK_KHR_swapchain",
|
||||
"VK_KHR_spirv_1_4",
|
||||
"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"
|
||||
"VK_EXT_descriptor_heap",
|
||||
"VK_KHR_deferred_host_operations",
|
||||
"VK_KHR_maintenance5",
|
||||
"VK_KHR_shader_untyped_pointers",
|
||||
"VK_EXT_device_fault"
|
||||
};
|
||||
const char* const layerNames[] = {
|
||||
"VK_LAYER_KHRONOS_validation"
|
||||
|
|
@ -79,6 +81,58 @@ const char* const layerNames[] = {
|
|||
void Device::CheckVkResult(VkResult result) {
|
||||
if (result != VK_SUCCESS)
|
||||
{
|
||||
if(result == VK_ERROR_DEVICE_LOST) {
|
||||
VkDeviceFaultCountsEXT faultCounts = {
|
||||
.sType = VK_STRUCTURE_TYPE_DEVICE_FAULT_COUNTS_EXT,
|
||||
.pNext = NULL,
|
||||
};
|
||||
Device::vkGetDeviceFaultInfoEXT(device, &faultCounts, NULL);
|
||||
|
||||
std::vector<VkDeviceFaultAddressInfoEXT> addressInfos(faultCounts.addressInfoCount);
|
||||
std::vector<VkDeviceFaultVendorInfoEXT> vendorInfos(faultCounts.vendorInfoCount);
|
||||
std::vector<char> vendorBinaryData(faultCounts.vendorBinarySize);
|
||||
|
||||
VkDeviceFaultInfoEXT faultInfo = {
|
||||
.sType = VK_STRUCTURE_TYPE_DEVICE_FAULT_INFO_EXT,
|
||||
.pNext = NULL,
|
||||
.pAddressInfos = addressInfos.data(),
|
||||
.pVendorInfos = vendorInfos.data(),
|
||||
.pVendorBinaryData = vendorBinaryData.data(),
|
||||
};
|
||||
Device::vkGetDeviceFaultInfoEXT(device, &faultCounts, &faultInfo);
|
||||
|
||||
std::println("{}", faultInfo.description);
|
||||
|
||||
std::println("{} AddressInfos:", addressInfos.size());
|
||||
for(const VkDeviceFaultAddressInfoEXT& info : addressInfos) {
|
||||
std::println("\t{} {}", static_cast<uint32_t>(info.addressType), info.reportedAddress);
|
||||
}
|
||||
|
||||
std::println("{} vendorInfos:", vendorInfos.size());
|
||||
for(const VkDeviceFaultVendorInfoEXT& info : vendorInfos) {
|
||||
std::println("\t{} {} {}", info.description, info.vendorFaultCode, info.vendorFaultData);
|
||||
}
|
||||
|
||||
if(!vendorBinaryData.empty()) {
|
||||
std::string ext = ".bin";
|
||||
if(vendorBinaryData.size() >= sizeof(VkDeviceFaultVendorBinaryHeaderVersionOneEXT)) {
|
||||
VkDeviceFaultVendorBinaryHeaderVersionOneEXT header;
|
||||
std::memcpy(&header, vendorBinaryData.data(), sizeof(header));
|
||||
if(header.vendorID == 0x10DE) { // NVIDIA
|
||||
ext = ".nv-gpudmp";
|
||||
}
|
||||
}
|
||||
const auto now = std::chrono::system_clock::now();
|
||||
const std::string dumpPath = std::format("gpu_crash_dump-{:%Y%m%d-%H%M%S}{}", now, ext);
|
||||
std::ofstream file(dumpPath, std::ios::binary);
|
||||
if(file.write(vendorBinaryData.data(), vendorBinaryData.size())) {
|
||||
std::println("Vendor binary saved to: {}", std::filesystem::canonical(dumpPath).string());
|
||||
} else {
|
||||
std::println(stderr, "Failed to write vendor binary to: {}", dumpPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
throw std::runtime_error(string_VkResult(result));
|
||||
}
|
||||
}
|
||||
|
|
@ -157,6 +211,33 @@ constexpr CrafterKeys keysym_to_crafter_key(xkb_keysym_t sym)
|
|||
case XKB_KEY_y: return CrafterKeys::Y;
|
||||
case XKB_KEY_z: return CrafterKeys::Z;
|
||||
|
||||
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;
|
||||
|
|
@ -246,8 +327,7 @@ constexpr CrafterKeys keysym_to_crafter_key(xkb_keysym_t sym)
|
|||
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));
|
||||
default: return CrafterKeys::CrafterKeysMax;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -407,31 +487,39 @@ void Device::keyboard_leave(void *data, wl_keyboard *keyboard, uint32_t serial,
|
|||
|
||||
}
|
||||
|
||||
|
||||
|
||||
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<std::uint8_t>(crafterKey)]) {
|
||||
Device::focusedWindow->onKeyHold[static_cast<std::uint8_t>(crafterKey)].Invoke();
|
||||
Device::focusedWindow->onAnyKeyHold.Invoke(crafterKey);
|
||||
} else{
|
||||
Device::focusedWindow->heldkeys[static_cast<std::uint8_t>(crafterKey)] = true;
|
||||
Device::focusedWindow->onKeyDown[static_cast<std::uint8_t>(crafterKey)].Invoke();
|
||||
Device::focusedWindow->onAnyKeyDown.Invoke(crafterKey);
|
||||
if (state == WL_KEYBOARD_KEY_STATE_PRESSED) {
|
||||
if (focusedWindow->heldkeys[(std::uint8_t)crafterKey]) {
|
||||
focusedWindow->onKeyHold[(std::uint8_t)crafterKey].Invoke();
|
||||
focusedWindow->onAnyKeyHold.Invoke(crafterKey);
|
||||
} else {
|
||||
focusedWindow->heldkeys[(std::uint8_t)crafterKey] = true;
|
||||
focusedWindow->onKeyDown[(std::uint8_t)crafterKey].Invoke();
|
||||
focusedWindow->onAnyKeyDown.Invoke(crafterKey);
|
||||
}
|
||||
} else{
|
||||
Device::focusedWindow->heldkeys[static_cast<std::uint8_t>(crafterKey)] = false;
|
||||
Device::focusedWindow->onKeyUp[static_cast<std::uint8_t>(crafterKey)].Invoke();
|
||||
Device::focusedWindow->onAnyKeyUp.Invoke(crafterKey);
|
||||
|
||||
std::string buf;
|
||||
buf.resize(16);
|
||||
int n = xkb_state_key_get_utf8(xkb_state, keycode, buf.data(), 16);
|
||||
if (n > 0) {
|
||||
if ((unsigned char)buf[0] >= 0x20 && buf[0] != 0x7f) {
|
||||
buf.resize(n);
|
||||
focusedWindow->onTextInput.Invoke(buf);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
focusedWindow->heldkeys[(std::uint8_t)crafterKey] = false;
|
||||
focusedWindow->onKeyUp[(std::uint8_t)crafterKey].Invoke();
|
||||
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) {
|
||||
|
||||
xkb_state_update_mask(xkb_state, mods_depressed, mods_latched, mods_locked, 0, 0, group);
|
||||
}
|
||||
|
||||
void Device::keyboard_repeat_info(void *data, wl_keyboard *keyboard, int32_t rate, int32_t delay) {
|
||||
|
|
@ -477,8 +565,19 @@ void Device::Initialize() {
|
|||
app.pEngineName = "Crafter.Graphics";
|
||||
app.apiVersion = VK_MAKE_VERSION(1, 4, 0);
|
||||
|
||||
VkValidationFeatureEnableEXT enables[] = {
|
||||
VK_VALIDATION_FEATURE_ENABLE_GPU_ASSISTED_EXT
|
||||
};
|
||||
|
||||
VkValidationFeaturesEXT validationFeatures = {
|
||||
.sType = VK_STRUCTURE_TYPE_VALIDATION_FEATURES_EXT,
|
||||
.enabledValidationFeatureCount = 1,
|
||||
.pEnabledValidationFeatures = enables
|
||||
};
|
||||
|
||||
VkInstanceCreateInfo instanceCreateInfo = {};
|
||||
instanceCreateInfo.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;
|
||||
instanceCreateInfo.pNext = &validationFeatures;
|
||||
instanceCreateInfo.pApplicationInfo = &app;
|
||||
instanceCreateInfo.enabledExtensionCount = sizeof(instanceExtensionNames) / sizeof(const char*);
|
||||
instanceCreateInfo.ppEnabledExtensionNames = instanceExtensionNames;
|
||||
|
|
@ -529,17 +628,10 @@ void Device::Initialize() {
|
|||
{
|
||||
VkPhysicalDevice device = physDevices[i];
|
||||
|
||||
VkPhysicalDeviceProperties2 properties2{
|
||||
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2,
|
||||
.pNext = &rayTracingProperties
|
||||
};
|
||||
vkGetPhysicalDeviceProperties2(device, &properties2);
|
||||
uint32_t score;
|
||||
|
||||
VkPhysicalDeviceProperties properties;
|
||||
vkGetPhysicalDeviceProperties(device, &properties);
|
||||
|
||||
uint32_t score;
|
||||
|
||||
switch (properties.deviceType)
|
||||
{
|
||||
default :
|
||||
|
|
@ -568,6 +660,11 @@ void Device::Initialize() {
|
|||
}
|
||||
}
|
||||
|
||||
VkPhysicalDeviceProperties2 properties2 {
|
||||
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2,
|
||||
.pNext = &rayTracingProperties
|
||||
};
|
||||
vkGetPhysicalDeviceProperties2(physDevice, &properties2);
|
||||
|
||||
uint32_t queueFamilyCount;
|
||||
vkGetPhysicalDeviceQueueFamilyProperties(physDevice, &queueFamilyCount, NULL);
|
||||
|
|
@ -593,21 +690,50 @@ void Device::Initialize() {
|
|||
queueCreateInfo.queueCount = 1;
|
||||
queueCreateInfo.pQueuePriorities = &priority;
|
||||
|
||||
VkPhysicalDeviceFaultFeaturesEXT faultFeatures2 = {
|
||||
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FAULT_FEATURES_EXT,
|
||||
};
|
||||
VkPhysicalDeviceFeatures2 features22 = {
|
||||
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2,
|
||||
.pNext = &faultFeatures2,
|
||||
};
|
||||
vkGetPhysicalDeviceFeatures2(physDevice, &features22);
|
||||
|
||||
VkPhysicalDeviceFaultFeaturesEXT faultFeatures = {
|
||||
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FAULT_FEATURES_EXT,
|
||||
.deviceFault = VK_TRUE,
|
||||
.deviceFaultVendorBinary = faultFeatures2.deviceFaultVendorBinary,
|
||||
};
|
||||
|
||||
VkPhysicalDeviceShaderUntypedPointersFeaturesKHR untypedPointersFeatures {
|
||||
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SHADER_UNTYPED_POINTERS_FEATURES_KHR,
|
||||
.pNext = &faultFeatures,
|
||||
.shaderUntypedPointers = VK_TRUE,
|
||||
};
|
||||
|
||||
VkPhysicalDeviceDescriptorHeapFeaturesEXT desciptorHeapFeatures {
|
||||
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_HEAP_FEATURES_EXT,
|
||||
.pNext = &untypedPointersFeatures,
|
||||
.descriptorHeap = VK_TRUE,
|
||||
};
|
||||
|
||||
VkPhysicalDevice16BitStorageFeatures bit16 {
|
||||
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_16BIT_STORAGE_FEATURES,
|
||||
.pNext = &desciptorHeapFeatures,
|
||||
.storageBuffer16BitAccess = VK_TRUE,
|
||||
};
|
||||
|
||||
VkPhysicalDeviceVulkan12Features features12 {
|
||||
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES,
|
||||
.pNext = &bit16,
|
||||
.shaderFloat16 = VK_TRUE,
|
||||
.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,
|
||||
.pNext = &features12,
|
||||
.rayTracingPipeline = VK_TRUE
|
||||
};
|
||||
|
||||
|
|
@ -617,21 +743,14 @@ void Device::Initialize() {
|
|||
.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,
|
||||
.pNext = &deviceAccelerationStructureFeature,
|
||||
.features = {
|
||||
.samplerAnisotropy = VK_TRUE
|
||||
.samplerAnisotropy = VK_TRUE,
|
||||
.shaderInt16 = VK_TRUE
|
||||
}
|
||||
};
|
||||
physical_features2.pNext = &ext_feature;
|
||||
|
||||
VkDeviceCreateInfo deviceCreateInfo = {};
|
||||
deviceCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
|
||||
|
|
@ -684,6 +803,11 @@ void Device::Initialize() {
|
|||
vkCreateRayTracingPipelinesKHR = reinterpret_cast<PFN_vkCreateRayTracingPipelinesKHR>(vkGetInstanceProcAddr(instance, "vkCreateRayTracingPipelinesKHR"));
|
||||
vkGetRayTracingShaderGroupHandlesKHR = reinterpret_cast<PFN_vkGetRayTracingShaderGroupHandlesKHR>(vkGetInstanceProcAddr(instance, "vkGetRayTracingShaderGroupHandlesKHR"));
|
||||
vkCmdTraceRaysKHR = reinterpret_cast<PFN_vkCmdTraceRaysKHR>(vkGetInstanceProcAddr(instance, "vkCmdTraceRaysKHR"));
|
||||
vkCmdBindResourceHeapEXT = reinterpret_cast<PFN_vkCmdBindResourceHeapEXT>(vkGetInstanceProcAddr(instance, "vkCmdBindResourceHeapEXT"));
|
||||
vkCmdBindSamplerHeapEXT = reinterpret_cast<PFN_vkCmdBindSamplerHeapEXT>(vkGetInstanceProcAddr(instance, "vkCmdBindSamplerHeapEXT"));
|
||||
vkWriteResourceDescriptorsEXT = reinterpret_cast<PFN_vkWriteResourceDescriptorsEXT>(vkGetInstanceProcAddr(instance, "vkWriteResourceDescriptorsEXT"));
|
||||
vkGetPhysicalDeviceDescriptorSizeEXT = reinterpret_cast<PFN_vkGetPhysicalDeviceDescriptorSizeEXT>(vkGetInstanceProcAddr(instance, "vkGetPhysicalDeviceDescriptorSizeEXT"));
|
||||
vkGetDeviceFaultInfoEXT = reinterpret_cast<PFN_vkGetDeviceFaultInfoEXT>(vkGetInstanceProcAddr(instance, "vkGetDeviceFaultInfoEXT"));
|
||||
#endif
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -57,3 +57,14 @@ Font::Font(const std::filesystem::path& fontFilePath) {
|
|||
this->descent = descent;
|
||||
this->lineGap = lineGap;
|
||||
}
|
||||
|
||||
std::uint32_t Font::GetLineWidth(const std::string_view text, float size) {
|
||||
float scale = stbtt_ScaleForPixelHeight(&font, size);
|
||||
std::uint32_t lineWidth = 0;
|
||||
for (const char c : text) {
|
||||
int advance, lsb;
|
||||
stbtt_GetCodepointHMetrics(&font, c, &advance, &lsb);
|
||||
lineWidth += (int)(advance * scale);
|
||||
}
|
||||
return lineWidth;
|
||||
}
|
||||
|
|
@ -114,6 +114,12 @@ void RenderingElement3D::BuildTLAS(VkCommandBuffer cmd, std::uint32_t index) {
|
|||
0, nullptr,
|
||||
0, nullptr
|
||||
);
|
||||
|
||||
VkAccelerationStructureDeviceAddressInfoKHR addrInfo {
|
||||
.sType = VK_STRUCTURE_TYPE_ACCELERATION_STRUCTURE_DEVICE_ADDRESS_INFO_KHR,
|
||||
.accelerationStructure = tlases[index].accelerationStructure
|
||||
};
|
||||
tlases[index].address = Device::vkGetAccelerationStructureDeviceAddressKHR(Device::device, &addrInfo);
|
||||
}
|
||||
|
||||
#endif
|
||||
141
implementations/Crafter.Graphics-Rendertarget.cpp
Normal file
141
implementations/Crafter.Graphics-Rendertarget.cpp
Normal file
|
|
@ -0,0 +1,141 @@
|
|||
/*
|
||||
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
|
||||
module Crafter.Graphics:Rendertarget_impl;
|
||||
#ifdef CRAFTER_GRAPHICS_RENDERER_VULKAN
|
||||
import :Rendertarget;
|
||||
import :Window;
|
||||
import :DescriptorHeapVulkan;
|
||||
import :RenderingElement2DVulkan;
|
||||
import std;
|
||||
using namespace Crafter;
|
||||
|
||||
|
||||
RendertargetVulkan::RendertargetVulkan(std::uint16_t sizeX, std::uint16_t sizeY) : RendertargetBase(sizeX, sizeY) {
|
||||
|
||||
}
|
||||
|
||||
void RendertargetVulkan::UpdateElements() {
|
||||
elements.clear();
|
||||
std::sort(transform.children.begin(), transform.children.end(), [](Transform2D* a, Transform2D* b){ return a->anchor.z < b->anchor.z; });
|
||||
for(Transform2D* child : transform.children) {
|
||||
SetOrderResursive(child);
|
||||
}
|
||||
}
|
||||
|
||||
void RendertargetVulkan::CreateBuffer(std::uint8_t frame) {
|
||||
transformBuffer[frame].Resize(VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_2_SHADER_DEVICE_ADDRESS_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, elements.size()+1);
|
||||
RenderingElement2DVulkanTransformInfo* val = reinterpret_cast<RenderingElement2DVulkanTransformInfo*>(reinterpret_cast<char*>(transformBuffer[frame].value) + sizeof(RenderingElement2DVulkanTransformInfo));
|
||||
std::uint16_t* sizePtr = reinterpret_cast<std::uint16_t*>(transformBuffer[frame].value);
|
||||
*sizePtr = static_cast<std::uint16_t>(elements.size());
|
||||
for(std::uint16_t i = 0; i < elements.size(); i++) {
|
||||
val[i].bufferX = elements[i]->bufferX;
|
||||
val[i].bufferY = elements[i]->bufferY;
|
||||
}
|
||||
transformBuffer[frame].FlushDevice();
|
||||
}
|
||||
|
||||
void RendertargetVulkan::ReorderBuffer(std::uint8_t frame) {
|
||||
RenderingElement2DVulkanTransformInfo* val = reinterpret_cast<RenderingElement2DVulkanTransformInfo*>(reinterpret_cast<char*>(transformBuffer[frame].value) + sizeof(RenderingElement2DVulkanTransformInfo));
|
||||
for(std::uint16_t i = 0; i < elements.size(); i++) {
|
||||
val[i].scaled = elements[i]->scaled;
|
||||
val[i].bufferX = elements[i]->bufferX;
|
||||
val[i].bufferY = elements[i]->bufferY;
|
||||
}
|
||||
transformBuffer[frame].FlushDevice();
|
||||
}
|
||||
|
||||
void RendertargetVulkan::WriteDescriptors(std::span<VkResourceDescriptorInfoEXT> infos, std::span<VkHostAddressRangeEXT> ranges, std::uint16_t start, std::uint32_t bufferOffset, DescriptorHeapVulkan& descriptorHeap) {
|
||||
VkDeviceAddressRangeKHR transformRanges[Window::numFrames] = {
|
||||
{
|
||||
.address = transformBuffer[0].address,
|
||||
.size = transformBuffer[0].size
|
||||
},
|
||||
{
|
||||
.address = transformBuffer[1].address,
|
||||
.size = transformBuffer[1].size
|
||||
},
|
||||
{
|
||||
.address = transformBuffer[2].address,
|
||||
.size = transformBuffer[2].size
|
||||
}
|
||||
};
|
||||
|
||||
for(std::uint8_t i = 0; i < Window::numFrames; i++) {
|
||||
ranges[start + i] = {
|
||||
.address = descriptorHeap.resourceHeap[i].value + bufferOffset,
|
||||
.size = Device::descriptorHeapProperties.bufferDescriptorSize
|
||||
};
|
||||
infos[start + i] = {
|
||||
.sType = VK_STRUCTURE_TYPE_RESOURCE_DESCRIPTOR_INFO_EXT,
|
||||
.type = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
|
||||
.data = { .pAddressRange = &transformRanges[i]}
|
||||
};
|
||||
}
|
||||
|
||||
start += 3;
|
||||
bufferOffset += Device::descriptorHeapProperties.bufferDescriptorSize;
|
||||
|
||||
std::vector<VkDeviceAddressRangeKHR> bufferRanges(elements.size() * Window::numFrames);
|
||||
|
||||
std::uint16_t rangeOffset = 0;
|
||||
|
||||
for(std::uint8_t i2 = 0; i2 < Window::numFrames; i2++) {
|
||||
for(std::uint16_t i = 0; i < elements.size(); i++) {
|
||||
ranges[start + i] = {
|
||||
.address = descriptorHeap.resourceHeap[i2].value + bufferOffset + Device::descriptorHeapProperties.bufferDescriptorSize * i,
|
||||
.size = Device::descriptorHeapProperties.bufferDescriptorSize
|
||||
};
|
||||
infos[start + i] = {
|
||||
.sType = VK_STRUCTURE_TYPE_RESOURCE_DESCRIPTOR_INFO_EXT,
|
||||
.type = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
|
||||
.data = { .pAddressRange = &bufferRanges[i]}
|
||||
};
|
||||
bufferRanges[rangeOffset + i] = {
|
||||
.address = elements[i]->buffers[i2]->address,
|
||||
.size = elements[i]->buffers[i2]->size
|
||||
};
|
||||
}
|
||||
start += elements.size();
|
||||
rangeOffset += elements.size();
|
||||
}
|
||||
|
||||
Device::vkWriteResourceDescriptorsEXT(Device::device, start, infos.data(), ranges.data());
|
||||
|
||||
for(std::uint8_t i = 0; i < Window::numFrames; i++) {
|
||||
descriptorHeap.resourceHeap[i].FlushDevice();
|
||||
}
|
||||
}
|
||||
|
||||
void RendertargetVulkan::SetOrderResursive(Transform2D* elementTransform) {
|
||||
RenderingElement2DVulkanBase* renderer = dynamic_cast<RenderingElement2DVulkanBase*>(elementTransform);
|
||||
if(renderer) {
|
||||
renderer->index = elements.size();
|
||||
elements.push_back(renderer);
|
||||
}
|
||||
std::sort(elementTransform->children.begin(), elementTransform->children.end(), [](Transform2D* a, Transform2D* b){ return a->anchor.z < b->anchor.z; });
|
||||
for(Transform2D* childTransform : elementTransform->children) {
|
||||
SetOrderResursive(childTransform);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
|
@ -28,6 +28,6 @@ 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) {
|
||||
Anchor2D::Anchor2D(float x, float y, float width, float height, float offsetX, float offsetY, std::uint8_t z, bool maintainAspectRatio): x(x), y(y), width(width), height(height), offsetX(offsetX), offsetY(offsetY), z(z), maintainAspectRatio(maintainAspectRatio) {
|
||||
|
||||
}
|
||||
|
|
@ -62,6 +62,8 @@ import :MouseElement;
|
|||
import :Device;
|
||||
#ifdef CRAFTER_GRAPHICS_RENDERER_VULKAN
|
||||
import :VulkanTransition;
|
||||
import :DescriptorHeapVulkan;
|
||||
import :PipelineRTVulkan;
|
||||
#endif
|
||||
import std;
|
||||
|
||||
|
|
@ -270,49 +272,65 @@ LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
|
|||
PostQuitMessage(0);
|
||||
break;
|
||||
}
|
||||
case WM_KEYDOWN:{
|
||||
if ((lParam & (1 << 30)) == 0) { // only first press
|
||||
case WM_KEYDOWN:
|
||||
case WM_SYSKEYDOWN: { // SYSKEYDOWN catches Alt combos, F10, etc.
|
||||
CrafterKeys crafterKey = vk_to_crafter_key(wParam);
|
||||
if(window->heldkeys[static_cast<std::uint8_t>(crafterKey)]) {
|
||||
window->onKeyHold[static_cast<std::uint8_t>(crafterKey)].Invoke();
|
||||
bool isRepeat = (lParam & (1 << 30)) != 0;
|
||||
|
||||
if (isRepeat) {
|
||||
window->onKeyHold[(uint8_t)crafterKey].Invoke();
|
||||
window->onAnyKeyHold.Invoke(crafterKey);
|
||||
} else{
|
||||
window->heldkeys[static_cast<std::uint8_t>(crafterKey)] = true;
|
||||
window->onKeyDown[static_cast<std::uint8_t>(crafterKey)].Invoke();
|
||||
} else {
|
||||
window->heldkeys[(uint8_t)crafterKey] = true;
|
||||
window->onKeyDown[(uint8_t)crafterKey].Invoke();
|
||||
window->onAnyKeyDown.Invoke(crafterKey);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case WM_KEYUP: {
|
||||
|
||||
case WM_KEYUP:
|
||||
case WM_SYSKEYUP: {
|
||||
CrafterKeys crafterKey = vk_to_crafter_key(wParam);
|
||||
window->heldkeys[static_cast<std::uint8_t>(crafterKey)] = false;
|
||||
window->onKeyUp[static_cast<std::uint8_t>(crafterKey)].Invoke();
|
||||
window->heldkeys[(uint8_t)crafterKey] = false;
|
||||
window->onKeyUp[(uint8_t)crafterKey].Invoke();
|
||||
window->onAnyKeyUp.Invoke(crafterKey);
|
||||
break;
|
||||
}
|
||||
case WM_MOUSEMOVE: {
|
||||
int x = LOWORD(lParam);
|
||||
int y = HIWORD(lParam);
|
||||
|
||||
Vector<float, 2> 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(!element->mouseHover) {
|
||||
element->mouseHover = true;
|
||||
element->onMouseEnter.Invoke();
|
||||
case WM_CHAR: {
|
||||
// wParam is a UTF-16 code unit. May be a surrogate — buffer until we have a pair.
|
||||
wchar_t wc = (wchar_t)wParam;
|
||||
|
||||
// Filter control characters (backspace=0x08, tab=0x09, enter=0x0D, escape=0x1B, etc.)
|
||||
if (wc < 0x20 || wc == 0x7f) break;
|
||||
|
||||
// Handle UTF-16 surrogate pairs (characters outside the BMP, e.g. emoji).
|
||||
static wchar_t highSurrogate = 0;
|
||||
wchar_t utf16[2];
|
||||
int utf16Len;
|
||||
|
||||
if (wc >= 0xD800 && wc <= 0xDBFF) {
|
||||
// High surrogate — stash it and wait for the low surrogate.
|
||||
highSurrogate = wc;
|
||||
break;
|
||||
} else if (wc >= 0xDC00 && wc <= 0xDFFF) {
|
||||
// Low surrogate — pair with the stashed high surrogate.
|
||||
if (highSurrogate == 0) break; // orphaned low surrogate, ignore
|
||||
utf16[0] = highSurrogate;
|
||||
utf16[1] = wc;
|
||||
utf16Len = 2;
|
||||
highSurrogate = 0;
|
||||
} else {
|
||||
utf16[0] = wc;
|
||||
utf16Len = 1;
|
||||
}
|
||||
} else if(element->mouseHover) {
|
||||
element->mouseHover = false
|
||||
element->onMouseLeave.Invoke();
|
||||
|
||||
// Convert UTF-16 to UTF-8.
|
||||
char utf8[8];
|
||||
int n = WideCharToMultiByte(CP_UTF8, 0, utf16, utf16Len, utf8, sizeof(utf8), nullptr, nullptr);
|
||||
if (n > 0) {
|
||||
window->onTextInput.Invoke(std::string(utf8, n));
|
||||
}
|
||||
}
|
||||
}
|
||||
window->mouseElements.erase(std::remove(window->mouseElements.begin(), window->mouseElements.end(), static_cast<MouseElement*>(nullptr)), window->mouseElements.end());
|
||||
break;
|
||||
}
|
||||
case WM_LBUTTONDOWN: {
|
||||
|
|
@ -367,6 +385,14 @@ LRESULT CALLBACK WndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam) {
|
|||
break;
|
||||
}
|
||||
|
||||
case WM_SETCURSOR: {
|
||||
if (LOWORD(lParam) == HTCLIENT && window->cursorHandle) {
|
||||
SetCursor(window->cursorHandle);
|
||||
return TRUE;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default: return DefWindowProc(hwnd, msg, wParam, lParam);
|
||||
}
|
||||
return 0;
|
||||
|
|
@ -596,6 +622,34 @@ void Window::SetCusorImage(std::uint16_t sizeX, std::uint16_t sizeY) {
|
|||
wl_surface_damage(cursorSurface, 0, 0, sizeX, sizeY);
|
||||
wl_surface_commit(cursorSurface);
|
||||
#endif
|
||||
#ifdef CRAFTER_GRAPHICS_WINDOW_WIN32
|
||||
if (cursorBitmap) {
|
||||
DeleteObject(cursorBitmap);
|
||||
}
|
||||
if (cursorHandle) {
|
||||
DestroyCursor(cursorHandle);
|
||||
cursorHandle = nullptr;
|
||||
}
|
||||
|
||||
BITMAPINFO bmi = {};
|
||||
bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
|
||||
bmi.bmiHeader.biWidth = sizeX;
|
||||
bmi.bmiHeader.biHeight = -(int)sizeY; // top-down
|
||||
bmi.bmiHeader.biPlanes = 1;
|
||||
bmi.bmiHeader.biBitCount = 32;
|
||||
bmi.bmiHeader.biCompression = BI_RGB;
|
||||
|
||||
HDC hdc = GetDC(nullptr);
|
||||
cursorBitmap = CreateDIBSection(hdc, &bmi, DIB_RGB_COLORS, reinterpret_cast<void**>(&cursorRenderer.buffer[0]), nullptr, 0);
|
||||
ReleaseDC(nullptr, hdc);
|
||||
|
||||
if (!cursorBitmap) {
|
||||
throw std::runtime_error("CreateDIBSection failed for cursor");
|
||||
}
|
||||
|
||||
cursorSizeX = sizeX;
|
||||
cursorSizeY = sizeY;
|
||||
#endif
|
||||
}
|
||||
|
||||
void Window::SetCusorImageDefault() {
|
||||
|
|
@ -604,9 +658,21 @@ void Window::SetCusorImageDefault() {
|
|||
wl_surface_destroy(cursorSurface);
|
||||
cursorSurface = nullptr;
|
||||
#endif
|
||||
#ifdef CRAFTER_GRAPHICS_WINDOW_WIN32
|
||||
if (cursorHandle) {
|
||||
DestroyCursor(cursorHandle);
|
||||
cursorHandle = nullptr;
|
||||
}
|
||||
if (cursorBitmap) {
|
||||
DeleteObject(cursorBitmap);
|
||||
cursorBitmap = nullptr;
|
||||
}
|
||||
// Setting nullptr will make WM_SETCURSOR fall through to the default
|
||||
#endif
|
||||
}
|
||||
|
||||
void Window::UpdateCursorImage() {
|
||||
#ifdef CRAFTER_GRAPHICS_WINDOW_WAYLAND
|
||||
cursorRenderer.Render(0);
|
||||
for(std::uint32_t i = 0; i < cursorBufferOldSize / 4; i++) {
|
||||
std::swap(cursorRenderer.buffer[0][i].b, cursorRenderer.buffer[0][i].r);
|
||||
|
|
@ -614,6 +680,35 @@ void Window::UpdateCursorImage() {
|
|||
wl_surface_attach(cursorSurface, cursorWlBuffer, 0, 0);
|
||||
wl_surface_damage(cursorSurface, 0, 0, 9999999, 99999999);
|
||||
wl_surface_commit(cursorSurface);
|
||||
#endif
|
||||
#ifdef CRAFTER_GRAPHICS_WINDOW_WIN32
|
||||
cursorRenderer.Render(0);
|
||||
|
||||
// Swap R and B channels (renderer is RGBA, GDI DIB is BGRA)
|
||||
for (std::uint32_t i = 0; i < (std::uint32_t)(cursorSizeX * cursorSizeY); i++) {
|
||||
std::swap(cursorRenderer.buffer[0][i].r, cursorRenderer.buffer[0][i].b);
|
||||
}
|
||||
|
||||
// Create a mask bitmap (all zeros = fully opaque, alpha comes from color bitmap)
|
||||
HBITMAP hMask = CreateBitmap(cursorSizeX, cursorSizeY, 1, 1, nullptr);
|
||||
|
||||
ICONINFO ii = {};
|
||||
ii.fIcon = FALSE;
|
||||
ii.xHotspot = 0;
|
||||
ii.yHotspot = 0;
|
||||
ii.hbmMask = hMask;
|
||||
ii.hbmColor = cursorBitmap;
|
||||
|
||||
if (cursorHandle) {
|
||||
DestroyCursor(cursorHandle);
|
||||
}
|
||||
cursorHandle = (HCURSOR)CreateIconIndirect(&ii);
|
||||
DeleteObject(hMask);
|
||||
|
||||
if (cursorHandle) {
|
||||
SetCursor(cursorHandle);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
void Window::StartSync() {
|
||||
|
|
@ -736,19 +831,31 @@ void Window::Render() {
|
|||
}
|
||||
#endif
|
||||
|
||||
vkCmdBindPipeline(drawCmdBuffers[currentBuffer], VK_PIPELINE_BIND_POINT_RAY_TRACING_KHR, rtPipeline);
|
||||
VkBindDescriptorSetsInfo bindDescriptorSetsInfo{
|
||||
.sType = VK_STRUCTURE_TYPE_BIND_DESCRIPTOR_SETS_INFO,
|
||||
.stageFlags = VK_SHADER_STAGE_ALL,
|
||||
.layout = rtPipelineLayout,
|
||||
.firstSet = 0,
|
||||
.descriptorSetCount = static_cast<std::uint32_t>(descriptorsRt.size()),
|
||||
.pDescriptorSets = descriptorsRt.data()
|
||||
vkCmdBindPipeline(drawCmdBuffers[currentBuffer], VK_PIPELINE_BIND_POINT_RAY_TRACING_KHR, pipeline->pipeline);
|
||||
|
||||
VkBindHeapInfoEXT resourceHeapInfo = {
|
||||
.sType = VK_STRUCTURE_TYPE_BIND_HEAP_INFO_EXT,
|
||||
.heapRange = {
|
||||
.address = descriptorHeap->resourceHeap[currentBuffer].address,
|
||||
.size = static_cast<std::uint32_t>(descriptorHeap->resourceHeap[currentBuffer].size)
|
||||
},
|
||||
.reservedRangeOffset = (descriptorHeap->resourceHeap[currentBuffer].size - Device::descriptorHeapProperties.minResourceHeapReservedRange) & ~(Device::descriptorHeapProperties.imageDescriptorAlignment - 1),
|
||||
.reservedRangeSize = Device::descriptorHeapProperties.minResourceHeapReservedRange
|
||||
};
|
||||
Device::vkCmdBindResourceHeapEXT(drawCmdBuffers[currentBuffer], &resourceHeapInfo);
|
||||
|
||||
vkCmdBindDescriptorSets2(drawCmdBuffers[currentBuffer], &bindDescriptorSetsInfo);
|
||||
Device::vkCmdTraceRaysKHR(drawCmdBuffers[currentBuffer], &raygenRegion, &missRegion, &hitRegion, &callableRegion, width, height, 1);
|
||||
VkBindHeapInfoEXT samplerHeapInfo = {
|
||||
.sType = VK_STRUCTURE_TYPE_BIND_HEAP_INFO_EXT,
|
||||
.heapRange = {
|
||||
.address = descriptorHeap->samplerHeap[currentBuffer].address,
|
||||
.size = static_cast<std::uint32_t>(descriptorHeap->samplerHeap[currentBuffer].size)
|
||||
},
|
||||
.reservedRangeOffset = descriptorHeap->samplerHeap[currentBuffer].size - Device::descriptorHeapProperties.minSamplerHeapReservedRange,
|
||||
.reservedRangeSize = Device::descriptorHeapProperties.minSamplerHeapReservedRange
|
||||
};
|
||||
Device::vkCmdBindSamplerHeapEXT(drawCmdBuffers[currentBuffer], &samplerHeapInfo);
|
||||
|
||||
Device::vkCmdTraceRaysKHR(drawCmdBuffers[currentBuffer], &pipeline->raygenRegion, &pipeline->missRegion, &pipeline->hitRegion, &pipeline->callableRegion, width, height, 1);
|
||||
|
||||
VkImageMemoryBarrier image_memory_barrier2 {
|
||||
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
|
||||
|
|
@ -918,9 +1025,6 @@ void Window::CreateSwapchain()
|
|||
|
||||
// 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 };
|
||||
|
|
@ -929,34 +1033,27 @@ void Window::CreateSwapchain()
|
|||
// 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 = {
|
||||
for (std::uint8_t i = 0; i < numFrames; i++) {
|
||||
imageViews[i] = {
|
||||
.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
|
||||
.flags = 0,
|
||||
.image = images[i],
|
||||
.viewType = VK_IMAGE_VIEW_TYPE_2D,
|
||||
.format = colorFormat,
|
||||
.components = {
|
||||
VK_COMPONENT_SWIZZLE_R,
|
||||
VK_COMPONENT_SWIZZLE_G,
|
||||
VK_COMPONENT_SWIZZLE_B,
|
||||
VK_COMPONENT_SWIZZLE_A
|
||||
},
|
||||
.subresourceRange = {
|
||||
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
|
||||
.baseMipLevel = 0,
|
||||
.levelCount = 1,
|
||||
.baseArrayLayer = 0,
|
||||
.layerCount = 1,
|
||||
},
|
||||
};
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
75
interfaces/Crafter.Graphics-DescriptorHeapVulkan.cppm
Normal file
75
interfaces/Crafter.Graphics-DescriptorHeapVulkan.cppm
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
/*
|
||||
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
|
||||
export module Crafter.Graphics:DescriptorHeapVulkan;
|
||||
#ifdef CRAFTER_GRAPHICS_RENDERER_VULKAN
|
||||
import std;
|
||||
import :Device;
|
||||
import :Window;
|
||||
import :Types;
|
||||
import :VulkanBuffer;
|
||||
|
||||
export namespace Crafter {
|
||||
struct DescriptorHeapVulkan {
|
||||
VulkanBuffer<std::uint8_t, true> resourceHeap[Window::numFrames];
|
||||
VulkanBuffer<std::uint8_t, true> samplerHeap[Window::numFrames];
|
||||
std::uint32_t bufferStartOffset;
|
||||
std::uint16_t bufferStartElement;
|
||||
|
||||
void Initialize(std::uint16_t images, std::uint16_t buffers, std::uint16_t samplers) {
|
||||
std::uint32_t descriptorRegion = images * Device::descriptorHeapProperties.imageDescriptorSize + buffers * Device::descriptorHeapProperties.bufferDescriptorSize;
|
||||
std::uint32_t alignedDescriptorRegion = (descriptorRegion + Device::descriptorHeapProperties.imageDescriptorAlignment - 1) & ~(Device::descriptorHeapProperties.imageDescriptorAlignment - 1);
|
||||
std::uint32_t resourceSize = alignedDescriptorRegion + Device::descriptorHeapProperties.minResourceHeapReservedRange;
|
||||
std::uint32_t samplerSize = samplers * Device::descriptorHeapProperties.samplerDescriptorSize + Device::descriptorHeapProperties.minSamplerHeapReservedRange;
|
||||
bufferStartElement = images * Device::descriptorHeapProperties.imageDescriptorSize / Device::descriptorHeapProperties.bufferDescriptorSize;
|
||||
|
||||
if(images > 0 && bufferStartElement == 0) {
|
||||
bufferStartElement = 1;
|
||||
}
|
||||
bufferStartOffset = bufferStartElement * Device::descriptorHeapProperties.bufferDescriptorSize;
|
||||
for(std::uint8_t i = 0; i < Window::numFrames; i++) {
|
||||
resourceHeap[i].Resize(VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT | VK_BUFFER_USAGE_DESCRIPTOR_HEAP_BIT_EXT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, resourceSize);
|
||||
samplerHeap[i].Resize(VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT | VK_BUFFER_USAGE_DESCRIPTOR_HEAP_BIT_EXT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, samplerSize);
|
||||
}
|
||||
}
|
||||
inline static std::uint32_t GetBufferOffset(std::uint16_t images, std::uint16_t buffers) {
|
||||
std::uint32_t bufferStartElement = images * Device::descriptorHeapProperties.imageDescriptorSize / Device::descriptorHeapProperties.bufferDescriptorSize;
|
||||
|
||||
if(images > 0 && bufferStartElement == 0) {
|
||||
bufferStartElement = 1;
|
||||
}
|
||||
return bufferStartElement * Device::descriptorHeapProperties.bufferDescriptorSize;
|
||||
}
|
||||
inline static std::uint16_t GetBufferOffsetElement(std::uint16_t images, std::uint16_t buffers) {
|
||||
std::uint16_t bufferStartElement = images * Device::descriptorHeapProperties.imageDescriptorSize / Device::descriptorHeapProperties.bufferDescriptorSize;
|
||||
|
||||
if(images > 0 && bufferStartElement == 0) {
|
||||
bufferStartElement = 1;
|
||||
}
|
||||
|
||||
return bufferStartElement;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -1,187 +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_RENDERER_VULKAN
|
||||
#include "vulkan/vulkan.h"
|
||||
#endif
|
||||
export module Crafter.Graphics:DescriptorPoolVulkan;
|
||||
#ifdef CRAFTER_GRAPHICS_RENDERER_VULKAN
|
||||
import std;
|
||||
import :Device;
|
||||
import :Types;
|
||||
import :DescriptorSetLayoutVulkan;
|
||||
import Crafter.Event;
|
||||
|
||||
export namespace Crafter {
|
||||
struct DescriptorEntry {
|
||||
VkDescriptorType type;
|
||||
bool occured = true;
|
||||
};
|
||||
|
||||
class DescriptorPool {
|
||||
public:
|
||||
std::vector<VkDescriptorSet> sets;
|
||||
VkDescriptorPool descriptorPool = VK_NULL_HANDLE;
|
||||
|
||||
template <typename Shader>
|
||||
consteval static void GetOccuringDescriptors(std::array<DescriptorEntry, 20>& types) {
|
||||
for (const VkDescriptorSetLayoutBinding& binding : Shader::descriptors) {
|
||||
for (DescriptorEntry& type : types) {
|
||||
if (type.type == binding.descriptorType) {
|
||||
type.occured = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename... Shaders>
|
||||
consteval static std::uint32_t GetUniqueDiscriptorCount() {
|
||||
std::array<DescriptorEntry, 20> types = {{{VK_DESCRIPTOR_TYPE_SAMPLER, 0},{VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 0},{VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, 0},{VK_DESCRIPTOR_TYPE_STORAGE_IMAGE, 0},{VK_DESCRIPTOR_TYPE_UNIFORM_TEXEL_BUFFER, 0},{VK_DESCRIPTOR_TYPE_STORAGE_TEXEL_BUFFER, 0},{VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 0},{VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 0},{VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER_DYNAMIC, 0},{VK_DESCRIPTOR_TYPE_STORAGE_BUFFER_DYNAMIC, 0},{VK_DESCRIPTOR_TYPE_INPUT_ATTACHMENT, 0},{VK_DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK, 0},{VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_KHR, 0},{VK_DESCRIPTOR_TYPE_ACCELERATION_STRUCTURE_NV, 0},{VK_DESCRIPTOR_TYPE_SAMPLE_WEIGHT_IMAGE_QCOM, 0},{VK_DESCRIPTOR_TYPE_BLOCK_MATCH_IMAGE_QCOM, 0},{VK_DESCRIPTOR_TYPE_MUTABLE_EXT, 0},{VK_DESCRIPTOR_TYPE_PARTITIONED_ACCELERATION_STRUCTURE_NV, 0},{VK_DESCRIPTOR_TYPE_INLINE_UNIFORM_BLOCK_EXT, 0},{VK_DESCRIPTOR_TYPE_MUTABLE_VALVE, 0}}};
|
||||
|
||||
(GetOccuringDescriptors<Shaders>(types), ... );
|
||||
|
||||
std::uint32_t size = 0;
|
||||
for(DescriptorEntry& type : types) {
|
||||
if(type.occured) {
|
||||
size++;
|
||||
}
|
||||
}
|
||||
|
||||
return size;
|
||||
}
|
||||
|
||||
template <typename... Shaders>
|
||||
consteval static std::array<VkDescriptorPoolSize, GetUniqueDiscriptorCount<Shaders...>()> GetPoolSizes() {
|
||||
std::array<VkDescriptorPoolSize, GetUniqueDiscriptorCount<Shaders...>()> types = {};
|
||||
for(std::uint32_t i = 0; i < GetUniqueDiscriptorCount<Shaders...>(); i++){
|
||||
types[i].descriptorCount = 12345;
|
||||
}
|
||||
|
||||
([&] {
|
||||
for (const VkDescriptorSetLayoutBinding& binding : Shaders::descriptors) {
|
||||
bool found = false;
|
||||
for(VkDescriptorPoolSize& type : types) {
|
||||
if(type.type == binding.descriptorType && type.descriptorCount != 12345) {
|
||||
type.descriptorCount += binding.descriptorCount;
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
if(!found) {
|
||||
for(std::uint32_t i = 0; i < GetUniqueDiscriptorCount<Shaders...>(); i++){
|
||||
if(types[i].descriptorCount == 12345) {
|
||||
types[i].type = binding.descriptorType;
|
||||
types[i].descriptorCount = binding.descriptorCount;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}(),
|
||||
...);
|
||||
|
||||
return types;
|
||||
}
|
||||
|
||||
constexpr static std::vector<VkDescriptorPoolSize> GetPoolSizes(const std::span<const DescriptorSetLayoutVulkan> shaders) {
|
||||
std::vector<VkDescriptorPoolSize> types;
|
||||
|
||||
for(const DescriptorSetLayoutVulkan& shader : shaders) {
|
||||
for (const VkDescriptorSetLayoutBinding& binding : shader.descriptors) {
|
||||
for(VkDescriptorPoolSize& type : types) {
|
||||
if(type.type == binding.descriptorType) {
|
||||
type.descriptorCount += binding.descriptorCount;
|
||||
goto inner;
|
||||
}
|
||||
}
|
||||
types.emplace_back(binding.descriptorType, binding.descriptorCount);
|
||||
inner:;
|
||||
}
|
||||
}
|
||||
|
||||
return types;
|
||||
}
|
||||
|
||||
template <typename... Shaders>
|
||||
constexpr static std::vector<VkDescriptorPoolSize> GetPoolSizesCombined(const std::span<const DescriptorSetLayoutVulkan> shaders) {
|
||||
std::vector<VkDescriptorPoolSize> types;
|
||||
|
||||
for(const DescriptorSetLayoutVulkan& shader : shaders) {
|
||||
for (const VkDescriptorSetLayoutBinding& binding : shader.descriptors) {
|
||||
for(VkDescriptorPoolSize& type : types) {
|
||||
if(type.type == binding.descriptorType) {
|
||||
type.descriptorCount += binding.descriptorCount;
|
||||
goto inner;
|
||||
}
|
||||
}
|
||||
types.emplace_back(binding.descriptorType, binding.descriptorCount);
|
||||
inner:;
|
||||
}
|
||||
}
|
||||
|
||||
([&] {
|
||||
for (const VkDescriptorSetLayoutBinding& binding : Shaders::descriptors) {
|
||||
for(VkDescriptorPoolSize& type : types) {
|
||||
if(type.type == binding.descriptorType) {
|
||||
type.descriptorCount += binding.descriptorCount;
|
||||
goto inner2;
|
||||
}
|
||||
}
|
||||
types.emplace_back(binding.descriptorType, binding.descriptorCount);
|
||||
inner2:;
|
||||
}
|
||||
}(),
|
||||
...);
|
||||
|
||||
return types;
|
||||
}
|
||||
|
||||
public:
|
||||
~DescriptorPool() {
|
||||
vkDestroyDescriptorPool(Device::device, descriptorPool, nullptr);
|
||||
}
|
||||
void BuildPool(std::span<const VkDescriptorPoolSize> poolSizes, std::span<const VkDescriptorSetLayout> setLayouts) {
|
||||
if(descriptorPool != VK_NULL_HANDLE) {
|
||||
vkDestroyDescriptorPool(Device::device, descriptorPool, nullptr);
|
||||
}
|
||||
|
||||
sets.resize(setLayouts.size());
|
||||
|
||||
VkDescriptorPoolCreateInfo descriptorPoolInfo {
|
||||
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO,
|
||||
.maxSets = static_cast<std::uint32_t>(sets.size()),
|
||||
.poolSizeCount = static_cast<std::uint32_t>(poolSizes.size()),
|
||||
.pPoolSizes = poolSizes.data()
|
||||
};
|
||||
|
||||
Device::CheckVkResult(vkCreateDescriptorPool(Device::device, &descriptorPoolInfo, nullptr, &descriptorPool));
|
||||
|
||||
VkDescriptorSetAllocateInfo allocInfo {
|
||||
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO,
|
||||
.descriptorPool = descriptorPool,
|
||||
.descriptorSetCount = static_cast<std::uint32_t>(sets.size()),
|
||||
.pSetLayouts = setLayouts.data(),
|
||||
};
|
||||
|
||||
Device::CheckVkResult(vkAllocateDescriptorSets(Device::device, &allocInfo, sets.data()));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -1,62 +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_RENDERER_VULKAN
|
||||
#include "vulkan/vulkan.h"
|
||||
#endif
|
||||
export module Crafter.Graphics:DescriptorSetLayoutVulkan;
|
||||
#ifdef CRAFTER_GRAPHICS_RENDERER_VULKAN
|
||||
import std;
|
||||
import :Device;
|
||||
import :Types;
|
||||
import Crafter.Event;
|
||||
|
||||
export namespace Crafter {
|
||||
class DescriptorSetLayoutVulkan {
|
||||
public:
|
||||
VkDescriptorSetLayout layout;
|
||||
std::vector<VkDescriptorSetLayoutBinding> descriptors;
|
||||
DescriptorSetLayoutVulkan(std::vector<VkDescriptorSetLayoutBinding>&& layouts) : descriptors(std::move(layouts)) {
|
||||
VkDescriptorSetLayoutCreateInfo descriptorLayoutInfoMesh = {
|
||||
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO,
|
||||
.bindingCount = static_cast<std::uint32_t>(descriptors.size()),
|
||||
.pBindings = descriptors.data()
|
||||
};
|
||||
Device::CheckVkResult(vkCreateDescriptorSetLayout(Device::device, &descriptorLayoutInfoMesh, nullptr, &layout));
|
||||
}
|
||||
};
|
||||
|
||||
template<std::uint32_t DescriptorCount, const std::array<VkDescriptorSetLayoutBinding, DescriptorCount> Descriptors>
|
||||
class DescriptorSetLayoutVulkanConst {
|
||||
public:
|
||||
inline static VkDescriptorSetLayout layout;
|
||||
constexpr static std::span<const VkDescriptorSetLayoutBinding> descriptors = Descriptors;
|
||||
static void Init() {
|
||||
VkDescriptorSetLayoutCreateInfo descriptorLayoutInfoMesh = {
|
||||
.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO,
|
||||
.bindingCount = DescriptorCount,
|
||||
.pBindings = Descriptors.data()
|
||||
};
|
||||
Device::CheckVkResult(vkCreateDescriptorSetLayout(Device::device, &descriptorLayoutInfoMesh, nullptr, &layout));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -114,9 +114,20 @@ export namespace Crafter {
|
|||
inline static PFN_vkCreateRayTracingPipelinesKHR vkCreateRayTracingPipelinesKHR;
|
||||
inline static PFN_vkGetRayTracingShaderGroupHandlesKHR vkGetRayTracingShaderGroupHandlesKHR;
|
||||
inline static PFN_vkCmdTraceRaysKHR vkCmdTraceRaysKHR;
|
||||
inline static PFN_vkCmdBindResourceHeapEXT vkCmdBindResourceHeapEXT;
|
||||
inline static PFN_vkCmdBindSamplerHeapEXT vkCmdBindSamplerHeapEXT;
|
||||
inline static PFN_vkWriteResourceDescriptorsEXT vkWriteResourceDescriptorsEXT;
|
||||
inline static PFN_vkGetPhysicalDeviceDescriptorSizeEXT vkGetPhysicalDeviceDescriptorSizeEXT;
|
||||
inline static PFN_vkGetDeviceFaultInfoEXT vkGetDeviceFaultInfoEXT;
|
||||
|
||||
inline static VkPhysicalDeviceMemoryProperties memoryProperties;
|
||||
|
||||
inline static VkPhysicalDeviceDescriptorHeapPropertiesEXT descriptorHeapProperties = {
|
||||
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_HEAP_PROPERTIES_EXT
|
||||
};
|
||||
inline static VkPhysicalDeviceRayTracingPipelinePropertiesKHR rayTracingProperties = {
|
||||
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RAY_TRACING_PIPELINE_PROPERTIES_KHR
|
||||
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_RAY_TRACING_PIPELINE_PROPERTIES_KHR,
|
||||
.pNext = &descriptorHeapProperties
|
||||
};
|
||||
|
||||
static void CheckVkResult(VkResult result);
|
||||
|
|
|
|||
|
|
@ -34,5 +34,6 @@ namespace Crafter {
|
|||
std::int_fast32_t lineGap;
|
||||
stbtt_fontinfo font;
|
||||
Font(const std::filesystem::path& font);
|
||||
std::uint32_t GetLineWidth(const std::string_view text, float size);
|
||||
};
|
||||
}
|
||||
|
|
@ -21,9 +21,7 @@ export module Crafter.Graphics:ForwardDeclarations;
|
|||
import std;
|
||||
|
||||
export namespace Crafter {
|
||||
template<std::uint8_t Frames = 1>
|
||||
struct RendertargetBase;
|
||||
|
||||
struct GridElement;
|
||||
struct Window;
|
||||
}
|
||||
|
|
@ -33,8 +33,7 @@ export namespace Crafter {
|
|||
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) {
|
||||
|
||||
}
|
||||
template<std::uint8_t Frames>
|
||||
void UpdatePositionImpl(RendertargetBase<Frames>& window, Transform2D& parent) {
|
||||
void UpdatePosition(RendertargetBase& window, Transform2D& parent) override {
|
||||
ScaleElement(parent);
|
||||
std::int32_t cellWidth = (paddingX * 2) - (spacingX * (columns - 1)) / columns;
|
||||
std::int32_t cellHeight = (paddingY * 2) - (spacingY * (rows - 1)) / rows;
|
||||
|
|
@ -61,11 +60,5 @@ export namespace Crafter {
|
|||
}
|
||||
}
|
||||
}
|
||||
void UpdatePosition(RendertargetBase<1>& window, Transform2D& parent) override {
|
||||
UpdatePositionImpl<1>(window, parent);
|
||||
}
|
||||
void UpdatePosition(RendertargetBase<3>& window, Transform2D& parent) override {
|
||||
UpdatePositionImpl<3>(window, parent);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
@ -37,7 +37,7 @@ export namespace Crafter {
|
|||
std::uint8_t mipLevels;
|
||||
VkImage image;
|
||||
VkDeviceMemory imageMemory;
|
||||
VulkanBuffer<PixelType, true, false, false> buffer;
|
||||
VulkanBuffer<PixelType, true> buffer;
|
||||
VkImageView imageView;
|
||||
VkDescriptorImageInfo descriptor;
|
||||
|
||||
|
|
|
|||
|
|
@ -32,10 +32,10 @@ export namespace Crafter {
|
|||
#ifdef CRAFTER_GRAPHICS_RENDERER_VULKAN
|
||||
class Mesh {
|
||||
public:
|
||||
VulkanBuffer<char, false, true, false> scratchBuffer;
|
||||
VulkanBuffer<char, false, true, false> blasBuffer;
|
||||
VulkanBuffer<Vector<float, 3, 3>, true, true, false> vertexBuffer;
|
||||
VulkanBuffer<std::uint32_t, true, true, false> indexBuffer;
|
||||
VulkanBuffer<char, false> scratchBuffer;
|
||||
VulkanBuffer<char, false> blasBuffer;
|
||||
VulkanBuffer<Vector<float, 3, 3>, true> vertexBuffer;
|
||||
VulkanBuffer<std::uint32_t, true> indexBuffer;
|
||||
VkAccelerationStructureGeometryTrianglesDataKHR blasData;
|
||||
VkAccelerationStructureGeometryKHR blas;
|
||||
VkAccelerationStructureKHR accelerationStructure;
|
||||
|
|
|
|||
|
|
@ -30,26 +30,16 @@ import :ShaderBindingTableVulkan;
|
|||
import :Types;
|
||||
|
||||
export namespace Crafter {
|
||||
class PipelineRTVulkan {
|
||||
public:
|
||||
struct PipelineRTVulkan {
|
||||
VkPipeline pipeline;
|
||||
VkPipelineLayout pipelineLayout;
|
||||
std::vector<std::uint8_t> shaderHandles;
|
||||
VulkanBuffer<std::uint8_t, true, true, false> sbtBuffer;
|
||||
VulkanBuffer<std::uint8_t, true> sbtBuffer;
|
||||
VkStridedDeviceAddressRegionKHR raygenRegion;
|
||||
VkStridedDeviceAddressRegionKHR missRegion;
|
||||
VkStridedDeviceAddressRegionKHR hitRegion;
|
||||
VkStridedDeviceAddressRegionKHR callableRegion;
|
||||
|
||||
void Init(VkCommandBuffer cmd, std::span<VkDescriptorSetLayout> setLayouts, std::span<VkRayTracingShaderGroupCreateInfoKHR> raygenGroups, std::span<VkRayTracingShaderGroupCreateInfoKHR> missGroups, std::span<VkRayTracingShaderGroupCreateInfoKHR> hitGroups, ShaderBindingTableVulkan& shaderTable) {
|
||||
VkPipelineLayoutCreateInfo pipelineLayoutInfo {
|
||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
|
||||
.setLayoutCount = static_cast<std::uint32_t>(setLayouts.size()),
|
||||
.pSetLayouts = setLayouts.data()
|
||||
};
|
||||
|
||||
Device::CheckVkResult(vkCreatePipelineLayout(Device::device, &pipelineLayoutInfo, nullptr, &pipelineLayout));
|
||||
|
||||
void Init(VkCommandBuffer cmd, std::span<VkRayTracingShaderGroupCreateInfoKHR> raygenGroups, std::span<VkRayTracingShaderGroupCreateInfoKHR> missGroups, std::span<VkRayTracingShaderGroupCreateInfoKHR> hitGroups, ShaderBindingTableVulkan& shaderTable) {
|
||||
std::vector<VkRayTracingShaderGroupCreateInfoKHR> groups;
|
||||
groups.reserve(raygenGroups.size() + missGroups.size() + hitGroups.size());
|
||||
|
||||
|
|
@ -57,14 +47,21 @@ export namespace Crafter {
|
|||
groups.insert(groups.end(), missGroups.begin(), missGroups.end());
|
||||
groups.insert(groups.end(), hitGroups.begin(), hitGroups.end());
|
||||
|
||||
VkRayTracingPipelineCreateInfoKHR rtPipelineInfo{
|
||||
VkPipelineCreateFlags2CreateInfo flags2 = {
|
||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_CREATE_FLAGS_2_CREATE_INFO,
|
||||
.flags = VK_PIPELINE_CREATE_2_DESCRIPTOR_HEAP_BIT_EXT
|
||||
};
|
||||
|
||||
VkRayTracingPipelineCreateInfoKHR rtPipelineInfo {
|
||||
.sType = VK_STRUCTURE_TYPE_RAY_TRACING_PIPELINE_CREATE_INFO_KHR,
|
||||
.pNext = &flags2,
|
||||
.flags = 0,
|
||||
.stageCount = static_cast<std::uint32_t>(shaderTable.shaderStages.size()),
|
||||
.pStages = shaderTable.shaderStages.data(),
|
||||
.groupCount = static_cast<std::uint32_t>(groups.size()),
|
||||
.pGroups = groups.data(),
|
||||
.maxPipelineRayRecursionDepth = 1,
|
||||
.layout = pipelineLayout
|
||||
.layout = VK_NULL_HANDLE
|
||||
};
|
||||
|
||||
Device::CheckVkResult(Device::vkCreateRayTracingPipelinesKHR(Device::device, {}, {}, 1, &rtPipelineInfo, nullptr, &pipeline));
|
||||
|
|
@ -113,188 +110,9 @@ export namespace Crafter {
|
|||
callableRegion.stride = 0;
|
||||
callableRegion.size = 0;
|
||||
}
|
||||
};
|
||||
|
||||
template <std::uint32_t GeneralShader, std::uint32_t ClosestHitShader, std::uint32_t AnyHitShader, std::uint32_t IntersectionShader>
|
||||
struct ShaderGroup {
|
||||
static constexpr std::uint32_t generalShader = GeneralShader;
|
||||
static constexpr std::uint32_t closestHitShader = ClosestHitShader;
|
||||
static constexpr std::uint32_t anyHitShader = AnyHitShader;
|
||||
static constexpr std::uint32_t intersectionShader = IntersectionShader;
|
||||
};
|
||||
|
||||
template <typename Shaders, typename ShaderGroups>
|
||||
class PipelineRTVulkanConst {
|
||||
public:
|
||||
inline static VkPipeline pipeline;
|
||||
inline static VkPipelineLayout pipelineLayout;
|
||||
inline static std::vector<std::uint8_t> shaderHandles;
|
||||
inline static VulkanBuffer<std::uint8_t, true, true, false> sbtBuffer;
|
||||
inline static VkStridedDeviceAddressRegionKHR raygenRegion;
|
||||
inline static VkStridedDeviceAddressRegionKHR missRegion;
|
||||
inline static VkStridedDeviceAddressRegionKHR hitRegion;
|
||||
inline static VkStridedDeviceAddressRegionKHR callableRegion;
|
||||
|
||||
static void Init(VkCommandBuffer cmd, const std::span<const VkDescriptorSetLayout> setLayouts) {
|
||||
VkPipelineLayoutCreateInfo pipelineLayoutInfo {
|
||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
|
||||
.setLayoutCount = static_cast<std::uint32_t>(setLayouts.size()),
|
||||
.pSetLayouts = setLayouts.data()
|
||||
};
|
||||
|
||||
Device::CheckVkResult(vkCreatePipelineLayout(Device::device, &pipelineLayoutInfo, nullptr, &pipelineLayout));
|
||||
|
||||
constexpr auto groupIndexSeq = std::make_index_sequence<std::tuple_size_v<ShaderGroups>>{};
|
||||
|
||||
constexpr std::array<VkRayTracingShaderGroupCreateInfoKHR, std::tuple_size_v<ShaderGroups>> groups = GetShaderGroups(groupIndexSeq);
|
||||
|
||||
VkRayTracingPipelineCreateInfoKHR rtPipelineInfo{
|
||||
.sType = VK_STRUCTURE_TYPE_RAY_TRACING_PIPELINE_CREATE_INFO_KHR,
|
||||
.stageCount = static_cast<std::uint32_t>(ShaderBindingTableVulkanConst<Shaders>::shaderStages.size()),
|
||||
.pStages = ShaderBindingTableVulkanConst<Shaders>::shaderStages.data(),
|
||||
.groupCount = static_cast<std::uint32_t>(groups.size()),
|
||||
.pGroups = groups.data(),
|
||||
.maxPipelineRayRecursionDepth = 1,
|
||||
.layout = pipelineLayout
|
||||
};
|
||||
|
||||
Device::CheckVkResult(Device::vkCreateRayTracingPipelinesKHR(Device::device, {}, {}, 1, &rtPipelineInfo, nullptr, &pipeline));
|
||||
|
||||
std::size_t dataSize = Device::rayTracingProperties.shaderGroupHandleSize * rtPipelineInfo.groupCount;
|
||||
shaderHandles.resize(dataSize);
|
||||
Device::CheckVkResult(Device::vkGetRayTracingShaderGroupHandlesKHR(Device::device, pipeline, 0, rtPipelineInfo.groupCount, dataSize, shaderHandles.data()));
|
||||
|
||||
std::uint32_t sbtStride = AlignUp(Device::rayTracingProperties.shaderGroupHandleSize, Device::rayTracingProperties.shaderGroupHandleAlignment);
|
||||
|
||||
raygenRegion.stride = sbtStride;
|
||||
raygenRegion.deviceAddress = 0;
|
||||
raygenRegion.size = GetGroupCount<VK_SHADER_STAGE_RAYGEN_BIT_KHR>(groupIndexSeq) * sbtStride;
|
||||
|
||||
missRegion.stride = sbtStride;
|
||||
missRegion.deviceAddress = AlignUp(raygenRegion.size, Device::rayTracingProperties.shaderGroupBaseAlignment);
|
||||
missRegion.size = GetGroupCount<VK_SHADER_STAGE_MISS_BIT_KHR>(groupIndexSeq) * sbtStride;
|
||||
|
||||
hitRegion.stride = sbtStride;
|
||||
hitRegion.deviceAddress = AlignUp(missRegion.deviceAddress + missRegion.size, Device::rayTracingProperties.shaderGroupBaseAlignment);
|
||||
hitRegion.size = (GetGroupCount<VK_SHADER_STAGE_CLOSEST_HIT_BIT_KHR>(groupIndexSeq) * sbtStride) + (GetGroupCount<VK_SHADER_STAGE_ANY_HIT_BIT_KHR>(groupIndexSeq) * sbtStride);
|
||||
|
||||
std::size_t bufferSize = hitRegion.deviceAddress + hitRegion.size;
|
||||
sbtBuffer.Create(VK_BUFFER_USAGE_2_SHADER_BINDING_TABLE_BIT_KHR | VK_BUFFER_USAGE_SHADER_DEVICE_ADDRESS_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, bufferSize);
|
||||
|
||||
AddShaderGroupsToBuffer(sbtStride, groupIndexSeq);
|
||||
sbtBuffer.FlushDevice(cmd, VK_ACCESS_MEMORY_READ_BIT, VK_PIPELINE_STAGE_RAY_TRACING_SHADER_BIT_KHR);
|
||||
|
||||
raygenRegion.deviceAddress += sbtBuffer.address;
|
||||
missRegion.deviceAddress += sbtBuffer.address;
|
||||
hitRegion.deviceAddress += sbtBuffer.address;
|
||||
|
||||
callableRegion.deviceAddress = 0;
|
||||
callableRegion.stride = 0;
|
||||
callableRegion.size = 0;
|
||||
}
|
||||
private:
|
||||
template<std::size_t index>
|
||||
consteval static void AddShaderGroup(std::array<VkRayTracingShaderGroupCreateInfoKHR, std::tuple_size_v<ShaderGroups>>& groups) {
|
||||
using groupTemplate = std::tuple_element_t<index, ShaderGroups>;
|
||||
VkRayTracingShaderGroupTypeKHR groupType;
|
||||
if constexpr(groupTemplate::generalShader != VK_SHADER_UNUSED_KHR) {
|
||||
groupType = VK_RAY_TRACING_SHADER_GROUP_TYPE_GENERAL_KHR;
|
||||
} else if constexpr(groupTemplate::closestHitShader != VK_SHADER_UNUSED_KHR || groupTemplate::anyHitShader != VK_SHADER_UNUSED_KHR) {
|
||||
groupType = VK_RAY_TRACING_SHADER_GROUP_TYPE_TRIANGLES_HIT_GROUP_KHR;
|
||||
} else {
|
||||
static_assert(
|
||||
groupTemplate::generalShader != VK_SHADER_UNUSED_KHR ||
|
||||
groupTemplate::closestHitShader != VK_SHADER_UNUSED_KHR,
|
||||
"Shader group must define either a general or closest-hit shader"
|
||||
);
|
||||
}
|
||||
groups[index] = {
|
||||
.sType = VK_STRUCTURE_TYPE_RAY_TRACING_SHADER_GROUP_CREATE_INFO_KHR,
|
||||
.type = groupType,
|
||||
.generalShader = groupTemplate::generalShader,
|
||||
.closestHitShader = groupTemplate::closestHitShader,
|
||||
.anyHitShader = groupTemplate::anyHitShader,
|
||||
.intersectionShader = groupTemplate::intersectionShader
|
||||
};
|
||||
}
|
||||
template<std::size_t... Is>
|
||||
consteval static std::array<VkRayTracingShaderGroupCreateInfoKHR, std::tuple_size_v<ShaderGroups>> GetShaderGroups(std::index_sequence<Is...>) {
|
||||
std::array<VkRayTracingShaderGroupCreateInfoKHR, std::tuple_size_v<ShaderGroups>> groups{};
|
||||
(AddShaderGroup<Is>(groups), ...);
|
||||
return groups;
|
||||
}
|
||||
|
||||
template<std::size_t index, VkShaderStageFlagBits stage>
|
||||
consteval static void GetGroupCountImpl(std::uint32_t& count) {
|
||||
using groupTemplate = std::tuple_element_t<index, ShaderGroups>;
|
||||
if constexpr(groupTemplate::generalShader != VK_SHADER_UNUSED_KHR) {
|
||||
using shaderTemplate = std::tuple_element_t<groupTemplate::generalShader, Shaders>;
|
||||
if constexpr(shaderTemplate::_stage == stage) {
|
||||
count++;
|
||||
}
|
||||
} else if constexpr(groupTemplate::closestHitShader != VK_SHADER_UNUSED_KHR) {
|
||||
using shaderTemplate = std::tuple_element_t<groupTemplate::closestHitShader, Shaders>;
|
||||
if constexpr(shaderTemplate::_stage == stage) {
|
||||
count++;
|
||||
}
|
||||
} else if constexpr(groupTemplate::anyHitShader != VK_SHADER_UNUSED_KHR) {
|
||||
using shaderTemplate = std::tuple_element_t<groupTemplate::anyHitShader, Shaders>;
|
||||
if constexpr(shaderTemplate::_stage == stage) {
|
||||
count++;
|
||||
}
|
||||
} else {
|
||||
static_assert(
|
||||
groupTemplate::generalShader != VK_SHADER_UNUSED_KHR ||
|
||||
groupTemplate::closestHitShader != VK_SHADER_UNUSED_KHR,
|
||||
"Shader group must define either a general or closest-hit shader"
|
||||
);
|
||||
}
|
||||
}
|
||||
template<VkShaderStageFlagBits stage, std::size_t... Is>
|
||||
consteval static std::uint32_t GetGroupCount(std::index_sequence<Is...>) {
|
||||
std::uint32_t count = 0;
|
||||
(GetGroupCountImpl<Is, stage>(count), ...);
|
||||
return count;
|
||||
}
|
||||
|
||||
template<std::size_t index, VkShaderStageFlagBits stage>
|
||||
static void AddShaderGroupToBuffer(std::uint32_t sbtStride, std::uint32_t& offset) {
|
||||
using groupTemplate = std::tuple_element_t<index, ShaderGroups>;
|
||||
if constexpr(groupTemplate::generalShader != VK_SHADER_UNUSED_KHR) {
|
||||
using shaderTemplate = std::tuple_element_t<groupTemplate::generalShader, Shaders>;
|
||||
if constexpr(shaderTemplate::_stage == stage) {
|
||||
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<groupTemplate::closestHitShader, Shaders>;
|
||||
if constexpr(shaderTemplate::_stage == stage) {
|
||||
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<groupTemplate::anyHitShader, Shaders>;
|
||||
if constexpr(shaderTemplate::_stage == stage) {
|
||||
std::memcpy(sbtBuffer.value + offset, shaderHandles.data() + index * Device::rayTracingProperties.shaderGroupHandleSize, Device::rayTracingProperties.shaderGroupHandleSize);
|
||||
offset += sbtStride;
|
||||
}
|
||||
} else {
|
||||
static_assert(
|
||||
groupTemplate::generalShader != VK_SHADER_UNUSED_KHR ||
|
||||
groupTemplate::closestHitShader != VK_SHADER_UNUSED_KHR,
|
||||
"Shader group must define either a general or closest-hit shader"
|
||||
);
|
||||
}
|
||||
}
|
||||
template<std::size_t... Is>
|
||||
static void AddShaderGroupsToBuffer(std::uint32_t sbtStride, std::index_sequence<Is...>) {
|
||||
std::uint32_t offset = 0;
|
||||
(AddShaderGroupToBuffer<Is, VK_SHADER_STAGE_RAYGEN_BIT_KHR>(sbtStride, offset), ...);
|
||||
offset = AlignUp(offset, Device::rayTracingProperties.shaderGroupBaseAlignment);
|
||||
(AddShaderGroupToBuffer<Is, VK_SHADER_STAGE_MISS_BIT_KHR>(sbtStride, offset), ...);
|
||||
offset = AlignUp(offset, Device::rayTracingProperties.shaderGroupBaseAlignment);
|
||||
(AddShaderGroupToBuffer<Is, VK_SHADER_STAGE_CLOSEST_HIT_BIT_KHR>(sbtStride, offset), ...);
|
||||
(AddShaderGroupToBuffer<Is, VK_SHADER_STAGE_ANY_HIT_BIT_KHR>(sbtStride, offset), ...);
|
||||
~PipelineRTVulkan() {
|
||||
vkDestroyPipeline(Device::device, pipeline, nullptr);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,31 +29,31 @@ import :Types;
|
|||
import :Window;
|
||||
|
||||
export namespace Crafter {
|
||||
template<bool Scaling, bool Owning, bool Rotating, std::uint8_t Alignment = 0, std::uint8_t Frames = 1> requires ((!Rotating || Scaling) && (!Owning || Scaling))
|
||||
struct RenderingElement2D : RenderingElement2DBase<Frames>, ScalingBase<Scaling, Owning, Alignment>, RotatingBase<Rotating> {
|
||||
template<typename T, bool Scaling, bool Owning, bool Rotating, std::uint8_t Alignment = 0, std::uint8_t Frames = 1> requires ((!Rotating || Scaling) && (!Owning || Scaling))
|
||||
struct RenderingElement2D : RenderingElement2DBase<T, Frames>, ScalingBase<T, Scaling, Owning, Alignment>, RotatingBase<Rotating> {
|
||||
RenderingElement2D() = default;
|
||||
RenderingElement2D(Anchor2D anchor, OpaqueType opaque) : RenderingElement2DBase<Frames>(anchor, opaque) {
|
||||
RenderingElement2D(Anchor2D anchor, OpaqueType opaque) : RenderingElement2DBase<T, Frames>(anchor, opaque) {
|
||||
|
||||
}
|
||||
RenderingElement2D(Anchor2D anchor, OpaqueType opaque, std::uint32_t rotation) requires(Rotating) : RenderingElement2DBase<Frames>(anchor, opaque), RotatingBase<Rotating>(rotation) {
|
||||
RenderingElement2D(Anchor2D anchor, OpaqueType opaque, std::uint32_t rotation) requires(Rotating) : RenderingElement2DBase<T, Frames>(anchor, opaque), RotatingBase<Rotating>(rotation) {
|
||||
|
||||
}
|
||||
RenderingElement2D(Anchor2D anchor, OpaqueType opaque, std::uint32_t bufferWidth, std::uint32_t bufferHeight, Vector<std::uint8_t, 4, Alignment>* scalingBuffer) requires(Scaling && !Owning) : RenderingElement2DBase<Frames>(anchor, opaque), ScalingBase<Scaling, Owning, Alignment>(bufferWidth, bufferHeight, scalingBuffer) {
|
||||
RenderingElement2D(Anchor2D anchor, OpaqueType opaque, std::uint32_t bufferWidth, std::uint32_t bufferHeight, Vector<T, 4, Alignment>* scalingBuffer) requires(Scaling && !Owning) : RenderingElement2DBase<T, Frames>(anchor, opaque), ScalingBase<T, Scaling, Owning, Alignment>(bufferWidth, bufferHeight, scalingBuffer) {
|
||||
|
||||
}
|
||||
RenderingElement2D(Anchor2D anchor, OpaqueType opaque, std::uint32_t bufferWidth, std::uint32_t bufferHeight, Vector<std::uint8_t, 4, Alignment>* scalingBuffer, std::uint32_t rotation) requires(Scaling && !Owning && Rotating) : RenderingElement2DBase<Frames>(anchor, opaque), ScalingBase<Scaling, Owning, Alignment>(bufferWidth, bufferHeight, scalingBuffer), RotatingBase<Rotating>(rotation) {
|
||||
RenderingElement2D(Anchor2D anchor, OpaqueType opaque, std::uint32_t bufferWidth, std::uint32_t bufferHeight, Vector<T, 4, Alignment>* scalingBuffer, std::uint32_t rotation) requires(Scaling && !Owning && Rotating) : RenderingElement2DBase<T, Frames>(anchor, opaque), ScalingBase<T, Scaling, Owning, Alignment>(bufferWidth, bufferHeight, scalingBuffer), RotatingBase<Rotating>(rotation) {
|
||||
|
||||
}
|
||||
RenderingElement2D(Anchor2D anchor, OpaqueType opaque, std::uint32_t bufferWidth, std::uint32_t bufferHeight) requires(Owning) : RenderingElement2DBase<Frames>(anchor, opaque), ScalingBase<Scaling, Owning, Alignment>(bufferWidth, bufferHeight) {
|
||||
RenderingElement2D(Anchor2D anchor, OpaqueType opaque, std::uint32_t bufferWidth, std::uint32_t bufferHeight) requires(Owning) : RenderingElement2DBase<T, Frames>(anchor, opaque), ScalingBase<T, Scaling, Owning, Alignment>(bufferWidth, bufferHeight) {
|
||||
|
||||
}
|
||||
RenderingElement2D(Anchor2D anchor, OpaqueType opaque, std::uint32_t bufferWidth, std::uint32_t bufferHeight, std::uint32_t rotation) requires(Owning && Rotating) : RenderingElement2DBase<Frames>(anchor, opaque), ScalingBase<Scaling, Owning, Alignment>(bufferWidth, bufferHeight) , RotatingBase<Rotating>(rotation) {
|
||||
RenderingElement2D(Anchor2D anchor, OpaqueType opaque, std::uint32_t bufferWidth, std::uint32_t bufferHeight, std::uint32_t rotation) requires(Owning && Rotating) : RenderingElement2DBase<T, Frames>(anchor, opaque), ScalingBase<T, Scaling, Owning, Alignment>(bufferWidth, bufferHeight) , RotatingBase<Rotating>(rotation) {
|
||||
|
||||
}
|
||||
RenderingElement2D(Anchor2D anchor, TextureAsset<Vector<std::uint8_t, 4, Alignment>>& texture) requires(!Owning && Scaling) : RenderingElement2DBase<Frames>(anchor, texture.opaque), ScalingBase<Scaling, Owning, Alignment>(texture.pixels.data(), texture.sizeX, texture.sizeY) {
|
||||
RenderingElement2D(Anchor2D anchor, TextureAsset<Vector<T, 4, Alignment>>& texture) requires(!Owning && Scaling) : RenderingElement2DBase<T, Frames>(anchor, texture.opaque), ScalingBase<T, Scaling, Owning, Alignment>(texture.pixels.data(), texture.sizeX, texture.sizeY) {
|
||||
|
||||
}
|
||||
RenderingElement2D(Anchor2D anchor, TextureAsset<Vector<std::uint8_t, 4, Alignment>>& texture, std::uint32_t rotation) requires(!Owning && Scaling && Rotating) : RenderingElement2DBase<Frames>(anchor, texture.opaque), ScalingBase<Scaling, Owning, Alignment>(texture.pixels.data(), texture.sizeX, texture.sizeY), RotatingBase<Rotating>(rotation) {
|
||||
RenderingElement2D(Anchor2D anchor, TextureAsset<Vector<T, 4, Alignment>>& texture, std::uint32_t rotation) requires(!Owning && Scaling && Rotating) : RenderingElement2DBase<T, Frames>(anchor, texture.opaque), ScalingBase<T, Scaling, Owning, Alignment>(texture.pixels.data(), texture.sizeX, texture.sizeY), RotatingBase<Rotating>(rotation) {
|
||||
|
||||
}
|
||||
|
||||
|
|
@ -62,10 +62,10 @@ export namespace Crafter {
|
|||
|
||||
void ScaleNearestNeighbor() requires(Scaling) {
|
||||
for (std::uint32_t y = 0; y < this->scaled.size.y; y++) {
|
||||
std::uint32_t srcY = y * ScalingBase<true, Owning, Alignment>::bufferHeight / this->scaled.size.y;
|
||||
std::uint32_t srcY = y * ScalingBase<T, true, Owning, Alignment>::bufferHeight / this->scaled.size.y;
|
||||
for (std::uint32_t x = 0; x < this->scaled.size.x; x++) {
|
||||
std::uint32_t srcX = x * ScalingBase<true, Owning, Alignment>::bufferWidth / this->scaled.size.x;
|
||||
this->buffer[y * this->scaled.size.x + x] = ScalingBase<true, Owning, Alignment>::scalingBuffer[srcY * ScalingBase<true, Owning, Alignment>::bufferWidth + srcX];
|
||||
std::uint32_t srcX = x * ScalingBase<T, true, Owning, Alignment>::bufferWidth / this->scaled.size.x;
|
||||
this->buffer[y * this->scaled.size.x + x] = ScalingBase<T, true, Owning, Alignment>::scalingBuffer[srcY * ScalingBase<T, true, Owning, Alignment>::bufferWidth + srcX];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -97,15 +97,15 @@ export namespace Crafter {
|
|||
const float dstCy = (this->scaled.size.y - 1.0) * 0.5;
|
||||
|
||||
// Source center
|
||||
const float srcCx = (ScalingBase<true, Owning, Alignment>::bufferWidth - 1.0) * 0.5;
|
||||
const float srcCy = (ScalingBase<true, Owning, Alignment>::bufferHeight - 1.0) * 0.5;
|
||||
const float srcCx = (ScalingBase<T, true, Owning, Alignment>::bufferWidth - 1.0) * 0.5;
|
||||
const float srcCy = (ScalingBase<T, true, Owning, Alignment>::bufferHeight - 1.0) * 0.5;
|
||||
|
||||
const float c = std::cos(RotatingBase<true>::rotation);
|
||||
const float s = std::sin(RotatingBase<true>::rotation);
|
||||
|
||||
// Scale factors (destination → source)
|
||||
const float scaleX = static_cast<float>(ScalingBase<true, Owning, Alignment>::bufferWidth) / dstWidth;
|
||||
const float scaleY = static_cast<float>(ScalingBase<true, Owning, Alignment>::bufferHeight) / dstHeight;
|
||||
const float scaleX = static_cast<float>(ScalingBase<T, true, Owning, Alignment>::bufferWidth) / dstWidth;
|
||||
const float scaleY = static_cast<float>(ScalingBase<T, true, Owning, Alignment>::bufferHeight) / dstHeight;
|
||||
|
||||
for (std::uint32_t yB = 0; yB < this->scaled.size.y; ++yB) {
|
||||
for (std::uint32_t xB = 0; xB < this->scaled.size.x; ++xB) {
|
||||
|
|
@ -122,15 +122,15 @@ export namespace Crafter {
|
|||
const std::int32_t srcX = static_cast<std::int32_t>(std::round(sx));
|
||||
const std::int32_t srcY = static_cast<std::int32_t>(std::round(sy));
|
||||
|
||||
if (srcX >= 0 && srcX < ScalingBase<true, Owning, Alignment>::bufferWidth && srcY >= 0 && srcY < ScalingBase<true, Owning, Alignment>::bufferHeight) {
|
||||
this->buffer[yB * this->scaled.size.x + xB] = ScalingBase<true, Owning, Alignment>::scalingBuffer[srcY * ScalingBase<true, Owning, Alignment>::bufferWidth + srcX];
|
||||
if (srcX >= 0 && srcX < ScalingBase<T, true, Owning, Alignment>::bufferWidth && srcY >= 0 && srcY < ScalingBase<T, true, Owning, Alignment>::bufferHeight) {
|
||||
this->buffer[yB * this->scaled.size.x + xB] = ScalingBase<T, true, Owning, Alignment>::scalingBuffer[srcY * ScalingBase<T, true, Owning, Alignment>::bufferWidth + srcX];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void UpdatePosition(RendertargetBase<Frames>& window, Transform2D& parent) override {
|
||||
void UpdatePosition(RendertargetBase& window, Transform2D& parent) override {
|
||||
ScaleData2D oldScale = this->scaled;
|
||||
this->ScaleElement(parent);
|
||||
if constexpr(Scaling && !Rotating) {
|
||||
|
|
@ -153,7 +153,7 @@ export namespace Crafter {
|
|||
}
|
||||
}
|
||||
|
||||
std::vector<std::string_view> ResizeText(RendertargetBase<Frames>& window, Transform2D& parent, const std::string_view text, float& size, Font& font, TextOverflowMode overflowMode = TextOverflowMode::Clip, TextScaleMode scaleMode = TextScaleMode::None) {
|
||||
std::vector<std::string_view> ResizeText(RendertargetBase& window, Transform2D& parent, const std::string_view text, float& size, Font& font, TextOverflowMode overflowMode = TextOverflowMode::Clip, TextScaleMode scaleMode = TextScaleMode::None) {
|
||||
float scale = stbtt_ScaleForPixelHeight(&font.font, size);
|
||||
int baseline = (int)(font.ascent * scale);
|
||||
|
||||
|
|
@ -208,7 +208,7 @@ export namespace Crafter {
|
|||
size = std::min(maxFontHeight, maxFontWidth);
|
||||
} else {
|
||||
if constexpr(Scaling) {
|
||||
lines.resize(ScalingBase<true, Owning, Alignment>::bufferHeight / lines.size());
|
||||
lines.resize(ScalingBase<T, true, Owning, Alignment>::bufferHeight / lines.size());
|
||||
} else {
|
||||
lines.resize(this->scaled.size.y / lines.size());
|
||||
}
|
||||
|
|
@ -284,7 +284,7 @@ export namespace Crafter {
|
|||
size = this->scaled.size.y / lineHeightPerFont;
|
||||
} else {
|
||||
if constexpr(Scaling) {
|
||||
lines.resize(ScalingBase<true, Owning, Alignment>::bufferHeight / lines.size());
|
||||
lines.resize(ScalingBase<T, true, Owning, Alignment>::bufferHeight / lines.size());
|
||||
} else {
|
||||
lines.resize(this->scaled.size.y / lines.size());
|
||||
}
|
||||
|
|
@ -293,7 +293,27 @@ export namespace Crafter {
|
|||
|
||||
return lines;
|
||||
}
|
||||
void RenderText(std::span<const std::string_view> lines, float size, Vector<std::uint8_t, 4> color, Font& font, TextAlignment alignment = TextAlignment::Left, std::uint32_t offsetX = 0, std::uint32_t offsetY = 0, OpaqueType opaque = OpaqueType::FullyOpaque) {
|
||||
|
||||
int utf8_decode(const char* s, int* bytes_consumed) {
|
||||
unsigned char c = s[0];
|
||||
if (c < 0x80) {
|
||||
*bytes_consumed = 1;
|
||||
return c;
|
||||
} else if ((c & 0xE0) == 0xC0) {
|
||||
*bytes_consumed = 2;
|
||||
return ((c & 0x1F) << 6) | (s[1] & 0x3F);
|
||||
} else if ((c & 0xF0) == 0xE0) {
|
||||
*bytes_consumed = 3;
|
||||
return ((c & 0x0F) << 12) | ((s[1] & 0x3F) << 6) | (s[2] & 0x3F);
|
||||
} else if ((c & 0xF8) == 0xF0) {
|
||||
*bytes_consumed = 4;
|
||||
return ((c & 0x07) << 18) | ((s[1] & 0x3F) << 12) | ((s[2] & 0x3F) << 6) | (s[3] & 0x3F);
|
||||
}
|
||||
*bytes_consumed = 1;
|
||||
return 0xFFFD; // replacement char
|
||||
}
|
||||
|
||||
void RenderText(std::span<const std::string_view> lines, float size, Vector<T, 4> color, Font& font, TextAlignment alignment = TextAlignment::Left, std::uint32_t offsetX = 0, std::uint32_t offsetY = 0, OpaqueType opaque = OpaqueType::FullyOpaque) {
|
||||
float scale = stbtt_ScaleForPixelHeight(&font.font, size);
|
||||
int baseline = (int)(font.ascent * scale);
|
||||
std::uint32_t lineHeight = (font.ascent - font.descent) * scale;
|
||||
|
|
@ -320,8 +340,13 @@ export namespace Crafter {
|
|||
break;
|
||||
}
|
||||
|
||||
for (std::size_t i = 0; i < line.size(); ++i) {
|
||||
int codepoint = line[i];
|
||||
const char* p = line.data();
|
||||
const char* end = p + line.size();
|
||||
|
||||
while (p < end) {
|
||||
int bytes;
|
||||
int codepoint = utf8_decode(p, &bytes);
|
||||
p += bytes;
|
||||
|
||||
int ax;
|
||||
int lsb;
|
||||
|
|
@ -346,41 +371,19 @@ export namespace Crafter {
|
|||
|
||||
// Only draw pixels that are within our scaled buffer bounds
|
||||
if constexpr(Scaling) {
|
||||
if (bufferX >= 0 && bufferX < ScalingBase<true, Owning, Alignment>::bufferWidth && bufferY >= 0 && bufferY < ScalingBase<true, Owning, Alignment>::bufferHeight) {
|
||||
ScalingBase<true, Owning, Alignment>::scalingBuffer[bufferY * ScalingBase<true, Owning, Alignment>::bufferWidth + bufferX] = {color.r, color.g, color.b, bitmap[j * w + i]};
|
||||
if (bufferX >= 0 && bufferX < ScalingBase<T, true, Owning, Alignment>::bufferWidth && bufferY >= 0 && bufferY < ScalingBase<T, true, Owning, Alignment>::bufferHeight) {
|
||||
ScalingBase<T, true, Owning, Alignment>::scalingBuffer[bufferY * ScalingBase<T, true, Owning, Alignment>::bufferWidth + bufferX] = {color.r, color.g, color.b, static_cast<T>(bitmap[j * w + i])};
|
||||
}
|
||||
} else {
|
||||
if (bufferX >= 0 && bufferX < (int)this->scaled.size.x && bufferY >= 0 && bufferY < (int)this->scaled.size.y) {
|
||||
this->buffer[bufferY * this->scaled.size.x + bufferX] = {color.r, color.g, color.b, bitmap[j * w + i]};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case OpaqueType::SemiOpaque: {
|
||||
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<true, Owning, Alignment>::bufferWidth && bufferY >= 0 && bufferY < ScalingBase<true, Owning, Alignment>::bufferHeight) {
|
||||
ScalingBase<true, Owning, Alignment>::scalingBuffer[bufferY * ScalingBase<true, Owning, Alignment>::bufferWidth + bufferX] = {color.r, color.g, color.b, bitmap[j * w + i]};
|
||||
}
|
||||
} else {
|
||||
if (bufferX >= 0 && bufferX < (int)this->scaled.size.x && bufferY >= 0 && bufferY < (int)this->scaled.size.y) {
|
||||
std::uint8_t alpha = bitmap[j * w + i];
|
||||
if(alpha != 0) {
|
||||
this->buffer[bufferY * this->scaled.size.x + bufferX] = {color.r, color.g, color.b, bitmap[j * w + i]};
|
||||
}
|
||||
this->buffer[bufferY * this->scaled.size.x + bufferX] = {color.r, color.g, color.b, static_cast<T>(bitmap[j * w + i])};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case OpaqueType::SemiOpaque:
|
||||
case OpaqueType::Transparent: {
|
||||
for (int j = 0; j < h; j++) {
|
||||
for (int i = 0; i < w; i++) {
|
||||
|
|
@ -389,22 +392,21 @@ export namespace Crafter {
|
|||
|
||||
// Only draw pixels that are within our scaled buffer bounds
|
||||
if constexpr(Scaling) {
|
||||
if (bufferX >= 0 && bufferX < ScalingBase<true, Owning, Alignment>::bufferWidth && bufferY >= 0 && bufferY < ScalingBase<true, Owning, Alignment>::bufferHeight) {
|
||||
ScalingBase<true, Owning, Alignment>::scalingBuffer[bufferY * ScalingBase<true, Owning, Alignment>::bufferWidth + bufferX] = {color.r, color.g, color.b, bitmap[j * w + i]};
|
||||
if (bufferX >= 0 && bufferX < ScalingBase<T, true, Owning, Alignment>::bufferWidth && bufferY >= 0 && bufferY < ScalingBase<T, true, Owning, Alignment>::bufferHeight) {
|
||||
ScalingBase<T, true, Owning, Alignment>::scalingBuffer[bufferY * ScalingBase<T, true, Owning, Alignment>::bufferWidth + bufferX] = {color.r, color.g, color.b, static_cast<T>(bitmap[j * w + i])};
|
||||
}
|
||||
} else {
|
||||
if (bufferX >= 0 && bufferX < (int)this->scaled.size.x && bufferY >= 0 && bufferY < (int)this->scaled.size.y) {
|
||||
if constexpr(std::same_as<T, std::uint8_t>) {
|
||||
std::uint8_t alpha = bitmap[j * w + i];
|
||||
|
||||
if(alpha == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Vector<std::uint8_t, 4, Alignment> dst = this->buffer[bufferY * this->scaled.size.x + bufferX];
|
||||
Vector<T, 4, Alignment> dst = this->buffer[bufferY * this->scaled.size.x + bufferX];
|
||||
|
||||
float srcA = (alpha / 255.0f) * (color.a / 255.0f);
|
||||
float dstA = dst.a / 255.0f;
|
||||
|
||||
float oneMinusSrcA = 1.0f - color.a;
|
||||
|
||||
float outA = srcA + dstA * (1.0f - srcA);
|
||||
this->buffer[bufferY * this->scaled.size.x + bufferX] = Vector<std::uint8_t, 4, Alignment>(
|
||||
static_cast<std::uint8_t>((color.r * srcA + dst.r * dstA * (1.0f - srcA)) / outA),
|
||||
|
|
@ -412,6 +414,19 @@ export namespace Crafter {
|
|||
static_cast<std::uint8_t>((color.b * srcA + dst.b * dstA * (1.0f - srcA)) / outA),
|
||||
static_cast<std::uint8_t>(outA * 255)
|
||||
);
|
||||
} else if constexpr(std::same_as<T, _Float16>) {
|
||||
std::uint8_t alpha = bitmap[j * w + i];
|
||||
_Float16 srcA = (_Float16(alpha)/_Float16(255.0f))*color.a;
|
||||
Vector<_Float16, 4, Alignment> dst = this->buffer[bufferY * this->scaled.size.x + bufferX];
|
||||
|
||||
_Float16 outA = srcA + dst.a * (1.0f - srcA);
|
||||
this->buffer[bufferY * this->scaled.size.x + bufferX] = Vector<_Float16, 4, Alignment>(
|
||||
(color.r * srcA + dst.r * dst.a * (1.0f - srcA)),
|
||||
(color.g * srcA + dst.g * dst.a * (1.0f - srcA)),
|
||||
(color.b * srcA + dst.b * dst.a * (1.0f - srcA)),
|
||||
outA
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -422,8 +437,9 @@ export namespace Crafter {
|
|||
|
||||
x += (int)(ax * scale);
|
||||
|
||||
if (i + 1 < line.size()) {
|
||||
x += (int)stbtt_GetGlyphKernAdvance(&font.font, codepoint, line[i+1]);
|
||||
if (p + 1 < end) {
|
||||
int next;
|
||||
x += (int)stbtt_GetGlyphKernAdvance(&font.font, codepoint, utf8_decode(p+1, &next));
|
||||
}
|
||||
}
|
||||
currentY += lineHeight;
|
||||
|
|
|
|||
|
|
@ -30,6 +30,12 @@ export namespace Crafter {
|
|||
Right
|
||||
};
|
||||
|
||||
enum class TextVerticalAlignment {
|
||||
Top,
|
||||
Center,
|
||||
Bottom
|
||||
};
|
||||
|
||||
enum class TextOverflowMode {
|
||||
Clip,
|
||||
Wrap
|
||||
|
|
@ -42,9 +48,9 @@ export namespace Crafter {
|
|||
Buffer
|
||||
};
|
||||
|
||||
template<std::uint8_t Alignment = 0>
|
||||
template<typename T, std::uint8_t Alignment = 0>
|
||||
struct RenderElement2DScalingOwning {
|
||||
std::vector<Vector<std::uint8_t, 4, Alignment>> scalingBuffer;
|
||||
std::vector<Vector<T, 4, Alignment>> scalingBuffer;
|
||||
std::uint32_t bufferWidth;
|
||||
std::uint32_t bufferHeight;
|
||||
RenderElement2DScalingOwning() = default;
|
||||
|
|
@ -53,13 +59,13 @@ export namespace Crafter {
|
|||
}
|
||||
};
|
||||
|
||||
template<std::uint8_t Alignment = 0>
|
||||
template<typename T, std::uint8_t Alignment = 0>
|
||||
struct RenderElement2DScalingNonOwning {
|
||||
Vector<std::uint8_t, 4, Alignment>* scalingBuffer;
|
||||
Vector<T, 4, Alignment>* scalingBuffer;
|
||||
std::uint32_t bufferWidth;
|
||||
std::uint32_t bufferHeight;
|
||||
RenderElement2DScalingNonOwning() = default;
|
||||
RenderElement2DScalingNonOwning(Vector<std::uint8_t, 4, Alignment>* scalingBuffer, std::uint32_t bufferWidth, std::uint32_t bufferHeight) : scalingBuffer(scalingBuffer), bufferWidth(bufferWidth), bufferHeight(bufferHeight) {
|
||||
RenderElement2DScalingNonOwning(Vector<T, 4, Alignment>* scalingBuffer, std::uint32_t bufferWidth, std::uint32_t bufferHeight) : scalingBuffer(scalingBuffer), bufferWidth(bufferWidth), bufferHeight(bufferHeight) {
|
||||
|
||||
}
|
||||
};
|
||||
|
|
@ -76,13 +82,13 @@ export namespace Crafter {
|
|||
struct EmptyScalingBase {};
|
||||
struct EmptyRotatingBase {};
|
||||
|
||||
template<bool Scaling, bool Owning, std::uint8_t Alignment = 0>
|
||||
template<typename T, bool Scaling, bool Owning, std::uint8_t Alignment = 0>
|
||||
using ScalingBase =
|
||||
std::conditional_t<
|
||||
Scaling,
|
||||
std::conditional_t<Owning,
|
||||
RenderElement2DScalingOwning<Alignment>,
|
||||
RenderElement2DScalingNonOwning<Alignment>>,
|
||||
RenderElement2DScalingOwning<T, Alignment>,
|
||||
RenderElement2DScalingNonOwning<T, Alignment>>,
|
||||
EmptyScalingBase
|
||||
>;
|
||||
|
||||
|
|
@ -94,11 +100,11 @@ export namespace Crafter {
|
|||
EmptyRotatingBase
|
||||
>;
|
||||
|
||||
template<std::uint8_t Frames = 1>
|
||||
template<typename T, std::uint8_t Frames = 1>
|
||||
struct RenderingElement2DBase : Transform2D {
|
||||
ScaleData2D oldScale[Frames];
|
||||
bool redraw[Frames];
|
||||
std::vector<Vector<std::uint8_t, 4>> buffer;
|
||||
std::vector<Vector<T, 4, 4>> buffer;
|
||||
OpaqueType opaque;
|
||||
RenderingElement2DBase(Anchor2D anchor) : Transform2D(anchor) {
|
||||
for(std::uint8_t i = 0; i < Frames; i++) {
|
||||
|
|
@ -115,5 +121,16 @@ export namespace Crafter {
|
|||
redraw[i] = true;
|
||||
}
|
||||
}
|
||||
void CopyNearestNeighbor(Vector<T, 4>* dst, std::uint16_t dstSizeX, std::uint16_t dstScaledSizeX, std::uint16_t dstScaledSizeY, std::uint16_t offsetX, std::uint16_t offsetY) {
|
||||
for (std::uint16_t y = 0; y < dstScaledSizeY; y++) {
|
||||
std::uint16_t srcY = y * scaled.size.y / dstScaledSizeY;
|
||||
std::uint16_t dstY = y + offsetY;
|
||||
for (std::uint16_t x = 0; x < dstScaledSizeX; x++) {
|
||||
std::uint16_t srcX = x * scaled.size.x / dstScaledSizeX;
|
||||
std::uint16_t dstX = x + offsetX;
|
||||
dst[dstY * dstSizeX + dstX] = buffer[srcY * this->scaled.size.x + srcX];
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
477
interfaces/Crafter.Graphics-RenderingElement2DVulkan.cppm
Normal file
477
interfaces/Crafter.Graphics-RenderingElement2DVulkan.cppm
Normal file
|
|
@ -0,0 +1,477 @@
|
|||
/*
|
||||
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"
|
||||
#ifdef CRAFTER_GRAPHICS_RENDERER_VULKAN
|
||||
#include <vulkan/vulkan.h>
|
||||
#endif
|
||||
export module Crafter.Graphics:RenderingElement2DVulkan;
|
||||
#ifdef CRAFTER_GRAPHICS_RENDERER_VULKAN
|
||||
import Crafter.Asset;
|
||||
import std;
|
||||
import :Transform2D;
|
||||
import :VulkanBuffer;
|
||||
import :Types;
|
||||
import :Window;
|
||||
import :DescriptorHeapVulkan;
|
||||
import :Font;
|
||||
|
||||
export namespace Crafter {
|
||||
struct RenderingElement2DVulkanBase : Transform2D {
|
||||
std::uint16_t index;
|
||||
std::uint16_t bufferX;
|
||||
std::uint16_t bufferY;
|
||||
std::array<VulkanBufferBase*, Window::numFrames> buffers;
|
||||
RenderingElement2DVulkanBase(Anchor2D anchor) : Transform2D(anchor) {
|
||||
|
||||
}
|
||||
RenderingElement2DVulkanBase(Anchor2D anchor, std::uint16_t bufferX, std::uint16_t bufferY) : bufferX(bufferX), bufferY(bufferY), Transform2D(anchor) {
|
||||
|
||||
}
|
||||
RenderingElement2DVulkanBase(Anchor2D anchor, std::uint16_t bufferX, std::uint16_t bufferY, std::array<VulkanBufferBase*, Window::numFrames>&& buffers) : bufferX(bufferX), bufferY(bufferY), buffers(std::move(buffers)), Transform2D(anchor) {
|
||||
|
||||
}
|
||||
};
|
||||
|
||||
template<bool Owning, bool Mapped, bool Single = false>
|
||||
struct RenderingElement2DVulkan : RenderingElement2DVulkanBase {
|
||||
RenderingElement2DVulkan(Anchor2D anchor) : RenderingElement2DVulkanBase(anchor) {
|
||||
|
||||
}
|
||||
RenderingElement2DVulkan(Anchor2D anchor, RendertargetBase& target, Transform2D& parent) requires(Owning) : RenderingElement2DVulkanBase(anchor) {
|
||||
GetScale(target, parent);
|
||||
this->bufferX = this->scaled.size.x;
|
||||
this->bufferY = this->scaled.size.y;
|
||||
if(Single) {
|
||||
buffers[0] = new VulkanBuffer<Vector<_Float16, 4, 4>, Mapped>();
|
||||
static_cast<VulkanBuffer<Vector<_Float16, 4, 4>, Mapped>*>(buffers[0])->Create(VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_2_SHADER_DEVICE_ADDRESS_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, bufferX * bufferY);
|
||||
for(std::uint8_t i = 1; i < Window::numFrames; i++) {
|
||||
buffers[i] = buffers[0];
|
||||
}
|
||||
} else {
|
||||
for(std::uint8_t i = 0; i < Window::numFrames; i++) {
|
||||
buffers[i] = new VulkanBuffer<Vector<_Float16, 4, 4>, Mapped>();
|
||||
static_cast<VulkanBuffer<Vector<_Float16, 4, 4>, Mapped>*>(buffers[i])->Create(VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_2_SHADER_DEVICE_ADDRESS_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, bufferX * bufferY);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RenderingElement2DVulkan(Anchor2D anchor, std::uint16_t bufferX, std::uint16_t bufferY) requires(Owning) : RenderingElement2DVulkanBase(anchor, bufferX, bufferY) {
|
||||
if constexpr(Single) {
|
||||
buffers[0] = new VulkanBuffer<Vector<_Float16, 4, 4>, Mapped>();
|
||||
static_cast<VulkanBuffer<Vector<_Float16, 4, 4>, Mapped>*>(buffers[0])->Create(VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_2_SHADER_DEVICE_ADDRESS_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, bufferX * bufferY);
|
||||
for(std::uint8_t i = 1; i < Window::numFrames; i++) {
|
||||
buffers[i] = buffers[0];
|
||||
}
|
||||
} else {
|
||||
for(std::uint8_t i = 0; i < Window::numFrames; i++) {
|
||||
buffers[i] = new VulkanBuffer<Vector<_Float16, 4, 4>, Mapped>();
|
||||
static_cast<VulkanBuffer<Vector<_Float16, 4, 4>, Mapped>*>(buffers[i])->Create(VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_2_SHADER_DEVICE_ADDRESS_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, bufferX * bufferY);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RenderingElement2DVulkan(Anchor2D anchor, std::uint16_t bufferX, std::uint16_t bufferY, std::array<VulkanBufferBase*, Window::numFrames>&& buffers) requires(!Owning) : RenderingElement2DVulkanBase(anchor, bufferX, bufferY, std::move(buffers)) {
|
||||
|
||||
}
|
||||
|
||||
RenderingElement2DVulkan(Anchor2D anchor, const std::filesystem::path& assetPath) requires(Owning && Mapped) : RenderingElement2DVulkanBase(anchor) {
|
||||
TextureAssetInfo info = TextureAsset<_Float16>::LoadInfo(assetPath);
|
||||
this->bufferX = info.sizeX;
|
||||
this->bufferY = info.sizeY;
|
||||
if constexpr(Single) {
|
||||
buffers[0] = new VulkanBuffer<Vector<_Float16, 4, 4>, Mapped>();
|
||||
static_cast<VulkanBuffer<Vector<_Float16, 4, 4>, Mapped>*>(buffers[0])->Create(VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_2_SHADER_DEVICE_ADDRESS_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, bufferX * bufferY);
|
||||
for(std::uint8_t i = 1; i < Window::numFrames; i++) {
|
||||
buffers[i] = buffers[0];
|
||||
}
|
||||
TextureAsset<Vector<_Float16, 4, 4>>::Load(assetPath, static_cast<VulkanBuffer<Vector<_Float16, 4, 4>, Mapped>*>(buffers[0])->value, this->bufferX, this->bufferY);
|
||||
} else {
|
||||
for(std::uint8_t i = 0; i < Window::numFrames; i++) {
|
||||
buffers[i] = new VulkanBuffer<Vector<_Float16, 4, 4>, Mapped>();
|
||||
static_cast<VulkanBuffer<Vector<_Float16, 4, 4>, Mapped>*>(buffers[i])->Create(VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_2_SHADER_DEVICE_ADDRESS_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, bufferX * bufferY);
|
||||
}
|
||||
TextureAsset<Vector<_Float16, 4, 4>>::Load(assetPath, static_cast<VulkanBuffer<Vector<_Float16, 4, 4>, Mapped>*>(buffers[0])->value, this->bufferX, this->bufferY);
|
||||
for(std::uint8_t i = 1; i < Window::numFrames; i++) {
|
||||
std::memcpy(static_cast<VulkanBuffer<Vector<_Float16, 4, 4>, Mapped>*>(buffers[i])->value, static_cast<VulkanBuffer<Vector<_Float16, 4, 4>, Mapped>*>(buffers[0])->value, this->bufferX * this->bufferY * sizeof(_Float16));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
~RenderingElement2DVulkan() {
|
||||
if constexpr(Owning) {
|
||||
if constexpr(Single) {
|
||||
delete static_cast<VulkanBuffer<Vector<_Float16, 4, 4>, Mapped>*>(buffers[0]);
|
||||
} else {
|
||||
for(VulkanBufferBase* buffer : buffers) {
|
||||
delete static_cast<VulkanBuffer<Vector<_Float16, 4, 4>, Mapped>*>(buffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RenderingElement2DVulkan(RenderingElement2DVulkan&) = delete;
|
||||
RenderingElement2DVulkan& operator=(RenderingElement2DVulkan&) = delete;
|
||||
|
||||
void CreateBuffer(std::uint16_t bufferX, std::uint16_t bufferY) requires(Owning) {
|
||||
this->bufferX = this->scaled.size.x;
|
||||
this->bufferY = this->scaled.size.y;
|
||||
if constexpr(Single) {
|
||||
buffers[0] = new VulkanBuffer<Vector<_Float16, 4, 4>, Mapped>();
|
||||
static_cast<VulkanBuffer<Vector<_Float16, 4, 4>, Mapped>*>(buffers[0])->Create(VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_2_SHADER_DEVICE_ADDRESS_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, bufferX * bufferY);
|
||||
for(std::uint8_t i = 1; i < Window::numFrames; i++) {
|
||||
buffers[i] = buffers[0];
|
||||
}
|
||||
} else {
|
||||
for(std::uint8_t i = 0; i < Window::numFrames; i++) {
|
||||
buffers[i] = new VulkanBuffer<Vector<_Float16, 4, 4>, Mapped>();
|
||||
static_cast<VulkanBuffer<Vector<_Float16, 4, 4>, Mapped>*>(buffers[i])->Create(VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_2_SHADER_DEVICE_ADDRESS_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, bufferX * bufferY);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ResizeBuffer(RendertargetVulkan& window, DescriptorHeapVulkan& descriptorHeap, std::uint16_t bufferOffset, std::uint16_t bufferX, std::uint16_t bufferY) requires(Owning) {
|
||||
if constexpr(Single) {
|
||||
static_cast<VulkanBuffer<Vector<_Float16, 4, 4>, Mapped>*>(buffers[0])->Resize(VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_2_SHADER_DEVICE_ADDRESS_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, bufferX * bufferY);
|
||||
} else {
|
||||
for(VulkanBufferBase* buffer : buffers) {
|
||||
delete static_cast<VulkanBuffer<Vector<_Float16, 4, 4>, Mapped>*>(buffer)->Resize(VK_BUFFER_USAGE_STORAGE_BUFFER_BIT | VK_BUFFER_USAGE_2_SHADER_DEVICE_ADDRESS_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, bufferX * bufferY);
|
||||
}
|
||||
}
|
||||
this->bufferX = bufferX;
|
||||
this->bufferY = bufferY;
|
||||
for(std::uint8_t frame = 0; frame < Window::numFrames; frame++) {
|
||||
RenderingElement2DVulkanTransformInfo* val = reinterpret_cast<RenderingElement2DVulkanTransformInfo*>(reinterpret_cast<char*>(window.transformBuffer[frame].value) + sizeof(RenderingElement2DVulkanTransformInfo));
|
||||
val[index].bufferX = this->bufferX;
|
||||
val[index].bufferY = this->bufferY;
|
||||
window.transformBuffer[frame].FlushDevice();
|
||||
}
|
||||
|
||||
VkHostAddressRangeEXT ranges[3] = {
|
||||
{
|
||||
.address = descriptorHeap.resourceHeap[0].value + bufferOffset + Device::descriptorHeapProperties.bufferDescriptorSize * index,
|
||||
.size = Device::descriptorHeapProperties.bufferDescriptorSize
|
||||
},
|
||||
{
|
||||
.address = descriptorHeap.resourceHeap[1].value + bufferOffset + Device::descriptorHeapProperties.bufferDescriptorSize * index,
|
||||
.size = Device::descriptorHeapProperties.bufferDescriptorSize
|
||||
},
|
||||
{
|
||||
.address = descriptorHeap.resourceHeap[2].value + bufferOffset + Device::descriptorHeapProperties.bufferDescriptorSize * index,
|
||||
.size = Device::descriptorHeapProperties.bufferDescriptorSize
|
||||
},
|
||||
};
|
||||
|
||||
VkDeviceAddressRangeKHR bufferRanges[3] {
|
||||
{
|
||||
.address = buffers[0]->address,
|
||||
.size = buffers[0]->size
|
||||
},
|
||||
{
|
||||
.address = buffers[1]->address,
|
||||
.size = buffers[1]->size
|
||||
},
|
||||
{
|
||||
.address = buffers[2]->address,
|
||||
.size = buffers[2]->size
|
||||
},
|
||||
};
|
||||
|
||||
VkResourceDescriptorInfoEXT infos[3] = {
|
||||
{
|
||||
.sType = VK_STRUCTURE_TYPE_RESOURCE_DESCRIPTOR_INFO_EXT,
|
||||
.type = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
|
||||
.data = { .pAddressRange = &bufferRanges[0]}
|
||||
},
|
||||
{
|
||||
.sType = VK_STRUCTURE_TYPE_RESOURCE_DESCRIPTOR_INFO_EXT,
|
||||
.type = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
|
||||
.data = { .pAddressRange = &bufferRanges[1]}
|
||||
},
|
||||
{
|
||||
.sType = VK_STRUCTURE_TYPE_RESOURCE_DESCRIPTOR_INFO_EXT,
|
||||
.type = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
|
||||
.data = { .pAddressRange = &bufferRanges[2]}
|
||||
},
|
||||
};
|
||||
|
||||
Device::vkWriteResourceDescriptorsEXT(Device::device, 3, infos, ranges);
|
||||
for(std::uint8_t i = 0; i < Window::numFrames; i++) {
|
||||
descriptorHeap.resourceHeap[i].FlushDevice();
|
||||
}
|
||||
}
|
||||
|
||||
void UpdatePosition(RendertargetBase& window2, Transform2D& parent) override {
|
||||
RendertargetVulkan& window = static_cast<RendertargetVulkan&>(window2);
|
||||
this->ScaleElement(parent);
|
||||
RenderingElement2DVulkanTransformInfo* val = reinterpret_cast<RenderingElement2DVulkanTransformInfo*>(reinterpret_cast<char*>(window.transformBuffer[window.frame].value) + sizeof(RenderingElement2DVulkanTransformInfo));
|
||||
val[index].scaled = this->scaled;
|
||||
for(Transform2D* child : this->children) {
|
||||
child->UpdatePosition(window, *this);
|
||||
}
|
||||
}
|
||||
|
||||
void GetScale(RendertargetBase& window, Transform2D& parent) {
|
||||
this->ScaleElement(parent);
|
||||
for(Transform2D* child : this->children) {
|
||||
child->UpdatePosition(window, *this);
|
||||
}
|
||||
}
|
||||
|
||||
int utf8_decode(const char* s, int* bytes_consumed) {
|
||||
unsigned char c = s[0];
|
||||
if (c < 0x80) {
|
||||
*bytes_consumed = 1;
|
||||
return c;
|
||||
} else if ((c & 0xE0) == 0xC0) {
|
||||
*bytes_consumed = 2;
|
||||
return ((c & 0x1F) << 6) | (s[1] & 0x3F);
|
||||
} else if ((c & 0xF0) == 0xE0) {
|
||||
*bytes_consumed = 3;
|
||||
return ((c & 0x0F) << 12) | ((s[1] & 0x3F) << 6) | (s[2] & 0x3F);
|
||||
} else if ((c & 0xF8) == 0xF0) {
|
||||
*bytes_consumed = 4;
|
||||
return ((c & 0x07) << 18) | ((s[1] & 0x3F) << 12) | ((s[2] & 0x3F) << 6) | (s[3] & 0x3F);
|
||||
}
|
||||
*bytes_consumed = 1;
|
||||
return 0xFFFD; // replacement char
|
||||
}
|
||||
|
||||
void RenderText(std::span<const std::string_view> lines, float size, Vector<_Float16, 4> color, Font& font, TextAlignment alignment = TextAlignment::Left, std::uint32_t offsetX = 0, std::uint32_t offsetY = 0, OpaqueType opaque = OpaqueType::FullyOpaque) requires(Mapped) {
|
||||
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 x = 0;
|
||||
switch (alignment) {
|
||||
case TextAlignment::Left:
|
||||
x = 0;
|
||||
break;
|
||||
case TextAlignment::Center:
|
||||
x = (this->scaled.size.x - lineWidth) / 2;
|
||||
break;
|
||||
case TextAlignment::Right:
|
||||
x = this->scaled.size.x - lineWidth;
|
||||
break;
|
||||
}
|
||||
|
||||
const char* p = line.data();
|
||||
const char* end = p + line.size();
|
||||
|
||||
while (p < end) {
|
||||
int bytes;
|
||||
int codepoint = utf8_decode(p, &bytes);
|
||||
p += bytes;
|
||||
|
||||
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<unsigned char> bitmap(w * h);
|
||||
stbtt_MakeCodepointBitmap(&font.font, bitmap.data(), w, h, w, scale, scale, codepoint);
|
||||
|
||||
// Only render characters that fit within the scaled bounds
|
||||
switch(opaque) {
|
||||
case OpaqueType::FullyOpaque: {
|
||||
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;
|
||||
|
||||
if (bufferX >= 0 && bufferX < (int)this->bufferX && bufferY >= 0 && bufferY < (int)this->bufferY) {
|
||||
for(std::uint8_t frame = 0; frame < Window::numFrames; frame++) {
|
||||
static_cast<VulkanBuffer<Vector<_Float16, 4, 4>, true>*>(buffers[frame])->value[bufferY * this->bufferX + bufferX] = {color.r, color.g, color.b, static_cast<_Float16>(bitmap[j * w + i])};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case OpaqueType::SemiOpaque:
|
||||
case OpaqueType::Transparent: {
|
||||
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;
|
||||
|
||||
if (bufferX >= 0 && bufferX < (int)this->bufferX && bufferY >= 0 && bufferY < (int)this->bufferY) {
|
||||
std::uint8_t alpha = bitmap[j * w + i];
|
||||
_Float16 srcA = (_Float16(alpha)/_Float16(255.0f))*color.a;
|
||||
for(std::uint8_t frame = 0; frame < Window::numFrames; frame++) {
|
||||
Vector<_Float16, 4, 4> dst = static_cast<VulkanBuffer<Vector<_Float16, 4, 4>, true>*>(buffers[frame])->value[bufferY * this->bufferX + bufferX];
|
||||
|
||||
_Float16 outA = srcA + dst.a * (1.0f - srcA);
|
||||
static_cast<VulkanBuffer<Vector<_Float16, 4, 4>, true>*>(buffers[frame])->value[bufferY * this->bufferX + bufferX] = Vector<_Float16, 4, 4>(
|
||||
(color.r * srcA + dst.r * dst.a * (1.0f - srcA)),
|
||||
(color.g * srcA + dst.g * dst.a * (1.0f - srcA)),
|
||||
(color.b * srcA + dst.b * dst.a * (1.0f - srcA)),
|
||||
outA
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
x += (int)(ax * scale);
|
||||
|
||||
if (p + 1 < end) {
|
||||
int next;
|
||||
x += (int)stbtt_GetGlyphKernAdvance(&font.font, codepoint, utf8_decode(p+1, &next));
|
||||
}
|
||||
}
|
||||
currentY += lineHeight;
|
||||
}
|
||||
}
|
||||
|
||||
void RenderText(std::span<const std::string_view> lines, float size, Vector<_Float16, 4> color, Font& font, std::uint8_t frame, TextAlignment alignment = TextAlignment::Left, TextVerticalAlignment verticalAlignment = TextVerticalAlignment::Top, std::int32_t offsetX = 0, std::int32_t offsetY = 0, OpaqueType opaque = OpaqueType::FullyOpaque) requires(Mapped) {
|
||||
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;
|
||||
|
||||
std::uint32_t ogOffsetX = offsetX;
|
||||
std::uint32_t ogOffsetY = offsetY;
|
||||
|
||||
for(std::string_view line : lines) {
|
||||
offsetX = ogOffsetX;
|
||||
offsetY = ogOffsetY;
|
||||
|
||||
std::int32_t lineWidth = 0;
|
||||
for (const char c : line) {
|
||||
int advance, lsb;
|
||||
stbtt_GetCodepointHMetrics(&font.font, c, &advance, &lsb);
|
||||
lineWidth += (int)(advance * scale);
|
||||
}
|
||||
|
||||
switch (alignment) {
|
||||
case TextAlignment::Left:
|
||||
break;
|
||||
case TextAlignment::Center:
|
||||
offsetX -= lineWidth / 2;
|
||||
break;
|
||||
case TextAlignment::Right:
|
||||
offsetX -= lineWidth;
|
||||
break;
|
||||
}
|
||||
|
||||
switch (verticalAlignment) {
|
||||
case TextVerticalAlignment::Top:
|
||||
break;
|
||||
case TextVerticalAlignment::Center:
|
||||
offsetY += (lineHeight / 2) - (size);
|
||||
break;
|
||||
case TextVerticalAlignment::Bottom:
|
||||
offsetY += lineHeight;
|
||||
break;
|
||||
}
|
||||
|
||||
const char* p = line.data();
|
||||
const char* end = p + line.size();
|
||||
|
||||
while (p < end) {
|
||||
int bytes;
|
||||
int codepoint = utf8_decode(p, &bytes);
|
||||
p += bytes;
|
||||
|
||||
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<unsigned char> bitmap(w * h);
|
||||
stbtt_MakeCodepointBitmap(&font.font, bitmap.data(), w, h, w, scale, scale, codepoint);
|
||||
|
||||
// Only render characters that fit within the scaled bounds
|
||||
switch(opaque) {
|
||||
case OpaqueType::FullyOpaque: {
|
||||
for (int j = 0; j < h; j++) {
|
||||
for (int i = 0; i < w; i++) {
|
||||
int bufferX = offsetX + i + c_x1;
|
||||
int bufferY = currentY + j + c_y1 + offsetY;
|
||||
|
||||
if (bufferX >= 0 && bufferX < (int)this->bufferX && bufferY >= 0 && bufferY < (int)this->bufferY) {
|
||||
static_cast<VulkanBuffer<Vector<_Float16, 4, 4>, true>*>(buffers[frame])->value[bufferY * this->bufferX + bufferX] = {color.r, color.g, color.b, static_cast<_Float16>(bitmap[j * w + i])};
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case OpaqueType::SemiOpaque:
|
||||
case OpaqueType::Transparent: {
|
||||
for (int j = 0; j < h; j++) {
|
||||
for (int i = 0; i < w; i++) {
|
||||
int bufferX = offsetX + i + c_x1;
|
||||
int bufferY = currentY + j + c_y1 + offsetY;
|
||||
|
||||
if (bufferX >= 0 && bufferX < (int)this->bufferX && bufferY >= 0 && bufferY < (int)this->bufferY) {
|
||||
std::uint8_t alpha = bitmap[j * w + i];
|
||||
_Float16 srcA = (_Float16(alpha)/_Float16(255.0f))*color.a;
|
||||
Vector<_Float16, 4, 4> dst = static_cast<VulkanBuffer<Vector<_Float16, 4, 4>, true>*>(buffers[frame])->value[bufferY * this->bufferX + bufferX];
|
||||
|
||||
_Float16 outA = srcA + dst.a * (1.0f - srcA);
|
||||
static_cast<VulkanBuffer<Vector<_Float16, 4, 4>, true>*>(buffers[frame])->value[bufferY * this->bufferX + bufferX] = Vector<_Float16, 4, 4>(
|
||||
(color.r * srcA + dst.r * dst.a * (1.0f - srcA)),
|
||||
(color.g * srcA + dst.g * dst.a * (1.0f - srcA)),
|
||||
(color.b * srcA + dst.b * dst.a * (1.0f - srcA)),
|
||||
outA
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
offsetX += (int)(ax * scale);
|
||||
|
||||
if (p + 1 < end) {
|
||||
int next;
|
||||
offsetX += (int)stbtt_GetGlyphKernAdvance(&font.font, codepoint, utf8_decode(p+1, &next));
|
||||
}
|
||||
}
|
||||
currentY += lineHeight;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
#endif
|
||||
|
|
@ -27,20 +27,22 @@ import std;
|
|||
import :Mesh;
|
||||
import :VulkanBuffer;
|
||||
import Crafter.Math;
|
||||
import :Window;
|
||||
|
||||
export namespace Crafter {
|
||||
struct TlasWithBuffer {
|
||||
VulkanBuffer<char, false, true, false> buffer;
|
||||
VkDeviceAddress address;
|
||||
VulkanBuffer<char, false> buffer;
|
||||
VkAccelerationStructureKHR accelerationStructure;
|
||||
VulkanBuffer<VkAccelerationStructureInstanceKHR, true, true, false> instanceBuffer;
|
||||
VulkanBuffer<VkAccelerationStructureInstanceKHR, true> instanceBuffer;
|
||||
};
|
||||
|
||||
class RenderingElement3D {
|
||||
public:
|
||||
VkAccelerationStructureInstanceKHR instance;
|
||||
static std::vector<RenderingElement3D*> elements;
|
||||
inline static VulkanBuffer<char, false, true, false> scratchBuffer;
|
||||
inline static std::vector<TlasWithBuffer> tlases;
|
||||
inline static VulkanBuffer<char, false> scratchBuffer;
|
||||
inline static TlasWithBuffer tlases[Window::numFrames];
|
||||
static void BuildTLAS(VkCommandBuffer cmd, std::uint32_t index);
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,6 +16,12 @@ 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"
|
||||
#ifdef CRAFTER_GRAPHICS_RENDERER_VULKAN
|
||||
#include <vulkan/vulkan.h>
|
||||
#endif
|
||||
export module Crafter.Graphics:Rendertarget;
|
||||
import Crafter.Math;
|
||||
import Crafter.Asset;
|
||||
|
|
@ -23,18 +29,21 @@ import std;
|
|||
import :Types;
|
||||
import :Transform2D;
|
||||
import :RenderingElement2DBase;
|
||||
#ifdef CRAFTER_GRAPHICS_RENDERER_VULKAN
|
||||
import :Device;
|
||||
import :VulkanBuffer;
|
||||
#endif
|
||||
|
||||
export namespace Crafter {
|
||||
template<std::uint8_t Frames>
|
||||
struct RendertargetBase {
|
||||
#ifdef CRAFTER_TIMING
|
||||
std::vector<std::tuple<const Transform*, std::uint32_t, std::uint32_t, std::chrono::nanoseconds>> renderTimings;
|
||||
#endif
|
||||
Transform2D transform;
|
||||
std::int32_t sizeX;
|
||||
std::int32_t sizeY;
|
||||
std::uint16_t sizeX;
|
||||
std::uint16_t sizeY;
|
||||
RendertargetBase() = default;
|
||||
RendertargetBase(std::int16_t sizeX, std::int16_t sizeY) : sizeX(sizeX), sizeY(sizeY), transform({0, 0, 1, 1, 0, 0, 0}){
|
||||
RendertargetBase(std::uint16_t sizeX, std::uint16_t sizeY) : sizeX(sizeX), sizeY(sizeY), transform({0, 0, 1, 1, 0, 0, 0}){
|
||||
transform.scaled.size.x = sizeX;
|
||||
transform.scaled.size.y = sizeY;
|
||||
transform.scaled.position.x = 0;
|
||||
|
|
@ -42,15 +51,42 @@ export namespace Crafter {
|
|||
}
|
||||
};
|
||||
|
||||
#ifdef CRAFTER_GRAPHICS_RENDERER_VULKAN
|
||||
struct RenderingElement2DVulkanBase;
|
||||
|
||||
struct __attribute__((packed)) RenderingElement2DVulkanTransformInfo {
|
||||
ScaleData2D scaled; // 0 - 8 bytes
|
||||
std::uint16_t bufferX; // 8 - 2 bytes
|
||||
std::uint16_t bufferY; // 10 - 2 bytes
|
||||
//12 bytes total;
|
||||
};
|
||||
|
||||
|
||||
struct DescriptorHeapVulkan;
|
||||
struct RendertargetVulkan : RendertargetBase {
|
||||
std::uint8_t frame;
|
||||
std::vector<RenderingElement2DVulkanBase*> elements;
|
||||
VulkanBuffer<RenderingElement2DVulkanTransformInfo, true> transformBuffer[3];
|
||||
|
||||
RendertargetVulkan() = default;
|
||||
RendertargetVulkan(std::uint16_t sizeX, std::uint16_t sizeY);
|
||||
void UpdateElements();
|
||||
void CreateBuffer(std::uint8_t frame);
|
||||
void ReorderBuffer(std::uint8_t frame);
|
||||
void WriteDescriptors(std::span<VkResourceDescriptorInfoEXT> infos, std::span<VkHostAddressRangeEXT> ranges, std::uint16_t start, std::uint32_t bufferOffset, DescriptorHeapVulkan& descriptorHeap);
|
||||
void SetOrderResursive(Transform2D* elementTransform);
|
||||
};
|
||||
#endif
|
||||
|
||||
template<typename T, std::uint8_t Channels, std::uint8_t Alignment, std::uint8_t Frames>
|
||||
struct Rendertarget : RendertargetBase<Frames> {
|
||||
struct Rendertarget : RendertargetBase {
|
||||
Vector<T, Channels, Alignment>* buffer[Frames];
|
||||
Rendertarget() = default;
|
||||
Rendertarget(std::int16_t sizeX, std::int16_t sizeY) : RendertargetBase<Frames>(sizeX, sizeY) {
|
||||
Rendertarget(std::uint16_t sizeX, std::uint16_t sizeY) : RendertargetBase(sizeX, sizeY) {
|
||||
|
||||
}
|
||||
void RenderElement(Transform2D* elementTransform, std::uint8_t frame, std::vector<ClipRect>&& dirtyRects) {
|
||||
RenderingElement2DBase<Frames>* element = dynamic_cast<RenderingElement2DBase<Frames>*>(elementTransform);
|
||||
RenderingElement2DBase<T, Frames>* element = dynamic_cast<RenderingElement2DBase<T, Frames>*>(elementTransform);
|
||||
if(element) {
|
||||
#ifdef CRAFTER_TIMING
|
||||
auto start = std::chrono::high_resolution_clock::now();
|
||||
|
|
@ -61,70 +97,75 @@ export namespace Crafter {
|
|||
}
|
||||
|
||||
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);
|
||||
dirty.left = std::uint16_t(std::max(element->scaled.position.x, std::int16_t(dirty.left)));
|
||||
dirty.top = std::uint16_t(std::max(element->scaled.position.y,std::int16_t(dirty.top)));
|
||||
dirty.right = std::min(std::uint16_t(element->scaled.position.x+element->scaled.size.x), dirty.right);
|
||||
dirty.bottom = std::min(std::uint16_t(element->scaled.position.y+element->scaled.size.y), dirty.bottom);
|
||||
|
||||
const Vector<std::uint8_t, 4>* src_buffer = element->buffer.data();
|
||||
std::int32_t src_width = element->scaled.size.x;
|
||||
std::int32_t src_height = element->scaled.size.y;
|
||||
if(dirty.right <= dirty.left || dirty.bottom <= dirty.top) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const Vector<T, 4, 4>* src_buffer = element->buffer.data();
|
||||
std::uint16_t src_width = element->scaled.size.x;
|
||||
std::uint16_t src_height = element->scaled.size.y;
|
||||
|
||||
switch (element->opaque) {
|
||||
case OpaqueType::FullyOpaque:
|
||||
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;
|
||||
|
||||
this->buffer[frame][y * this->sizeX + x] = src_buffer[src_y * src_width + src_x];
|
||||
}
|
||||
case OpaqueType::FullyOpaque: {
|
||||
for (std::uint16_t y = dirty.top; y < dirty.bottom; y++) {
|
||||
std::uint16_t src_y = y - element->scaled.position.y;
|
||||
std::uint16_t src_x = dirty.left - element->scaled.position.x;
|
||||
std::memcpy(&this->buffer[frame][y * this->sizeX + dirty.left], &src_buffer[src_y * src_width + src_x], (dirty.right - dirty.left) * sizeof(Vector<T, Channels, Alignment>));
|
||||
}
|
||||
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<std::uint8_t, 4> src_pixel = src_buffer[src_y * src_width + src_x];
|
||||
|
||||
if (src_pixel.a == 0) {
|
||||
continue;
|
||||
}
|
||||
this->buffer[frame][y * this->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;
|
||||
Vector<T, Channels, Alignment> src = src_buffer[src_y * src_width + src_x];
|
||||
Vector<T, Channels, Alignment> dst = buffer[frame][y * this->sizeX + x];
|
||||
if constexpr(std::same_as<T, _Float16>) {
|
||||
for (std::uint16_t y = dirty.top; y < dirty.bottom; y++) {
|
||||
std::uint16_t src_y = y - element->scaled.position.y;
|
||||
std::uint16_t pixel_width = dirty.right - dirty.left;
|
||||
|
||||
if(src.a == 0) {
|
||||
continue;
|
||||
constexpr std::uint32_t simd_width = VectorF16<1, 1>::MaxElement / 4;
|
||||
std::uint32_t rows = pixel_width / simd_width;
|
||||
|
||||
for (std::uint32_t x = 0; x < rows; x++) {
|
||||
std::uint16_t px = dirty.left + x * simd_width;
|
||||
std::uint16_t src_x = px - element->scaled.position.x;
|
||||
|
||||
VectorF16<4, simd_width> src(&src_buffer[src_y * src_width + src_x].v[0]);
|
||||
VectorF16<4, simd_width> dst(&buffer[frame][y * this->sizeX + px].v[0]);
|
||||
VectorF16<4, simd_width> oneMinusSrcA = VectorF16<4, simd_width>(1) - src.Shuffle<{{3, 3, 3, 3}}>();
|
||||
VectorF16<4, simd_width> result = VectorF16<4, simd_width>::MulitplyAdd(dst, oneMinusSrcA, src);
|
||||
result.Store(&buffer[frame][y * this->sizeX + px].v[0]);
|
||||
}
|
||||
|
||||
float srcA = src.a / 255.0f;
|
||||
float dstA = dst.a / 255.0f;
|
||||
std::uint32_t remainder = pixel_width - (rows * simd_width);
|
||||
std::uint16_t remainder_start = dirty.left + rows * simd_width;
|
||||
|
||||
float outA = srcA + dstA * (1.0f - srcA);
|
||||
this->buffer[frame][y * this->sizeX + x] = Vector<T, Channels, Alignment>(
|
||||
static_cast<T>((src.r * srcA + dst.r * dstA * (1.0f - srcA)) / outA),
|
||||
static_cast<T>((src.g * srcA + dst.g * dstA * (1.0f - srcA)) / outA),
|
||||
static_cast<T>((src.b * srcA + dst.b * dstA * (1.0f - srcA)) / outA),
|
||||
static_cast<T>(outA * 255)
|
||||
for (std::uint8_t x = 0; x < remainder; x++) {
|
||||
std::uint16_t px = remainder_start + x;
|
||||
std::uint16_t src_x = px - element->scaled.position.x;
|
||||
|
||||
Vector<T, Channels, Alignment> src = src_buffer[src_y * src_width + src_x];
|
||||
Vector<T, Channels, Alignment> dst = buffer[frame][y * this->sizeX + px];
|
||||
_Float16 oneMinusSrcA = (_Float16)1.0f - src.a;
|
||||
|
||||
buffer[frame][y * this->sizeX + px] = Vector<T, Channels, Alignment>(
|
||||
src.r + dst.r * oneMinusSrcA,
|
||||
src.g + dst.g * oneMinusSrcA,
|
||||
src.b + dst.b * oneMinusSrcA,
|
||||
src.a + dst.a * oneMinusSrcA
|
||||
);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (std::uint16_t y = dirty.top; y < dirty.bottom; y++) {
|
||||
std::uint16_t src_y = y - element->scaled.position.y;
|
||||
std::uint16_t src_x = dirty.left - element->scaled.position.x;
|
||||
std::memcpy(&this->buffer[frame][y * this->sizeX + dirty.left], &src_buffer[src_y * src_width + src_x], (dirty.right - dirty.left) * sizeof(Vector<T, Channels, Alignment>));
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
@ -140,11 +181,15 @@ export namespace Crafter {
|
|||
}
|
||||
|
||||
void AddOldRects(Transform2D* elementTransform, std::uint8_t frame, std::vector<ClipRect>& clipRects) {
|
||||
RenderingElement2DBase<Frames>* element = dynamic_cast<RenderingElement2DBase<Frames>*>(elementTransform);
|
||||
RenderingElement2DBase<T, Frames>* element = dynamic_cast<RenderingElement2DBase<T, Frames>*>(elementTransform);
|
||||
if(element) {
|
||||
if(element->scaled.position.x != element->oldScale[frame].position.x || element->scaled.position.y != element->oldScale[frame].position.y || element->scaled.size.x != element->oldScale[frame].size.x || element->scaled.size.y != element->oldScale[frame].size.y || element->redraw[frame]) {
|
||||
clipRects.emplace_back(std::max(element->scaled.position.x, std::int32_t(0)), std::min(element->scaled.position.x + element->scaled.size.x, this->sizeX), std::max(element->scaled.position.y, std::int32_t(0)), std::min(element->scaled.position.y + element->scaled.size.y, this->sizeY));
|
||||
clipRects.emplace_back(std::max(element->oldScale[frame].position.x, std::int32_t(0)), std::min(element->oldScale[frame].position.x + element->oldScale[frame].size.x, this->sizeX), std::max(element->oldScale[frame].position.y, std::int32_t(0)), std::min(element->oldScale[frame].position.y + element->oldScale[frame].size.y, this->sizeY));
|
||||
clipRects.emplace_back(std::max(element->scaled.position.x, std::int16_t(0)), std::min(std::int16_t(element->scaled.position.x + element->scaled.size.x), std::int16_t(this->sizeX)), std::max(element->scaled.position.y, std::int16_t(0)), std::min(std::int16_t(element->scaled.position.y + element->scaled.size.y), std::int16_t(this->sizeY)));
|
||||
clipRects.emplace_back(std::max(element->oldScale[frame].position.x, std::int16_t(0)), std::min(std::int16_t(element->oldScale[frame].position.x + element->oldScale[frame].size.x), std::int16_t(this->sizeX)), std::max(element->oldScale[frame].position.y, std::int16_t(0)), std::min(std::int16_t(element->oldScale[frame].position.y + element->oldScale[frame].size.y), std::int16_t(this->sizeY)));
|
||||
element->oldScale[frame] = element->scaled;
|
||||
element->redraw[frame] = false;
|
||||
} else if(element->redraw[frame]) {
|
||||
clipRects.emplace_back(std::max(element->scaled.position.x, std::int16_t(0)), std::min(std::int16_t(element->scaled.position.x + element->scaled.size.x), std::int16_t(this->sizeX)), std::max(element->scaled.position.y, std::int16_t(0)), std::min(std::int16_t(element->scaled.position.y + element->scaled.size.y), std::int16_t(this->sizeY)));
|
||||
element->oldScale[frame] = element->scaled;
|
||||
element->redraw[frame] = false;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,36 +33,13 @@ export namespace Crafter {
|
|||
class ShaderBindingTableVulkan {
|
||||
public:
|
||||
std::vector<VkPipelineShaderStageCreateInfo> shaderStages;
|
||||
void Init(std::span<VulkanShader> shaders) {
|
||||
void Init(const std::span<const VulkanShader> shaders) {
|
||||
shaderStages.reserve(shaders.size());
|
||||
for(const VulkanShader& shader: shaders) {
|
||||
shaderStages.emplace_back(VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, nullptr, 0, shader.stage, shader.shader, shader.entrypoint.c_str(), nullptr);
|
||||
shaderStages.emplace_back(VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO, nullptr, 0, shader.stage, shader.shader, shader.entrypoint.c_str(), shader.specilizationInfo);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
template <typename Shaders>
|
||||
class ShaderBindingTableVulkanConst {
|
||||
public:
|
||||
inline static std::array<VkPipelineShaderStageCreateInfo, std::tuple_size_v<Shaders>> shaderStages;
|
||||
static void Init() {
|
||||
AddAllToSBT(std::make_index_sequence<std::tuple_size_v<Shaders>>{});
|
||||
}
|
||||
private:
|
||||
template<std::size_t index>
|
||||
static void AddToSBT() {
|
||||
shaderStages[index] = {
|
||||
.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
|
||||
.stage = std::tuple_element_t<index, Shaders>::_stage,
|
||||
.module = std::tuple_element_t<index, Shaders>::shader,
|
||||
.pName = std::tuple_element_t<index, Shaders>::_entrypoint.value
|
||||
};
|
||||
}
|
||||
template<std::size_t... Is>
|
||||
static void AddAllToSBT(std::index_sequence<Is...>) {
|
||||
(AddToSBT<Is>(), ...);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -28,21 +28,14 @@ import :Device;
|
|||
import :Types;
|
||||
|
||||
export namespace Crafter {
|
||||
template<size_t N>
|
||||
struct StringLiteral {
|
||||
constexpr StringLiteral(const char (&str)[N]) {
|
||||
std::copy_n(str, N, value);
|
||||
}
|
||||
|
||||
char value[N];
|
||||
};
|
||||
|
||||
class VulkanShader {
|
||||
public:
|
||||
std::vector<VkSpecializationMapEntry> specilizations;
|
||||
VkSpecializationInfo* specilizationInfo;
|
||||
VkShaderStageFlagBits stage;
|
||||
std::string entrypoint;
|
||||
VkShaderModule shader;
|
||||
VulkanShader(const std::filesystem::path& path, std::string entrypoint, VkShaderStageFlagBits stage) : stage(stage), entrypoint(entrypoint) {
|
||||
VulkanShader(const std::filesystem::path& path, std::string entrypoint, VkShaderStageFlagBits stage, VkSpecializationInfo* specilizationInfo) : stage(stage), entrypoint(entrypoint), specilizationInfo(specilizationInfo) {
|
||||
std::ifstream file(path, std::ios::binary);
|
||||
if (!file) {
|
||||
std::cerr << "Error: Could not open file " << path << std::endl;
|
||||
|
|
@ -69,44 +62,6 @@ export namespace Crafter {
|
|||
Device::CheckVkResult(vkCreateShaderModule(Device::device, &module_info, nullptr, &shader));
|
||||
}
|
||||
};
|
||||
|
||||
template <
|
||||
StringLiteral path,
|
||||
StringLiteral entrypoint,
|
||||
VkShaderStageFlagBits stage
|
||||
>
|
||||
class VulkanShaderConst {
|
||||
public:
|
||||
constexpr static VkShaderStageFlagBits _stage = stage;
|
||||
constexpr static StringLiteral _entrypoint = entrypoint;
|
||||
inline static VkShaderModule shader;
|
||||
static void CreateShader() {
|
||||
std::ifstream file(path.value, std::ios::binary);
|
||||
if (!file) {
|
||||
std::cerr << "Error: Could not open file " << path.value << std::endl;
|
||||
}
|
||||
|
||||
// Move to the end of the file to determine its size
|
||||
file.seekg(0, std::ios::end);
|
||||
std::streamsize size = file.tellg();
|
||||
file.seekg(0, std::ios::beg);
|
||||
|
||||
std::vector<std::uint32_t> spirv(size / sizeof(std::uint32_t));
|
||||
|
||||
// Read the data into the vector
|
||||
if (!file.read(reinterpret_cast<char*>(spirv.data()), size)) {
|
||||
std::cerr << "Error: Could not read data from file" << std::endl;
|
||||
}
|
||||
|
||||
file.close();
|
||||
|
||||
VkShaderModuleCreateInfo module_info{VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO};
|
||||
module_info.codeSize = spirv.size() * sizeof(uint32_t);
|
||||
module_info.pCode = spirv.data();
|
||||
|
||||
Device::CheckVkResult(vkCreateShaderModule(Device::device, &module_info, nullptr, &shader));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
@ -30,10 +30,10 @@ export namespace Crafter {
|
|||
float height;
|
||||
float offsetX;
|
||||
float offsetY;
|
||||
std::int32_t z;
|
||||
std::uint8_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);
|
||||
Anchor2D(float x, float y, float width, float height, float offsetX, float offsetY, std::uint8_t z, bool maintainAspectRatio = false);
|
||||
};
|
||||
|
||||
struct Transform2D {
|
||||
|
|
@ -49,14 +49,7 @@ export namespace Crafter {
|
|||
Transform2D& operator=(Transform2D&) = delete;
|
||||
virtual ~Transform2D() = default;
|
||||
|
||||
virtual void UpdatePosition(RendertargetBase<1>& window, Transform2D& parent) {
|
||||
ScaleElement(parent);
|
||||
for(Transform2D* child : children) {
|
||||
child->UpdatePosition(window, *this);
|
||||
}
|
||||
}
|
||||
|
||||
virtual void UpdatePosition(RendertargetBase<3>& window, Transform2D& parent) {
|
||||
virtual void UpdatePosition(RendertargetBase& window, Transform2D& parent) {
|
||||
ScaleElement(parent);
|
||||
for(Transform2D* child : children) {
|
||||
child->UpdatePosition(window, *this);
|
||||
|
|
@ -73,7 +66,6 @@ export namespace Crafter {
|
|||
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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,15 +33,15 @@ export namespace Crafter {
|
|||
};
|
||||
|
||||
struct ScaleData2D {
|
||||
Vector<std::int32_t, 2> position;
|
||||
Vector<std::int32_t, 2> size;
|
||||
Vector<std::int16_t, 2, 2> position;
|
||||
Vector<std::int16_t, 2, 2> size;
|
||||
};
|
||||
|
||||
struct ClipRect {
|
||||
std::int32_t left;
|
||||
std::int32_t right;
|
||||
std::int32_t top;
|
||||
std::int32_t bottom;
|
||||
std::uint16_t left;
|
||||
std::uint16_t right;
|
||||
std::uint16_t top;
|
||||
std::uint16_t bottom;
|
||||
};
|
||||
|
||||
struct FrameTime {
|
||||
|
|
@ -49,7 +49,7 @@ export namespace Crafter {
|
|||
std::chrono::duration<double> delta;
|
||||
};
|
||||
|
||||
enum class CrafterKeys {
|
||||
enum class CrafterKeys : std::uint8_t {
|
||||
// Alphabetic keys
|
||||
A, B, C, D, E, F, G, H, I, J, K, L, M,
|
||||
N, O, P, Q, R, S, T, U, V, W, X, Y, Z,
|
||||
|
|
@ -92,6 +92,151 @@ export namespace Crafter {
|
|||
CrafterKeysMax
|
||||
};
|
||||
|
||||
constexpr std::string CrafterKeyToString(CrafterKeys key) {
|
||||
switch (key) {
|
||||
// Alphabetic keys
|
||||
case CrafterKeys::A: return "A";
|
||||
case CrafterKeys::B: return "B";
|
||||
case CrafterKeys::C: return "C";
|
||||
case CrafterKeys::D: return "D";
|
||||
case CrafterKeys::E: return "E";
|
||||
case CrafterKeys::F: return "F";
|
||||
case CrafterKeys::G: return "G";
|
||||
case CrafterKeys::H: return "H";
|
||||
case CrafterKeys::I: return "I";
|
||||
case CrafterKeys::J: return "J";
|
||||
case CrafterKeys::K: return "K";
|
||||
case CrafterKeys::L: return "L";
|
||||
case CrafterKeys::M: return "M";
|
||||
case CrafterKeys::N: return "N";
|
||||
case CrafterKeys::O: return "O";
|
||||
case CrafterKeys::P: return "P";
|
||||
case CrafterKeys::Q: return "Q";
|
||||
case CrafterKeys::R: return "R";
|
||||
case CrafterKeys::S: return "S";
|
||||
case CrafterKeys::T: return "T";
|
||||
case CrafterKeys::U: return "U";
|
||||
case CrafterKeys::V: return "V";
|
||||
case CrafterKeys::W: return "W";
|
||||
case CrafterKeys::X: return "X";
|
||||
case CrafterKeys::Y: return "Y";
|
||||
case CrafterKeys::Z: return "Z";
|
||||
|
||||
// Numeric keys
|
||||
case CrafterKeys::_0: return "0";
|
||||
case CrafterKeys::_1: return "1";
|
||||
case CrafterKeys::_2: return "2";
|
||||
case CrafterKeys::_3: return "3";
|
||||
case CrafterKeys::_4: return "4";
|
||||
case CrafterKeys::_5: return "5";
|
||||
case CrafterKeys::_6: return "6";
|
||||
case CrafterKeys::_7: return "7";
|
||||
case CrafterKeys::_8: return "8";
|
||||
case CrafterKeys::_9: return "9";
|
||||
|
||||
// Function keys
|
||||
case CrafterKeys::F1: return "F1";
|
||||
case CrafterKeys::F2: return "F2";
|
||||
case CrafterKeys::F3: return "F3";
|
||||
case CrafterKeys::F4: return "F4";
|
||||
case CrafterKeys::F5: return "F5";
|
||||
case CrafterKeys::F6: return "F6";
|
||||
case CrafterKeys::F7: return "F7";
|
||||
case CrafterKeys::F8: return "F8";
|
||||
case CrafterKeys::F9: return "F9";
|
||||
case CrafterKeys::F10: return "F10";
|
||||
case CrafterKeys::F11: return "F11";
|
||||
case CrafterKeys::F12: return "F12";
|
||||
|
||||
// Control keys
|
||||
case CrafterKeys::Escape: return "Escape";
|
||||
case CrafterKeys::Tab: return "Tab";
|
||||
case CrafterKeys::Enter: return "Enter";
|
||||
case CrafterKeys::Space: return "Space";
|
||||
case CrafterKeys::Backspace: return "Backspace";
|
||||
case CrafterKeys::Delete: return "Delete";
|
||||
case CrafterKeys::Insert: return "Insert";
|
||||
case CrafterKeys::Home: return "Home";
|
||||
case CrafterKeys::End: return "End";
|
||||
case CrafterKeys::PageUp: return "PageUp";
|
||||
case CrafterKeys::PageDown: return "PageDown";
|
||||
case CrafterKeys::CapsLock: return "CapsLock";
|
||||
case CrafterKeys::NumLock: return "NumLock";
|
||||
case CrafterKeys::ScrollLock: return "ScrollLock";
|
||||
|
||||
// Modifier keys
|
||||
case CrafterKeys::LeftShift: return "LeftShift";
|
||||
case CrafterKeys::RightShift: return "RightShift";
|
||||
case CrafterKeys::LeftCtrl: return "LeftCtrl";
|
||||
case CrafterKeys::RightCtrl: return "RightCtrl";
|
||||
case CrafterKeys::LeftAlt: return "LeftAlt";
|
||||
case CrafterKeys::RightAlt: return "RightAlt";
|
||||
case CrafterKeys::LeftSuper: return "LeftSuper";
|
||||
case CrafterKeys::RightSuper: return "RightSuper";
|
||||
|
||||
// Arrow keys
|
||||
case CrafterKeys::Up: return "Up";
|
||||
case CrafterKeys::Down: return "Down";
|
||||
case CrafterKeys::Left: return "Left";
|
||||
case CrafterKeys::Right: return "Right";
|
||||
|
||||
// Keypad keys
|
||||
case CrafterKeys::keypad_0: return "Keypad0";
|
||||
case CrafterKeys::keypad_1: return "Keypad1";
|
||||
case CrafterKeys::keypad_2: return "Keypad2";
|
||||
case CrafterKeys::keypad_3: return "Keypad3";
|
||||
case CrafterKeys::keypad_4: return "Keypad4";
|
||||
case CrafterKeys::keypad_5: return "Keypad5";
|
||||
case CrafterKeys::keypad_6: return "Keypad6";
|
||||
case CrafterKeys::keypad_7: return "Keypad7";
|
||||
case CrafterKeys::keypad_8: return "Keypad8";
|
||||
case CrafterKeys::keypad_9: return "Keypad9";
|
||||
case CrafterKeys::keypad_enter: return "KeypadEnter";
|
||||
case CrafterKeys::keypad_plus: return "KeypadPlus";
|
||||
case CrafterKeys::keypad_minus: return "KeypadMinus";
|
||||
case CrafterKeys::keypad_multiply: return "KeypadMultiply";
|
||||
case CrafterKeys::keypad_divide: return "KeypadDivide";
|
||||
case CrafterKeys::keypad_decimal: return "KeypadDecimal";
|
||||
|
||||
// Punctuation and special keys
|
||||
case CrafterKeys::grave: return "Grave";
|
||||
case CrafterKeys::minus: return "Minus";
|
||||
case CrafterKeys::equal: return "Equal";
|
||||
case CrafterKeys::bracket_left: return "BracketLeft";
|
||||
case CrafterKeys::bracket_right: return "BracketRight";
|
||||
case CrafterKeys::backslash: return "Backslash";
|
||||
case CrafterKeys::semicolon: return "Semicolon";
|
||||
case CrafterKeys::quote: return "Quote";
|
||||
case CrafterKeys::comma: return "Comma";
|
||||
case CrafterKeys::period: return "Period";
|
||||
case CrafterKeys::slash: return "Slash";
|
||||
case CrafterKeys::print_screen: return "PrintScreen";
|
||||
case CrafterKeys::pause: return "Pause";
|
||||
case CrafterKeys::menu: return "Menu";
|
||||
|
||||
// Additional keys
|
||||
case CrafterKeys::volume_up: return "VolumeUp";
|
||||
case CrafterKeys::volume_down: return "VolumeDown";
|
||||
case CrafterKeys::volume_mute: return "VolumeMute";
|
||||
case CrafterKeys::media_play: return "MediaPlay";
|
||||
case CrafterKeys::media_stop: return "MediaStop";
|
||||
case CrafterKeys::media_prev: return "MediaPrev";
|
||||
case CrafterKeys::media_next: return "MediaNext";
|
||||
case CrafterKeys::browser_back: return "BrowserBack";
|
||||
case CrafterKeys::browser_forward: return "BrowserForward";
|
||||
case CrafterKeys::browser_refresh: return "BrowserRefresh";
|
||||
case CrafterKeys::browser_stop: return "BrowserStop";
|
||||
case CrafterKeys::browser_search: return "BrowserSearch";
|
||||
case CrafterKeys::browser_home: return "BrowserHome";
|
||||
case CrafterKeys::launch_mail: return "LaunchMail";
|
||||
case CrafterKeys::launch_calculator: return "LaunchCalculator";
|
||||
case CrafterKeys::launch_media_player:return "LaunchMediaPlayer";
|
||||
|
||||
case CrafterKeys::CrafterKeysMax: return "Unknown";
|
||||
}
|
||||
return "Unknown";
|
||||
}
|
||||
|
||||
template <typename T, typename T2>
|
||||
constexpr T AlignUp(T value, T2 alignment) {
|
||||
return (value + alignment - 1) & ~(alignment - 1);
|
||||
|
|
|
|||
|
|
@ -31,24 +31,12 @@ import :Device;
|
|||
namespace Crafter {
|
||||
export class VulkanBufferBase {
|
||||
public:
|
||||
VkDescriptorBufferInfo descriptor;
|
||||
VkDeviceAddress address;
|
||||
std::uint32_t size;
|
||||
VkBuffer buffer = VK_NULL_HANDLE;
|
||||
VkDeviceMemory memory;
|
||||
};
|
||||
|
||||
export class VulkanBufferAdressable {
|
||||
public:
|
||||
VkDeviceAddress address;
|
||||
};
|
||||
export class VulkanBufferAdressableEmpty {};
|
||||
template<bool Adressable>
|
||||
using VulkanBufferAdressableConditional =
|
||||
std::conditional_t<
|
||||
Adressable,
|
||||
VulkanBufferAdressable,
|
||||
VulkanBufferAdressableEmpty
|
||||
>;
|
||||
|
||||
export template<typename T>
|
||||
class VulkanBufferMapped {
|
||||
public:
|
||||
|
|
@ -63,33 +51,12 @@ namespace Crafter {
|
|||
VulkanBufferMappedEmpty
|
||||
>;
|
||||
|
||||
|
||||
export template <typename T, bool Mapped, bool Adressable, bool Staged> requires ((Mapped && !Staged) || (!Mapped && Staged) || (!Mapped && !Staged))
|
||||
class VulkanBuffer;
|
||||
|
||||
export template <typename T>
|
||||
class VulkanBufferStaged {
|
||||
VulkanBuffer<T, true, false, false>* stagingBuffer;
|
||||
};
|
||||
export class VulkanBufferStagedEmpty {};
|
||||
template<typename T, bool Staged>
|
||||
using VulkanBufferStagedConditional =
|
||||
std::conditional_t<
|
||||
Staged,
|
||||
VulkanBufferStaged<T>,
|
||||
VulkanBufferStagedEmpty
|
||||
>;
|
||||
|
||||
|
||||
export template <typename T, bool Mapped, bool Adressable, bool Staged> requires ((Mapped && !Staged) || (!Mapped && Staged) || (!Mapped && !Staged))
|
||||
class VulkanBuffer : public VulkanBufferBase, public VulkanBufferMappedConditional<T, Mapped>, public VulkanBufferAdressableConditional<Adressable>, public VulkanBufferStagedConditional<T, Staged> {
|
||||
export template <typename T, bool Mapped>
|
||||
class VulkanBuffer : public VulkanBufferBase, public VulkanBufferMappedConditional<T, Mapped> {
|
||||
public:
|
||||
VulkanBuffer() = default;
|
||||
void Create(VkBufferUsageFlags usageFlags, VkMemoryPropertyFlags memoryPropertyFlags, std::uint32_t count) {
|
||||
if constexpr(Staged) {
|
||||
new (&VulkanBufferMappedConditional<T, true>::stagingBuffer) VulkanBuffer<T, true, false, false>();
|
||||
VulkanBufferMappedConditional<T, true>::stagingBuffer->Create(VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT, count);
|
||||
}
|
||||
size = count * sizeof(T);
|
||||
|
||||
VkBufferCreateInfo bufferCreateInfo {};
|
||||
bufferCreateInfo.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;
|
||||
|
|
@ -104,30 +71,21 @@ namespace Crafter {
|
|||
.allocationSize = memReqs.size,
|
||||
.memoryTypeIndex = Device::GetMemoryType(memReqs.memoryTypeBits, memoryPropertyFlags)
|
||||
};
|
||||
if constexpr(Adressable) {
|
||||
|
||||
VkMemoryAllocateFlagsInfoKHR allocFlagsInfo {
|
||||
.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_FLAGS_INFO_KHR,
|
||||
.flags = VK_MEMORY_ALLOCATE_DEVICE_ADDRESS_BIT_KHR,
|
||||
};
|
||||
memAlloc.pNext = &allocFlagsInfo;
|
||||
Device::CheckVkResult(vkAllocateMemory(Device::device, &memAlloc, nullptr, &memory));
|
||||
} else {
|
||||
Device::CheckVkResult(vkAllocateMemory(Device::device, &memAlloc, nullptr, &memory));
|
||||
}
|
||||
|
||||
descriptor.offset = 0;
|
||||
descriptor.buffer = buffer;
|
||||
descriptor.range = VK_WHOLE_SIZE;
|
||||
|
||||
Device::CheckVkResult(vkBindBufferMemory(Device::device, buffer, memory, 0));
|
||||
|
||||
if constexpr(Adressable) {
|
||||
VkBufferDeviceAddressInfo addressInfo = {
|
||||
.sType = VK_STRUCTURE_TYPE_BUFFER_DEVICE_ADDRESS_INFO,
|
||||
.buffer = buffer
|
||||
};
|
||||
VulkanBufferAdressableConditional<true>::address = vkGetBufferDeviceAddress(Device::device, &addressInfo);
|
||||
}
|
||||
address = vkGetBufferDeviceAddress(Device::device, &addressInfo);
|
||||
|
||||
if constexpr(Mapped) {
|
||||
Device::CheckVkResult(vkMapMemory(Device::device, memory, 0, memReqs.size, 0, reinterpret_cast<void**>(&(VulkanBufferMappedConditional<T, true>::value))));
|
||||
|
|
@ -141,9 +99,6 @@ namespace Crafter {
|
|||
vkDestroyBuffer(Device::device, buffer, nullptr);
|
||||
vkFreeMemory(Device::device, memory, nullptr);
|
||||
buffer = VK_NULL_HANDLE;
|
||||
if constexpr(Staged) {
|
||||
delete VulkanBufferMappedConditional<T, true>::stagingBuffer;
|
||||
}
|
||||
}
|
||||
|
||||
void Resize(VkBufferUsageFlags usageFlags, VkMemoryPropertyFlags memoryPropertyFlags, std::uint32_t count) {
|
||||
|
|
@ -157,7 +112,7 @@ namespace Crafter {
|
|||
VkBufferCopy copyRegion = {
|
||||
.srcOffset = 0,
|
||||
.dstOffset = 0,
|
||||
.size = descriptor.range
|
||||
.size = size
|
||||
};
|
||||
|
||||
vkCmdCopyBuffer(
|
||||
|
|
@ -238,36 +193,15 @@ namespace Crafter {
|
|||
vkInvalidateMappedMemoryRanges(Device::device, 1, &range);
|
||||
}
|
||||
|
||||
void FlushDevice(VkCommandBuffer cmd) requires(Staged) {
|
||||
VulkanBufferStagedConditional<T, true>::stagingBuffer.FlushDevice(VK_ACCESS_TRANSFER_READ_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT);
|
||||
VulkanBufferStagedConditional<T, true>::stagingBuffer.Copy(cmd, this);
|
||||
}
|
||||
|
||||
void FlushDevice(VkCommandBuffer cmd, VkAccessFlags dstAccessMask, VkPipelineStageFlags dstStageMask) requires(Staged) {
|
||||
VulkanBufferStagedConditional<T, true>::stagingBuffer.FlushDevice(VK_ACCESS_TRANSFER_READ_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT);
|
||||
VulkanBufferStagedConditional<T, true>::stagingBuffer.Copy(cmd, this, VK_ACCESS_TRANSFER_WRITE_BIT, dstAccessMask, VK_PIPELINE_STAGE_TRANSFER_BIT, dstStageMask);
|
||||
}
|
||||
|
||||
void FlushHost(VkCommandBuffer cmd) requires(Staged) {
|
||||
Copy(cmd, VulkanBufferStagedConditional<T, true>::stagingBuffer);
|
||||
VulkanBufferStagedConditional<T, true>::stagingBuffer.FlushHost();
|
||||
}
|
||||
|
||||
|
||||
VulkanBuffer(VulkanBuffer&& other) {
|
||||
descriptor = other.descriptor;
|
||||
buffer = other.buffer;
|
||||
memory = other.memory;
|
||||
size = other.size;
|
||||
other.buffer = VK_NULL_HANDLE;
|
||||
if constexpr(Adressable) {
|
||||
VulkanBufferAdressableConditional<true>::address = other.VulkanBufferAdressableConditional<true>::address;
|
||||
}
|
||||
address = other.address;
|
||||
if constexpr(Mapped) {
|
||||
VulkanBufferMappedConditional<T, true>::value = other.VulkanBufferMappedConditional<T, true>::value;
|
||||
}
|
||||
if constexpr(Staged) {
|
||||
VulkanBufferStagedConditional<T, true>::stagingBuffer = other.VulkanBufferStagedConditional<T, true>::stagingBuffer;
|
||||
}
|
||||
};
|
||||
|
||||
~VulkanBuffer() {
|
||||
|
|
|
|||
|
|
@ -41,6 +41,9 @@ module;
|
|||
#ifdef CRAFTER_GRAPHICS_RENDERER_VULKAN
|
||||
#include "vulkan/vulkan.h"
|
||||
#endif
|
||||
#ifdef CRAFTER_GRAPHICS_WINDOW_WIN32
|
||||
#include <windows.h>
|
||||
#endif
|
||||
|
||||
export module Crafter.Graphics:Window;
|
||||
import std;
|
||||
|
|
@ -48,9 +51,6 @@ import :Types;
|
|||
import :Rendertarget;
|
||||
import :Transform2D;
|
||||
import Crafter.Event;
|
||||
#ifdef CRAFTER_GRAPHICS_RENDERER_VULKAN
|
||||
import :PipelineRTVulkan;
|
||||
#endif
|
||||
|
||||
export namespace Crafter {
|
||||
#ifdef CRAFTER_GRAPHICS_RENDERER_VULKAN
|
||||
|
|
@ -60,6 +60,8 @@ export namespace Crafter {
|
|||
// Command buffer submission and execution
|
||||
VkSemaphore renderComplete;
|
||||
};
|
||||
struct PipelineRTVulkan;
|
||||
struct DescriptorHeapVulkan;
|
||||
#endif
|
||||
|
||||
struct MouseElement;
|
||||
|
|
@ -80,6 +82,7 @@ export namespace Crafter {
|
|||
Event<CrafterKeys> onAnyKeyDown;
|
||||
Event<CrafterKeys> onAnyKeyHold;
|
||||
Event<CrafterKeys> onAnyKeyUp;
|
||||
Event<const std::string_view> onTextInput;
|
||||
Event<void> onMouseRightClick;
|
||||
Event<void> onMouseLeftClick;
|
||||
Event<void> onMouseRightHold;
|
||||
|
|
@ -128,6 +131,13 @@ export namespace Crafter {
|
|||
void LogTiming();
|
||||
#endif
|
||||
|
||||
#ifdef CRAFTER_GRAPHICS_WINDOW_WIN32
|
||||
HBITMAP cursorBitmap = nullptr;
|
||||
HCURSOR cursorHandle = nullptr;
|
||||
std::uint16_t cursorSizeX = 0;
|
||||
std::uint16_t cursorSizeY = 0;
|
||||
#endif
|
||||
|
||||
#ifdef CRAFTER_GRAPHICS_WINDOW_WAYLAND
|
||||
float scale;
|
||||
#ifdef CRAFTER_GRAPHICS_RENDERER_SOFTWARE
|
||||
|
|
@ -168,17 +178,6 @@ export namespace Crafter {
|
|||
#endif
|
||||
|
||||
#ifdef CRAFTER_GRAPHICS_RENDERER_VULKAN
|
||||
std::vector<VkDescriptorSet> descriptorsRt;
|
||||
template <typename Pipeline>
|
||||
void SetPipelineRT() {
|
||||
rtPipeline = Pipeline::pipeline;
|
||||
rtPipelineLayout = Pipeline::pipelineLayout;
|
||||
raygenRegion = Pipeline::raygenRegion;
|
||||
missRegion = Pipeline::missRegion;
|
||||
hitRegion = Pipeline::hitRegion;
|
||||
callableRegion = Pipeline::callableRegion;
|
||||
}
|
||||
void SetPipelineRT(PipelineRTVulkan& pipeline);
|
||||
VkCommandBuffer StartInit();
|
||||
void FinishInit();
|
||||
VkCommandBuffer GetCmd();
|
||||
|
|
@ -190,19 +189,15 @@ export namespace Crafter {
|
|||
VkFormat colorFormat;
|
||||
VkColorSpaceKHR colorSpace;
|
||||
VkImage images[numFrames];
|
||||
VkImageView imageViews[numFrames];
|
||||
VkImageViewCreateInfo imageViews[numFrames];
|
||||
std::thread thread;
|
||||
VkCommandBuffer drawCmdBuffers[numFrames];
|
||||
VkSubmitInfo submitInfo;
|
||||
Semaphores semaphores;
|
||||
std::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;
|
||||
PipelineRTVulkan* pipeline;
|
||||
DescriptorHeapVulkan* descriptorHeap;
|
||||
#endif
|
||||
};
|
||||
}
|
||||
|
|
@ -38,14 +38,14 @@ export import :ForwardDeclarations;
|
|||
export import :Device;
|
||||
export import :VulkanTransition;
|
||||
export import :VulkanBuffer;
|
||||
export import :DescriptorPoolVulkan;
|
||||
export import :ShaderVulkan;
|
||||
export import :ShaderBindingTableVulkan;
|
||||
export import :PipelineRTVulkan;
|
||||
export import :RenderingElement3D;
|
||||
export import :ImageVulkan;
|
||||
export import :SamplerVulkan;
|
||||
export import :DescriptorSetLayoutVulkan;
|
||||
export import :DescriptorHeapVulkan;
|
||||
export import :RenderingElement2DVulkan;
|
||||
#endif
|
||||
|
||||
// export import :WindowWaylandVulkan;
|
||||
|
|
|
|||
11
project.json
11
project.json
|
|
@ -10,7 +10,8 @@
|
|||
"implementations/Crafter.Graphics-Transform2D",
|
||||
"implementations/Crafter.Graphics-Device",
|
||||
"implementations/Crafter.Graphics-Mesh",
|
||||
"implementations/Crafter.Graphics-RenderingElement3D"
|
||||
"implementations/Crafter.Graphics-RenderingElement3D",
|
||||
"implementations/Crafter.Graphics-Rendertarget"
|
||||
],
|
||||
"interfaces": [
|
||||
"interfaces/Crafter.Graphics-Window",
|
||||
|
|
@ -28,15 +29,15 @@
|
|||
"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-DescriptorHeapVulkan",
|
||||
"interfaces/Crafter.Graphics-Rendertarget",
|
||||
"interfaces/Crafter.Graphics-ForwardDeclarations"
|
||||
"interfaces/Crafter.Graphics-ForwardDeclarations",
|
||||
"interfaces/Crafter.Graphics-RenderingElement2DVulkan"
|
||||
],
|
||||
"type": "library"
|
||||
},
|
||||
|
|
@ -53,7 +54,7 @@
|
|||
},
|
||||
{
|
||||
"name": "win32",
|
||||
"libs": ["kernel32", "user32"],
|
||||
"libs": ["kernel32", "user32", "gdi32"],
|
||||
"extends": ["base"],
|
||||
"defines": [
|
||||
{
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue