Skip to content

Commit

Permalink
Add support for KHR_mesh_quantization.
Browse files Browse the repository at this point in the history
For handling both normalized and unnormalized accessor in shader, boolean flag that indicates the normalization is embeded at the 4-th bit (8) from LSB.
  • Loading branch information
stripe2933 committed Feb 20, 2025
1 parent ea2d37b commit 001c603
Show file tree
Hide file tree
Showing 16 changed files with 266 additions and 68 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ Blazingly fast[^1] Vulkan glTF viewer.
- Support glTF 2.0 extensions:
- [`KHR_materials_unlit`](https://github.com/KhronosGroup/glTF/tree/main/extensions/2.0/Khronos/KHR_materials_unlit) for lighting independent material shading
- [`KHR_materials_variants`](https://github.com/KhronosGroup/glTF/tree/main/extensions/2.0/Khronos/KHR_materials_variants)
- [`KHR_mesh_quantization`](https://github.com/KhronosGroup/glTF/tree/main/extensions/2.0/Khronos/KHR_mesh_quantization)
- [`KHR_texture_basisu`](https://github.com/KhronosGroup/glTF/tree/main/extensions/2.0/Khronos/KHR_texture_basisu) for BC7 GPU compression texture decoding
- [`KHR_texture_transform`](https://github.com/KhronosGroup/glTF/tree/main/extensions/2.0/Khronos/KHR_texture_transform)
- [`EXT_mesh_gpu_instancing`](https://github.com/KhronosGroup/glTF/tree/main/extensions/2.0/Vendor/EXT_mesh_gpu_instancing) for instancing multiple meshes with the same geometry
Expand Down
22 changes: 22 additions & 0 deletions impl/vulkan/Frame.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import :helpers.optional;
import :helpers.ranges;
import :vulkan.ag.DepthPrepass;
import :vulkan.buffer.IndirectDrawCommands;
import :vulkan.shader_type.Accessor;

constexpr auto NO_INDEX = std::numeric_limits<std::uint16_t>::max();

Expand Down Expand Up @@ -147,6 +148,7 @@ auto vk_gltf_viewer::vulkan::Frame::update(const ExecutionTask &task) -> UpdateR

if (material.unlit) {
result.pipeline = sharedData.getUnlitPrimitiveRenderer({
.positionComponentType = accessors.positionAccessor.componentType,
.baseColorTexcoordComponentType = material.pbrData.baseColorTexture.transform([&](const fastgltf::TextureInfo &textureInfo) {
return accessors.texcoordAccessors.at(textureInfo.texCoordIndex).componentType;
}),
Expand All @@ -162,6 +164,13 @@ auto vk_gltf_viewer::vulkan::Frame::update(const ExecutionTask &task) -> UpdateR
}
else {
result.pipeline = sharedData.getPrimitiveRenderer({
.positionComponentType = accessors.positionAccessor.componentType,
.normalComponentType = accessors.normalAccessor.transform([](const shader_type::Accessor &accessor) {
return accessor.componentType;
}),
.tangentComponentType = accessors.tangentAccessor.transform([](const shader_type::Accessor &accessor) {
return accessor.componentType;
}),
.texcoordComponentTypes = accessors.texcoordAccessors
| std::views::transform([](const auto &info) {
return info.componentType;
Expand Down Expand Up @@ -202,6 +211,13 @@ auto vk_gltf_viewer::vulkan::Frame::update(const ExecutionTask &task) -> UpdateR
}
else {
result.pipeline = sharedData.getPrimitiveRenderer({
.positionComponentType = accessors.positionAccessor.componentType,
.normalComponentType = accessors.normalAccessor.transform([](const shader_type::Accessor &accessor) {
return accessor.componentType;
}),
.tangentComponentType = accessors.tangentAccessor.transform([](const shader_type::Accessor &accessor) {
return accessor.componentType;
}),
.texcoordComponentTypes = accessors.texcoordAccessors
| std::views::transform([](const auto &info) {
return info.componentType;
Expand Down Expand Up @@ -238,6 +254,7 @@ auto vk_gltf_viewer::vulkan::Frame::update(const ExecutionTask &task) -> UpdateR
const fastgltf::Material& material = task.gltf->asset.materials[*primitive.materialIndex];
if (material.alphaMode == fastgltf::AlphaMode::Mask) {
result.pipeline = sharedData.getMaskDepthRenderer({
.positionComponentType = accessors.positionAccessor.componentType,
.baseColorTexcoordComponentType = material.pbrData.baseColorTexture.transform([&](const fastgltf::TextureInfo &textureInfo) {
return accessors.texcoordAccessors.at(textureInfo.texCoordIndex).componentType;
}),
Expand All @@ -253,13 +270,15 @@ auto vk_gltf_viewer::vulkan::Frame::update(const ExecutionTask &task) -> UpdateR
}
else {
result.pipeline = sharedData.getDepthRenderer({
.positionComponentType = accessors.positionAccessor.componentType,
.positionMorphTargetWeightCount = static_cast<std::uint32_t>(accessors.positionMorphTargetAccessors.size()),
});
}
result.cullMode = material.doubleSided ? vk::CullModeFlagBits::eNone : vk::CullModeFlagBits::eBack;
}
else {
result.pipeline = sharedData.getDepthRenderer({
.positionComponentType = accessors.positionAccessor.componentType,
.positionMorphTargetWeightCount = static_cast<std::uint32_t>(accessors.positionMorphTargetAccessors.size()),
});
}
Expand All @@ -279,6 +298,7 @@ auto vk_gltf_viewer::vulkan::Frame::update(const ExecutionTask &task) -> UpdateR
const fastgltf::Material &material = task.gltf->asset.materials[*primitive.materialIndex];
if (material.alphaMode == fastgltf::AlphaMode::Mask) {
result.pipeline = sharedData.getMaskJumpFloodSeedRenderer({
.positionComponentType = accessors.positionAccessor.componentType,
.baseColorTexcoordComponentType = material.pbrData.baseColorTexture.transform([&](const fastgltf::TextureInfo &textureInfo) {
return accessors.texcoordAccessors.at(textureInfo.texCoordIndex).componentType;
}),
Expand All @@ -294,13 +314,15 @@ auto vk_gltf_viewer::vulkan::Frame::update(const ExecutionTask &task) -> UpdateR
}
else {
result.pipeline = sharedData.getJumpFloodSeedRenderer({
.positionComponentType = accessors.positionAccessor.componentType,
.positionMorphTargetWeightCount = static_cast<std::uint32_t>(accessors.positionMorphTargetAccessors.size()),
});
}
result.cullMode = material.doubleSided ? vk::CullModeFlagBits::eNone : vk::CullModeFlagBits::eBack;
}
else {
result.pipeline = sharedData.getJumpFloodSeedRenderer({
.positionComponentType = accessors.positionAccessor.componentType,
.positionMorphTargetWeightCount = static_cast<std::uint32_t>(accessors.positionMorphTargetAccessors.size()),
});
}
Expand Down
1 change: 1 addition & 0 deletions interface/MainApp.cppm
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ namespace vk_gltf_viewer {
static constexpr fastgltf::Extensions SUPPORTED_EXTENSIONS
= fastgltf::Extensions::KHR_materials_unlit
| fastgltf::Extensions::KHR_materials_variants
| fastgltf::Extensions::KHR_mesh_quantization
| fastgltf::Extensions::KHR_texture_basisu
| fastgltf::Extensions::KHR_texture_transform
| fastgltf::Extensions::EXT_mesh_gpu_instancing;
Expand Down
3 changes: 2 additions & 1 deletion interface/vulkan/buffer/PrimitiveAttributes.cppm
Original file line number Diff line number Diff line change
Expand Up @@ -239,7 +239,8 @@ namespace vk_gltf_viewer::vulkan::buffer {
const auto getGpuAccessor = [&](std::size_t accessorIndex) {
const fastgltf::Accessor &accessor = asset.accessors[accessorIndex];
shader_type::Accessor result {
.componentType = static_cast<std::uint8_t>(getGLComponentType(accessor.componentType) - getGLComponentType(fastgltf::ComponentType::Byte)),
.componentType = static_cast<std::uint8_t>((accessor.normalized ? 8U : 0U)
| (getGLComponentType(accessor.componentType) - getGLComponentType(fastgltf::ComponentType::Byte))),
.componentCount = static_cast<std::uint8_t>(getNumComponents(accessor.type)),
};

Expand Down
7 changes: 6 additions & 1 deletion interface/vulkan/pipeline/DepthRenderer.cppm
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import :vulkan.specialization_constants.SpecializationMap;
namespace vk_gltf_viewer::vulkan::inline pipeline {
export class DepthRendererSpecialization {
public:
std::uint8_t positionComponentType = 0;
std::uint32_t positionMorphTargetWeightCount = 0;

[[nodiscard]] bool operator==(const DepthRendererSpecialization&) const = default;
Expand Down Expand Up @@ -60,16 +61,18 @@ namespace vk_gltf_viewer::vulkan::inline pipeline {

private:
struct VertexShaderSpecializationData {
std::uint32_t positionComponentType;
std::uint32_t positionMorphTargetWeightCount;
};

[[nodiscard]] VertexShaderSpecializationData getVertexShaderSpecializationData() const {
return { positionMorphTargetWeightCount };
return { positionComponentType, positionMorphTargetWeightCount };
}
};

class MaskDepthRendererSpecialization {
public:
std::uint8_t positionComponentType;
std::optional<std::uint8_t> baseColorTexcoordComponentType;
std::optional<std::uint8_t> colorAlphaComponentType;
std::uint32_t positionMorphTargetWeightCount = 0;
Expand Down Expand Up @@ -124,6 +127,7 @@ namespace vk_gltf_viewer::vulkan::inline pipeline {

private:
struct VertexShaderSpecializationData {
std::uint32_t positionComponentType;
std::uint32_t texcoordComponentType = 5126; // FLOAT
std::uint32_t colorComponentType = 5126; // FLOAT
std::uint32_t positionMorphTargetWeightCount;
Expand All @@ -142,6 +146,7 @@ namespace vk_gltf_viewer::vulkan::inline pipeline {

[[nodiscard]] VertexShaderSpecializationData getVertexShaderSpecializationData() const {
VertexShaderSpecializationData result {
.positionComponentType = positionComponentType,
.positionMorphTargetWeightCount = positionMorphTargetWeightCount,
};

Expand Down
7 changes: 6 additions & 1 deletion interface/vulkan/pipeline/JumpFloodSeedRenderer.cppm
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import :vulkan.specialization_constants.SpecializationMap;
namespace vk_gltf_viewer::vulkan::inline pipeline {
export class JumpFloodSeedRendererSpecialization {
public:
std::uint8_t positionComponentType = 0;
std::uint32_t positionMorphTargetWeightCount = 0;

[[nodiscard]] bool operator==(const JumpFloodSeedRendererSpecialization&) const = default;
Expand Down Expand Up @@ -60,16 +61,18 @@ namespace vk_gltf_viewer::vulkan::inline pipeline {

private:
struct VertexShaderSpecializationData {
std::uint32_t positionComponentType;
std::uint32_t positionMorphTargetWeightCount;
};

[[nodiscard]] VertexShaderSpecializationData getVertexShaderSpecializationData() const {
return { positionMorphTargetWeightCount };
return { positionComponentType, positionMorphTargetWeightCount };
}
};

class MaskJumpFloodSeedRendererSpecialization {
public:
std::uint8_t positionComponentType;
std::optional<std::uint8_t> baseColorTexcoordComponentType;
std::optional<std::uint8_t> colorAlphaComponentType;
std::uint32_t positionMorphTargetWeightCount = 0;
Expand Down Expand Up @@ -124,6 +127,7 @@ namespace vk_gltf_viewer::vulkan::inline pipeline {

private:
struct VertexShaderSpecializationData {
std::uint32_t positionComponentType;
std::uint32_t texcoordComponentType = 5126; // FLOAT
std::uint32_t colorComponentType = 5126; // FLOAT
std::uint32_t positionMorphTargetWeightCount;
Expand All @@ -142,6 +146,7 @@ namespace vk_gltf_viewer::vulkan::inline pipeline {

[[nodiscard]] VertexShaderSpecializationData getVertexShaderSpecializationData() const {
VertexShaderSpecializationData result {
.positionComponentType = positionComponentType,
.positionMorphTargetWeightCount = positionMorphTargetWeightCount,
};

Expand Down
40 changes: 26 additions & 14 deletions interface/vulkan/pipeline/PrimitiveRenderer.cppm
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ import :vulkan.specialization_constants.SpecializationMap;
namespace vk_gltf_viewer::vulkan::inline pipeline {
export class PrimitiveRendererSpecialization {
public:
std::uint8_t positionComponentType;
std::optional<std::uint8_t> normalComponentType;
std::optional<std::uint8_t> tangentComponentType;
boost::container::static_vector<std::uint8_t, 4> texcoordComponentTypes;
std::optional<std::pair<std::uint8_t, std::uint8_t>> colorComponentCountAndType;
bool fragmentShaderGeneratedTBN;
Expand Down Expand Up @@ -158,11 +161,10 @@ namespace vk_gltf_viewer::vulkan::inline pipeline {

private:
struct VertexShaderSpecializationData {
std::uint32_t packedTexcoordComponentTypes = 0x06060606; // [FLOAT, FLOAT, FLOAT, FLOAT]
std::uint32_t colorComponentCount = 0;
std::uint32_t colorComponentType = 5126; // FLOAT
std::uint32_t morphTargetWeightCount = 0;
std::uint32_t packedMorphTargetAvailability = 0x00000000;
std::uint32_t packedAttributeComponentTypes;
std::uint32_t colorComponentCount;
std::uint32_t morphTargetWeightCount;
std::uint32_t packedMorphTargetAvailability;
};

struct FragmentShaderSpecializationData {
Expand All @@ -185,21 +187,31 @@ namespace vk_gltf_viewer::vulkan::inline pipeline {
| (hasTangentMorphTarget ? 4U : 0U),
};

for (auto [i, componentType] : texcoordComponentTypes | ranges::views::enumerate) {
assert(ranges::one_of(componentType, 1 /* UNSIGNED_BYTE */, 3 /* UNSIGNED_SHORT */, 6 /* FLOAT */));

// Step 1: clear the i-th byte (=[8*(i+1):8*i] bits)
result.packedTexcoordComponentTypes &= ~(0xFFU << (8 * i));
// Packed components types are:
// PCT & 0xF -> POSITION
// (PCT >> 4) & 0xF -> NORMAL
// (PCT >> 8) & 0xF -> TANGENT
// (PCT >> 12) & 0xF -> TEXCOORD_0 // <- TEXCOORD_<i> attributes starts from third.
// (PCT >> 16) & 0xF -> TEXCOORD_1
// (PCT >> 20) & 0xF -> TEXCOORD_2
// (PCT >> 24) & 0xF -> TEXCOORD_3
// (PCT >> 28) & 0xF -> COLOR_0
result.packedAttributeComponentTypes |= static_cast<std::uint32_t>(positionComponentType);
if (normalComponentType) {
result.packedAttributeComponentTypes |= static_cast<std::uint32_t>(*normalComponentType) << 4;
}
if (tangentComponentType) {
result.packedAttributeComponentTypes |= static_cast<std::uint32_t>(*tangentComponentType) << 8;
}

// Step 2: set the i-th byte to the componentType
result.packedTexcoordComponentTypes |= static_cast<std::uint32_t>(componentType) << (8 * i);
for (auto [i, componentType] : std::views::zip(std::views::iota(3), texcoordComponentTypes)) {
result.packedAttributeComponentTypes |= static_cast<std::uint32_t>(componentType) << (4 * i);
}

if (colorComponentCountAndType) {
assert(ranges::one_of(colorComponentCountAndType->first, 3, 4));
assert(ranges::one_of(colorComponentCountAndType->second, 1 /* UNSIGNED_BYTE */, 3 /* UNSIGNED_SHORT */, 6 /* FLOAT */));
result.colorComponentCount = colorComponentCountAndType->first;
result.colorComponentType = colorComponentCountAndType->second;
result.packedAttributeComponentTypes |= static_cast<std::uint32_t>(colorComponentCountAndType->second) << 28;
}

return result;
Expand Down
3 changes: 3 additions & 0 deletions interface/vulkan/pipeline/UnlitPrimitiveRenderer.cppm
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import :vulkan.specialization_constants.SpecializationMap;
namespace vk_gltf_viewer::vulkan::inline pipeline {
export class UnlitPrimitiveRendererSpecialization {
public:
std::uint8_t positionComponentType;
std::optional<std::uint8_t> baseColorTexcoordComponentType;
std::optional<std::pair<std::uint8_t, std::uint8_t>> colorComponentCountAndType;
std::uint32_t positionMorphTargetWeightCount = 0;
Expand Down Expand Up @@ -147,6 +148,7 @@ namespace vk_gltf_viewer::vulkan::inline pipeline {

private:
struct VertexShaderSpecializationData {
std::uint32_t positionComponentType;
std::uint32_t texcoordComponentType = 5126; // FLOAT
std::uint32_t colorComponentCount = 0;
std::uint32_t colorComponentType = 5126; // FLOAT
Expand All @@ -166,6 +168,7 @@ namespace vk_gltf_viewer::vulkan::inline pipeline {

[[nodiscard]] VertexShaderSpecializationData getVertexShaderSpecializationData() const {
VertexShaderSpecializationData result {
.positionComponentType = positionComponentType,
.positionMorphTargetWeightCount = positionMorphTargetWeightCount,
};

Expand Down
5 changes: 3 additions & 2 deletions shaders/depth.vert
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@
#include "indexing.glsl"
#include "types.glsl"

layout (constant_id = 0) const uint POSITION_MORPH_TARGET_WEIGHT_COUNT = 0;
layout (constant_id = 0) const uint POSITION_COMPONENT_TYPE = 0;
layout (constant_id = 1) const uint POSITION_MORPH_TARGET_WEIGHT_COUNT = 0;

layout (location = 0) flat out uint outNodeIndex;

Expand All @@ -38,6 +39,6 @@ layout (push_constant) uniform PushConstant {
void main(){
outNodeIndex = NODE_INDEX;

vec3 inPosition = getPosition(POSITION_MORPH_TARGET_WEIGHT_COUNT);
vec3 inPosition = getPosition(POSITION_COMPONENT_TYPE, POSITION_MORPH_TARGET_WEIGHT_COUNT);
gl_Position = pc.projectionView * TRANSFORM * vec4(inPosition, 1.0);
}
32 changes: 32 additions & 0 deletions shaders/dequantize.glsl
Original file line number Diff line number Diff line change
@@ -1,34 +1,66 @@
#ifndef DEQUANTIZE_GLSL
#define DEQUANTIZE_GLSL

float dequantize(int8_t data) {
return max(float(data) / 127.0, -1.0);
}

float dequantize(uint8_t data) {
return float(data) / 255.0;
}

float dequantize(int16_t data) {
return max(float(data) / 32767.0, -1.0);
}

float dequantize(uint16_t data) {
return float(data) / 65536.0;
}

vec2 dequantize(i8vec2 data) {
return max(vec2(data) / 127.0, -1.0);
}

vec2 dequantize(u8vec2 data) {
return vec2(data) / 255.0;
}

vec2 dequantize(i16vec2 data) {
return max(vec2(data) / 32767.0, -1.0);
}

vec2 dequantize(u16vec2 data) {
return vec2(data) / 65536.0;
}

vec3 dequantize(i8vec3 data) {
return max(vec3(data) / 127.0, -1.0);
}

vec3 dequantize(u8vec3 data) {
return vec3(data) / 255.0;
}

vec3 dequantize(i16vec3 data) {
return max(vec3(data) / 32767.0, -1.0);
}

vec3 dequantize(u16vec3 data) {
return vec3(data) / 65536.0;
}

vec4 dequantize(i8vec4 data) {
return max(vec4(data) / 127.0, -1.0);
}

vec4 dequantize(u8vec4 data) {
return vec4(data) / 255.0;
}

vec4 dequantize(i16vec4 data) {
return max(vec4(data) / 32767.0, -1.0);
}

vec4 dequantize(u16vec4 data) {
return vec4(data) / 65536.0;
}
Expand Down
Loading

0 comments on commit 001c603

Please sign in to comment.