vertex buffer quantization (#777)

This commit is contained in:
Turánszki János
2023-11-16 18:23:23 +01:00
committed by GitHub
parent ed15992c1b
commit 1479fe9084
45 changed files with 1001 additions and 388 deletions
+245 -52
View File
@@ -460,13 +460,15 @@ namespace wi::scene
generalBuffer = {};
streamoutBuffer = {};
ib = {};
vb_pos_nor_wind = {};
vb_pos_wind = {};
vb_nor = {};
vb_tan = {};
vb_uvs = {};
vb_atl = {};
vb_col = {};
vb_bon = {};
so_pos_nor_wind = {};
so_pos = {};
so_nor = {};
so_tan = {};
so_pre = {};
BLASes.clear();
@@ -562,6 +564,108 @@ namespace wi::scene
const size_t uv_count = std::max(vertex_uvset_0.size(), vertex_uvset_1.size());
// Bounds computation:
XMFLOAT3 _min = XMFLOAT3(std::numeric_limits<float>::max(), std::numeric_limits<float>::max(), std::numeric_limits<float>::max());
XMFLOAT3 _max = XMFLOAT3(std::numeric_limits<float>::lowest(), std::numeric_limits<float>::lowest(), std::numeric_limits<float>::lowest());
for (size_t i = 0; i < vertex_positions.size(); ++i)
{
const XMFLOAT3& pos = vertex_positions[i];
_min = wi::math::Min(_min, pos);
_max = wi::math::Max(_max, pos);
}
aabb = AABB(_min, _max);
if (IsQuantizedPositionsDisabled())
{
position_format = vertex_windweights.empty() ? Vertex_POS32::FORMAT : Vertex_POS32W::FORMAT;
}
else
{
// Determine minimum precision for positions:
const float target_precision = 1.0f / 1000.0f; // millimeter
position_format = Vertex_POS10::FORMAT;
for (size_t i = 0; i < vertex_positions.size(); ++i)
{
const XMFLOAT3& pos = vertex_positions[i];
const uint8_t wind = vertex_windweights.empty() ? 0xFF : vertex_windweights[i];
if (position_format == Vertex_POS10::FORMAT)
{
Vertex_POS10 v;
v.FromFULL(aabb, pos, wind);
XMFLOAT3 p = v.GetPOS(aabb);
if (
std::abs(p.x - pos.x) <= target_precision &&
std::abs(p.y - pos.y) <= target_precision &&
std::abs(p.z - pos.z) <= target_precision &&
wind == v.GetWind()
)
{
// success, continue to next vertex with 8 bits
continue;
}
position_format = Vertex_POS16::FORMAT; // failed, increase to 16 bits
}
if (position_format == Vertex_POS16::FORMAT)
{
Vertex_POS16 v;
v.FromFULL(aabb, pos, wind);
XMFLOAT3 p = v.GetPOS(aabb);
if (
std::abs(p.x - pos.x) <= target_precision &&
std::abs(p.y - pos.y) <= target_precision &&
std::abs(p.z - pos.z) <= target_precision &&
wind == v.GetWind()
)
{
// success, continue to next vertex with 16 bits
continue;
}
position_format = vertex_windweights.empty() ? Vertex_POS32::FORMAT : Vertex_POS32W::FORMAT; // failed, increase to 32 bits
break; // since 32 bit is the max, we can bail out
}
}
if (IsFormatUnorm(position_format))
{
// This is done to avoid 0 scaling on any axis of the UNORM remap matrix of the AABB
// It specifically solves a problem with hardware raytracing which treats AABB with zero axis as invisible
if (aabb._max.x - aabb._min.x < std::numeric_limits<float>::epsilon())
{
aabb._max.x += std::numeric_limits<float>::epsilon();
aabb._min.x -= std::numeric_limits<float>::epsilon();
}
if (aabb._max.y - aabb._min.y < std::numeric_limits<float>::epsilon())
{
aabb._max.y += std::numeric_limits<float>::epsilon();
aabb._min.y -= std::numeric_limits<float>::epsilon();
}
if (aabb._max.z - aabb._min.z < std::numeric_limits<float>::epsilon())
{
aabb._max.z += std::numeric_limits<float>::epsilon();
aabb._min.z -= std::numeric_limits<float>::epsilon();
}
}
}
// Determine UV range for normalization:
if (!vertex_uvset_0.empty() || !vertex_uvset_1.empty())
{
const XMFLOAT2* uv0_stream = vertex_uvset_0.empty() ? vertex_uvset_1.data() : vertex_uvset_0.data();
const XMFLOAT2* uv1_stream = vertex_uvset_1.empty() ? vertex_uvset_0.data() : vertex_uvset_1.data();
uv_range_min = XMFLOAT2(std::numeric_limits<float>::max(), std::numeric_limits<float>::max());
uv_range_max = XMFLOAT2(std::numeric_limits<float>::lowest(), std::numeric_limits<float>::lowest());
for (size_t i = 0; i < uv_count; ++i)
{
uv_range_max = wi::math::Max(uv_range_max, uv0_stream[i]);
uv_range_max = wi::math::Max(uv_range_max, uv1_stream[i]);
uv_range_min = wi::math::Min(uv_range_min, uv0_stream[i]);
uv_range_min = wi::math::Min(uv_range_min, uv1_stream[i]);
}
}
const size_t position_stride = GetFormatStride(position_format);
GPUBufferDesc bd;
if (device->CheckCapability(GraphicsDeviceCapability::CACHE_COHERENT_UMA))
{
@@ -580,8 +684,9 @@ namespace wi::scene
}
const uint64_t alignment = device->GetMinOffsetAlignment(&bd);
bd.size =
AlignTo(vertex_positions.size() * position_stride, alignment) + // position will be first to have 0 offset for flexible alignment!
AlignTo(indices.size() * GetIndexStride(), alignment) +
AlignTo(vertex_positions.size() * sizeof(Vertex_POS), alignment) +
AlignTo(vertex_normals.size() * sizeof(Vertex_NOR), alignment) +
AlignTo(vertex_tangents.size() * sizeof(Vertex_TAN), alignment) +
AlignTo(uv_count * sizeof(Vertex_UVS), alignment) +
AlignTo(vertex_atlas.size() * sizeof(Vertex_TEX), alignment) +
@@ -607,6 +712,78 @@ namespace wi::scene
uint8_t* buffer_data = (uint8_t*)dest;
uint64_t buffer_offset = 0ull;
// vertexBuffer - POSITION + WIND:
switch (position_format)
{
case Vertex_POS10::FORMAT:
{
vb_pos_wind.offset = buffer_offset;
vb_pos_wind.size = vertex_positions.size() * sizeof(Vertex_POS10);
Vertex_POS10* vertices = (Vertex_POS10*)(buffer_data + buffer_offset);
buffer_offset += AlignTo(vb_pos_wind.size, alignment);
for (size_t i = 0; i < vertex_positions.size(); ++i)
{
XMFLOAT3 pos = vertex_positions[i];
const uint8_t wind = vertex_windweights.empty() ? 0xFF : vertex_windweights[i];
Vertex_POS10 vert;
vert.FromFULL(aabb, pos, wind);
std::memcpy(vertices + i, &vert, sizeof(vert));
}
}
break;
case Vertex_POS16::FORMAT:
{
vb_pos_wind.offset = buffer_offset;
vb_pos_wind.size = vertex_positions.size() * sizeof(Vertex_POS16);
Vertex_POS16* vertices = (Vertex_POS16*)(buffer_data + buffer_offset);
buffer_offset += AlignTo(vb_pos_wind.size, alignment);
for (size_t i = 0; i < vertex_positions.size(); ++i)
{
XMFLOAT3 pos = vertex_positions[i];
const uint8_t wind = vertex_windweights.empty() ? 0xFF : vertex_windweights[i];
Vertex_POS16 vert;
vert.FromFULL(aabb, pos, wind);
std::memcpy(vertices + i, &vert, sizeof(vert));
}
}
break;
case Vertex_POS32::FORMAT:
{
vb_pos_wind.offset = buffer_offset;
vb_pos_wind.size = vertex_positions.size() * sizeof(Vertex_POS32);
Vertex_POS32* vertices = (Vertex_POS32*)(buffer_data + buffer_offset);
buffer_offset += AlignTo(vb_pos_wind.size, alignment);
for (size_t i = 0; i < vertex_positions.size(); ++i)
{
const XMFLOAT3& pos = vertex_positions[i];
const uint8_t wind = vertex_windweights.empty() ? 0xFF : vertex_windweights[i];
Vertex_POS32 vert;
vert.FromFULL(pos);
std::memcpy(vertices + i, &vert, sizeof(vert));
}
}
break;
case Vertex_POS32W::FORMAT:
{
vb_pos_wind.offset = buffer_offset;
vb_pos_wind.size = vertex_positions.size() * sizeof(Vertex_POS32W);
Vertex_POS32W* vertices = (Vertex_POS32W*)(buffer_data + buffer_offset);
buffer_offset += AlignTo(vb_pos_wind.size, alignment);
for (size_t i = 0; i < vertex_positions.size(); ++i)
{
const XMFLOAT3& pos = vertex_positions[i];
const uint8_t wind = vertex_windweights.empty() ? 0xFF : vertex_windweights[i];
Vertex_POS32W vert;
vert.FromFULL(pos, wind);
std::memcpy(vertices + i, &vert, sizeof(vert));
}
}
break;
default:
assert(0);
break;
}
// Create index buffer GPU data:
if (GetIndexFormat() == IndexBufferFormat::UINT32)
{
@@ -628,32 +805,23 @@ namespace wi::scene
}
}
XMFLOAT3 _min = XMFLOAT3(std::numeric_limits<float>::max(), std::numeric_limits<float>::max(), std::numeric_limits<float>::max());
XMFLOAT3 _max = XMFLOAT3(std::numeric_limits<float>::lowest(), std::numeric_limits<float>::lowest(), std::numeric_limits<float>::lowest());
// vertexBuffer - POSITION + NORMAL + WIND:
// vertexBuffer - NORMALS:
if (!vertex_normals.empty())
{
vb_pos_nor_wind.offset = buffer_offset;
vb_pos_nor_wind.size = vertex_positions.size() * sizeof(Vertex_POS);
Vertex_POS* vertices = (Vertex_POS*)(buffer_data + buffer_offset);
buffer_offset += AlignTo(vb_pos_nor_wind.size, alignment);
for (size_t i = 0; i < vertex_positions.size(); ++i)
vb_nor.offset = buffer_offset;
vb_nor.size = vertex_normals.size() * sizeof(Vertex_NOR);
Vertex_NOR* vertices = (Vertex_NOR*)(buffer_data + buffer_offset);
buffer_offset += AlignTo(vb_nor.size, alignment);
for (size_t i = 0; i < vertex_normals.size(); ++i)
{
const XMFLOAT3& pos = vertex_positions[i];
XMFLOAT3 nor = vertex_normals.empty() ? XMFLOAT3(1, 1, 1) : vertex_normals[i];
XMStoreFloat3(&nor, XMVector3Normalize(XMLoadFloat3(&nor)));
const uint8_t wind = vertex_windweights.empty() ? 0xFF : vertex_windweights[i];
Vertex_POS vert;
vert.FromFULL(pos, nor, wind);
Vertex_NOR vert;
vert.FromFULL(nor);
std::memcpy(vertices + i, &vert, sizeof(vert));
_min = wi::math::Min(_min, pos);
_max = wi::math::Max(_max, pos);
}
}
aabb = AABB(_min, _max);
// vertexBuffer - TANGENTS
if (!vertex_tangents.empty())
{
@@ -682,8 +850,8 @@ namespace wi::scene
for (size_t i = 0; i < uv_count; ++i)
{
Vertex_UVS vert;
vert.uv0.FromFULL(uv0_stream[i]);
vert.uv1.FromFULL(uv1_stream[i]);
vert.uv0.FromFULL(uv0_stream[i], uv_range_min, uv_range_max);
vert.uv1.FromFULL(uv1_stream[i], uv_range_min, uv_range_max);
std::memcpy(vertices + i, &vert, sizeof(vert));
}
}
@@ -812,10 +980,15 @@ namespace wi::scene
ib.subresource_srv = device->CreateSubresource(&generalBuffer, SubresourceType::SRV, ib.offset, ib.size, &ib_format);
ib.descriptor_srv = device->GetDescriptorIndex(&generalBuffer, SubresourceType::SRV, ib.subresource_srv);
assert(vb_pos_nor_wind.IsValid());
vb_pos_nor_wind.subresource_srv = device->CreateSubresource(&generalBuffer, SubresourceType::SRV, vb_pos_nor_wind.offset, vb_pos_nor_wind.size, &Vertex_POS::FORMAT);
vb_pos_nor_wind.descriptor_srv = device->GetDescriptorIndex(&generalBuffer, SubresourceType::SRV, vb_pos_nor_wind.subresource_srv);
assert(vb_pos_wind.IsValid());
vb_pos_wind.subresource_srv = device->CreateSubresource(&generalBuffer, SubresourceType::SRV, vb_pos_wind.offset, vb_pos_wind.size, &position_format);
vb_pos_wind.descriptor_srv = device->GetDescriptorIndex(&generalBuffer, SubresourceType::SRV, vb_pos_wind.subresource_srv);
if (vb_nor.IsValid())
{
vb_nor.subresource_srv = device->CreateSubresource(&generalBuffer, SubresourceType::SRV, vb_nor.offset, vb_nor.size, &Vertex_NOR::FORMAT);
vb_nor.descriptor_srv = device->GetDescriptorIndex(&generalBuffer, SubresourceType::SRV, vb_nor.subresource_srv);
}
if (vb_tan.IsValid())
{
vb_tan.subresource_srv = device->CreateSubresource(&generalBuffer, SubresourceType::SRV, vb_tan.offset, vb_tan.size, &Vertex_TAN::FORMAT);
@@ -864,9 +1037,12 @@ namespace wi::scene
{
desc.misc_flags |= ResourceMiscFlag::RAY_TRACING;
}
const uint64_t alignment = device->GetMinOffsetAlignment(&desc);
const uint64_t alignment = device->GetMinOffsetAlignment(&desc) * sizeof(Vertex_POS32); // additional alignment for RGB32F
desc.size =
AlignTo(vertex_positions.size() * sizeof(Vertex_POS) * 2, alignment) + // *2 because prevpos also goes into this!
AlignTo(vertex_positions.size() * sizeof(Vertex_POS32), alignment) + // pos
AlignTo(vertex_positions.size() * sizeof(Vertex_POS32), alignment) + // prevpos
AlignTo(vertex_normals.size() * sizeof(Vertex_NOR), alignment) +
AlignTo(vertex_tangents.size() * sizeof(Vertex_TAN), alignment)
;
@@ -876,13 +1052,32 @@ namespace wi::scene
uint64_t buffer_offset = 0ull;
so_pos_nor_wind.offset = buffer_offset;
so_pos_nor_wind.size = vb_pos_nor_wind.size;
buffer_offset += AlignTo(so_pos_nor_wind.size, alignment);
so_pos_nor_wind.subresource_srv = device->CreateSubresource(&streamoutBuffer, SubresourceType::SRV, so_pos_nor_wind.offset, so_pos_nor_wind.size, &Vertex_POS::FORMAT);
so_pos_nor_wind.subresource_uav = device->CreateSubresource(&streamoutBuffer, SubresourceType::UAV, so_pos_nor_wind.offset, so_pos_nor_wind.size, &Vertex_POS::FORMAT);
so_pos_nor_wind.descriptor_srv = device->GetDescriptorIndex(&streamoutBuffer, SubresourceType::SRV, so_pos_nor_wind.subresource_srv);
so_pos_nor_wind.descriptor_uav = device->GetDescriptorIndex(&streamoutBuffer, SubresourceType::UAV, so_pos_nor_wind.subresource_uav);
so_pos.offset = buffer_offset;
so_pos.size = vertex_positions.size() * sizeof(Vertex_POS32);
buffer_offset += AlignTo(so_pos.size, alignment);
so_pos.subresource_srv = device->CreateSubresource(&streamoutBuffer, SubresourceType::SRV, so_pos.offset, so_pos.size, &Vertex_POS32::FORMAT);
so_pos.subresource_uav = device->CreateSubresource(&streamoutBuffer, SubresourceType::UAV, so_pos.offset, so_pos.size); // UAV can't have RGB32_F format!
so_pos.descriptor_srv = device->GetDescriptorIndex(&streamoutBuffer, SubresourceType::SRV, so_pos.subresource_srv);
so_pos.descriptor_uav = device->GetDescriptorIndex(&streamoutBuffer, SubresourceType::UAV, so_pos.subresource_uav);
so_pre.offset = buffer_offset;
so_pre.size = so_pos.size;
buffer_offset += AlignTo(so_pre.size, alignment);
so_pre.subresource_srv = device->CreateSubresource(&streamoutBuffer, SubresourceType::SRV, so_pre.offset, so_pre.size, &Vertex_POS32::FORMAT);
so_pre.subresource_uav = device->CreateSubresource(&streamoutBuffer, SubresourceType::UAV, so_pre.offset, so_pre.size); // UAV can't have RGB32_F format!
so_pre.descriptor_srv = device->GetDescriptorIndex(&streamoutBuffer, SubresourceType::SRV, so_pre.subresource_srv);
so_pre.descriptor_uav = device->GetDescriptorIndex(&streamoutBuffer, SubresourceType::UAV, so_pre.subresource_uav);
if (vb_nor.IsValid())
{
so_nor.offset = buffer_offset;
so_nor.size = vb_nor.size;
buffer_offset += AlignTo(so_nor.size, alignment);
so_nor.subresource_srv = device->CreateSubresource(&streamoutBuffer, SubresourceType::SRV, so_nor.offset, so_nor.size, &Vertex_NOR::FORMAT);
so_nor.subresource_uav = device->CreateSubresource(&streamoutBuffer, SubresourceType::UAV, so_nor.offset, so_nor.size, &Vertex_NOR::FORMAT);
so_nor.descriptor_srv = device->GetDescriptorIndex(&streamoutBuffer, SubresourceType::SRV, so_nor.subresource_srv);
so_nor.descriptor_uav = device->GetDescriptorIndex(&streamoutBuffer, SubresourceType::UAV, so_nor.subresource_uav);
}
if (vb_tan.IsValid())
{
@@ -894,14 +1089,6 @@ namespace wi::scene
so_tan.descriptor_srv = device->GetDescriptorIndex(&streamoutBuffer, SubresourceType::SRV, so_tan.subresource_srv);
so_tan.descriptor_uav = device->GetDescriptorIndex(&streamoutBuffer, SubresourceType::UAV, so_tan.subresource_uav);
}
so_pre.offset = buffer_offset;
so_pre.size = vb_pos_nor_wind.size;
buffer_offset += AlignTo(so_pre.size, alignment);
so_pre.subresource_srv = device->CreateSubresource(&streamoutBuffer, SubresourceType::SRV, so_pre.offset, so_pre.size, &Vertex_POS::FORMAT);
so_pre.subresource_uav = device->CreateSubresource(&streamoutBuffer, SubresourceType::UAV, so_pre.offset, so_pre.size, &Vertex_POS::FORMAT);
so_pre.descriptor_srv = device->GetDescriptorIndex(&streamoutBuffer, SubresourceType::SRV, so_pre.subresource_srv);
so_pre.descriptor_uav = device->GetDescriptorIndex(&streamoutBuffer, SubresourceType::UAV, so_pre.subresource_uav);
}
void MeshComponent::CreateRaytracingRenderData()
{
@@ -939,14 +1126,22 @@ namespace wi::scene
auto& geometry = desc.bottom_level.geometries.back();
geometry.type = RaytracingAccelerationStructureDesc::BottomLevel::Geometry::Type::TRIANGLES;
geometry.triangles.vertex_buffer = generalBuffer;
geometry.triangles.vertex_byte_offset = vb_pos_nor_wind.offset;
geometry.triangles.vertex_byte_offset = vb_pos_wind.offset;
geometry.triangles.index_buffer = generalBuffer;
geometry.triangles.index_format = GetIndexFormat();
geometry.triangles.index_count = subset.indexCount;
geometry.triangles.index_offset = ib.offset / GetIndexStride() + subset.indexOffset;
geometry.triangles.vertex_count = (uint32_t)vertex_positions.size();
geometry.triangles.vertex_format = Format::R32G32B32_FLOAT;
geometry.triangles.vertex_stride = sizeof(MeshComponent::Vertex_POS);
if (so_pos.IsValid())
{
geometry.triangles.vertex_format = Vertex_POS32::FORMAT;
geometry.triangles.vertex_stride = sizeof(Vertex_POS32);
}
else
{
geometry.triangles.vertex_format = position_format == Format::R32G32B32A32_FLOAT ? Format::R32G32B32_FLOAT : position_format;
geometry.triangles.vertex_stride = GetFormatStride(position_format);
}
}
bool success = device->CreateRaytracingAccelerationStructure(&desc, &BLASes[lod]);
@@ -1616,20 +1811,18 @@ namespace wi::scene
void SoftBodyPhysicsComponent::CreateFromMesh(const MeshComponent& mesh)
{
vertex_positions_simulation.resize(mesh.vertex_positions.size());
vertex_normals_simulation.resize(mesh.vertex_normals.size());
vertex_tangents_tmp.resize(mesh.vertex_tangents.size());
vertex_tangents_simulation.resize(mesh.vertex_tangents.size());
XMMATRIX W = XMLoadFloat4x4(&worldMatrix);
XMFLOAT3 _min = XMFLOAT3(std::numeric_limits<float>::max(), std::numeric_limits<float>::max(), std::numeric_limits<float>::max());
XMFLOAT3 _max = XMFLOAT3(std::numeric_limits<float>::lowest(), std::numeric_limits<float>::lowest(), std::numeric_limits<float>::lowest());
for (size_t i = 0; i < vertex_positions_simulation.size(); ++i)
XMMATRIX W = XMLoadFloat4x4(&worldMatrix);
for (size_t i = 0; i < mesh.vertex_positions.size(); ++i)
{
XMFLOAT3 pos = mesh.vertex_positions[i];
XMStoreFloat3(&pos, XMVector3Transform(XMLoadFloat3(&pos), W));
XMFLOAT3 nor = mesh.vertex_normals.empty() ? XMFLOAT3(1, 1, 1) : mesh.vertex_normals[i];
XMStoreFloat3(&nor, XMVector3Normalize(XMVector3TransformNormal(XMLoadFloat3(&nor), W)));
const uint8_t wind = mesh.vertex_windweights.empty() ? 0xFF : mesh.vertex_windweights[i];
vertex_positions_simulation[i].FromFULL(pos, nor, wind);
vertex_positions_simulation[i].FromFULL(pos);
_min = wi::math::Min(_min, pos);
_max = wi::math::Max(_max, pos);
}