render collada scene

This commit is contained in:
Zack Buhman 2026-04-11 22:27:12 -05:00
parent 1456e04200
commit 0a33f58f04
23 changed files with 4821 additions and 183 deletions

View File

@ -21,10 +21,11 @@ CFLAGS += -Wno-error=array-bounds
CFLAGS += -Wno-unknown-pragmas CFLAGS += -Wno-unknown-pragmas
CFLAGS += -fno-strict-aliasing CFLAGS += -fno-strict-aliasing
CFLAGS += -I./include CFLAGS += -I./include
CFLAGS += -I./data
CFLAGS += -I../SDL3-dist/include CFLAGS += -I../SDL3-dist/include
CFLAGS += -fpic 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 LDFLAGS += -lm
ifeq ($(UNAME),Linux) ifeq ($(UNAME),Linux)
@ -40,7 +41,14 @@ OBJS = \
src/volk/volk.o \ src/volk/volk.o \
src/file.o \ src/file.o \
src/pack.o \ src/pack.o \
src/dds_validate.o src/dds_validate.o \
src/vulkan_helper.o \
src/collada/scene/vulkan.o \
src/collada/scene.o \
src/collada/node_state.o
SCENES = \
data/scenes/shadow_test/shadow_test.o
ifeq ($(UNAME),Darwin) ifeq ($(UNAME),Darwin)
LIBS = \ LIBS = \
@ -61,7 +69,7 @@ all: main
%.o: %.s %.o: %.s
$(AS) $< -o $@ $(AS) $< -o $@
main: $(OBJS) $(LIBS) $(BINS) $(SHADERS) main: $(OBJS) $(LIBS) $(SCENES)
$(CC) $(ARCH) $(LDFLAGS) $(FLAGS) $(OPT) $(DEBUG) $^ -o $@ $(CC) $(ARCH) $(LDFLAGS) $(FLAGS) $(OPT) $(DEBUG) $^ -o $@
%.spv: %.hlsl %.spv: %.hlsl

File diff suppressed because one or more lines are too long

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,3 @@
namespace shadow_test {
extern collada::types::descriptor const descriptor;
}

Binary file not shown.

View File

Binary file not shown.

View File

@ -1,4 +1,7 @@
shader/triangle.spv shader/triangle.spv
shader/collada.spv
data/scenes/shadow_test/shadow_test.vtx
data/scenes/shadow_test/shadow_test.idx
checker.idx checker.idx
checker.vtx checker.vtx
checker.data checker.data

54
include/check.h Normal file
View File

@ -0,0 +1,54 @@
#pragma once
#define SDL_CHECK(f) \
{ \
bool result = (f); \
if (result != true) { \
fprintf(stderr, "SDL: %s %s L%d error: `%s`\n", __FILE__, __func__, __LINE__, SDL_GetError()); \
exit(EXIT_FAILURE); \
} \
}
#define SDL_CHECK_NONNULL(f) \
{ \
void * ptr = (void *)(f); \
if (ptr == nullptr) { \
fprintf(stderr, "SDL: %s %s L%d error: `%s`\n", __FILE__, __func__, __LINE__, SDL_GetError()); \
exit(EXIT_FAILURE); \
} \
}
#define VK_CHECK(f) \
{ \
VkResult result = (f); \
if (result != VK_SUCCESS) { \
fprintf(stderr, "VK: %s %s L%d error: `%s`\n", __FILE__, __func__, __LINE__, string_VkResult(result)); \
exit(EXIT_FAILURE); \
} \
}
#define VK_CHECK_SWAPCHAIN(f) \
{ \
VkResult result = (f); \
if (result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR) { \
updateSwapchain = true; \
} else if (result != VK_SUCCESS) { \
fprintf(stderr, "VK: %s %s L%d error: `%s`\n", __FILE__, __func__, __LINE__, string_VkResult(result)); \
exit(EXIT_FAILURE); \
} \
}
#define ASSERT(expr, msg) \
{ \
bool result = (expr); \
if (result != true) { \
fprintf(stderr, "%s %s L%d error: `%s`\n", __FILE__, __func__, __LINE__, msg); \
exit(EXIT_FAILURE); \
} \
}
#if defined(_MSC_VER) && !defined(__clang__) // MSVC
#define UNREACHABLE() __assume(false);
#else // GCC, Clang
#define UNREACHABLE() __builtin_unreachable();
#endif

38
include/collada/inputs.h Normal file
View File

@ -0,0 +1,38 @@
#pragma once
#include "collada/types.h"
namespace collada::inputs {
inline static uint32_t format_size(types::input_format format)
{
switch (format) {
case types::input_format::FLOAT1: return 1 * 4;
case types::input_format::FLOAT2: return 2 * 4;
case types::input_format::FLOAT3: return 3 * 4;
case types::input_format::FLOAT4: return 4 * 4;
case types::input_format::INT1: return 1 * 4;
case types::input_format::INT2: return 2 * 4;
case types::input_format::INT3: return 3 * 4;
case types::input_format::INT4: return 4 * 4;
default: assert(false);
}
}
static types::input_element const input_elements_blendindices_0_4_blendweight_0_4[] = {
{
.semantic = "BLENDINDICES",
.semantic_index = 0,
.format = types::input_format::INT4,
},
{
.semantic = "BLENDWEIGHT",
.semantic_index = 0,
.format = types::input_format::FLOAT4,
},
};
static types::inputs const skin_inputs = {
.elements = input_elements_blendindices_0_4_blendweight_0_4,
.elements_count = 2,
};
}

View File

@ -0,0 +1,32 @@
#pragma once
#include "directxmath/directxmath.h"
#include "collada/types.h"
namespace collada::instance_types {
struct XM_ALIGNED_DATA(16) lookat {
XMVECTOR eye;
XMVECTOR at;
XMVECTOR up;
};
struct XM_ALIGNED_DATA(16) transform {
union {
instance_types::lookat lookat;
XMMATRIX matrix;
XMVECTOR vector;
};
types::transform_type type;
};
struct node {
// immutable state
types::node const * node;
// mutable state
transform * transforms;
XMMATRIX world;
};
}

View File

@ -0,0 +1,13 @@
#pragma once
#include "collada/types.h"
#include "collada/instance_types.h"
namespace collada::node_state {
struct state {
instance_types::node * node_instances;
void allocate_node_instances(types::node const * const * const nodes, int nodes_count);
void update_node_world_transform(instance_types::node & node_instance);
};
};

23
include/collada/scene.h Normal file
View File

@ -0,0 +1,23 @@
#pragma once
#include "collada/types.h"
#include "collada/instance_types.h"
#include "collada/node_state.h"
#include "collada/scene/vulkan.h"
namespace collada::scene {
struct state {
types::descriptor const * descriptor;
node_state::state node_state;
collada::scene::vulkan vulkan;
void load_scene(types::descriptor const * const descriptor);
void draw();
void update(XMMATRIX const & projection,
XMMATRIX const & view,
float t);
};
}

View File

@ -0,0 +1,96 @@
#pragma once
#include "volk/volk.h"
#include "collada/types.h"
#include "collada/instance_types.h"
#include "collada/scene/vulkan.h"
#include "shader_data.h"
namespace collada::scene {
struct vulkan {
// externally initialized, opaque handle
VkInstance instance;
VkDevice device;
VkPipelineLayout pipelineLayout;
VkDescriptorSetLayout descriptorSetLayout;
// externally initialized, structures
VkPhysicalDeviceProperties physicalDeviceProperties;
VkPhysicalDeviceMemoryProperties physicalDeviceMemoryProperties;
// externally initialized, enum
VkFormat colorFormat;
VkFormat depthFormat;
// externally initialized, pointers
ShaderData * shaderData;
ShaderDataDevice const * shaderDataDevice;
// method initialized
VkShaderModule shaderModule;
VkPipeline * pipelines;
struct {
VkDeviceSize indexOffset;
VkBuffer buffer;
VkDeviceMemory memory;
} vertexIndex;
// per-frame
VkCommandBuffer commandBuffer;
uint32_t frameIndex;
//////////////////////////////////////////////////////////////////////
// called directly
//////////////////////////////////////////////////////////////////////
void initial_state(VkInstance instance,
VkDevice device,
VkPipelineLayout pipelineLayout,
VkDescriptorSetLayout descriptorSetLayout,
VkPhysicalDeviceProperties const & physicalDeviceProperties,
VkPhysicalDeviceMemoryProperties const & physicalDeviceMemoryProperties,
VkFormat colorFormat,
VkFormat depthFormat,
ShaderData * shaderData,
ShaderDataDevice const * shaderDataDevice);
void per_frame_state(uint32_t frameIndex);
//////////////////////////////////////////////////////////////////////
// called by initial_state
//////////////////////////////////////////////////////////////////////
void load_shader();
//////////////////////////////////////////////////////////////////////
// called by state::load_scene
//////////////////////////////////////////////////////////////////////
void load_vertex_index_buffer(char const * vertex_filename,
char const * index_filename);
void create_pipelines(collada::types::descriptor const * const descriptor);
//////////////////////////////////////////////////////////////////////
// called by state::draw
//////////////////////////////////////////////////////////////////////
void draw_geometry(types::geometry const & geometry,
types::instance_material const * const instance_materials,
int const instance_materials_count);
void draw_instance_geometries(types::instance_geometry const * const instance_geometries,
int const instance_geometries_count);
void draw_node(int32_t node_index,
types::node const & node,
instance_types::node const & node_instance);
//////////////////////////////////////////////////////////////////////
// called by state::update
//////////////////////////////////////////////////////////////////////
void transfer_transforms(XMMATRIX const & projection,
XMMATRIX const & view,
int nodes_count,
instance_types::node const * const node_instances);
};
}

410
include/collada/types.h Normal file
View File

@ -0,0 +1,410 @@
#pragma once
namespace collada::types {
struct float2 {
float const x;
float const y;
};
struct float3 {
float const x;
float const y;
float const z;
};
struct float4 {
float const x;
float const y;
float const z;
float const w;
};
struct float7 {
float const a;
float const b;
float const c;
float const d;
float const e;
float const f;
float const g;
};
struct matrix {
float const _11, _12, _13, _14;
float const _21, _22, _23, _24;
float const _31, _32, _33, _34;
float const _41, _42, _43, _44;
};
//////////////////////////////////////////////////////////////////////
// geometry
//////////////////////////////////////////////////////////////////////
enum class input_format {
FLOAT1,
FLOAT2,
FLOAT3,
FLOAT4,
INT1,
INT2,
INT3,
INT4,
};
struct input_element {
char const * const semantic;
int const semantic_index;
enum input_format const format;
};
// inputs uniqueness is by evaluted pointer
struct inputs {
input_element const * const elements;
int const elements_count;
};
struct triangles {
int const count;
int const index_offset;
int const inputs_index;
};
struct mesh {
// `triangles` must become a union if non-triangles are implemented.
// instance_geometry is an index into this array.
types::triangles const * triangles;
int const triangles_count;
int const vertex_buffer_offset;
int const vertex_buffer_size;
int const index_buffer_offset;
int const index_buffer_size;
};
struct geometry {
types::mesh mesh;
};
//////////////////////////////////////////////////////////////////////
// light
//////////////////////////////////////////////////////////////////////
enum class light_type {
AMBIENT,
DIRECTIONAL,
POINT,
SPOT,
};
struct light {
light_type type;
float3 color;
};
//////////////////////////////////////////////////////////////////////
// image
//////////////////////////////////////////////////////////////////////
struct image {
const char * uri;
};
//////////////////////////////////////////////////////////////////////
// effect
//////////////////////////////////////////////////////////////////////
enum class color_or_texture_type {
COLOR,
TEXTURE,
};
struct texture {
int const image_index; // index in to images
};
struct color_or_texture {
color_or_texture_type type;
union {
float4 color;
types::texture texture;
};
};
struct blinn {
color_or_texture const emission;
color_or_texture const ambient;
color_or_texture const diffuse;
color_or_texture const specular;
float const shininess;
color_or_texture const reflective;
float const reflectivity;
color_or_texture const transparent;
float const transparency;
float const index_of_refraction;
};
struct lambert {
color_or_texture const emission;
color_or_texture const ambient;
color_or_texture const diffuse;
color_or_texture const reflective;
float const reflectivity;
color_or_texture const transparent;
float const transparency;
float const index_of_refraction;
};
struct phong {
color_or_texture const emission;
color_or_texture const ambient;
color_or_texture const diffuse;
color_or_texture const specular;
float const shininess;
color_or_texture const reflective;
float const reflectivity;
color_or_texture const transparent;
float const transparency;
float const index_of_refraction;
};
struct constant {
float4 const color;
color_or_texture const reflective;
float const reflectivity;
color_or_texture const transparent;
float const transparency;
float const index_of_refraction;
};
enum class effect_type {
BLINN,
LAMBERT,
PHONG,
CONSTANT,
};
struct effect {
effect_type const type;
union {
types::blinn const blinn;
types::lambert const lambert;
types::phong const phong;
types::constant const constant;
};
};
//////////////////////////////////////////////////////////////////////
// node
//////////////////////////////////////////////////////////////////////
struct lookat {
float3 const eye;
float3 const at;
float3 const up;
};
enum class transform_type {
LOOKAT,
MATRIX,
ROTATE,
SCALE,
SKEW,
TRANSLATE,
};
struct transform {
transform_type const type;
union {
types::lookat const lookat;
types::matrix const matrix;
float4 const rotate;
float3 const scale;
float7 const skew;
float3 const translate;
};
};
enum class node_type {
JOINT,
NODE,
};
struct material {
types::effect const * const effect;
};
struct bind_vertex_input {
int input_set; // TEXCOORD semantic input slot
};
struct instance_material {
int const element_index; // an index into mesh.triangles
types::material const * const material;
// heavily simplified from collada data model
bind_vertex_input const emission;
bind_vertex_input const ambient;
bind_vertex_input const diffuse;
bind_vertex_input const specular;
};
struct instance_geometry {
types::geometry const * const geometry;
instance_material const * const instance_materials;
int const instance_materials_count;
};
struct skin {
types::geometry const * const geometry; // source
matrix const bind_shape_matrix; // one per skin
matrix const * const inverse_bind_matrices; // one per joint
int const vertex_buffer_offset;
int const vertex_buffer_size;
};
struct controller {
types::skin skin;
};
struct instance_controller {
types::controller const * const controller;
//node const * const skeleton; // a reference to the root of the joint heirarchy
int const * const joint_node_indices; // one per joint
int const joint_count;
instance_material const * const instance_materials;
int const instance_materials_count;
};
struct instance_light {
types::light const * const light;
};
//////////////////////////////////////////////////////////////////////
// animation
//////////////////////////////////////////////////////////////////////
enum class interpolation {
LINEAR,
BEZIER,
};
struct source {
union {
float const * const float_array;
enum interpolation const * const interpolation_array;
};
int const count;
int const stride;
};
struct sampler {
source const input;
source const output;
source const in_tangent;
source const out_tangent;
source const interpolation;
};
enum class target_attribute {
A, // alpha color component
ANGLE, // euler angle
B, // blue color component
G, // green color component
P, // third texture component
Q, // fourth texture component
R, // red color component
S, // first texture coordinate
T, // second texture coordinate
TIME, // time in seconds
U, // first generic parameter
V, // second generic parameter
W, // fourth cartesian coordinate
X, // first cartesian coordinate
Y, // second cartesian coordinate
Z, // third cartesian coordinate
ALL,
};
struct channel {
sampler const * const source_sampler;
int const target_node_index; // an index into the nodes array
int const target_transform_index;
types::target_attribute const target_attribute;
};
/*
struct animation {
animation const * const animations; // nested animations
int const animations_count;
channels const * const channels;
int const channels_count;
};
*/
struct camera {
float xfov;
float yfov;
float znear;
float zfar;
float aspect_ratio;
};
//////////////////////////////////////////////////////////////////////
// scene
//////////////////////////////////////////////////////////////////////
struct node {
char const * name;
int const parent_index;
node_type const type;
transform const * const transforms;
int const transforms_count;
instance_geometry const * const instance_geometries;
int const instance_geometries_count;
instance_controller const * const instance_controllers;
int const instance_controllers_count;
instance_light const * const instance_lights;
int const instance_lights_count;
channel const * const * const channels;
int const channels_count;
//node const * const * const nodes;
//int const nodes_count;
};
struct descriptor {
// these are only the top-level nodes; nodes may have children
node const * const * const nodes;
int const nodes_count;
inputs const * const inputs_list;
int const inputs_list_count;
image const * const * const images;
int const images_count;
//animation const * const animations;
//int const animations_count;
// hmm, this part is not really platform-agnostic:
char const * const position_normal_texture_buffer;
char const * const joint_weight_buffer;
char const * const index_buffer;
};
}

21
include/shader_data.h Normal file
View File

@ -0,0 +1,21 @@
#pragma once
#include "directxmath/directxmath.h"
constexpr uint32_t maxFramesInFlight{ 2 };
struct ShaderData {
XMFLOAT4X4 projection;
XMFLOAT4X4 modelView[16];
XMFLOAT4 lightPosition;
};
struct ShaderDataDevice {
VkDeviceMemory memory;
VkDeviceAddress stride;
void * mappedData;
struct {
VkBuffer buffer{ VK_NULL_HANDLE };
VkDeviceAddress deviceAddress{};
} frame[maxFramesInFlight];
};

18
include/vulkan_helper.h Normal file
View File

@ -0,0 +1,18 @@
#pragma once
#include <assert.h>
inline static constexpr VkDeviceSize roundAlignment(VkDeviceSize offset, VkDeviceSize alignment)
{
// must be a power of two
assert(alignment && ((alignment & (alignment - 1)) == 0));
return (offset + (alignment - 1)) & (-alignment);
}
VkDeviceSize allocateFromMemoryRequirements(VkDevice device,
VkPhysicalDeviceMemoryProperties const & physicalDeviceMemoryProperties,
VkMemoryRequirements const & memoryRequirements,
VkMemoryPropertyFlags memoryPropertyFlags,
VkMemoryAllocateFlags memoryAllocateFlags,
uint32_t count,
VkDeviceMemory * memory);

66
shader/collada.hlsl Normal file
View File

@ -0,0 +1,66 @@
struct VSInput
{
float3 Position : POSITION0;
float3 Normal : NORMAL0;
float3 Texture : TEXCOORD0;
};
struct VSOutput
{
float4 Position : SV_POSITION;
float3 Normal : NORMAL0;
float2 Texture : TEXCOORD0;
float3 LightDirection : NORMAL1;
float3 ViewDirection : NORMAL2;
};
struct ShaderData
{
column_major float4x4 Projection;
column_major float4x4 ModelView[16];
float4 LightPosition; // view space
};
[[vk::binding(0, 0)]] ConstantBuffer<ShaderData> data;
struct PushConstant {
int ModelViewIndex;
};
[[vk::push_constant]]
struct PushConstant constants;
[shader("vertex")]
VSOutput VSMain(VSInput input)
{
float4x4 modelView = data.ModelView[constants.ModelViewIndex];
VSOutput output = (VSOutput)0;
output.Position = mul(data.Projection, mul(modelView, float4(input.Position.xyz, 1.0))) * float4(-1, -1, 1, 1);
output.Normal = mul((float3x3)modelView, input.Normal);
output.Texture = input.Texture.xy * 1.0;
float4 viewPosition = mul(modelView, float4(input.Position.xyz, 1.0));
output.LightDirection = (data.LightPosition - viewPosition).xyz;
output.ViewDirection = -viewPosition.xyz;
return output;
}
[shader("pixel")]
float4 PSMain(VSOutput input) : SV_TARGET
{
//float3 color = texture.Sample(samplers[0], input.Texture).bgr;
float3 N = normalize(input.Normal);
float3 L = normalize(input.LightDirection);
float3 V = normalize(input.ViewDirection);
float3 R = reflect(-L, N);
const float a = 16.0;
const float specularIntensity = 0.8;
float3 specular = pow(max(dot(R, V), 0), a) * specularIntensity;
float3 diffuse = max(dot(N, L), 0.001);
return float4(diffuse + specular, 1.0);
}

117
src/collada/node_state.cpp Normal file
View File

@ -0,0 +1,117 @@
#include <stdio.h>
#include "directxmath/directxmath.h"
#include "new.h"
#include "collada/node_state.h"
namespace collada::node_state {
//////////////////////////////////////////////////////////////////////
// transforms
//////////////////////////////////////////////////////////////////////
inline static void load_transform(types::transform const & transform,
instance_types::transform * instance_transform)
{
switch (transform.type) {
case types::transform_type::LOOKAT:
instance_transform->lookat.eye = XMLoadFloat3((XMFLOAT3 *)&transform.lookat.eye);
instance_transform->lookat.at = XMLoadFloat3((XMFLOAT3 *)&transform.lookat.at);
instance_transform->lookat.up = XMLoadFloat3((XMFLOAT3 *)&transform.lookat.up);
break;
case types::transform_type::MATRIX:
instance_transform->matrix = XMMatrixTranspose(XMLoadFloat4x4((XMFLOAT4X4 *)&transform.matrix));
break;
case types::transform_type::ROTATE:
instance_transform->vector = XMLoadFloat4((XMFLOAT4 *)&transform.rotate);
break;
case types::transform_type::SCALE:
instance_transform->vector = XMLoadFloat3((XMFLOAT3*)&transform.scale);
break;
case types::transform_type::TRANSLATE:
instance_transform->vector = XMLoadFloat3((XMFLOAT3*)&transform.translate);
break;
default:
assert(false);
}
instance_transform->type = transform.type;
}
inline static void initialize_node_transforms(instance_types::node & node_instance)
{
for (int i = 0; i < node_instance.node->transforms_count; i++) {
load_transform(node_instance.node->transforms[i], &node_instance.transforms[i]);
}
}
inline static void allocate_node_instance(instance_types::node & node_instance,
types::node const * const node)
{
node_instance.node = node;
node_instance.transforms = NewM<instance_types::transform>(node->transforms_count);
initialize_node_transforms(node_instance);
}
void state::allocate_node_instances(types::node const * const * const nodes, int nodes_count)
{
node_instances = NewM<instance_types::node>(nodes_count);
for (int i = 0; i < nodes_count; i++) {
allocate_node_instance(node_instances[i], nodes[i]);
}
for (int i = 0; i < nodes_count; i++) {
update_node_world_transform(node_instances[i]);
}
}
//////////////////////////////////////////////////////////////////////
// world matrix
//////////////////////////////////////////////////////////////////////
inline static bool vector_equal(XMVECTOR V1, XMVECTOR V2)
{
uint32_t CR;
XMVectorEqualR(&CR, V1, V2);
return XMComparisonAllTrue(CR);
}
inline static XMMATRIX transform_matrix(instance_types::transform const& transform)
{
switch (transform.type) {
case types::transform_type::TRANSLATE:
return XMMatrixTranslationFromVector(transform.vector);
case types::transform_type::ROTATE:
//assert(!vector_equal(XMVectorSetW(transform.vector, 0), XMVectorZero()));
if (vector_equal(XMVectorSetW(transform.vector, 0), XMVectorZero()))
return XMMatrixIdentity();
return XMMatrixRotationNormal(transform.vector,
XMConvertToRadians(XMVectorGetW(transform.vector)));
case types::transform_type::SCALE:
return XMMatrixScalingFromVector(transform.vector);
case types::transform_type::MATRIX:
return transform.matrix;
default:
fprintf(stderr, "unknown transform type %d\n", (int)transform.type);
assert(false);
break;
}
}
void state::update_node_world_transform(instance_types::node & node_instance)
{
XMMATRIX world;
if (node_instance.node->parent_index >= 0)
world = node_instances[node_instance.node->parent_index].world;
else
world = XMMatrixIdentity();
for (int i = 0; i < node_instance.node->transforms_count; i++) {
world = transform_matrix(node_instance.transforms[i]) * world;
}
node_instance.world = world;
}
}

50
src/collada/scene.cpp Normal file
View File

@ -0,0 +1,50 @@
#include "collada/scene.h"
#include <stdio.h>
namespace collada::scene {
void state::load_scene(types::descriptor const * const descriptor)
{
this->descriptor = descriptor;
vulkan.create_pipelines(descriptor);
vulkan.load_vertex_index_buffer(descriptor->position_normal_texture_buffer,
descriptor->index_buffer);
node_state.allocate_node_instances(descriptor->nodes, descriptor->nodes_count);
}
void state::draw()
{
for (int i = 0; i < descriptor->nodes_count; i++) {
types::node const & node = *descriptor->nodes[i];
instance_types::node const & node_instance = node_state.node_instances[i];
if (node.instance_geometries_count <= 0)
continue;
vulkan.draw_node(i,
node,
node_instance);
}
}
void state::update(XMMATRIX const & projection,
XMMATRIX const & view,
float t)
{
//t = animate::loop(t / 1.0f, 1.0f);
for (int i = 0; i < descriptor->nodes_count; i++) {
//animate::animate_node(node_state.node_instances[i], t);
node_state.update_node_world_transform(node_state.node_instances[i]);
}
vulkan.transfer_transforms(projection,
view,
descriptor->nodes_count,
node_state.node_instances);
}
}

View File

@ -0,0 +1,426 @@
#include <string.h>
#include <stdio.h>
#include <stdint.h>
#include <assert.h>
#include "volk/volk.h"
#include "vulkan/vk_enum_string_helper.h"
#include "collada/inputs.h"
#include "collada/scene/vulkan.h"
#include "vulkan_helper.h"
#include "check.h"
#include "new.h"
#include "file.h"
inline static uint32_t vulkan_semantic_location(char const * const semantic, int semantic_index)
{
if (strcmp(semantic, "POSITION") == 0 && semantic_index == 0) {
return 0;
}
if (strcmp(semantic, "NORMAL") == 0 && semantic_index == 0) {
return 1;
}
if (strcmp(semantic, "TEXCOORD") == 0 && semantic_index == 0) {
return 2;
}
if (strcmp(semantic, "BLENDINDICES") == 0 && semantic_index == 0) {
return 3;
}
if (strcmp(semantic, "BLENDWEIGHT") == 0 && semantic_index == 0) {
return 4;
}
fprintf(stderr, "unknown semantic %s index %d\n", semantic, semantic_index);
assert(false);
}
inline static VkFormat vulkan_format(collada::types::input_format format)
{
switch (format) {
case collada::types::input_format::FLOAT1: return VK_FORMAT_R32_SFLOAT;
case collada::types::input_format::FLOAT2: return VK_FORMAT_R32G32_SFLOAT;
case collada::types::input_format::FLOAT3: return VK_FORMAT_R32G32B32_SFLOAT;
case collada::types::input_format::FLOAT4: return VK_FORMAT_R32G32B32A32_SFLOAT;
case collada::types::input_format::INT1: return VK_FORMAT_R32_SINT;
case collada::types::input_format::INT2: return VK_FORMAT_R32G32_SINT;
case collada::types::input_format::INT3: return VK_FORMAT_R32G32B32_SINT;
case collada::types::input_format::INT4: return VK_FORMAT_R32G32B32A32_SINT;
default: assert(false);
}
}
inline static uint32_t vulkan_load_layout(collada::types::inputs const & inputs,
uint32_t binding,
uint32_t start_offset,
VkVertexInputAttributeDescription * vertexAttributeDescriptions)
{
uint32_t offset = start_offset;
for (int i = 0; i < inputs.elements_count; i++) {
uint32_t location = vulkan_semantic_location(inputs.elements[i].semantic, inputs.elements[i].semantic_index);
VkFormat format = vulkan_format(inputs.elements[i].format);
vertexAttributeDescriptions[i].location = location;
vertexAttributeDescriptions[i].binding = binding;
vertexAttributeDescriptions[i].format = format;
vertexAttributeDescriptions[i].offset = offset;
offset += collada::inputs::format_size(inputs.elements[i].format);
}
return offset;
}
inline static void vulkan_vertex_input_states(collada::types::descriptor const * const descriptor,
VkPipelineVertexInputStateCreateInfo * vertexInputStates,
VkVertexInputBindingDescription * vertexBindingDescriptions)
{
for (int i = 0; i < descriptor->inputs_list_count; i++) {
collada::types::inputs const & inputs = descriptor->inputs_list[i];
VkVertexInputAttributeDescription * vertexAttributeDescriptions = NewM<VkVertexInputAttributeDescription>(inputs.elements_count);
uint32_t stride = vulkan_load_layout(inputs,
0, // binding
0, // start_offset
vertexAttributeDescriptions);
vertexBindingDescriptions[i] = {
.binding = 0,
.stride = stride,
.inputRate = VK_VERTEX_INPUT_RATE_VERTEX
};
vertexInputStates[i] = {
.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO,
.vertexBindingDescriptionCount = 1,
.pVertexBindingDescriptions = &vertexBindingDescriptions[i],
.vertexAttributeDescriptionCount = (uint32_t)inputs.elements_count,
.pVertexAttributeDescriptions = vertexAttributeDescriptions,
};
}
}
namespace collada::scene {
void vulkan::initial_state(VkInstance instance,
VkDevice device,
VkPipelineLayout pipelineLayout,
VkDescriptorSetLayout descriptorSetLayout,
VkPhysicalDeviceProperties const & physicalDeviceProperties,
VkPhysicalDeviceMemoryProperties const & physicalDeviceMemoryProperties,
VkFormat colorFormat,
VkFormat depthFormat,
ShaderData * shaderData,
ShaderDataDevice const * shaderDataDevice)
{
this->instance = instance;
this->device = device;
this->pipelineLayout = pipelineLayout;
this->descriptorSetLayout = descriptorSetLayout;
this->physicalDeviceProperties = physicalDeviceProperties;
this->physicalDeviceMemoryProperties = physicalDeviceMemoryProperties;
this->colorFormat = colorFormat;
this->depthFormat = depthFormat;
this->shaderData = shaderData;
this->shaderDataDevice = shaderDataDevice;
load_shader();
}
//////////////////////////////////////////////////////////////////////
// vertex index buffer
//////////////////////////////////////////////////////////////////////
void vulkan::load_vertex_index_buffer(char const * vertex_filename,
char const * index_filename)
{
uint32_t vertexSize;
void const * vertexStart = file::open(vertex_filename, &vertexSize);
uint32_t indexSize;
void const * indexStart = file::open(index_filename, &indexSize);
vertexIndex.indexOffset = vertexSize; // + vertexJWStart;
// create buffer
VkDeviceSize bufferSize{ vertexSize + indexSize };
VkBufferCreateInfo vertexIndexBufferCreateInfo{
.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
.size = bufferSize,
.usage = VK_BUFFER_USAGE_VERTEX_BUFFER_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT,
.sharingMode = VK_SHARING_MODE_EXCLUSIVE
};
VK_CHECK(vkCreateBuffer(device, &vertexIndexBufferCreateInfo, nullptr, &vertexIndex.buffer));
// allocate memory
VkMemoryRequirements memoryRequirements;
vkGetBufferMemoryRequirements(device, vertexIndex.buffer, &memoryRequirements);
VkMemoryPropertyFlags memoryPropertyFlags{ VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT };
VkMemoryAllocateFlags memoryAllocateFlags{};
allocateFromMemoryRequirements(device,
physicalDeviceMemoryProperties,
memoryRequirements,
memoryPropertyFlags,
memoryAllocateFlags,
1,
&vertexIndex.memory);
VK_CHECK(vkBindBufferMemory(device, vertexIndex.buffer, vertexIndex.memory, 0));
// copy data
void * vertexIndexMappedData;
VK_CHECK(vkMapMemory(device, vertexIndex.memory, 0, vertexIndexBufferCreateInfo.size, 0, &vertexIndexMappedData));
memcpy((void *)(((ptrdiff_t)vertexIndexMappedData) + 0), vertexStart, vertexSize);
memcpy((void *)(((ptrdiff_t)vertexIndexMappedData) + vertexSize), indexStart, indexSize);
VkMappedMemoryRange mappedMemoryRange{
.sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE,
.memory = vertexIndex.memory,
.offset = 0,
.size = VK_WHOLE_SIZE,
};
vkFlushMappedMemoryRanges(device, 1, &mappedMemoryRange);
vkUnmapMemory(device, vertexIndex.memory);
}
//////////////////////////////////////////////////////////////////////
// shader
//////////////////////////////////////////////////////////////////////
void vulkan::load_shader()
{
uint32_t shaderSize;
void const * shaderStart = file::open("shader/collada.spv", &shaderSize);
VkShaderModuleCreateInfo shaderModuleCreateInfo{
.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO,
.codeSize = shaderSize,
.pCode = (uint32_t *)shaderStart
};
VK_CHECK(vkCreateShaderModule(device, &shaderModuleCreateInfo, nullptr, &shaderModule));
}
//////////////////////////////////////////////////////////////////////
// pipeline
//////////////////////////////////////////////////////////////////////
void vulkan::create_pipelines(collada::types::descriptor const * const descriptor)
{
VkPushConstantRange pushConstantRange{
.stageFlags = VK_SHADER_STAGE_VERTEX_BIT,
.size = (sizeof (int32_t))
};
VkPipelineLayoutCreateInfo pipelineLayoutCreateInfo{
.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO,
.setLayoutCount = 1,
.pSetLayouts = &descriptorSetLayout,
.pushConstantRangeCount = 1,
.pPushConstantRanges = &pushConstantRange
};
VK_CHECK(vkCreatePipelineLayout(device, &pipelineLayoutCreateInfo, nullptr, &pipelineLayout));
VkPipelineInputAssemblyStateCreateInfo inputAssemblyState{
.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO,
.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST
};
VkPipelineShaderStageCreateInfo shaderStages[2]{
{
.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
.stage = VK_SHADER_STAGE_VERTEX_BIT,
.module = shaderModule,
.pName = "VSMain"
},
{
.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO,
.stage = VK_SHADER_STAGE_FRAGMENT_BIT,
.module = shaderModule,
.pName = "PSMain"
}
};
VkPipelineViewportStateCreateInfo viewportState{
.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO,
.viewportCount = 1,
.scissorCount = 1
};
constexpr uint32_t dynamicStateCount = 2;
VkDynamicState dynamicStates[dynamicStateCount]{
VK_DYNAMIC_STATE_VIEWPORT,
VK_DYNAMIC_STATE_SCISSOR,
};
VkPipelineDynamicStateCreateInfo dynamicState{
.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO,
.dynamicStateCount = dynamicStateCount,
.pDynamicStates = dynamicStates
};
VkPipelineDepthStencilStateCreateInfo depthStencilState{
.sType = VK_STRUCTURE_TYPE_PIPELINE_DEPTH_STENCIL_STATE_CREATE_INFO,
.depthTestEnable = VK_TRUE,
.depthWriteEnable = VK_TRUE,
.depthCompareOp = VK_COMPARE_OP_LESS_OR_EQUAL,
.stencilTestEnable = VK_TRUE,
.front = {
.failOp = VK_STENCIL_OP_REPLACE,
.passOp = VK_STENCIL_OP_REPLACE,
.depthFailOp = VK_STENCIL_OP_REPLACE,
.compareOp = VK_COMPARE_OP_ALWAYS,
.compareMask = 0x01,
.writeMask = 0x01,
.reference = 1,
},
};
VkPipelineRenderingCreateInfo renderingCreateInfo{
.sType = VK_STRUCTURE_TYPE_PIPELINE_RENDERING_CREATE_INFO,
.colorAttachmentCount = 1,
.pColorAttachmentFormats = &colorFormat,
.depthAttachmentFormat = depthFormat,
.stencilAttachmentFormat = depthFormat
};
VkPipelineColorBlendAttachmentState blendAttachment{
.colorWriteMask = 0xF
};
VkPipelineColorBlendStateCreateInfo colorBlendState{
.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO,
.attachmentCount = 1,
.pAttachments = &blendAttachment
};
VkPipelineRasterizationStateCreateInfo rasterizationState{
.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO,
.cullMode = VK_CULL_MODE_BACK_BIT,
//.cullMode = VK_CULL_MODE_NONE,
.frontFace = VK_FRONT_FACE_COUNTER_CLOCKWISE,
.lineWidth = 1.0f
};
VkPipelineMultisampleStateCreateInfo multisampleState{
.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO,
.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT
};
VkPipelineVertexInputStateCreateInfo * vertexInputStates = NewM<VkPipelineVertexInputStateCreateInfo>(descriptor->inputs_list_count);
VkVertexInputBindingDescription * vertexBindingDescriptions = NewM<VkVertexInputBindingDescription>(descriptor->inputs_list_count);
vulkan_vertex_input_states(descriptor,
vertexInputStates,
vertexBindingDescriptions);
VkGraphicsPipelineCreateInfo * pipelineCreateInfos = NewM<VkGraphicsPipelineCreateInfo>(descriptor->inputs_list_count);
for (int i = 0; i < descriptor->inputs_list_count; i++) {
pipelineCreateInfos[i] = {
.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO,
.pNext = &renderingCreateInfo,
.stageCount = 2,
.pStages = shaderStages,
.pVertexInputState = &vertexInputStates[i],
.pInputAssemblyState = &inputAssemblyState,
.pViewportState = &viewportState,
.pRasterizationState = &rasterizationState,
.pMultisampleState = &multisampleState,
.pDepthStencilState = &depthStencilState,
.pColorBlendState = &colorBlendState,
.pDynamicState = &dynamicState,
.layout = pipelineLayout
};
};
pipelines = NewM<VkPipeline>(descriptor->inputs_list_count);
VK_CHECK(vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, descriptor->inputs_list_count, pipelineCreateInfos, nullptr, pipelines));
free(vertexBindingDescriptions);
for (int i = 0; i < descriptor->inputs_list_count; i++) {
free((void *)vertexInputStates[i].pVertexAttributeDescriptions);
}
free(vertexInputStates);
}
//////////////////////////////////////////////////////////////////////
// draw
//////////////////////////////////////////////////////////////////////
void vulkan::draw_geometry(types::geometry const & geometry,
types::instance_material const * const instance_materials,
int const instance_materials_count)
{
types::mesh const& mesh = geometry.mesh;
vkCmdBindIndexBuffer(commandBuffer, vertexIndex.buffer, vertexIndex.indexOffset + mesh.index_buffer_offset, VK_INDEX_TYPE_UINT32);
for (int j = 0; j < instance_materials_count; j++) {
types::instance_material const& instance_material = instance_materials[j];
types::triangles const& triangles = mesh.triangles[instance_material.element_index];
//set_instance_material(instance_material);
VkDeviceSize vertexOffset{ (VkDeviceSize)mesh.vertex_buffer_offset };
vkCmdBindVertexBuffers(commandBuffer, 0, 1, &vertexIndex.buffer, &vertexOffset);
vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelines[triangles.inputs_index]);
uint32_t indexCount = triangles.count * 3;
vkCmdDrawIndexed(commandBuffer, indexCount, 1, triangles.index_offset, 0, 0);
}
}
void vulkan::draw_instance_geometries(types::instance_geometry const * const instance_geometries,
int const instance_geometries_count)
{
for (int i = 0; i < instance_geometries_count; i++) {
types::instance_geometry const &instance_geometry = instance_geometries[i];
draw_geometry(*instance_geometry.geometry,
instance_geometry.instance_materials,
instance_geometry.instance_materials_count);
}
}
void vulkan::transfer_transforms(XMMATRIX const & projection,
XMMATRIX const & view,
int nodes_count,
instance_types::node const * const node_instances)
{
// store
XMStoreFloat4x4(&shaderData->projection, projection);
XMVECTOR lightPosition = XMVector3Transform(XMVectorSet(-42, -40, 156, 0), view);
XMStoreFloat4(&shaderData->lightPosition, lightPosition);
for (int i = 0; i < nodes_count; i++) {
XMMATRIX model_view = node_instances[i].world * view;
XMStoreFloat4x4(&shaderData->modelView[i], model_view);
}
// copy
size_t frameOffset = shaderDataDevice->stride * frameIndex;
void * frameData = (void *)(((VkDeviceSize)shaderDataDevice->mappedData) + frameOffset);
VkDeviceSize frameSize{ (sizeof (ShaderData)) };
memcpy(frameData, &shaderData->projection, frameSize);
// flush
VkDeviceSize flushSize{ roundAlignment(frameSize, physicalDeviceProperties.limits.nonCoherentAtomSize) };
VkMappedMemoryRange shaderDataMemoryRange{
.sType = VK_STRUCTURE_TYPE_MAPPED_MEMORY_RANGE,
.memory = shaderDataDevice->memory,
.offset = frameOffset,
.size = flushSize,
};
vkFlushMappedMemoryRanges(device, 1, &shaderDataMemoryRange);
}
void vulkan::draw_node(int32_t node_index,
types::node const & node,
instance_types::node const & node_instance)
{
vkCmdPushConstants(commandBuffer, pipelineLayout, VK_SHADER_STAGE_VERTEX_BIT, 0, (sizeof (int32_t)), &node_index);
draw_instance_geometries(node.instance_geometries, node.instance_geometries_count);
}
}

View File

@ -7,9 +7,17 @@
#include "SDL3/SDL_vulkan.h" #include "SDL3/SDL_vulkan.h"
#include "directxmath/directxmath.h" #include "directxmath/directxmath.h"
#include "check.h"
#include "new.h" #include "new.h"
#include "file.h" #include "file.h"
#include "dds_validate.h" #include "dds_validate.h"
#include "vulkan_helper.h"
#include "shader_data.h"
#include "collada/scene.h"
#include "collada/scene/vulkan.h"
#include "scenes/shadow_test/shadow_test.h"
template <typename T> template <typename T>
inline static constexpr T min(T a, T b) inline static constexpr T min(T a, T b)
@ -29,61 +37,6 @@ inline static constexpr T clamp(T n, T minVal, T maxVal)
return min(max(n, minVal), maxVal); return min(max(n, minVal), maxVal);
} }
#define SDL_CHECK(f) \
{ \
bool result = (f); \
if (result != true) { \
fprintf(stderr, "SDL: %s %s L%d error: `%s`\n", __FILE__, __func__, __LINE__, SDL_GetError()); \
exit(EXIT_FAILURE); \
} \
}
#define SDL_CHECK_NONNULL(f) \
{ \
void * ptr = (void *)(f); \
if (ptr == nullptr) { \
fprintf(stderr, "SDL: %s %s L%d error: `%s`\n", __FILE__, __func__, __LINE__, SDL_GetError()); \
exit(EXIT_FAILURE); \
} \
}
#define VK_CHECK(f) \
{ \
VkResult result = (f); \
if (result != VK_SUCCESS) { \
fprintf(stderr, "VK: %s %s L%d error: `%s`\n", __FILE__, __func__, __LINE__, string_VkResult(result)); \
exit(EXIT_FAILURE); \
} \
}
#define VK_CHECK_SWAPCHAIN(f) \
{ \
VkResult result = (f); \
if (result == VK_ERROR_OUT_OF_DATE_KHR || result == VK_SUBOPTIMAL_KHR) { \
updateSwapchain = true; \
} else if (result != VK_SUCCESS) { \
fprintf(stderr, "VK: %s %s L%d error: `%s`\n", __FILE__, __func__, __LINE__, string_VkResult(result)); \
exit(EXIT_FAILURE); \
} \
}
#define ASSERT(expr, msg) \
{ \
bool result = (expr); \
if (result != true) { \
fprintf(stderr, "%s %s L%d error: `%s`\n", __FILE__, __func__, __LINE__, msg); \
exit(EXIT_FAILURE); \
} \
}
#if defined(_MSC_VER) && !defined(__clang__) // MSVC
#define UNREACHABLE() __assume(false);
#else // GCC, Clang
#define UNREACHABLE() __builtin_unreachable();
#endif
constexpr uint32_t maxFramesInFlight{ 2 };
VkInstance instance{ VK_NULL_HANDLE }; VkInstance instance{ VK_NULL_HANDLE };
VkDevice device{ VK_NULL_HANDLE }; VkDevice device{ VK_NULL_HANDLE };
VkQueue queue{ VK_NULL_HANDLE }; VkQueue queue{ VK_NULL_HANDLE };
@ -131,25 +84,7 @@ VkDescriptorSet textureDescriptorSet{ VK_NULL_HANDLE };
XMINT2 windowSize{}; XMINT2 windowSize{};
struct ShaderData {
XMFLOAT4X4 transform;
XMFLOAT4X4 modelView;
XMFLOAT4 lightPosition;
uint32_t selected;
};
ShaderData shaderData{}; ShaderData shaderData{};
struct ShaderDataDevice {
VkDeviceMemory memory;
VkDeviceAddress stride;
void * mappedData;
struct {
VkBuffer buffer{ VK_NULL_HANDLE };
VkDeviceAddress deviceAddress{};
} frame[maxFramesInFlight];
};
ShaderDataDevice shaderDataDevice{}; ShaderDataDevice shaderDataDevice{};
void print_memoryPropertyFlags(VkMemoryPropertyFlags propertyFlags) void print_memoryPropertyFlags(VkMemoryPropertyFlags propertyFlags)
@ -167,48 +102,22 @@ void print_memoryPropertyFlags(VkMemoryPropertyFlags propertyFlags)
printf("\n"); printf("\n");
} }
uint32_t findMemoryTypeIndex(VkPhysicalDeviceMemoryProperties const & memoryProperties, uint32_t memoryTypeBits, VkMemoryPropertyFlags propertyFlags)
{
// find an exact match
for (uint32_t i = 0; i < memoryProperties.memoryTypeCount; i++) {
if (!(memoryTypeBits & (1u << i)))
continue;
if (memoryProperties.memoryTypes[i].propertyFlags == propertyFlags) {
return i;
}
}
// find a partial match
for (uint32_t i = 0; i < memoryProperties.memoryTypeCount; i++) {
if (!(memoryTypeBits & (1u << i)))
continue;
if ((memoryProperties.memoryTypes[i].propertyFlags & propertyFlags) == propertyFlags) {
return i;
}
}
ASSERT(false, "no memory type index matching memoryTypeBits and propertyFlags");
UNREACHABLE();
}
XMMATRIX currentProjection() XMMATRIX currentProjection()
{ {
float fov_angle_y = XMConvertToRadians(45 * 1.0); float fov_angle_y = XMConvertToRadians(45 * 1.0);
float aspect_ratio = (float)windowSize.x / (float)windowSize.y; float aspect_ratio = (float)windowSize.x / (float)windowSize.y;
float near_z = 0.1; float near_z = 0.1;
float far_z = 100.0; float far_z = 1000.0;
XMMATRIX projection = XMMatrixPerspectiveFovRH(fov_angle_y, aspect_ratio, near_z, far_z); XMMATRIX projection = XMMatrixPerspectiveFovLH(fov_angle_y, aspect_ratio, near_z, far_z);
return projection; return projection;
} }
XMMATRIX currentView() XMMATRIX currentView()
{ {
XMVECTOR eye = XMVectorSet(0, -3, 0, 0); XMVECTOR eye = XMVectorSet(-57, 159, 269, 0);
XMVECTOR at = XMVectorSet(0, 0, 0, 0); XMVECTOR at = XMVectorSet(0, 0, 0, 0);
XMVECTOR up = XMVectorSet(0, 0, 1, 0); XMVECTOR up = XMVectorSet(0, 0, 1, 0);
XMMATRIX view = XMMatrixLookAtRH(eye, at, up); XMMATRIX view = XMMatrixLookAtLH(eye, at, up);
return view; return view;
} }
@ -220,7 +129,7 @@ XMMATRIX currentModel()
return XMMatrixTranslation(0, 0, 0.0) * XMMatrixRotationX(theta) * XMMatrixRotationZ(XM_PI * 0.5f); return XMMatrixTranslation(0, 0, 0.0) * XMMatrixRotationX(theta) * XMMatrixRotationZ(XM_PI * 0.5f);
} }
void recreateSwapchain(VkSurfaceFormatKHR surfaceFormat, VkFormat depthFormat, VkPhysicalDeviceMemoryProperties const & memoryProperties, VkSurfaceCapabilitiesKHR const & surfaceCapabilities) void recreateSwapchain(VkSurfaceFormatKHR surfaceFormat, VkFormat depthFormat, VkPhysicalDeviceMemoryProperties const & physicalDeviceMemoryProperties, VkSurfaceCapabilitiesKHR const & surfaceCapabilities)
{ {
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
// swapchain and images // swapchain and images
@ -339,14 +248,14 @@ void recreateSwapchain(VkSurfaceFormatKHR surfaceFormat, VkFormat depthFormat, V
VkMemoryPropertyFlags depthImageMemoryPropertyFlags{ VkMemoryPropertyFlags depthImageMemoryPropertyFlags{
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT
}; };
uint32_t depthImageMemoryTypeIndex = findMemoryTypeIndex(memoryProperties, depthImageMemoryRequirements.memoryTypeBits, depthImageMemoryPropertyFlags); VkMemoryAllocateFlags depthImageMemoryAllocateFlags{ };
VkMemoryAllocateInfo depthImageAllocateInfo{ allocateFromMemoryRequirements(device,
.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, physicalDeviceMemoryProperties,
.allocationSize = depthImageMemoryRequirements.size, depthImageMemoryRequirements,
.memoryTypeIndex = depthImageMemoryTypeIndex, depthImageMemoryPropertyFlags,
}; depthImageMemoryAllocateFlags,
1,
VK_CHECK(vkAllocateMemory(device, &depthImageAllocateInfo, nullptr, &depthImageMemory)); &depthImageMemory);
VK_CHECK(vkBindImageMemory(device, depthImage, depthImageMemory, 0)); VK_CHECK(vkBindImageMemory(device, depthImage, depthImageMemory, 0));
VkImageViewCreateInfo depthViewCreateInfo{ VkImageViewCreateInfo depthViewCreateInfo{
@ -363,41 +272,6 @@ void recreateSwapchain(VkSurfaceFormatKHR surfaceFormat, VkFormat depthFormat, V
VK_CHECK(vkCreateImageView(device, &depthViewCreateInfo, nullptr, &depthImageView)); VK_CHECK(vkCreateImageView(device, &depthViewCreateInfo, nullptr, &depthImageView));
} }
inline static constexpr VkDeviceSize roundAlignment(VkDeviceSize offset, VkDeviceSize alignment)
{
// must be a power of two
assert(alignment && ((alignment & (alignment - 1)) == 0));
return (offset + (alignment - 1)) & (-alignment);
}
VkDeviceSize allocateFromMemoryRequirements(VkPhysicalDeviceMemoryProperties2 const & physicalDeviceMemoryProperties,
VkMemoryRequirements const & memoryRequirements,
VkMemoryPropertyFlags memoryPropertyFlags,
VkMemoryAllocateFlags memoryAllocateFlags,
uint32_t count,
VkDeviceMemory * memory)
{
uint32_t memoryTypeIndex = findMemoryTypeIndex(physicalDeviceMemoryProperties.memoryProperties,
memoryRequirements.memoryTypeBits,
memoryPropertyFlags);
VkDeviceSize stride = (count == 1) ? memoryRequirements.size : roundAlignment(memoryRequirements.size, memoryRequirements.alignment);
VkMemoryAllocateFlagsInfo memoryAllocateFlagsInfo{
.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_FLAGS_INFO,
.flags = memoryAllocateFlags,
};
VkMemoryAllocateInfo memoryAllocateInfo{
.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
.pNext = &memoryAllocateFlagsInfo,
.allocationSize = stride * count,
.memoryTypeIndex = memoryTypeIndex,
};
VK_CHECK(vkAllocateMemory(device, &memoryAllocateInfo, nullptr, memory));
return stride;
}
inline static int positive_modulo(int i, unsigned int n) { inline static int positive_modulo(int i, unsigned int n) {
return (i % n + n) % n; return (i % n + n) % n;
} }
@ -484,21 +358,21 @@ int main()
// memory // memory
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
VkPhysicalDeviceMemoryProperties2 physicalDeviceMemoryProperties{ VkPhysicalDeviceMemoryProperties2 physicalDeviceMemoryProperties2{
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_PROPERTIES_2, .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_MEMORY_PROPERTIES_2,
.pNext = nullptr .pNext = nullptr
}; };
vkGetPhysicalDeviceMemoryProperties2(physicalDevice, &physicalDeviceMemoryProperties); vkGetPhysicalDeviceMemoryProperties2(physicalDevice, &physicalDeviceMemoryProperties2);
VkPhysicalDeviceMemoryProperties const & physicalDeviceMemoryProperties = physicalDeviceMemoryProperties2.memoryProperties;
if constexpr (true) { if constexpr (true) {
VkPhysicalDeviceMemoryProperties const & memoryProperties = physicalDeviceMemoryProperties.memoryProperties; for (uint32_t i = 0; i < physicalDeviceMemoryProperties.memoryTypeCount; i++) {
for (uint32_t i = 0; i < memoryProperties.memoryTypeCount; i++) {
printf("memoryTypes[%u].propertyFlags: ", i); printf("memoryTypes[%u].propertyFlags: ", i);
print_memoryPropertyFlags(memoryProperties.memoryTypes[i].propertyFlags); print_memoryPropertyFlags(physicalDeviceMemoryProperties.memoryTypes[i].propertyFlags);
printf("memoryTypes[%u].heapIndex: %u\n", i, memoryProperties.memoryTypes[i].heapIndex); printf("memoryTypes[%u].heapIndex: %u\n", i, physicalDeviceMemoryProperties.memoryTypes[i].heapIndex);
} }
for (uint32_t i = 0; i < memoryProperties.memoryHeapCount; i++) { for (uint32_t i = 0; i < physicalDeviceMemoryProperties.memoryHeapCount; i++) {
printf("memoryHeaps[%u].size %lu\n", i, memoryProperties.memoryHeaps[i].size); printf("memoryHeaps[%u].size %lu\n", i, physicalDeviceMemoryProperties.memoryHeaps[i].size);
printf("memoryHeaps[%u].flags %08x\n", i, memoryProperties.memoryHeaps[i].flags); printf("memoryHeaps[%u].flags %08x\n", i, physicalDeviceMemoryProperties.memoryHeaps[i].flags);
} }
} }
@ -585,7 +459,7 @@ int main()
ASSERT(depthFormat != VK_FORMAT_UNDEFINED, "no depth format with VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT"); ASSERT(depthFormat != VK_FORMAT_UNDEFINED, "no depth format with VK_FORMAT_FEATURE_DEPTH_STENCIL_ATTACHMENT_BIT");
printf("depthFormat: %s\n", string_VkFormat(depthFormat)); printf("depthFormat: %s\n", string_VkFormat(depthFormat));
recreateSwapchain(surfaceFormat, depthFormat, physicalDeviceMemoryProperties.memoryProperties, surfaceCapabilities); recreateSwapchain(surfaceFormat, depthFormat, physicalDeviceMemoryProperties, surfaceCapabilities);
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
// mesh // mesh
@ -613,7 +487,8 @@ int main()
VkMemoryPropertyFlags memoryPropertyFlags{ VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT }; VkMemoryPropertyFlags memoryPropertyFlags{ VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT };
VkMemoryAllocateFlags memoryAllocateFlags{}; VkMemoryAllocateFlags memoryAllocateFlags{};
allocateFromMemoryRequirements(physicalDeviceMemoryProperties, allocateFromMemoryRequirements(device,
physicalDeviceMemoryProperties,
memoryRequirements, memoryRequirements,
memoryPropertyFlags, memoryPropertyFlags,
memoryAllocateFlags, memoryAllocateFlags,
@ -659,7 +534,8 @@ int main()
VkMemoryPropertyFlags memoryPropertyFlags{ VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT }; VkMemoryPropertyFlags memoryPropertyFlags{ VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT | VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT };
VkMemoryAllocateFlags memoryAllocateFlags{ }; VkMemoryAllocateFlags memoryAllocateFlags{ };
shaderDataDevice.stride = allocateFromMemoryRequirements(physicalDeviceMemoryProperties, shaderDataDevice.stride = allocateFromMemoryRequirements(device,
physicalDeviceMemoryProperties,
memoryRequirements, memoryRequirements,
memoryPropertyFlags, memoryPropertyFlags,
memoryAllocateFlags, memoryAllocateFlags,
@ -748,15 +624,14 @@ int main()
VkMemoryPropertyFlags textureImageMemoryPropertyFlags{ VkMemoryPropertyFlags textureImageMemoryPropertyFlags{
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT
}; };
uint32_t textureImageMemoryTypeIndex = findMemoryTypeIndex(physicalDeviceMemoryProperties.memoryProperties, textureImageMemoryRequirements.memoryTypeBits, textureImageMemoryPropertyFlags); VkMemoryAllocateFlags textureImageMemoryAllocateFlags{ };
allocateFromMemoryRequirements(device,
VkMemoryAllocateInfo textureImageAllocateInfo{ physicalDeviceMemoryProperties,
.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO, textureImageMemoryRequirements,
.allocationSize = textureImageMemoryRequirements.size, textureImageMemoryPropertyFlags,
.memoryTypeIndex = textureImageMemoryTypeIndex, textureImageMemoryAllocateFlags,
}; 1,
&textureImageMemory);
VK_CHECK(vkAllocateMemory(device, &textureImageAllocateInfo, nullptr, &textureImageMemory));
VK_CHECK(vkBindImageMemory(device, textureImage, textureImageMemory, 0)); VK_CHECK(vkBindImageMemory(device, textureImage, textureImageMemory, 0));
VkImageViewCreateInfo textureViewCreateInfo{ VkImageViewCreateInfo textureViewCreateInfo{
@ -786,14 +661,15 @@ int main()
VkMemoryPropertyFlags textureSourceBufferMemoryPropertyFlags{ VkMemoryPropertyFlags textureSourceBufferMemoryPropertyFlags{
VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT
}; };
uint32_t textureSourceBufferMemoryTypeIndex = findMemoryTypeIndex(physicalDeviceMemoryProperties.memoryProperties, textureSourceBufferMemoryRequirements.memoryTypeBits, textureSourceBufferMemoryPropertyFlags); VkMemoryAllocateFlags textureSourceBufferMemoryAllocateFlags{ };
VkMemoryAllocateInfo textureSourceBufferAllocateInfo{
.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
.allocationSize = textureSourceBufferMemoryRequirements.size,
.memoryTypeIndex = textureSourceBufferMemoryTypeIndex,
};
VkDeviceMemory textureSourceBufferMemory; VkDeviceMemory textureSourceBufferMemory;
VK_CHECK(vkAllocateMemory(device, &textureSourceBufferAllocateInfo, nullptr, &textureSourceBufferMemory)); allocateFromMemoryRequirements(device,
physicalDeviceMemoryProperties,
textureSourceBufferMemoryRequirements,
textureSourceBufferMemoryPropertyFlags,
textureSourceBufferMemoryAllocateFlags,
1,
&textureSourceBufferMemory);
VK_CHECK(vkBindBufferMemory(device, textureSourceBuffer, textureSourceBufferMemory, 0)); VK_CHECK(vkBindBufferMemory(device, textureSourceBuffer, textureSourceBufferMemory, 0));
void * textureSourceMappedData; void * textureSourceMappedData;
@ -1290,6 +1166,25 @@ int main()
}; };
VK_CHECK(vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 2, pipelineCreateInfos, nullptr, pipelines)); VK_CHECK(vkCreateGraphicsPipelines(device, VK_NULL_HANDLE, 2, pipelineCreateInfos, nullptr, pipelines));
//////////////////////////////////////////////////////////////////////
// initialize collada
//////////////////////////////////////////////////////////////////////
collada::scene::state collada_state;
collada_state.vulkan.initial_state(instance,
device,
pipelineLayout,
uniformBufferDescriptorSetLayout,
physicalDeviceProperties,
physicalDeviceMemoryProperties,
surfaceFormat.format,
depthFormat,
&shaderData,
&shaderDataDevice);
collada_state.load_scene(&shadow_test::descriptor);
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
// loop // loop
////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////
@ -1330,6 +1225,8 @@ int main()
} }
// shader data // shader data
/*
XMMATRIX model = currentModel(); XMMATRIX model = currentModel();
XMMATRIX view = currentView(); XMMATRIX view = currentView();
XMMATRIX modelView = model * view; XMMATRIX modelView = model * view;
@ -1351,7 +1248,7 @@ int main()
.size = flushSize, .size = flushSize,
}; };
vkFlushMappedMemoryRanges(device, 1, &shaderDataMemoryRange); vkFlushMappedMemoryRanges(device, 1, &shaderDataMemoryRange);
*/
// wait for fence // wait for fence
VK_CHECK(vkWaitForFences(device, 1, &fences[frameIndex], true, UINT64_MAX)); VK_CHECK(vkWaitForFences(device, 1, &fences[frameIndex], true, UINT64_MAX));
@ -1437,6 +1334,8 @@ int main()
vkCmdBeginRendering(commandBuffer, &renderingInfo); vkCmdBeginRendering(commandBuffer, &renderingInfo);
VkViewport viewport{ VkViewport viewport{
.x = 0,
.y = 0,//static_cast<float>(windowSize.y),
.width = static_cast<float>(windowSize.x), .width = static_cast<float>(windowSize.x),
.height = static_cast<float>(windowSize.y), .height = static_cast<float>(windowSize.y),
.minDepth = 0.0f, .minDepth = 0.0f,
@ -1446,12 +1345,13 @@ int main()
VkRect2D scissor{ .extent{ .width = (uint32_t)windowSize.x, .height = (uint32_t)windowSize.y } }; VkRect2D scissor{ .extent{ .width = (uint32_t)windowSize.x, .height = (uint32_t)windowSize.y } };
vkCmdSetScissor(commandBuffer, 0, 1, &scissor); vkCmdSetScissor(commandBuffer, 0, 1, &scissor);
VkDeviceSize vertexOffset{ 0 }; /*
VkDescriptorSet descriptorSets[2] = { VkDescriptorSet descriptorSets[2] = {
uniformBufferDescriptorSets[frameIndex], uniformBufferDescriptorSets[frameIndex],
textureDescriptorSet, textureDescriptorSet,
}; };
vkCmdBindDescriptorSets(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 2, descriptorSets, 0, nullptr); vkCmdBindDescriptorSets(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelineLayout, 0, 2, descriptorSets, 0, nullptr);
VkDeviceSize vertexOffset{ 0 };
vkCmdBindVertexBuffers(commandBuffer, 0, 1, &vertexIndexBuffer, &vertexOffset); vkCmdBindVertexBuffers(commandBuffer, 0, 1, &vertexIndexBuffer, &vertexOffset);
VkDeviceSize indexOffset{ vertexBufferSize }; VkDeviceSize indexOffset{ vertexBufferSize };
vkCmdBindIndexBuffer(commandBuffer, vertexIndexBuffer, indexOffset, VK_INDEX_TYPE_UINT32); vkCmdBindIndexBuffer(commandBuffer, vertexIndexBuffer, indexOffset, VK_INDEX_TYPE_UINT32);
@ -1459,10 +1359,25 @@ int main()
VkDeviceSize indexCount{ 2400 }; VkDeviceSize indexCount{ 2400 };
vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelines[MAIN_PIPELINE]); vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelines[MAIN_PIPELINE]);
vkCmdDrawIndexed(commandBuffer, indexCount, 3, 0, 0, 0); vkCmdDrawIndexed(commandBuffer, indexCount, 1, 0, 0, 0);
//vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelines[OUTLINE_PIPELINE]); //vkCmdBindPipeline(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipelines[OUTLINE_PIPELINE]);
//vkCmdDrawIndexed(commandBuffer, indexCount, 3, 0, 0, 0); //vkCmdDrawIndexed(commandBuffer, indexCount, 1, 0, 0, 0);
*/
collada_state.vulkan.commandBuffer = commandBuffer;
collada_state.vulkan.frameIndex = frameIndex;
XMMATRIX projection = currentProjection();
XMMATRIX view = currentView();
collada_state.update(projection, view, 0);
vkCmdBindDescriptorSets(commandBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS,
collada_state.vulkan.pipelineLayout,
0, 1, &uniformBufferDescriptorSets[frameIndex],
0, nullptr);
collada_state.draw();
vkCmdEndRendering(commandBuffer); vkCmdEndRendering(commandBuffer);
@ -1520,7 +1435,7 @@ int main()
updateSwapchain = false; updateSwapchain = false;
VK_CHECK(vkDeviceWaitIdle(device)); VK_CHECK(vkDeviceWaitIdle(device));
VK_CHECK(vkGetPhysicalDeviceSurfaceCapabilitiesKHR(physicalDevice, surface, &surfaceCapabilities)); VK_CHECK(vkGetPhysicalDeviceSurfaceCapabilitiesKHR(physicalDevice, surface, &surfaceCapabilities));
recreateSwapchain(surfaceFormat, depthFormat, physicalDeviceMemoryProperties.memoryProperties, surfaceCapabilities); recreateSwapchain(surfaceFormat, depthFormat, physicalDeviceMemoryProperties, surfaceCapabilities);
} }
} }

64
src/vulkan_helper.cpp Normal file
View File

@ -0,0 +1,64 @@
#include <stdio.h>
#include <stdlib.h>
#include "volk/volk.h"
#include "vulkan/vk_enum_string_helper.h"
#include "check.h"
#include "vulkan_helper.h"
inline static uint32_t findMemoryTypeIndex(VkPhysicalDeviceMemoryProperties const & memoryProperties, uint32_t memoryTypeBits, VkMemoryPropertyFlags propertyFlags)
{
// find an exact match
for (uint32_t i = 0; i < memoryProperties.memoryTypeCount; i++) {
if (!(memoryTypeBits & (1u << i)))
continue;
if (memoryProperties.memoryTypes[i].propertyFlags == propertyFlags) {
return i;
}
}
// find a partial match
for (uint32_t i = 0; i < memoryProperties.memoryTypeCount; i++) {
if (!(memoryTypeBits & (1u << i)))
continue;
if ((memoryProperties.memoryTypes[i].propertyFlags & propertyFlags) == propertyFlags) {
return i;
}
}
ASSERT(false, "no memory type index matching memoryTypeBits and propertyFlags");
UNREACHABLE();
}
VkDeviceSize allocateFromMemoryRequirements(VkDevice device,
VkPhysicalDeviceMemoryProperties const & physicalDeviceMemoryProperties,
VkMemoryRequirements const & memoryRequirements,
VkMemoryPropertyFlags memoryPropertyFlags,
VkMemoryAllocateFlags memoryAllocateFlags,
uint32_t count,
VkDeviceMemory * memory)
{
uint32_t memoryTypeIndex = findMemoryTypeIndex(physicalDeviceMemoryProperties,
memoryRequirements.memoryTypeBits,
memoryPropertyFlags);
VkDeviceSize stride = (count == 1) ? memoryRequirements.size : roundAlignment(memoryRequirements.size, memoryRequirements.alignment);
VkMemoryAllocateFlagsInfo memoryAllocateFlagsInfo{
.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_FLAGS_INFO,
.flags = memoryAllocateFlags,
};
VkMemoryAllocateInfo memoryAllocateInfo{
.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
.pNext = &memoryAllocateFlagsInfo,
.allocationSize = stride * count,
.memoryTypeIndex = memoryTypeIndex,
};
VK_CHECK(vkAllocateMemory(device, &memoryAllocateInfo, nullptr, memory));
return stride;
}