Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[glTF] Load scenes with UINT8 indices #482

Merged
merged 10 commits into from
Nov 26, 2024
75 changes: 52 additions & 23 deletions src/ppx/scene/scene_gltf_loader.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1189,22 +1189,28 @@ ppx::Result GltfLoader::LoadMeshData(

struct BatchInfo
{
scene::MaterialRef material = nullptr;
uint32_t indexDataOffset = 0; // Must have 4 byte alignment
uint32_t indexDataSize = 0;
uint32_t positionDataOffset = 0; // Must have 4 byte alignment
uint32_t positionDataSize = 0;
uint32_t attributeDataOffset = 0; // Must have 4 byte alignment
uint32_t attributeDataSize = 0;
grfx::Format indexFormat = grfx::FORMAT_UNDEFINED;
uint32_t indexCount = 0;
uint32_t vertexCount = 0;
ppx::AABB boundingBox = {};
scene::MaterialRef material = nullptr;
// Start of the index plane in the final repacked GPU buffer.
uint32_t indexDataOffset = 0; // Must have 4 byte alignment
// Total size of the index plane in the final repacked GPU buffer.
uint32_t indexDataSize = 0;
uint32_t positionDataOffset = 0; // Must have 4 byte alignment
uint32_t positionDataSize = 0;
uint32_t attributeDataOffset = 0; // Must have 4 byte alignment
uint32_t attributeDataSize = 0;
// Format of the input index buffer.
grfx::Format indexFormat = grfx::FORMAT_UNDEFINED;
// Format of the index plane in the final repacked GPU buffer.
grfx::Format repackedIndexFormat = grfx::FORMAT_UNDEFINED;
// How many indices are in the input index buffer.
uint32_t indexCount = 0;
uint32_t vertexCount = 0;
ppx::AABB boundingBox = {};
};

// Build out batch infos
std::vector<BatchInfo> batchInfos;
//
// Size of the final GPU buffer to allocate. Must account for growth during repacking.
uint32_t totalDataSize = 0;
//
for (cgltf_size primIdx = 0; primIdx < pGltfMesh->primitives_count; ++primIdx) {
Expand All @@ -1225,18 +1231,28 @@ ppx::Result GltfLoader::LoadMeshData(
// Get index format
//
// It's valid for this to be UNDEFINED, means the primitive doesn't have any index data.
// However, if it's not UNDEFINED, UINT16, or UINT32 then it's a format we can't handle.
// However, if it's not UNDEFINED, UINT8, UINT16, or UINT32 then it's a format we can't handle.
//
auto indexFormat = GetFormat(pGltfPrimitive->indices);
if ((indexFormat != grfx::FORMAT_UNDEFINED) && (indexFormat != grfx::FORMAT_R16_UINT) && (indexFormat != grfx::FORMAT_R32_UINT)) {
PPX_ASSERT_MSG(false, "GLTF mesh primitive has unrecognized index format");
if ((indexFormat != grfx::FORMAT_UNDEFINED) && (indexFormat != grfx::FORMAT_R8_UINT) && (indexFormat != grfx::FORMAT_R16_UINT) && (indexFormat != grfx::FORMAT_R32_UINT)) {
PPX_ASSERT_MSG(false, "GLTF mesh primitive has unrecognized index format: " << ToString(indexFormat));
return ppx::ERROR_SCENE_INVALID_SOURCE_GEOMETRY_INDEX_TYPE;
}

// Index data size
// UINT8 index buffer availability varies: Vulkan requires an extension, whereas DX12 lacks support entirely.
// If it's not supported then repack as UINT16 (the smallest mandated size for both).
auto repackedIndexFormat = indexFormat;
if (repackedIndexFormat == grfx::FORMAT_R8_UINT && !loadParams.pDevice->IndexTypeUint8Supported()) {
PPX_LOG_INFO("Device doesn't support UINT8 index buffers! Repacking data as UINT16.");
repackedIndexFormat = grfx::FORMAT_R16_UINT;
}

// Index data size of input
const uint32_t indexCount = !IsNull(pGltfPrimitive->indices) ? static_cast<uint32_t>(pGltfPrimitive->indices->count) : 0;
const uint32_t indexElementSize = grfx::GetFormatDescription(indexFormat)->bytesPerTexel;
const uint32_t indexDataSize = indexCount * indexElementSize;
// If we repack indices into a buffer of a different format then we need to account for disparity between input and output sizes.
const uint32_t repackedSizeRatio = grfx::GetFormatDescription(repackedIndexFormat)->bytesPerTexel / indexElementSize;
const uint32_t indexDataSize = indexCount * indexElementSize * repackedSizeRatio;

// Get position accessor
const VertexAccessors gltflAccessors = GetVertexAccessors(pGltfPrimitive);
Expand Down Expand Up @@ -1269,6 +1285,7 @@ ppx::Result GltfLoader::LoadMeshData(
batchInfo.attributeDataOffset = attributeDataOffset;
batchInfo.attributeDataSize = attributeDataSize;
batchInfo.indexFormat = indexFormat;
batchInfo.repackedIndexFormat = repackedIndexFormat;
batchInfo.indexCount = indexCount;

// Material
Expand Down Expand Up @@ -1359,19 +1376,23 @@ ppx::Result GltfLoader::LoadMeshData(
//
bool genTopologyIndices = false;
if (batch.indexFormat == grfx::FORMAT_UNDEFINED) {
genTopologyIndices = true;
batch.indexFormat = (batch.vertexCount < 65536) ? grfx::FORMAT_R16_UINT : grfx::FORMAT_R32_UINT;
genTopologyIndices = true;
batch.indexFormat = (batch.vertexCount < 65536) ? grfx::FORMAT_R16_UINT : grfx::FORMAT_R32_UINT;
batch.repackedIndexFormat = batch.indexFormat;
}

// Create genTopologyIndices so we can repack gemetry data into position planar + packed vertex attributes.
// Create targetGeometry so we can repack gemetry data into position planar + packed vertex attributes.
Geometry targetGeometry = {};
const bool hasAttributes = (loadParams.requiredVertexAttributes.mask != 0);
//
{
auto createInfo = hasAttributes ? GeometryCreateInfo::PositionPlanarU16() : GeometryCreateInfo::PlanarU16();
if (batch.indexFormat == grfx::FORMAT_R32_UINT) {
if (batch.repackedIndexFormat == grfx::FORMAT_R32_UINT) {
createInfo = hasAttributes ? GeometryCreateInfo::PositionPlanarU32() : GeometryCreateInfo::PlanarU32();
}
else if (batch.repackedIndexFormat == grfx::FORMAT_R8_UINT) {
createInfo = hasAttributes ? GeometryCreateInfo::PositionPlanarU8() : GeometryCreateInfo::PlanarU8();
}
// clang-format off
if (loadParams.requiredVertexAttributes.bits.texCoords) createInfo.AddTexCoord(targetTexCoordFormat);
if (loadParams.requiredVertexAttributes.bits.normals) createInfo.AddNormal(targetNormalFormat);
Expand Down Expand Up @@ -1411,6 +1432,13 @@ ppx::Result GltfLoader::LoadMeshData(
targetGeometry.AppendIndex(*pGltfIndex);
}
}
// UINT8
else if (batch.indexFormat == grfx::FORMAT_R8_UINT) {
const uint8_t* pGltfIndex = static_cast<const uint8_t*>(pGltfIndices);
for (cgltf_size i = 0; i < pGltfAccessor->count; ++i, ++pGltfIndex) {
targetGeometry.AppendIndex(*pGltfIndex);
}
}
}
}

Expand Down Expand Up @@ -1526,7 +1554,7 @@ ppx::Result GltfLoader::LoadMeshData(
const uint32_t repackedPositionBufferSize = targetGeometry.GetVertexBuffer(0)->GetSize();
const uint32_t repackedAttributeBufferSize = hasAttributes ? targetGeometry.GetVertexBuffer(1)->GetSize() : 0;
if (repackedIndexBufferSize != batch.indexDataSize) {
PPX_ASSERT_MSG(false, "repacked index buffer size does not match batch's index data size");
PPX_ASSERT_MSG(false, "repacked index buffer size (" << repackedIndexBufferSize << ") does not match batch's index data size (" << batch.indexDataSize << ")");
return ppx::ERROR_SCENE_INVALID_SOURCE_GEOMETRY_INDEX_DATA;
}
if (repackedPositionBufferSize != batch.positionDataSize) {
Expand Down Expand Up @@ -1590,7 +1618,8 @@ ppx::Result GltfLoader::LoadMeshData(
for (uint32_t batchIdx = 0; batchIdx < CountU32(batchInfos); ++batchIdx) {
const auto& batch = batchInfos[batchIdx];

const grfx::IndexType indexType = (batch.indexFormat == grfx::FORMAT_R32_UINT) ? grfx::INDEX_TYPE_UINT32 : grfx::INDEX_TYPE_UINT16;
const grfx::IndexType indexType = (batch.repackedIndexFormat == grfx::FORMAT_R32_UINT) ? grfx::INDEX_TYPE_UINT32 : (batch.repackedIndexFormat == grfx::FORMAT_R8_UINT) ? grfx::INDEX_TYPE_UINT8
footballhead marked this conversation as resolved.
Show resolved Hide resolved
: grfx::INDEX_TYPE_UINT16;
grfx::IndexBufferView indexBufferView = grfx::IndexBufferView(targetGpuBuffer, indexType, batch.indexDataOffset, batch.indexDataSize);

grfx::VertexBufferView positionBufferView = grfx::VertexBufferView(targetGpuBuffer, targetPositionElementSize, batch.positionDataOffset, batch.positionDataSize);
Expand Down
Loading