Define gaussian splat chunk data contract
This commit is contained in:
@@ -4,6 +4,7 @@
|
|||||||
#include <XCEngine/Core/Containers/Array.h>
|
#include <XCEngine/Core/Containers/Array.h>
|
||||||
#include <XCEngine/Core/Math/Bounds.h>
|
#include <XCEngine/Core/Math/Bounds.h>
|
||||||
#include <XCEngine/Core/Math/Quaternion.h>
|
#include <XCEngine/Core/Math/Quaternion.h>
|
||||||
|
#include <XCEngine/Core/Math/Vector2.h>
|
||||||
#include <XCEngine/Core/Math/Vector3.h>
|
#include <XCEngine/Core/Math/Vector3.h>
|
||||||
#include <XCEngine/Core/Math/Vector4.h>
|
#include <XCEngine/Core/Math/Vector4.h>
|
||||||
#include <XCEngine/Core/Types.h>
|
#include <XCEngine/Core/Types.h>
|
||||||
@@ -45,6 +46,7 @@ enum class GaussianSplatSectionFormat : Core::uint32 {
|
|||||||
constexpr Core::uint32 kGaussianSplatSHCoefficientCount = 45;
|
constexpr Core::uint32 kGaussianSplatSHCoefficientCount = 45;
|
||||||
constexpr Core::uint32 kGaussianSplatSHColorChannelCount = 3;
|
constexpr Core::uint32 kGaussianSplatSHColorChannelCount = 3;
|
||||||
constexpr Core::uint32 kGaussianSplatMaxSHOrder = 3;
|
constexpr Core::uint32 kGaussianSplatMaxSHOrder = 3;
|
||||||
|
constexpr Core::uint32 kGaussianSplatChunkSize = 256;
|
||||||
|
|
||||||
Core::uint32 ResolveGaussianSplatSHOrderFromCoefficientCount(Core::uint32 coefficientCount);
|
Core::uint32 ResolveGaussianSplatSHOrderFromCoefficientCount(Core::uint32 coefficientCount);
|
||||||
Core::uint32 ResolveGaussianSplatSHOrderFromSectionStride(Core::uint32 elementStride);
|
Core::uint32 ResolveGaussianSplatSHOrderFromSectionStride(Core::uint32 elementStride);
|
||||||
@@ -67,6 +69,24 @@ struct GaussianSplatSHRecord {
|
|||||||
float coefficients[kGaussianSplatSHCoefficientCount] = {};
|
float coefficients[kGaussianSplatSHCoefficientCount] = {};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct GaussianSplatChunkRecord {
|
||||||
|
Core::uint32 colR = 0u;
|
||||||
|
Core::uint32 colG = 0u;
|
||||||
|
Core::uint32 colB = 0u;
|
||||||
|
Core::uint32 colA = 0u;
|
||||||
|
Math::Vector2 posX = Math::Vector2::Zero();
|
||||||
|
Math::Vector2 posY = Math::Vector2::Zero();
|
||||||
|
Math::Vector2 posZ = Math::Vector2::Zero();
|
||||||
|
Core::uint32 sclX = 0u;
|
||||||
|
Core::uint32 sclY = 0u;
|
||||||
|
Core::uint32 sclZ = 0u;
|
||||||
|
Core::uint32 shR = 0u;
|
||||||
|
Core::uint32 shG = 0u;
|
||||||
|
Core::uint32 shB = 0u;
|
||||||
|
};
|
||||||
|
|
||||||
|
static_assert(sizeof(GaussianSplatChunkRecord) == 64u, "GaussianSplatChunkRecord layout must stay Unity-compatible");
|
||||||
|
|
||||||
struct GaussianSplatSection {
|
struct GaussianSplatSection {
|
||||||
GaussianSplatSectionType type = GaussianSplatSectionType::Unknown;
|
GaussianSplatSectionType type = GaussianSplatSectionType::Unknown;
|
||||||
GaussianSplatSectionFormat format = GaussianSplatSectionFormat::Unknown;
|
GaussianSplatSectionFormat format = GaussianSplatSectionFormat::Unknown;
|
||||||
|
|||||||
@@ -295,16 +295,36 @@ SampleArtifactData BuildSampleArtifactData() {
|
|||||||
sh[1].coefficients[index] = -0.02f * static_cast<float>(index + 1u);
|
sh[1].coefficients[index] = -0.02f * static_cast<float>(index + 1u);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const GaussianSplatChunkRecord chunks[1] = {
|
||||||
|
{
|
||||||
|
0x00010002u,
|
||||||
|
0x00030004u,
|
||||||
|
0x00050006u,
|
||||||
|
0x00070008u,
|
||||||
|
Vector2(-1.0f, 2.0f),
|
||||||
|
Vector2(-3.0f, 4.0f),
|
||||||
|
Vector2(-5.0f, 6.0f),
|
||||||
|
0x0009000Au,
|
||||||
|
0x000B000Cu,
|
||||||
|
0x000D000Eu,
|
||||||
|
0x000F0010u,
|
||||||
|
0x00110012u,
|
||||||
|
0x00130014u
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
SampleArtifactData sample;
|
SampleArtifactData sample;
|
||||||
sample.metadata.contentVersion = 1u;
|
sample.metadata.contentVersion = 1u;
|
||||||
sample.metadata.splatCount = 2u;
|
sample.metadata.splatCount = 2u;
|
||||||
|
sample.metadata.chunkCount = 1u;
|
||||||
sample.metadata.bounds.SetMinMax(Vector3(-2.0f, -1.0f, -3.0f), Vector3(5.0f, 4.0f, 6.0f));
|
sample.metadata.bounds.SetMinMax(Vector3(-2.0f, -1.0f, -3.0f), Vector3(5.0f, 4.0f, 6.0f));
|
||||||
sample.metadata.positionFormat = GaussianSplatSectionFormat::VectorFloat32;
|
sample.metadata.positionFormat = GaussianSplatSectionFormat::VectorFloat32;
|
||||||
sample.metadata.otherFormat = GaussianSplatSectionFormat::OtherFloat32;
|
sample.metadata.otherFormat = GaussianSplatSectionFormat::OtherFloat32;
|
||||||
sample.metadata.colorFormat = GaussianSplatSectionFormat::ColorRGBA32F;
|
sample.metadata.colorFormat = GaussianSplatSectionFormat::ColorRGBA32F;
|
||||||
sample.metadata.shFormat = GaussianSplatSectionFormat::SHFloat32;
|
sample.metadata.shFormat = GaussianSplatSectionFormat::SHFloat32;
|
||||||
|
sample.metadata.chunkFormat = GaussianSplatSectionFormat::ChunkFloat32;
|
||||||
|
|
||||||
sample.sections.Reserve(4u);
|
sample.sections.Reserve(5u);
|
||||||
size_t payloadOffset = 0u;
|
size_t payloadOffset = 0u;
|
||||||
auto appendSection = [&](GaussianSplatSectionType type,
|
auto appendSection = [&](GaussianSplatSectionType type,
|
||||||
GaussianSplatSectionFormat format,
|
GaussianSplatSectionFormat format,
|
||||||
@@ -355,6 +375,13 @@ SampleArtifactData BuildSampleArtifactData() {
|
|||||||
sizeof(sh),
|
sizeof(sh),
|
||||||
2u,
|
2u,
|
||||||
sizeof(GaussianSplatSHRecord));
|
sizeof(GaussianSplatSHRecord));
|
||||||
|
appendSection(
|
||||||
|
GaussianSplatSectionType::Chunks,
|
||||||
|
GaussianSplatSectionFormat::ChunkFloat32,
|
||||||
|
chunks,
|
||||||
|
sizeof(chunks),
|
||||||
|
1u,
|
||||||
|
sizeof(GaussianSplatChunkRecord));
|
||||||
|
|
||||||
return sample;
|
return sample;
|
||||||
}
|
}
|
||||||
@@ -392,41 +419,49 @@ TEST(RenderResourceCacheTest, GetOrCreateGaussianSplatUploadsStructuredSectionsA
|
|||||||
EXPECT_EQ(cached->residencyState, RenderResourceCache::GaussianSplatResidencyState::GpuReady);
|
EXPECT_EQ(cached->residencyState, RenderResourceCache::GaussianSplatResidencyState::GpuReady);
|
||||||
EXPECT_EQ(cached->contentVersion, 1u);
|
EXPECT_EQ(cached->contentVersion, 1u);
|
||||||
EXPECT_EQ(cached->splatCount, 2u);
|
EXPECT_EQ(cached->splatCount, 2u);
|
||||||
EXPECT_EQ(cached->chunkCount, 0u);
|
EXPECT_EQ(cached->chunkCount, 1u);
|
||||||
EXPECT_EQ(cached->positions.elementStride, sizeof(GaussianSplatPositionRecord));
|
EXPECT_EQ(cached->positions.elementStride, sizeof(float) * 4u);
|
||||||
EXPECT_EQ(cached->other.elementStride, sizeof(GaussianSplatOtherRecord));
|
EXPECT_EQ(cached->other.elementStride, sizeof(GaussianSplatOtherRecord));
|
||||||
EXPECT_EQ(cached->color.elementStride, sizeof(GaussianSplatColorRecord));
|
EXPECT_EQ(cached->color.elementStride, sizeof(GaussianSplatColorRecord));
|
||||||
EXPECT_EQ(cached->sh.elementStride, sizeof(GaussianSplatSHRecord));
|
EXPECT_EQ(cached->sh.elementStride, sizeof(GaussianSplatSHRecord));
|
||||||
|
EXPECT_EQ(cached->chunks.elementStride, sizeof(GaussianSplatChunkRecord));
|
||||||
EXPECT_EQ(cached->positions.elementCount, 2u);
|
EXPECT_EQ(cached->positions.elementCount, 2u);
|
||||||
EXPECT_EQ(cached->other.elementCount, 2u);
|
EXPECT_EQ(cached->other.elementCount, 2u);
|
||||||
EXPECT_EQ(cached->color.elementCount, 2u);
|
EXPECT_EQ(cached->color.elementCount, 2u);
|
||||||
EXPECT_EQ(cached->sh.elementCount, 2u);
|
EXPECT_EQ(cached->sh.elementCount, 2u);
|
||||||
|
EXPECT_EQ(cached->chunks.elementCount, 1u);
|
||||||
ASSERT_NE(cached->positions.buffer, nullptr);
|
ASSERT_NE(cached->positions.buffer, nullptr);
|
||||||
ASSERT_NE(cached->positions.shaderResourceView, nullptr);
|
ASSERT_NE(cached->positions.shaderResourceView, nullptr);
|
||||||
ASSERT_NE(cached->color.buffer, nullptr);
|
ASSERT_NE(cached->color.buffer, nullptr);
|
||||||
ASSERT_NE(cached->sh.buffer, nullptr);
|
ASSERT_NE(cached->sh.buffer, nullptr);
|
||||||
|
ASSERT_NE(cached->chunks.buffer, nullptr);
|
||||||
|
|
||||||
const auto* uploadedPositions = static_cast<const MockCacheBuffer*>(cached->positions.buffer);
|
const auto* uploadedPositions = static_cast<const MockCacheBuffer*>(cached->positions.buffer);
|
||||||
ASSERT_GE(uploadedPositions->GetBytes().size(), sizeof(GaussianSplatPositionRecord) * 2u);
|
ASSERT_GE(uploadedPositions->GetBytes().size(), sizeof(float) * 4u * 2u);
|
||||||
const auto* uploadedPositionRecords = reinterpret_cast<const GaussianSplatPositionRecord*>(
|
const auto* uploadedPositionWords = reinterpret_cast<const float*>(uploadedPositions->GetBytes().data());
|
||||||
uploadedPositions->GetBytes().data());
|
EXPECT_FLOAT_EQ(uploadedPositionWords[0], 0.0f);
|
||||||
EXPECT_EQ(uploadedPositionRecords[0].position, Vector3(0.0f, 1.0f, 2.0f));
|
EXPECT_FLOAT_EQ(uploadedPositionWords[1], 1.0f);
|
||||||
EXPECT_EQ(uploadedPositionRecords[1].position, Vector3(3.0f, 4.0f, 5.0f));
|
EXPECT_FLOAT_EQ(uploadedPositionWords[2], 2.0f);
|
||||||
|
EXPECT_FLOAT_EQ(uploadedPositionWords[3], 0.0f);
|
||||||
|
EXPECT_FLOAT_EQ(uploadedPositionWords[4], 3.0f);
|
||||||
|
EXPECT_FLOAT_EQ(uploadedPositionWords[5], 4.0f);
|
||||||
|
EXPECT_FLOAT_EQ(uploadedPositionWords[6], 5.0f);
|
||||||
|
EXPECT_FLOAT_EQ(uploadedPositionWords[7], 0.0f);
|
||||||
|
|
||||||
EXPECT_EQ(state->createBufferCalls, 4);
|
EXPECT_EQ(state->createBufferCalls, 5);
|
||||||
EXPECT_EQ(state->createShaderViewCalls, 4);
|
EXPECT_EQ(state->createShaderViewCalls, 5);
|
||||||
|
|
||||||
const RenderResourceCache::CachedGaussianSplat* cachedAgain =
|
const RenderResourceCache::CachedGaussianSplat* cachedAgain =
|
||||||
cache.GetOrCreateGaussianSplat(&device, &gaussianSplat);
|
cache.GetOrCreateGaussianSplat(&device, &gaussianSplat);
|
||||||
EXPECT_EQ(cachedAgain, cached);
|
EXPECT_EQ(cachedAgain, cached);
|
||||||
EXPECT_EQ(state->createBufferCalls, 4);
|
EXPECT_EQ(state->createBufferCalls, 5);
|
||||||
EXPECT_EQ(state->createShaderViewCalls, 4);
|
EXPECT_EQ(state->createShaderViewCalls, 5);
|
||||||
|
|
||||||
cache.Shutdown();
|
cache.Shutdown();
|
||||||
EXPECT_EQ(state->shutdownBufferCalls, 4);
|
EXPECT_EQ(state->shutdownBufferCalls, 5);
|
||||||
EXPECT_EQ(state->destroyBufferCalls, 4);
|
EXPECT_EQ(state->destroyBufferCalls, 5);
|
||||||
EXPECT_EQ(state->shutdownShaderViewCalls, 4);
|
EXPECT_EQ(state->shutdownShaderViewCalls, 5);
|
||||||
EXPECT_EQ(state->destroyShaderViewCalls, 4);
|
EXPECT_EQ(state->destroyShaderViewCalls, 5);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST(RenderResourceCacheTest, GetOrCreateGaussianSplatSupportsArtifactRuntimeLoadPath) {
|
TEST(RenderResourceCacheTest, GetOrCreateGaussianSplatSupportsArtifactRuntimeLoadPath) {
|
||||||
@@ -459,8 +494,9 @@ TEST(RenderResourceCacheTest, GetOrCreateGaussianSplatSupportsArtifactRuntimeLoa
|
|||||||
ASSERT_NE(cached, nullptr);
|
ASSERT_NE(cached, nullptr);
|
||||||
EXPECT_EQ(cached->residencyState, RenderResourceCache::GaussianSplatResidencyState::GpuReady);
|
EXPECT_EQ(cached->residencyState, RenderResourceCache::GaussianSplatResidencyState::GpuReady);
|
||||||
EXPECT_EQ(cached->splatCount, 2u);
|
EXPECT_EQ(cached->splatCount, 2u);
|
||||||
EXPECT_EQ(state->createBufferCalls, 4);
|
EXPECT_EQ(cached->chunkCount, 1u);
|
||||||
EXPECT_EQ(state->createShaderViewCalls, 4);
|
EXPECT_EQ(state->createBufferCalls, 5);
|
||||||
|
EXPECT_EQ(state->createShaderViewCalls, 5);
|
||||||
}
|
}
|
||||||
|
|
||||||
manager.UnloadAll();
|
manager.UnloadAll();
|
||||||
|
|||||||
@@ -163,16 +163,36 @@ SampleArtifactData BuildSampleArtifactData() {
|
|||||||
sh[1].coefficients[index] = -0.02f * static_cast<float>(index + 1u);
|
sh[1].coefficients[index] = -0.02f * static_cast<float>(index + 1u);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const GaussianSplatChunkRecord chunks[1] = {
|
||||||
|
{
|
||||||
|
0x00010002u,
|
||||||
|
0x00030004u,
|
||||||
|
0x00050006u,
|
||||||
|
0x00070008u,
|
||||||
|
Vector2(-1.0f, 2.0f),
|
||||||
|
Vector2(-3.0f, 4.0f),
|
||||||
|
Vector2(-5.0f, 6.0f),
|
||||||
|
0x0009000Au,
|
||||||
|
0x000B000Cu,
|
||||||
|
0x000D000Eu,
|
||||||
|
0x000F0010u,
|
||||||
|
0x00110012u,
|
||||||
|
0x00130014u
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
SampleArtifactData sample;
|
SampleArtifactData sample;
|
||||||
sample.metadata.contentVersion = 3u;
|
sample.metadata.contentVersion = 3u;
|
||||||
sample.metadata.splatCount = 2u;
|
sample.metadata.splatCount = 2u;
|
||||||
|
sample.metadata.chunkCount = 1u;
|
||||||
sample.metadata.bounds.SetMinMax(Vector3(-2.0f, -1.0f, -3.0f), Vector3(5.0f, 4.0f, 6.0f));
|
sample.metadata.bounds.SetMinMax(Vector3(-2.0f, -1.0f, -3.0f), Vector3(5.0f, 4.0f, 6.0f));
|
||||||
sample.metadata.positionFormat = GaussianSplatSectionFormat::VectorFloat32;
|
sample.metadata.positionFormat = GaussianSplatSectionFormat::VectorFloat32;
|
||||||
sample.metadata.otherFormat = GaussianSplatSectionFormat::OtherFloat32;
|
sample.metadata.otherFormat = GaussianSplatSectionFormat::OtherFloat32;
|
||||||
sample.metadata.colorFormat = GaussianSplatSectionFormat::ColorRGBA32F;
|
sample.metadata.colorFormat = GaussianSplatSectionFormat::ColorRGBA32F;
|
||||||
sample.metadata.shFormat = GaussianSplatSectionFormat::SHFloat32;
|
sample.metadata.shFormat = GaussianSplatSectionFormat::SHFloat32;
|
||||||
|
sample.metadata.chunkFormat = GaussianSplatSectionFormat::ChunkFloat32;
|
||||||
|
|
||||||
sample.sections.Reserve(4u);
|
sample.sections.Reserve(5u);
|
||||||
size_t payloadOffset = 0u;
|
size_t payloadOffset = 0u;
|
||||||
auto appendSection = [&](GaussianSplatSectionType type,
|
auto appendSection = [&](GaussianSplatSectionType type,
|
||||||
GaussianSplatSectionFormat format,
|
GaussianSplatSectionFormat format,
|
||||||
@@ -223,6 +243,13 @@ SampleArtifactData BuildSampleArtifactData() {
|
|||||||
sizeof(sh),
|
sizeof(sh),
|
||||||
2u,
|
2u,
|
||||||
sizeof(GaussianSplatSHRecord));
|
sizeof(GaussianSplatSHRecord));
|
||||||
|
appendSection(
|
||||||
|
GaussianSplatSectionType::Chunks,
|
||||||
|
GaussianSplatSectionFormat::ChunkFloat32,
|
||||||
|
chunks,
|
||||||
|
sizeof(chunks),
|
||||||
|
1u,
|
||||||
|
sizeof(GaussianSplatChunkRecord));
|
||||||
|
|
||||||
return sample;
|
return sample;
|
||||||
}
|
}
|
||||||
@@ -317,13 +344,18 @@ TEST(GaussianSplatLoader, WritesAndLoadsArtifact) {
|
|||||||
ASSERT_NE(gaussianSplat, nullptr);
|
ASSERT_NE(gaussianSplat, nullptr);
|
||||||
EXPECT_EQ(gaussianSplat->GetContentVersion(), 3u);
|
EXPECT_EQ(gaussianSplat->GetContentVersion(), 3u);
|
||||||
EXPECT_EQ(gaussianSplat->GetSplatCount(), 2u);
|
EXPECT_EQ(gaussianSplat->GetSplatCount(), 2u);
|
||||||
|
EXPECT_EQ(gaussianSplat->GetChunkCount(), 1u);
|
||||||
EXPECT_EQ(gaussianSplat->GetBounds().GetMin(), Vector3(-2.0f, -1.0f, -3.0f));
|
EXPECT_EQ(gaussianSplat->GetBounds().GetMin(), Vector3(-2.0f, -1.0f, -3.0f));
|
||||||
EXPECT_EQ(gaussianSplat->GetBounds().GetMax(), Vector3(5.0f, 4.0f, 6.0f));
|
EXPECT_EQ(gaussianSplat->GetBounds().GetMax(), Vector3(5.0f, 4.0f, 6.0f));
|
||||||
ASSERT_EQ(gaussianSplat->GetSections().Size(), 4u);
|
ASSERT_EQ(gaussianSplat->GetSections().Size(), 5u);
|
||||||
const GaussianSplatSection* shSection = gaussianSplat->FindSection(GaussianSplatSectionType::SH);
|
const GaussianSplatSection* shSection = gaussianSplat->FindSection(GaussianSplatSectionType::SH);
|
||||||
ASSERT_NE(shSection, nullptr);
|
ASSERT_NE(shSection, nullptr);
|
||||||
EXPECT_EQ(shSection->elementCount, 2u);
|
EXPECT_EQ(shSection->elementCount, 2u);
|
||||||
EXPECT_EQ(shSection->elementStride, sizeof(GaussianSplatSHRecord));
|
EXPECT_EQ(shSection->elementStride, sizeof(GaussianSplatSHRecord));
|
||||||
|
const GaussianSplatSection* chunkSection = gaussianSplat->FindSection(GaussianSplatSectionType::Chunks);
|
||||||
|
ASSERT_NE(chunkSection, nullptr);
|
||||||
|
EXPECT_EQ(chunkSection->elementCount, 1u);
|
||||||
|
EXPECT_EQ(chunkSection->elementStride, sizeof(GaussianSplatChunkRecord));
|
||||||
ASSERT_NE(gaussianSplat->GetColorRecords(), nullptr);
|
ASSERT_NE(gaussianSplat->GetColorRecords(), nullptr);
|
||||||
EXPECT_EQ(gaussianSplat->GetColorRecords()[1].colorOpacity, Vector4(0.0f, 1.0f, 0.0f, 0.75f));
|
EXPECT_EQ(gaussianSplat->GetColorRecords()[1].colorOpacity, Vector4(0.0f, 1.0f, 0.0f, 0.75f));
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user