Skip to content

Commit

Permalink
Finished morph target implementation.
Browse files Browse the repository at this point in the history
  • Loading branch information
stripe2933 committed Jan 25, 2025
1 parent 7744870 commit 09b9a88
Show file tree
Hide file tree
Showing 17 changed files with 194 additions and 46 deletions.
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ target_sources(vk-gltf-viewer PRIVATE
interface/gltf/AssetSceneGpuBuffers.cppm
interface/gltf/AssetSceneHierarchy.cppm
interface/gltf/MaterialVariantsMapping.cppm
interface/gltf/MeshWeights.cppm
interface/helpers/AggregateHasher.cppm
interface/helpers/concepts.cppm
interface/helpers/fastgltf.cppm
Expand Down
4 changes: 2 additions & 2 deletions impl/MainApp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -657,15 +657,15 @@ vk_gltf_viewer::MainApp::Gltf::Gltf(
gpu { gpu },
assetGpuBuffers { asset, gpu, threadPool, assetExternalBuffers },
assetGpuTextures { asset, directory, gpu, threadPool, assetExternalBuffers },
sceneGpuBuffers { asset, scene, sceneHierarchy, gpu, assetExternalBuffers },
sceneGpuBuffers { asset, scene, sceneHierarchy, meshWeights, gpu, assetExternalBuffers },
sceneMiniball { gltf::algorithm::getMiniball(asset, scene, [this](std::size_t nodeIndex, std::size_t instanceIndex) {
return cast<double>(sceneGpuBuffers.getMeshNodeWorldTransform(nodeIndex, instanceIndex));
}) } { }

void vk_gltf_viewer::MainApp::Gltf::setScene(std::size_t sceneIndex) {
scene = asset.scenes[sceneIndex];
sceneHierarchy = { asset, scene };
sceneGpuBuffers = { asset, scene, sceneHierarchy, gpu, assetExternalBuffers };
sceneGpuBuffers = { asset, scene, sceneHierarchy, meshWeights, gpu, assetExternalBuffers };
sceneMiniball = gltf::algorithm::getMiniball(asset, scene, [this](std::size_t nodeIndex, std::size_t instanceIndex) {
return cast<double>(sceneGpuBuffers.getMeshNodeWorldTransform(nodeIndex, instanceIndex));
});
Expand Down
26 changes: 4 additions & 22 deletions impl/gltf/AssetGpuBuffers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -163,38 +163,20 @@ std::variant<vku::AllocatedBuffer, vku::MappedBuffer> vk_gltf_viewer::gltf::Asse
.pPositionMorphTargetAttributeMappingInfoBuffer = primitiveInfo.positionMorphTargetInfos.pMappingBuffer,
.pTexcoordAttributeMappingInfoBuffer = primitiveInfo.texcoordsInfo.pMappingBuffer,
.positionByteStride = primitiveInfo.positionInfo.byteStride,
.morphTargetCount = std::numeric_limits<std::uint32_t>::max(),
.materialIndex = primitiveInfo.materialIndex.transform(padMaterialIndex).value_or(0U),
};

if (primitiveInfo.positionMorphTargetInfos.pMappingBuffer) {
gpuPrimitive.pPositionMorphTargetAttributeMappingInfoBuffer
= primitiveInfo.positionMorphTargetInfos.pMappingBuffer;
gpuPrimitive.morphTargetCount = std::min<std::uint32_t>(
gpuPrimitive.morphTargetCount,
primitiveInfo.positionMorphTargetInfos.attributeInfos.size());
}
if (primitiveInfo.normalInfo) {
gpuPrimitive.pNormalBuffer = primitiveInfo.normalInfo->address;
gpuPrimitive.normalByteStride = primitiveInfo.normalInfo->byteStride;
if (primitiveInfo.normalMorphTargetInfos.pMappingBuffer) {
gpuPrimitive.pPositionMorphTargetAttributeMappingInfoBuffer
= primitiveInfo.positionMorphTargetInfos.pMappingBuffer;
gpuPrimitive.morphTargetCount = std::min<std::uint32_t>(
gpuPrimitive.morphTargetCount,
primitiveInfo.normalMorphTargetInfos.attributeInfos.size());
}
gpuPrimitive.pNormalMorphTargetAttributeMappingInfoBuffer
= primitiveInfo.normalMorphTargetInfos.pMappingBuffer;
}
if (primitiveInfo.tangentInfo) {
gpuPrimitive.pTangentBuffer = primitiveInfo.tangentInfo->address;
gpuPrimitive.tangentByteStride = primitiveInfo.tangentInfo->byteStride;
if (primitiveInfo.tangentMorphTargetInfos.pMappingBuffer) {
gpuPrimitive.pPositionMorphTargetAttributeMappingInfoBuffer
= primitiveInfo.positionMorphTargetInfos.pMappingBuffer;
gpuPrimitive.morphTargetCount = std::min<std::uint32_t>(
gpuPrimitive.morphTargetCount,
primitiveInfo.tangentMorphTargetInfos.attributeInfos.size());
}
gpuPrimitive.pTangentMorphTargetAttributeMappingInfoBuffer
= primitiveInfo.tangentMorphTargetInfos.pMappingBuffer;
}
if (primitiveInfo.colorInfo) {
gpuPrimitive.pColorBuffer = primitiveInfo.colorInfo->address;
Expand Down
17 changes: 14 additions & 3 deletions impl/gltf/AssetSceneGpuBuffers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,13 +44,24 @@ std::vector<std::uint32_t> vk_gltf_viewer::gltf::AssetSceneGpuBuffers::createIns
return result;
}

vku::AllocatedBuffer vk_gltf_viewer::gltf::AssetSceneGpuBuffers::createNodeBuffer(const vulkan::Gpu &gpu) const {
vku::AllocatedBuffer vk_gltf_viewer::gltf::AssetSceneGpuBuffers::createNodeBuffer(
const fastgltf::Asset &asset,
const MeshWeights &meshWeights,
const vulkan::Gpu &gpu
) const {
const vk::DeviceAddress nodeTransformBufferStartAddress = gpu.device.getBufferAddress({ meshNodeWorldTransformBuffer });

vku::AllocatedBuffer stagingBuffer = vku::MappedBuffer {
gpu.allocator,
std::from_range, instanceOffsets | std::views::transform([=](std::uint32_t offset) {
return nodeTransformBufferStartAddress + sizeof(fastgltf::math::fmat4x4) * offset;
std::from_range, ranges::views::upto(asset.nodes.size()) | std::views::transform([&](std::size_t nodeIndex) {
return std::array {
to_optional(asset.nodes[nodeIndex].meshIndex)
.transform([&](std::size_t meshIndex) {
return meshWeights.segments[meshIndex].startAddress;
})
.value_or(vk::DeviceAddress { 0 }),
nodeTransformBufferStartAddress + sizeof(fastgltf::math::fmat4x4) * instanceOffsets[nodeIndex],
};
}),
gpu.isUmaDevice ? vk::BufferUsageFlagBits::eStorageBuffer : vk::BufferUsageFlagBits::eTransferSrc,
}.unmap();
Expand Down
3 changes: 3 additions & 0 deletions interface/MainApp.cppm
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import :gltf.AssetGpuFallbackTexture;
import :gltf.AssetSceneGpuBuffers;
import :gltf.AssetSceneHierarchy;
import :gltf.MaterialVariantsMapping;
import :gltf.MeshWeights;
import :vulkan.dsl.Asset;
import :vulkan.dsl.ImageBasedLighting;
import :vulkan.dsl.Scene;
Expand Down Expand Up @@ -72,6 +73,8 @@ namespace vk_gltf_viewer {
gltf::AssetGpuBuffers assetGpuBuffers;
gltf::AssetGpuTextures assetGpuTextures;

gltf::MeshWeights meshWeights { asset, gpu };

/**
* @brief The glTF scene that is currently used by.
*
Expand Down
5 changes: 2 additions & 3 deletions interface/gltf/AssetGpuBuffers.cppm
Original file line number Diff line number Diff line change
Expand Up @@ -165,8 +165,8 @@ namespace vk_gltf_viewer::gltf {
std::uint8_t colorComponentType;
std::uint8_t colorComponentCount;
char _padding0_[2];
std::uint32_t morphTargetCount;
std::uint32_t materialIndex;
char _padding1_[4];
};
static_assert(sizeof(GpuPrimitive) == 80);
static_assert(offsetof(GpuPrimitive, positionByteStride) == 64);
Expand All @@ -175,8 +175,7 @@ namespace vk_gltf_viewer::gltf {
static_assert(offsetof(GpuPrimitive, colorByteStride) == 67);
static_assert(offsetof(GpuPrimitive, colorComponentType) == 68);
static_assert(offsetof(GpuPrimitive, colorComponentCount) == 69);
static_assert(offsetof(GpuPrimitive, morphTargetCount) == 72);
static_assert(offsetof(GpuPrimitive, materialIndex) == 76);
static_assert(offsetof(GpuPrimitive, materialIndex) == 72);

std::unordered_map<const fastgltf::Primitive*, AssetPrimitiveInfo> primitiveInfos = createPrimitiveInfos();

Expand Down
10 changes: 8 additions & 2 deletions interface/gltf/AssetSceneGpuBuffers.cppm
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ export import fastgltf;
import :gltf.algorithm.traversal;
export import :gltf.AssetPrimitiveInfo;
export import :gltf.AssetSceneHierarchy;
export import :gltf.MeshWeights;
import :helpers.concepts;
import :helpers.fastgltf;
import :helpers.functional;
Expand Down Expand Up @@ -49,12 +50,13 @@ namespace vk_gltf_viewer::gltf {
const fastgltf::Asset &asset [[clang::lifetimebound]],
const fastgltf::Scene &scene [[clang::lifetimebound]],
const AssetSceneHierarchy &sceneHierarchy,
const MeshWeights &meshWeights,
const vulkan::Gpu &gpu [[clang::lifetimebound]],
const BufferDataAdapter &adapter = {}
) : pAsset { &asset },
instanceCounts { createInstanceCounts(scene) },
meshNodeWorldTransformBuffer { createMeshNodeWorldTransformBuffer(scene, sceneHierarchy, gpu.allocator, adapter) },
nodeBuffer { createNodeBuffer(gpu) } { }
nodeBuffer { createNodeBuffer(asset, meshWeights, gpu) } { }

/**
* @brief Get world transform matrix of \p nodeIndex-th mesh node's \p instanceIndex-th instance in the scene.
Expand Down Expand Up @@ -215,6 +217,10 @@ namespace vk_gltf_viewer::gltf {
};
}

[[nodiscard]] vku::AllocatedBuffer createNodeBuffer(const vulkan::Gpu &gpu) const;
[[nodiscard]] vku::AllocatedBuffer createNodeBuffer(
const fastgltf::Asset &asset,
const MeshWeights &meshWeights,
const vulkan::Gpu &gpu
) const;
};
}
107 changes: 107 additions & 0 deletions interface/gltf/MeshWeights.cppm
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
export module vk_gltf_viewer:gltf.MeshWeights;

import std;
export import fastgltf;
import :helpers.ranges;
export import :vulkan.Gpu;

#define FWD(...) static_cast<decltype(__VA_ARGS__)&&>(__VA_ARGS__)
#define LIFT(...) [&](auto &&...xs) { return __VA_ARGS__(FWD(xs)...); }

template <typename T>
class writeonly {
T volatile *addr;

public:
explicit writeonly(std::uintptr_t addr)
: addr { reinterpret_cast<T*>(addr) } { }

void operator=(const T &t) volatile { *addr = t; }

[[nodiscard]] std::uintptr_t address() const noexcept { return addr; }
};

/**
* @brief Create a combined buffer from given segments (a range of byte data) and return each segments' start offsets.
*
* Example: Two segments { 0xAA, 0xBB, 0xCC } and { 0xDD, 0xEE } will be combined to { 0xAA, 0xBB, 0xCC, 0xDD, 0xEE }, and their start offsets are { 0, 3 }.
*
* @tparam R Range type of data segments.
* @param allocator VMA allocator to allocate the buffer.
* @param segments Range of data segments. Each segment will be converted to <tt>std::span<const std::byte></tt>, therefore segment's elements must be trivially copyable.
* @param usage Usage flags of the result buffer.
* @return Pair of buffer and each segments' start offsets vector.
*/
template <std::ranges::forward_range R>
requires (
std::ranges::sized_range<std::ranges::range_value_t<R>>
&& std::same_as<std::ranges::range_value_t<std::ranges::range_value_t<R>>, std::byte>)
[[nodiscard]] std::pair<vku::MappedBuffer, std::vector<vk::DeviceSize>> createCombinedBuffer(
vma::Allocator allocator,
R &&segments,
vk::BufferUsageFlags usage
) {
// Calculate each segments' copy destination offsets.
std::vector<vk::DeviceSize> copyOffsets { std::from_range, segments | std::views::transform(LIFT(std::size)) };
vk::DeviceSize sizeTotal = copyOffsets.back();
std::exclusive_scan(copyOffsets.begin(), copyOffsets.end(), copyOffsets.begin(), vk::DeviceSize { 0 });
sizeTotal += copyOffsets.back();

if (sizeTotal == 0) {
throw std::invalid_argument { "No data to write" };
}

// Create buffer.
vku::MappedBuffer buffer { allocator, vk::BufferCreateInfo { {}, sizeTotal, usage } };

// Copy segments to the buffer.
std::byte *mapped = static_cast<std::byte*>(buffer.data);
for (auto &&segment : FWD(segments)) {
mapped = std::ranges::copy(FWD(segment), mapped).out;
}

return { std::move(buffer), std::move(copyOffsets) };
}

namespace vk_gltf_viewer::gltf {
class MeshWeights {
public:
struct Segment {
vk::DeviceAddress startAddress;
writeonly<std::uint32_t> count;
std::span<writeonly<float>> weights;
};

std::vector<Segment> segments;

MeshWeights(const fastgltf::Asset &asset, const vulkan::Gpu &gpu [[clang::lifetimebound]])
: buffer { createBuffer(asset, gpu) } { }

private:
vku::MappedBuffer buffer;

[[nodiscard]] vku::MappedBuffer createBuffer(const fastgltf::Asset &asset, const vulkan::Gpu &gpu) {
auto [buffer, copyOffsets] = createCombinedBuffer(
gpu.allocator,
asset.meshes | std::views::transform([](const fastgltf::Mesh &mesh) {
// [count, weight0, weight1, ..., weight(count-1)]
return ranges::views::concat(
std::bit_cast<std::array<std::byte, 4>>(static_cast<std::uint32_t>(mesh.weights.size())),
as_bytes(std::span { mesh.weights }));
}),
vk::BufferUsageFlagBits::eStorageBuffer | vk::BufferUsageFlagBits::eShaderDeviceAddress);

segments.reserve(copyOffsets.size());
const vk::DeviceAddress bufferAddress = gpu.device.getBufferAddress({ buffer });
for (const auto &[mesh, copyOffset] : std::views::zip(asset.meshes, copyOffsets)) {
const std::uintptr_t segmentStart = reinterpret_cast<std::uintptr_t>(buffer.data) + copyOffset;
segments.emplace_back(
bufferAddress + copyOffset,
writeonly<std::uint32_t> { segmentStart },
std::span { reinterpret_cast<writeonly<float>*>(segmentStart + sizeof(float)), mesh.weights.size() });
}

return std::move(buffer);
}
};
}
8 changes: 7 additions & 1 deletion shaders/depth.vert
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,13 @@
#include "indexing.glsl"
#include "types.glsl"

layout (std430, buffer_reference, buffer_reference_align = 64) readonly buffer Node { mat4 transforms[]; };
layout (buffer_reference, buffer_reference_align = 4) readonly buffer MeshWeights { uint count; float[] weights; };
layout (buffer_reference, buffer_reference_align = 64) readonly buffer InstanceTransforms { mat4 data[]; };

struct Node {
MeshWeights meshWeights;
InstanceTransforms instanceTransforms;
};

layout (location = 0) flat out uint outNodeIndex;

Expand Down
2 changes: 1 addition & 1 deletion shaders/indexing.glsl
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
#define PRIMITIVE_INDEX gl_BaseInstance & 0xFFFFU
#define PRIMITIVE primitives[PRIMITIVE_INDEX]
#define NODE_INDEX gl_BaseInstance >> 16U
#define TRANSFORM Node(nodes[NODE_INDEX]).transforms[gl_InstanceIndex - gl_BaseInstance]
#define TRANSFORM nodes[NODE_INDEX].instanceTransforms.data[gl_InstanceIndex - gl_BaseInstance]
#define MATERIAL_INDEX PRIMITIVE.materialIndex
#define MATERIAL materials[MATERIAL_INDEX]

Expand Down
8 changes: 7 additions & 1 deletion shaders/jump_flood_seed.vert
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,13 @@
#include "indexing.glsl"
#include "types.glsl"

layout (std430, buffer_reference, buffer_reference_align = 64) readonly buffer Node { mat4 transforms[]; };
layout (buffer_reference, buffer_reference_align = 4) readonly buffer MeshWeights { uint count; float[] weights; };
layout (buffer_reference, buffer_reference_align = 64) readonly buffer InstanceTransforms { mat4 data[]; };

struct Node {
MeshWeights meshWeights;
InstanceTransforms instanceTransforms;
};

layout (set = 0, binding = 0) readonly buffer PrimitiveBuffer {
Primitive primitives[];
Expand Down
8 changes: 7 additions & 1 deletion shaders/mask_depth.vert
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,13 @@

#define HAS_VARIADIC_OUT HAS_BASE_COLOR_TEXTURE || HAS_COLOR_ALPHA_ATTRIBUTE

layout (std430, buffer_reference, buffer_reference_align = 64) readonly buffer Node { mat4 transforms[]; };
layout (buffer_reference, buffer_reference_align = 4) readonly buffer MeshWeights { uint count; float[] weights; };
layout (buffer_reference, buffer_reference_align = 64) readonly buffer InstanceTransforms { mat4 data[]; };

struct Node {
MeshWeights meshWeights;
InstanceTransforms instanceTransforms;
};

layout (location = 0) flat out uint outNodeIndex;
layout (location = 1) flat out uint outMaterialIndex;
Expand Down
8 changes: 7 additions & 1 deletion shaders/mask_jump_flood_seed.vert
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,13 @@

#define HAS_VARIADIC_OUT HAS_BASE_COLOR_TEXTURE || HAS_COLOR_ALPHA_ATTRIBUTE

layout (std430, buffer_reference, buffer_reference_align = 64) readonly buffer Node { mat4 transforms[]; };
layout (buffer_reference, buffer_reference_align = 4) readonly buffer MeshWeights { uint count; float[] weights; };
layout (buffer_reference, buffer_reference_align = 64) readonly buffer InstanceTransforms { mat4 data[]; };

struct Node {
MeshWeights meshWeights;
InstanceTransforms instanceTransforms;
};

layout (location = 0) flat out uint outMaterialIndex;
#if HAS_VARIADIC_OUT
Expand Down
8 changes: 7 additions & 1 deletion shaders/primitive.vert
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,13 @@

#define HAS_VARIADIC_OUT !FRAGMENT_SHADER_GENERATED_TBN || TEXCOORD_COUNT >= 1 || HAS_COLOR_ATTRIBUTE

layout (std430, buffer_reference, buffer_reference_align = 64) readonly buffer Node { mat4 transforms[]; };
layout (buffer_reference, buffer_reference_align = 4) readonly buffer MeshWeights { uint count; float[] weights; };
layout (buffer_reference, buffer_reference_align = 64) readonly buffer InstanceTransforms { mat4 data[]; };

struct Node {
MeshWeights meshWeights;
InstanceTransforms instanceTransforms;
};

layout (location = 0) out vec3 outPosition;
layout (location = 1) flat out uint outMaterialIndex;
Expand Down
2 changes: 1 addition & 1 deletion shaders/types.glsl
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,8 @@ struct Primitive {
uint8_t colorComponentType;
uint8_t colorComponentCount;
uint8_t _padding0_[2];
uint morphTargetCount;
uint materialIndex;
float _padding1_;
};

#endif
Expand Down
8 changes: 7 additions & 1 deletion shaders/unlit_primitive.vert
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,13 @@

#define HAS_VARIADIC_OUT HAS_BASE_COLOR_TEXTURE || HAS_COLOR_ATTRIBUTE

layout (std430, buffer_reference, buffer_reference_align = 64) readonly buffer Node { mat4 transforms[]; };
layout (buffer_reference, buffer_reference_align = 4) readonly buffer MeshWeights { uint count; float[] weights; };
layout (buffer_reference, buffer_reference_align = 64) readonly buffer InstanceTransforms { mat4 data[]; };

struct Node {
MeshWeights meshWeights;
InstanceTransforms instanceTransforms;
};

layout (location = 0) flat out uint outMaterialIndex;
#if HAS_VARIADIC_OUT
Expand Down
Loading

0 comments on commit 09b9a88

Please sign in to comment.