From 8a9198c3e8e6d25ceca6364a345c88cd4e259428 Mon Sep 17 00:00:00 2001 From: Zack Buhman Date: Sun, 12 Apr 2026 23:24:43 -0500 Subject: [PATCH] collada: use storagebuffers, draw material colors --- Makefile | 2 +- data/scenes/shadow_test/shadow_test.cpp | 12 +-- include/collada/scene/vulkan.h | 10 +- include/collada/types.h | 2 +- shader/collada.hlsl | 35 ++----- src/collada/scene.cpp | 1 + src/collada/scene/vulkan.cpp | 124 ++++++++++++++++++------ 7 files changed, 116 insertions(+), 70 deletions(-) diff --git a/Makefile b/Makefile index 19d6106..0e972ca 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/data/scenes/shadow_test/shadow_test.cpp b/data/scenes/shadow_test/shadow_test.cpp index 5873acc..c03c885 100644 --- a/data/scenes/shadow_test/shadow_test.cpp +++ b/data/scenes/shadow_test/shadow_test.cpp @@ -1740,7 +1740,7 @@ transform const transforms_node_plane001[] = { instance_material const instance_geometry_instance_materials_node_plane001_0[] = { { .element_index = 0, // an index into mesh.triangles - .material = &material_material__148_material, + .material_index = 5, // an index into materials .emission = { .input_set = -1 }, .ambient = { .input_set = -1 }, @@ -1850,7 +1850,7 @@ transform const transforms_node_torus_knot001[] = { instance_material const instance_geometry_instance_materials_node_torus_knot001_0[] = { { .element_index = 0, // an index into mesh.triangles - .material = &material_coloreffectr134g110b8_material, + .material_index = 1, // an index into materials .emission = { .input_set = -1 }, .ambient = { .input_set = -1 }, @@ -1956,7 +1956,7 @@ transform const transforms_node_cone001[] = { instance_material const instance_geometry_instance_materials_node_cone001_0[] = { { .element_index = 0, // an index into mesh.triangles - .material = &material_coloreffectr6g134b6_material, + .material_index = 3, // an index into materials .emission = { .input_set = -1 }, .ambient = { .input_set = -1 }, @@ -2020,7 +2020,7 @@ transform const transforms_node_box001[] = { instance_material const instance_geometry_instance_materials_node_box001_0[] = { { .element_index = 0, // an index into mesh.triangles - .material = &material_coloreffectr88g88b225_material, + .material_index = 4, // an index into materials .emission = { .input_set = -1 }, .ambient = { .input_set = -1 }, @@ -2087,9 +2087,9 @@ instance_light const instance_lights_node_point001[] = { }; channel const * const node_channels_node_point001[] = { + &node_channel_node_point001_translation_y, &node_channel_node_point001_translation_x, &node_channel_node_point001_translation_z, - &node_channel_node_point001_translation_y, }; node const node_node_point001 = { @@ -2177,9 +2177,9 @@ instance_light const instance_lights_node_point002[] = { }; channel const * const node_channels_node_point002[] = { - &node_channel_node_point002_translation_x, &node_channel_node_point002_translation_z, &node_channel_node_point002_translation_y, + &node_channel_node_point002_translation_x, }; node const node_node_point002 = { diff --git a/include/collada/scene/vulkan.h b/include/collada/scene/vulkan.h index a11d1cc..f1fdb41 100644 --- a/include/collada/scene/vulkan.h +++ b/include/collada/scene/vulkan.h @@ -14,18 +14,17 @@ namespace collada::scene { }; struct Node { XMFLOAT4X4 modelView; - //int materialIndex; - //int _padding0[3]; - //int _padding1[4 * 3]; }; - static_assert((sizeof (Node)) % 64 == 0); struct MaterialColor { XMFLOAT4 emission; XMFLOAT4 ambient; XMFLOAT4 diffuse; XMFLOAT4 specular; }; - static_assert((sizeof (MaterialColor)) % 64 == 0); + struct PushConstant { + int nodeIndex; + int materialIndex; + }; struct vulkan { // externally initialized, opaque handle @@ -121,6 +120,7 @@ namespace collada::scene { void create_uniform_buffers(collada::types::descriptor const * const descriptor); void create_descriptor_sets(); void write_descriptor_sets(collada::types::descriptor const * const descriptor); + void load_material_constants(collada::types::descriptor const * const descriptor); ////////////////////////////////////////////////////////////////////// // called by state::draw diff --git a/include/collada/types.h b/include/collada/types.h index 47a97ed..490264f 100644 --- a/include/collada/types.h +++ b/include/collada/types.h @@ -241,7 +241,7 @@ namespace collada::types { struct instance_material { int const element_index; // an index into mesh.triangles - types::material const * const material; + int const material_index; // an index into materials // heavily simplified from collada data model bind_vertex_input const emission; diff --git a/shader/collada.hlsl b/shader/collada.hlsl index 2ae952e..b1aea66 100644 --- a/shader/collada.hlsl +++ b/shader/collada.hlsl @@ -12,12 +12,12 @@ struct VSOutput float2 Texture : TEXCOORD0; float3 LightDirection : NORMAL1; float3 ViewDirection : NORMAL2; + nointerpolation int MaterialIndex : materialindex; }; struct Node { column_major float4x4 ModelView; - //int MaterialIndex; }; struct Scene @@ -34,44 +34,24 @@ struct MaterialColor float4 Specular; }; -/* -struct Nodes { - Node n[16]; -}; - -struct MaterialColors { - MaterialColor mc[16]; -}; -*/ - -struct SceneNodes { - Scene Scene; -}; - -struct Nodes { - Node n[11]; -}; - // set 0: per-frame [[vk::binding(0, 0)]] ConstantBuffer Scene; -[[vk::binding(1, 0)]] ConstantBuffer Nodes; -//[[vk::binding(1, 0)]] cbuffer asdf { Nodes Nodes; } +[[vk::binding(1, 0)]] StructuredBuffer Nodes; // set 1: constant -//[[vk::binding(0, 1)]] ConstantBuffer MaterialColors; +[[vk::binding(0, 1)]] StructuredBuffer MaterialColors; struct PushConstant { int NodeIndex; + int MaterialIndex; }; -[[vk::push_constant]] -struct PushConstant constants; +[[vk::push_constant]] PushConstant constants; [shader("vertex")] VSOutput VSMain(VSInput input) { - //[constants.NodeIndex] - float4x4 modelView = Nodes.n[constants.NodeIndex].ModelView; + float4x4 modelView = Nodes[constants.NodeIndex].ModelView; VSOutput output = (VSOutput)0; output.Position = mul(Scene.Projection, mul(modelView, float4(input.Position.xyz, 1.0))) * float4(-1, -1, 1, 1); @@ -89,6 +69,7 @@ VSOutput VSMain(VSInput input) float4 PSMain(VSOutput input) : SV_TARGET { //float3 color = texture.Sample(samplers[0], input.Texture).bgr; + float4 diffuseColor = MaterialColors[constants.MaterialIndex].Diffuse; float3 N = normalize(input.Normal); float3 L = normalize(input.LightDirection); @@ -100,5 +81,5 @@ float4 PSMain(VSOutput input) : SV_TARGET float3 specular = pow(max(dot(R, V), 0), a) * specularIntensity; float3 diffuse = max(dot(N, L), 0.001); - return float4(diffuse + specular, 1.0); + return float4(diffuse * diffuseColor.xyz + specular, 1.0); } diff --git a/src/collada/scene.cpp b/src/collada/scene.cpp index 1c55405..b4f4caf 100644 --- a/src/collada/scene.cpp +++ b/src/collada/scene.cpp @@ -13,6 +13,7 @@ namespace collada::scene { vulkan.create_uniform_buffers(descriptor); vulkan.create_descriptor_sets(); vulkan.write_descriptor_sets(descriptor); + vulkan.load_material_constants(descriptor); vulkan.create_pipelines(descriptor); node_state.allocate_node_instances(descriptor->nodes, descriptor->nodes_count); diff --git a/src/collada/scene/vulkan.cpp b/src/collada/scene/vulkan.cpp index 59238ac..3710f70 100644 --- a/src/collada/scene/vulkan.cpp +++ b/src/collada/scene/vulkan.cpp @@ -181,7 +181,7 @@ namespace collada::scene { ////////////////////////////////////////////////////////////////////// - // uniform buffers + // uniform and storage buffers ////////////////////////////////////////////////////////////////////// void vulkan::create_uniform_buffers(collada::types::descriptor const * const descriptor) @@ -210,7 +210,7 @@ namespace collada::scene { VkBufferCreateInfo nodesBufferCreateInfo{ .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, .size = (sizeof (Node)) * descriptor->nodes_count, - .usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, + .usage = VK_BUFFER_USAGE_STORAGE_BUFFER_BIT, .sharingMode = VK_SHARING_MODE_EXCLUSIVE }; VK_CHECK(vkCreateBuffer(device, &nodesBufferCreateInfo, nullptr, &shaderDataDevice.frame[i].nodesBuffer)); @@ -221,7 +221,7 @@ namespace collada::scene { VkBufferCreateInfo materialColorsBufferCreateInfo{ .sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO, .size = (sizeof (MaterialColor)) * descriptor->materials_count, - .usage = VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, + .usage = VK_BUFFER_USAGE_STORAGE_BUFFER_BIT, .sharingMode = VK_SHARING_MODE_EXCLUSIVE }; VK_CHECK(vkCreateBuffer(device, &materialColorsBufferCreateInfo, nullptr, &shaderDataDevice.constant.materialColorsBuffer)); @@ -231,14 +231,14 @@ namespace collada::scene { VkMemoryPropertyFlags memoryPropertyFlags{ VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT }; VkMemoryAllocateFlags memoryAllocateFlags{ }; - VkDeviceSize allocationSize = allocateFromMemoryRequirements2(device, - physicalDeviceMemoryProperties, - memoryPropertyFlags, - memoryAllocateFlags, - uniformBufferDescriptorCount, - memoryRequirements, - &shaderDataDevice.memory, - offsets); + allocateFromMemoryRequirements2(device, + physicalDeviceMemoryProperties, + memoryPropertyFlags, + memoryAllocateFlags, + uniformBufferDescriptorCount, + memoryRequirements, + &shaderDataDevice.memory, + offsets); VkDeviceSize offset{ 0 }; VkDeviceSize size{ VK_WHOLE_SIZE }; @@ -263,11 +263,6 @@ namespace collada::scene { shaderDataDevice.constant.materialColorsMapped = (void *)(((size_t)shaderDataDevice.mappedData) + shaderDataDevice.constant.materialColorsOffset); VK_CHECK(vkBindBufferMemory(device, shaderDataDevice.constant.materialColorsBuffer, shaderDataDevice.memory, shaderDataDevice.constant.materialColorsOffset)); - // if materialColorSize rounded to a multiple of nonCoherentAtomSize is larger than the size of the allocated memory, round down to VK_WHOLE_SIZE - if (shaderDataDevice.constant.materialColorsOffset + shaderDataDevice.constant.materialColorsSize > allocationSize) { - shaderDataDevice.constant.materialColorsSize = VK_WHOLE_SIZE; - } - assert(offsetsIndex == uniformBufferDescriptorCount); } @@ -280,16 +275,20 @@ namespace collada::scene { // // pool // - VkDescriptorPoolSize descriptorPoolSizes[1]{ + VkDescriptorPoolSize descriptorPoolSizes[2]{ { .type = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, - .descriptorCount = uniformBufferDescriptorCount + 1, // why + 1? + .descriptorCount = maxFrames + 1, // why +1? + }, + { + .type = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, + .descriptorCount = maxFrames + 1, // +1 for materialColors } }; VkDescriptorPoolCreateInfo descriptorPoolCreateInfo{ .sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO, .maxSets = 2, - .poolSizeCount = 1, + .poolSizeCount = 2, .pPoolSizes = descriptorPoolSizes }; VK_CHECK(vkCreateDescriptorPool(device, &descriptorPoolCreateInfo, nullptr, &descriptorPool)); @@ -308,7 +307,7 @@ namespace collada::scene { }, { .binding = 1, - .descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, + .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, .descriptorCount = 1, .stageFlags = VK_SHADER_STAGE_VERTEX_BIT } @@ -343,9 +342,9 @@ namespace collada::scene { VkDescriptorSetLayoutBinding descriptorSetLayoutBindings[bindingCount]{ { .binding = 0, - .descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, + .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, .descriptorCount = 1, - .stageFlags = VK_SHADER_STAGE_VERTEX_BIT + .stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT }, }; @@ -402,7 +401,7 @@ namespace collada::scene { .dstSet = descriptorSets0[i], .dstBinding = 1, .descriptorCount = 1, - .descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, + .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, .pBufferInfo = &nodesDescriptorBufferInfos[i] }; } @@ -417,7 +416,7 @@ namespace collada::scene { .dstSet = descriptorSet1, .dstBinding = 0, .descriptorCount = 1, - .descriptorType = VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, + .descriptorType = VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, .pBufferInfo = &materialColorsDescriptorBufferInfo }; @@ -426,6 +425,63 @@ namespace collada::scene { vkUpdateDescriptorSets(device, writeIndex, writeDescriptorSets, 0, nullptr); } + ////////////////////////////////////////////////////////////////////// + // material constants + ////////////////////////////////////////////////////////////////////// + + void vulkan::load_material_constants(collada::types::descriptor const * const descriptor) + { + // store + for (int i = 0; i < descriptor->materials_count; i++) { + collada::types::effect const * const effect = descriptor->materials[i]->effect; + switch (effect->type) { + case collada::types::effect_type::BLINN: + shaderData.materialColors[i].emission = *(XMFLOAT4 *)(&effect->blinn.emission.color); + shaderData.materialColors[i].ambient = *(XMFLOAT4 *)(&effect->blinn.ambient.color); + shaderData.materialColors[i].diffuse = *(XMFLOAT4 *)(&effect->blinn.diffuse.color); + shaderData.materialColors[i].specular = *(XMFLOAT4 *)(&effect->blinn.specular.color); + break; + case collada::types::effect_type::LAMBERT: + shaderData.materialColors[i].emission = *(XMFLOAT4 *)(&effect->lambert.emission.color); + shaderData.materialColors[i].ambient = *(XMFLOAT4 *)(&effect->lambert.ambient.color); + shaderData.materialColors[i].diffuse = *(XMFLOAT4 *)(&effect->lambert.diffuse.color); + shaderData.materialColors[i].specular = XMFLOAT4{0, 0, 0, 0}; + break; + case collada::types::effect_type::PHONG: + shaderData.materialColors[i].emission = *(XMFLOAT4 *)(&effect->phong.emission.color); + shaderData.materialColors[i].ambient = *(XMFLOAT4 *)(&effect->phong.ambient.color); + shaderData.materialColors[i].diffuse = *(XMFLOAT4 *)(&effect->phong.diffuse.color); + shaderData.materialColors[i].specular = *(XMFLOAT4 *)(&effect->phong.specular.color); + break; + case collada::types::effect_type::CONSTANT: + shaderData.materialColors[i].emission = *(XMFLOAT4 *)(&effect->constant.color); + shaderData.materialColors[i].ambient = XMFLOAT4{0, 0, 0, 0}; + shaderData.materialColors[i].diffuse = XMFLOAT4{0, 0, 0, 0}; + shaderData.materialColors[i].specular = XMFLOAT4{0, 0, 0, 0}; + break; + default: + assert(false); + break; + } + } + + // copy + memcpy(shaderDataDevice.constant.materialColorsMapped, &shaderData.materialColors[0], (sizeof (MaterialColor)) * descriptor->materials_count); + + // flush + + VkDeviceSize materialColorsFlushSize{ shaderDataDevice.constant.materialColorsSize }; + VkMappedMemoryRange shaderDataMemoryRanges[1]{ + { + .sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE, + .memory = shaderDataDevice.memory, + .offset = shaderDataDevice.constant.materialColorsOffset, + .size = roundAlignment(materialColorsFlushSize, physicalDeviceProperties.limits.nonCoherentAtomSize), + }, + }; + vkFlushMappedMemoryRanges(device, 1, shaderDataMemoryRanges); + } + ////////////////////////////////////////////////////////////////////// // shader ////////////////////////////////////////////////////////////////////// @@ -449,9 +505,12 @@ namespace collada::scene { void vulkan::create_pipelines(collada::types::descriptor const * const descriptor) { - VkPushConstantRange pushConstantRange{ - .stageFlags = VK_SHADER_STAGE_VERTEX_BIT, - .size = (sizeof (int32_t)) + VkPushConstantRange pushConstantRanges[1]{ + { + .stageFlags = VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT, + .offset = 0, + .size = (sizeof (PushConstant)) + } }; VkPipelineLayoutCreateInfo pipelineLayoutCreateInfo{ @@ -459,7 +518,7 @@ namespace collada::scene { .setLayoutCount = 2, .pSetLayouts = descriptorSetLayouts, .pushConstantRangeCount = 1, - .pPushConstantRanges = &pushConstantRange + .pPushConstantRanges = pushConstantRanges }; VK_CHECK(vkCreatePipelineLayout(device, &pipelineLayoutCreateInfo, nullptr, &pipelineLayout)); @@ -599,7 +658,10 @@ namespace collada::scene { types::instance_material const& instance_material = instance_materials[j]; types::triangles const& triangles = mesh.triangles[instance_material.element_index]; - //set_instance_material(instance_material); + VkShaderStageFlags stageFlags{ VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT }; + uint32_t materialIndex = instance_material.material_index; + uint32_t offset{ (offsetof (PushConstant, materialIndex)) }; + vkCmdPushConstants(commandBuffer, pipelineLayout, stageFlags, offset, (sizeof (uint32_t)), &materialIndex); VkDeviceSize vertexOffset{ (VkDeviceSize)mesh.vertex_buffer_offset }; vkCmdBindVertexBuffers(commandBuffer, 0, 1, &vertexIndex.buffer, &vertexOffset); @@ -667,7 +729,9 @@ namespace collada::scene { types::node const & node, instance_types::node const & node_instance) { - vkCmdPushConstants(commandBuffer, pipelineLayout, VK_SHADER_STAGE_VERTEX_BIT, 0, (sizeof (int32_t)), &node_index); + VkShaderStageFlags stageFlags{ VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT }; + uint32_t offset{ (offsetof (PushConstant, nodeIndex)) }; + vkCmdPushConstants(commandBuffer, pipelineLayout, stageFlags, offset, (sizeof (uint32_t)), &node_index); draw_instance_geometries(node.instance_geometries, node.instance_geometries_count); }