collada: use storagebuffers, draw material colors

This commit is contained in:
Zack Buhman 2026-04-12 23:24:43 -05:00
parent c30394c3ed
commit b8e370f417
7 changed files with 115 additions and 68 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

@ -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 = {

View File

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

View File

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

View File

@ -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,34 +34,16 @@ 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> Scene;
[[vk::binding(1, 0)]] ConstantBuffer<Nodes> Nodes;
//[[vk::binding(1, 0)]] cbuffer asdf { Nodes Nodes; }
[[vk::binding(1, 0)]] StructuredBuffer<Node> Nodes;
// set 1: constant
//[[vk::binding(0, 1)]] ConstantBuffer<MaterialColor[]> MaterialColors;
[[vk::binding(0, 1)]] StructuredBuffer<MaterialColor> MaterialColors;
struct PushConstant {
int NodeIndex;
int MaterialIndex;
};
[[vk::push_constant]]
@ -70,8 +52,7 @@ struct 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 +70,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 +82,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);
}

View File

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

View File

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