Skip to content

Commit

Permalink
Move to interleaved vertex arrays for SplatRenderer
Browse files Browse the repository at this point in the history
* GaussianCloud moved to AttribData style API.
* Moved all cooking of data for rendering into GaussianCloud,
  So setting up interleaved vertex arrays in SplatRenderer is straight forward.
* Overall 2x loading time improvement
* ~20% fps improvement on large scenes
* Still some cleanup necessary before merging into main.
  See AJT: comments
  • Loading branch information
hyperlogic committed Jun 27, 2024
1 parent 13cb609 commit a464ca4
Show file tree
Hide file tree
Showing 8 changed files with 332 additions and 206 deletions.
6 changes: 3 additions & 3 deletions src/app.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -228,9 +228,9 @@ static std::shared_ptr<PointCloud> LoadPointCloud(const std::string& plyFilename
return pointCloud;
}

static std::shared_ptr<GaussianCloud> LoadGaussianCloud(const std::string& plyFilename)
static std::shared_ptr<GaussianCloud> LoadGaussianCloud(const std::string& plyFilename, bool useLinearColors)
{
auto gaussianCloud = std::make_shared<GaussianCloud>();
auto gaussianCloud = std::make_shared<GaussianCloud>(useLinearColors);

if (!gaussianCloud->ImportPly(plyFilename))
{
Expand Down Expand Up @@ -528,7 +528,7 @@ bool App::Init()
Log::D("Could not find input.ply\n");
}

gaussianCloud = LoadGaussianCloud(plyFilename);
gaussianCloud = LoadGaussianCloud(plyFilename, isFramebufferSRGBEnabled);
if (!gaussianCloud)
{
Log::E("Error loading GaussianCloud\n");
Expand Down
200 changes: 176 additions & 24 deletions src/gaussiancloud.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@
#include <string>
#include <string.h>

#include <glm/gtc/quaternion.hpp>

#ifdef TRACY_ENABLE
#include <tracy/Tracy.hpp>
#else
Expand All @@ -25,8 +27,48 @@

#include "ply.h"

GaussianCloud::GaussianCloud()
struct GaussianData
{
GaussianData() noexcept {}
float posWithAlpha[4]; // center of the gaussian in object coordinates, with alpha in w
float r_sh0[4]; // sh coeff for red channel (up to third-order)
float r_sh1[4];
float r_sh2[4];
float r_sh3[4];
float g_sh0[4]; // sh coeff for green channel
float g_sh1[4];
float g_sh2[4];
float g_sh3[4];
float b_sh0[4]; // sh coeff for blue channel
float b_sh1[4];
float b_sh2[4];
float b_sh3[4];
float cov3_col0[3]; // 3x3 covariance matrix of the splat in object coordinates.
float cov3_col1[3];
float cov3_col2[3];
};

glm::mat3 ComputeCovMat(float rot[4], float scale[3])
{
glm::quat q(rot[0], rot[1], rot[2], rot[3]);
glm::mat3 R(glm::normalize(q));
glm::mat3 S(glm::vec3(expf(scale[0]), 0.0f, 0.0f),
glm::vec3(0.0f, expf(scale[1]), 0.0f),
glm::vec3(0.0f, 0.0f, expf(scale[2])));
return R * S * glm::transpose(S) * glm::transpose(R);
}

float ComputeAlpha(float opacity)
{
return 1.0f / (1.0f + expf(-opacity));
}

GaussianCloud::GaussianCloud(bool useLinearColorsIn) :
numGaussians(0),
gaussianSize(0),
useLinearColors(useLinearColorsIn)
{
;
}

bool GaussianCloud::ImportPly(const std::string& plyFilename)
Expand Down Expand Up @@ -61,7 +103,6 @@ bool GaussianCloud::ImportPly(const std::string& plyFilename)
Ply::PropertyInfo rot[4];
} props;


{
ZoneScopedNC("ply.GetProps", tracy::Color::Green);

Expand Down Expand Up @@ -113,36 +154,124 @@ bool GaussianCloud::ImportPly(const std::string& plyFilename)

}

GaussianData* gd;
{
ZoneScopedNC("ply.resize", tracy::Color::Red4);
gaussianVec.resize(ply.GetVertexCount());
ZoneScopedNC("alloc data", tracy::Color::Red4);

// AJT: TODO support with and without sh.

numGaussians = ply.GetVertexCount();
gaussianSize = sizeof(GaussianData);
gd = new GaussianData[numGaussians];
data.reset(gd);

// GL_FLOAT = 0x1406
posWithAlphaAttrib = {4, 0x1406, (int)gaussianSize, offsetof(GaussianData, posWithAlpha)};
r_sh0Attrib = {4, 0x1406, (int)gaussianSize, offsetof(GaussianData, r_sh0)};
r_sh1Attrib = {4, 0x1406, (int)gaussianSize, offsetof(GaussianData, r_sh1)};
r_sh2Attrib = {4, 0x1406, (int)gaussianSize, offsetof(GaussianData, r_sh2)};
r_sh3Attrib = {4, 0x1406, (int)gaussianSize, offsetof(GaussianData, r_sh3)};
g_sh0Attrib = {4, 0x1406, (int)gaussianSize, offsetof(GaussianData, g_sh0)};
g_sh1Attrib = {4, 0x1406, (int)gaussianSize, offsetof(GaussianData, g_sh1)};
g_sh2Attrib = {4, 0x1406, (int)gaussianSize, offsetof(GaussianData, g_sh2)};
g_sh3Attrib = {4, 0x1406, (int)gaussianSize, offsetof(GaussianData, g_sh3)};
b_sh0Attrib = {4, 0x1406, (int)gaussianSize, offsetof(GaussianData, b_sh0)};
b_sh1Attrib = {4, 0x1406, (int)gaussianSize, offsetof(GaussianData, b_sh1)};
b_sh2Attrib = {4, 0x1406, (int)gaussianSize, offsetof(GaussianData, b_sh2)};
b_sh3Attrib = {4, 0x1406, (int)gaussianSize, offsetof(GaussianData, b_sh3)};
cov3_col0Attrib = {3, 0x1406, (int)gaussianSize, offsetof(GaussianData, cov3_col0)};
cov3_col1Attrib = {3, 0x1406, (int)gaussianSize, offsetof(GaussianData, cov3_col1)};
cov3_col2Attrib = {3, 0x1406, (int)gaussianSize, offsetof(GaussianData, cov3_col2)};
}

{
ZoneScopedNC("ply.ForEachVertex", tracy::Color::Blue);
int i = 0;
ply.ForEachVertex([this, &i, &props](const uint8_t* data, size_t size)
ply.ForEachVertex([this, gd, &i, &props](const uint8_t* data, size_t size)
{
gaussianVec[i].position[0] = props.x.Get<float>(data);
gaussianVec[i].position[1] = props.y.Get<float>(data);
gaussianVec[i].position[2] = props.z.Get<float>(data);
for (int j = 0; j < 3; j++)
{
gaussianVec[i].f_dc[j] = props.f_dc[j].Get<float>(data);
}
for (int j = 0; j < 45; j++)
gd[i].posWithAlpha[0] = props.x.Get<float>(data);
gd[i].posWithAlpha[1] = props.y.Get<float>(data);
gd[i].posWithAlpha[2] = props.z.Get<float>(data);
gd[i].posWithAlpha[3] = ComputeAlpha(props.opacity.Get<float>(data));

// AJT: TODO check for useLinearColors

gd[i].r_sh0[0] = props.f_dc[0].Get<float>(data);
gd[i].r_sh0[1] = props.f_rest[0].Get<float>(data);
gd[i].r_sh0[2] = props.f_rest[1].Get<float>(data);
gd[i].r_sh0[3] = props.f_rest[2].Get<float>(data);
gd[i].r_sh1[0] = props.f_rest[3].Get<float>(data);
gd[i].r_sh1[1] = props.f_rest[4].Get<float>(data);
gd[i].r_sh1[2] = props.f_rest[5].Get<float>(data);
gd[i].r_sh1[3] = props.f_rest[6].Get<float>(data);
gd[i].r_sh2[0] = props.f_rest[7].Get<float>(data);
gd[i].r_sh2[1] = props.f_rest[8].Get<float>(data);
gd[i].r_sh2[2] = props.f_rest[9].Get<float>(data);
gd[i].r_sh2[3] = props.f_rest[10].Get<float>(data);
gd[i].r_sh3[0] = props.f_rest[11].Get<float>(data);
gd[i].r_sh3[1] = props.f_rest[12].Get<float>(data);
gd[i].r_sh3[2] = props.f_rest[13].Get<float>(data);
gd[i].r_sh3[3] = props.f_rest[14].Get<float>(data);

gd[i].g_sh0[0] = props.f_dc[1].Get<float>(data);
gd[i].g_sh0[1] = props.f_rest[15].Get<float>(data);
gd[i].g_sh0[2] = props.f_rest[16].Get<float>(data);
gd[i].g_sh0[3] = props.f_rest[17].Get<float>(data);
gd[i].g_sh1[0] = props.f_rest[18].Get<float>(data);
gd[i].g_sh1[1] = props.f_rest[19].Get<float>(data);
gd[i].g_sh1[2] = props.f_rest[20].Get<float>(data);
gd[i].g_sh1[3] = props.f_rest[21].Get<float>(data);
gd[i].g_sh2[0] = props.f_rest[22].Get<float>(data);
gd[i].g_sh2[1] = props.f_rest[23].Get<float>(data);
gd[i].g_sh2[2] = props.f_rest[24].Get<float>(data);
gd[i].g_sh2[3] = props.f_rest[25].Get<float>(data);
gd[i].g_sh3[0] = props.f_rest[26].Get<float>(data);
gd[i].g_sh3[1] = props.f_rest[27].Get<float>(data);
gd[i].g_sh3[2] = props.f_rest[28].Get<float>(data);
gd[i].g_sh3[3] = props.f_rest[29].Get<float>(data);

gd[i].b_sh0[0] = props.f_dc[2].Get<float>(data);
gd[i].b_sh0[1] = props.f_rest[30].Get<float>(data);
gd[i].b_sh0[2] = props.f_rest[31].Get<float>(data);
gd[i].b_sh0[3] = props.f_rest[32].Get<float>(data);
gd[i].b_sh1[0] = props.f_rest[33].Get<float>(data);
gd[i].b_sh1[1] = props.f_rest[34].Get<float>(data);
gd[i].b_sh1[2] = props.f_rest[35].Get<float>(data);
gd[i].b_sh1[3] = props.f_rest[36].Get<float>(data);
gd[i].b_sh2[0] = props.f_rest[37].Get<float>(data);
gd[i].b_sh2[1] = props.f_rest[38].Get<float>(data);
gd[i].b_sh2[2] = props.f_rest[39].Get<float>(data);
gd[i].b_sh2[3] = props.f_rest[40].Get<float>(data);
gd[i].b_sh3[0] = props.f_rest[41].Get<float>(data);
gd[i].b_sh3[1] = props.f_rest[42].Get<float>(data);
gd[i].b_sh3[2] = props.f_rest[43].Get<float>(data);
gd[i].b_sh3[3] = props.f_rest[44].Get<float>(data);

float rot[4] =
{
gaussianVec[i].f_rest[j] = props.f_rest[j].Get<float>(data);
}
gaussianVec[i].opacity = props.opacity.Get<float>(data);
for (int j = 0; j < 3; j++)
props.rot[0].Get<float>(data),
props.rot[1].Get<float>(data),
props.rot[2].Get<float>(data),
props.rot[3].Get<float>(data)
};
float scale[4] =
{
gaussianVec[i].scale[j] = props.scale[j].Get<float>(data);
}
for (int j = 0; j < 4; j++)
{
gaussianVec[i].rot[j] = props.rot[j].Get<float>(data);
}
props.scale[0].Get<float>(data),
props.scale[1].Get<float>(data),
props.scale[2].Get<float>(data)
};

glm::mat3 V = ComputeCovMat(rot, scale);
gd[i].cov3_col0[0] = V[0][0];
gd[i].cov3_col0[1] = V[0][1];
gd[i].cov3_col0[2] = V[0][2];
gd[i].cov3_col1[0] = V[1][0];
gd[i].cov3_col1[1] = V[1][1];
gd[i].cov3_col1[2] = V[1][2];
gd[i].cov3_col2[0] = V[2][0];
gd[i].cov3_col2[1] = V[2][1];
gd[i].cov3_col2[2] = V[2][2];

i++;
});
}
Expand All @@ -152,6 +281,9 @@ bool GaussianCloud::ImportPly(const std::string& plyFilename)

bool GaussianCloud::ExportPly(const std::string& plyFilename) const
{
// AJT: TODO FIXME:
return false;

std::ofstream plyFile(plyFilename, std::ios::binary);
if (!plyFile.is_open())
{
Expand All @@ -162,7 +294,7 @@ bool GaussianCloud::ExportPly(const std::string& plyFilename) const
// ply files have unix line endings.
plyFile << "ply\n";
plyFile << "format binary_little_endian 1.0\n";
plyFile << "element vertex " << gaussianVec.size() << "\n";
plyFile << "element vertex " << numGaussians << "\n";
plyFile << "property float x\n";
plyFile << "property float y\n";
plyFile << "property float z\n";
Expand Down Expand Up @@ -227,19 +359,24 @@ bool GaussianCloud::ExportPly(const std::string& plyFilename) const
plyFile << "property float rot_3\n";
plyFile << "end_header\n";

/* AJT: TODO FIXME
const size_t GAUSSIAN_SIZE = 62 * sizeof(float);
static_assert(sizeof(Gaussian) >= GAUSSIAN_SIZE);
for (auto&& g : gaussianVec)
{
plyFile.write((char*)&g, GAUSSIAN_SIZE);
}
*/

return true;
}

void GaussianCloud::InitDebugCloud()
{
// AJT: TODO fix me

#if 0
gaussianVec.clear();

//
Expand Down Expand Up @@ -310,11 +447,14 @@ void GaussianCloud::InitDebugCloud()
g.scale[0] = S; g.scale[1] = S; g.scale[2] = S;
g.rot[0] = 1.0f; g.rot[1] = 0.0f; g.rot[2] = 0.0f; g.rot[3] = 0.0f;
gaussianVec.push_back(g);
#endif
}

// only keep the nearest splats
void GaussianCloud::PruneSplats(const glm::vec3& origin, uint32_t numSplats)
{
// AJT: TODO fixme
#if 0
if (static_cast<size_t>(numSplats) >= gaussianVec.size())
{
return;
Expand Down Expand Up @@ -342,4 +482,16 @@ void GaussianCloud::PruneSplats(const glm::vec3& origin, uint32_t numSplats)
}

gaussianVec.swap(newGaussianVec);
#endif
}

void GaussianCloud::ForEachAttrib(const AttribData& attribData, const AttribCallback& cb) const
{
const uint8_t* bytePtr = (uint8_t*)data.get();
bytePtr += attribData.offset;
for (size_t i = 0; i < GetNumGaussians(); i++)
{
cb((const void*)bytePtr);
bytePtr += attribData.stride;
}
}
Loading

0 comments on commit a464ca4

Please sign in to comment.