diff --git a/Makefile b/Makefile index e3cc623..a801336 100644 --- a/Makefile +++ b/Makefile @@ -25,7 +25,7 @@ CFLAGS += -I./data CFLAGS += -I../SDL3-dist/include CFLAGS += -fpic -#FLAGS += -fstack-protector -fstack-protector-all -fno-omit-frame-pointer -fsanitize=address +FLAGS += -fstack-protector -fstack-protector-all -fno-omit-frame-pointer -fsanitize=address LDFLAGS += -lm ifeq ($(UNAME),Linux) diff --git a/include/minecraft/vulkan.h b/include/minecraft/vulkan.h index c88fed9..e2a1b95 100644 --- a/include/minecraft/vulkan.h +++ b/include/minecraft/vulkan.h @@ -1,12 +1,29 @@ #pragma once +#include "directxmath/directxmath.h" #include "volk/volk.h" #include "minecraft/vulkan/per_world.h" namespace minecraft::vulkan { + struct Scene { + XMFLOAT4X4 projection; + XMFLOAT4X4 view; + XMFLOAT4X4 shadowProjection; + XMFLOAT4X4 shadowView; + XMFLOAT4 lightPosition; + }; + struct vulkan { + static constexpr uint32_t maxFrames = 2; + static constexpr uint32_t perFrameDescriptorCount = 1; + static constexpr uint32_t uniformBufferDescriptorCount = maxFrames * perFrameDescriptorCount; + static constexpr uint32_t bindingCount = uniformBufferDescriptorCount + 0; + + static constexpr int perVertexSize = (3 + 3 + 2) * 2; + static constexpr int perInstanceSize = (3 + 1 + 3 + 1) * 2; + struct { VkDeviceSize jointWeightOffset; VkDeviceSize indexOffset; @@ -35,8 +52,22 @@ namespace minecraft::vulkan { per_world * worlds; - static constexpr int perVertexSize = (3 + 3 + 2) * 2; - static constexpr int perInstanceSize = (3 + 1 + 3 + 1) * 2; + VkDescriptorPool descriptorPool{ VK_NULL_HANDLE }; + + VkDescriptorSetLayout descriptorSetLayouts[1]; // unrelated to maxFrames, unrelated to descriptorCount + VkDescriptorSet descriptorSets0[maxFrames]; + + struct { + VkDeviceMemory memory; + VkDeviceSize memorySize; + void * mappedData; + struct { // must match perFrameDescriptorCount + VkBuffer sceneBuffer; + VkDeviceAddress sceneOffset; + VkDeviceAddress sceneSize; + Scene * sceneMapped; + } frame[maxFrames]; + } shaderDataDevice; void initial_state(VkInstance instance, VkDevice device, @@ -52,6 +83,14 @@ namespace minecraft::vulkan { void load_shader(); void create_pipeline(); void load_worlds(); - void draw(VkCommandBuffer commandBuffer); + void create_uniform_buffers(); + void create_descriptor_sets(); + void write_descriptor_sets(); + void transfer_transforms(XMMATRIX const & projection, + XMMATRIX const & view, + uint32_t frameIndex); + + void draw(VkCommandBuffer commandBuffer, + uint32_t frameIndex); }; } diff --git a/shader/minecraft.hlsl b/shader/minecraft.hlsl index ec50a2a..7f2cb4f 100644 --- a/shader/minecraft.hlsl +++ b/shader/minecraft.hlsl @@ -16,11 +16,24 @@ struct VSOutput float4 Position : SV_POSITION; }; +struct Scene +{ + column_major float4x4 Projection; + column_major float4x4 View; + column_major float4x4 ShadowProjection; + column_major float4x4 ShadowView; + float4 LightPosition; // view space +}; + +// set 0: per-frame +[[vk::binding(0, 0)]] ConstantBuffer Scene; + [shader("vertex")] VSOutput VSMain(VSInput input) { VSOutput output = (VSOutput)0; - output.Position = float4(input.Position.xyz + input.BlockPosition, 1.0); + float4 Position = float4(input.Position.xyz + input.BlockPosition, 1.0); + output.Position = mul(Scene.Projection, mul(Scene.View, Position)); return output; } diff --git a/src/main.cpp b/src/main.cpp index d9d1478..a93791f 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -722,6 +722,12 @@ int main() lightPositionWorld, collada_state.descriptor->nodes_count, collada_state.node_state.node_instances); + + // minecraft + + minecraft_state.transfer_transforms(projection, + view, + frameIndex); } ////////////////////////////////////////////////////////////////////// @@ -922,7 +928,7 @@ int main() //collada_state.vulkan.pipelineIndex = 2; // geometry shader pipeline //collada_state.draw(); - minecraft_state.draw(commandBuffer); + minecraft_state.draw(commandBuffer, frameIndex); vkCmdEndRendering(commandBuffer); diff --git a/src/minecraft/vulkan.cpp b/src/minecraft/vulkan.cpp index ae1e7c5..90d5b43 100644 --- a/src/minecraft/vulkan.cpp +++ b/src/minecraft/vulkan.cpp @@ -47,6 +47,9 @@ namespace minecraft::vulkan { { load_vertex_index_buffer("data/minecraft/per_vertex.vtx", "data/minecraft/configuration.idx"); load_shader(); + create_uniform_buffers(); + create_descriptor_sets(); + write_descriptor_sets(); create_pipeline(); load_worlds(); } @@ -139,8 +142,8 @@ namespace minecraft::vulkan { { VkPipelineLayoutCreateInfo pipelineLayoutCreateInfo{ .sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO, - //.setLayoutCount = 2, - //.pSetLayouts = descriptorSetLayouts, + .setLayoutCount = 1, + .pSetLayouts = descriptorSetLayouts, //.pushConstantRangeCount = 0, //.pPushConstantRanges = nullptr }; @@ -332,6 +335,150 @@ namespace minecraft::vulkan { VK_CHECK(vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 1, pipelineCreateInfos, nullptr, &pipeline)); } + ////////////////////////////////////////////////////////////////////// + // uniform buffers + ////////////////////////////////////////////////////////////////////// + + void vulkan::create_uniform_buffers() + { + VkMemoryRequirements memoryRequirements[uniformBufferDescriptorCount]; + VkDeviceSize offsets[uniformBufferDescriptorCount]; + + uint32_t memoryRequirementsIndex = 0; + // per-frame + for (uint32_t i = 0; i < maxFrames; i++) { + VkBufferCreateInfo sceneBufferCreateInfo{ + .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, + .size = (sizeof (Scene)), + .usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, + .sharingMode = VK_SHARING_MODE_EXCLUSIVE + }; + + VK_CHECK(vkCreateBuffer(device, &sceneBufferCreateInfo, nullptr, &shaderDataDevice.frame[i].sceneBuffer)); + vkGetBufferMemoryRequirements(device, shaderDataDevice.frame[i].sceneBuffer, &memoryRequirements[memoryRequirementsIndex++]); + } + + assert(memoryRequirementsIndex == uniformBufferDescriptorCount); + + VkMemoryPropertyFlags memoryPropertyFlags{ VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT }; + VkMemoryAllocateFlags memoryAllocateFlags{ }; + shaderDataDevice.memorySize = allocateFromMemoryRequirements2(device, + physicalDeviceProperties.limits.nonCoherentAtomSize, + physicalDeviceMemoryProperties, + memoryPropertyFlags, + memoryAllocateFlags, + uniformBufferDescriptorCount, + memoryRequirements, + &shaderDataDevice.memory, + offsets); + + VkDeviceSize offset{ 0 }; + VkDeviceSize size{ VK_WHOLE_SIZE }; + VkMemoryMapFlags flags{ 0 }; + VK_CHECK(vkMapMemory(device, shaderDataDevice.memory, offset, size, flags, &shaderDataDevice.mappedData)); + + uint32_t offsetsIndex = 0; + // this must match the same order as memoryRequirements + for (uint32_t i = 0; i < maxFrames; i++) { + shaderDataDevice.frame[i].sceneOffset = offsets[offsetsIndex]; + shaderDataDevice.frame[i].sceneSize = memoryRequirements[offsetsIndex++].size; + shaderDataDevice.frame[i].sceneMapped = (Scene *)(((size_t)shaderDataDevice.mappedData) + shaderDataDevice.frame[i].sceneOffset); + VK_CHECK(vkBindBufferMemory(device, shaderDataDevice.frame[i].sceneBuffer, shaderDataDevice.memory, shaderDataDevice.frame[i].sceneOffset)); + } + assert(offsetsIndex == uniformBufferDescriptorCount); + } + + ////////////////////////////////////////////////////////////////////// + // descriptor sets + ////////////////////////////////////////////////////////////////////// + + void vulkan::create_descriptor_sets() + { + // + // pool + // + constexpr int descriptorPoolSizesCount = 1; + VkDescriptorPoolSize descriptorPoolSizes[descriptorPoolSizesCount]{ + { + .type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, + .descriptorCount = (maxFrames * 1), + }, + }; + VkDescriptorPoolCreateInfo descriptorPoolCreateInfo{ + .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO, + .maxSets = maxFrames, + .poolSizeCount = descriptorPoolSizesCount, + .pPoolSizes = descriptorPoolSizes + }; + VK_CHECK(vkCreateDescriptorPool(device, &descriptorPoolCreateInfo, nullptr, &descriptorPool)); + + // + // uniform buffer descriptor set layout/allocation (set 0, per-frame) + // + { + constexpr int bindingCount = 1; + VkDescriptorSetLayoutBinding descriptorSetLayoutBindings[bindingCount]{ + { + .binding = 0, + .descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, + .descriptorCount = 1, + .stageFlags = VK_SHADER_STAGE_VERTEX_BIT + } + }; + + VkDescriptorSetLayoutCreateInfo descriptorSetLayoutCreateInfo{ + .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO, + .bindingCount = bindingCount, + .pBindings = descriptorSetLayoutBindings + }; + VK_CHECK(vkCreateDescriptorSetLayout(device, &descriptorSetLayoutCreateInfo, nullptr, &descriptorSetLayouts[0])); + + VkDescriptorSetLayout setLayouts[maxFrames]; + for (uint32_t i = 0; i < maxFrames; i++) { + setLayouts[i] = descriptorSetLayouts[0]; + }; + + VkDescriptorSetAllocateInfo descriptorSetAllocateInfo{ + .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO, + .descriptorPool = descriptorPool, + .descriptorSetCount = maxFrames, + .pSetLayouts = setLayouts + }; + VK_CHECK(vkAllocateDescriptorSets(device, &descriptorSetAllocateInfo, descriptorSets0)); + } + } + + ////////////////////////////////////////////////////////////////////// + // descriptor set writes + ////////////////////////////////////////////////////////////////////// + + void vulkan::write_descriptor_sets() + { + VkWriteDescriptorSet writeDescriptorSets[bindingCount]; + uint32_t writeIndex = 0; + + VkDescriptorBufferInfo sceneDescriptorBufferInfos[maxFrames]; + + for (uint32_t i = 0; i < maxFrames; i++) { + sceneDescriptorBufferInfos[i] = { + .buffer = shaderDataDevice.frame[i].sceneBuffer, + .offset = 0, + .range = shaderDataDevice.frame[i].sceneSize, + }; + writeDescriptorSets[writeIndex++] = { + .sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET, + .dstSet = descriptorSets0[i], + .dstBinding = 0, + .descriptorCount = 1, + .descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, + .pBufferInfo = &sceneDescriptorBufferInfos[i] + }; + } + + assert(writeIndex == bindingCount); + vkUpdateDescriptorSets(device, writeIndex, writeDescriptorSets, 0, nullptr); + } + ////////////////////////////////////////////////////////////////////// // load worlds ////////////////////////////////////////////////////////////////////// @@ -345,12 +492,50 @@ namespace minecraft::vulkan { } } + ////////////////////////////////////////////////////////////////////// + // scene data + ////////////////////////////////////////////////////////////////////// + + void vulkan::transfer_transforms(XMMATRIX const & projection, + XMMATRIX const & view, + uint32_t frameIndex) + { + XMStoreFloat4x4(&shaderDataDevice.frame[frameIndex].sceneMapped->projection, projection); + XMStoreFloat4x4(&shaderDataDevice.frame[frameIndex].sceneMapped->view, view); + + // flush + constexpr int mappedMemoryRangesCount = 1; + VkMappedMemoryRange mappedMemoryRanges[mappedMemoryRangesCount]{ + { + .sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE, + .memory = shaderDataDevice.memory, + .offset = shaderDataDevice.frame[frameIndex].sceneOffset, + .size = shaderDataDevice.frame[frameIndex].sceneSize, + } + }; + alignMappedMemoryRanges(physicalDeviceProperties.limits.nonCoherentAtomSize, + shaderDataDevice.memorySize, + mappedMemoryRangesCount, + mappedMemoryRanges); + vkFlushMappedMemoryRanges(device, mappedMemoryRangesCount, mappedMemoryRanges); + } + ////////////////////////////////////////////////////////////////////// // draw ////////////////////////////////////////////////////////////////////// - void vulkan::draw(VkCommandBuffer commandBuffer) + void vulkan::draw(VkCommandBuffer commandBuffer, + uint32_t frameIndex) { + VkDescriptorSet descriptorSets[2] = { + descriptorSets0[frameIndex], + }; + vkCmdBindDescriptorSets(commandBuffer, + VK_PIPELINE_BIND_POINT_GRAPHICS, + pipelineLayout, + 0, 1, descriptorSets, + 0, nullptr); + vkCmdBindIndexBuffer(commandBuffer, vertexIndex.buffer, vertexIndex.indexOffset, VK_INDEX_TYPE_UINT16); VkBuffer vertexBuffers[2]{ vertexIndex.buffer,