minecraft: send view and projection matrices to vertex shader

This commit is contained in:
Zack Buhman 2026-04-30 13:53:58 -05:00
parent 4cc844addf
commit 46924c8451
5 changed files with 252 additions and 9 deletions

View File

@ -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)

View File

@ -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);
};
}

View File

@ -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> 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;
}

View File

@ -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);

View File

@ -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,