Skip to content

Commit

Permalink
Merge branch 'skinned-replacement' into 'main'
Browse files Browse the repository at this point in the history
[REMIX-1475] loading skinned replacements

See merge request lightspeedrtx/dxvk-remix-nv!192
  • Loading branch information
MarkEHenderson committed May 23, 2023
2 parents f7a72cd + a7a10c9 commit 657a328
Show file tree
Hide file tree
Showing 6 changed files with 88 additions and 6 deletions.
3 changes: 3 additions & 0 deletions src/dxvk/rtx_render/rtx_context.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -804,6 +804,9 @@ namespace dxvk {
skinningData.numBones = 0;
skinningData.numBonesPerVertex = 0;
}

// Store the numBonesPerVertex in the RasterGeometry as well to allow it to be overridden
geoData.numBonesPerVertex = skinningData.numBonesPerVertex;

// Reset the future
m_rtState.futureSkinningData = std::shared_future<SkinningData>();
Expand Down
4 changes: 3 additions & 1 deletion src/dxvk/rtx_render/rtx_geometry_utils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,8 @@ namespace dxvk {

SkinningArgs params {};

assert(drawCallState.getGeometryData().blendWeightBuffer.defined());

memcpy(&params.bones[0], &drawCallState.getSkinningState().pBoneMatrices[0], sizeof(Matrix4) * drawCallState.getSkinningState().numBones);

params.dstPositionStride = geo.positionBuffer.stride();
Expand All @@ -158,7 +160,7 @@ namespace dxvk {
params.blendIndicesOffset = drawCallState.getGeometryData().blendIndicesBuffer.offsetFromSlice();
params.numVertices = geo.vertexCount;
params.useIndices = drawCallState.getGeometryData().blendIndicesBuffer.defined() ? 1 : 0;
params.numBones = drawCallState.getSkinningState().numBonesPerVertex;
params.numBones = drawCallState.getGeometryData().numBonesPerVertex;

// At some point, its more efficient to do these calculations on the GPU, this limit is somewhat arbitrary however, and might require better tuning...
const uint32_t kNumVerticesToProcessOnCPU = 256;
Expand Down
73 changes: 72 additions & 1 deletion src/dxvk/rtx_render/rtx_mod_usd.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
#include <pxr/usd/usdLux/distantLight.h>
#include "pxr/usd/usdLux/light.h"
#include <pxr/usd/usdLux/blackbody.h>
#include <pxr/usd/usdSkel/bindingAPI.h>
#include <pxr/usd/usdShade/materialBindingAPI.h>
#include <pxr/base/arch/fileSystem.h>
#include "../../lssusd/usd_include_end.h"
Expand Down Expand Up @@ -658,6 +659,8 @@ void UsdMod::Impl::processPrim(Args& args, pxr::UsdPrim& prim) {
pxr::VtArray<pxr::GfVec3f> points;
pxr::VtArray<pxr::GfVec3f> normals;
pxr::VtArray<pxr::GfVec2f> uvs;
pxr::VtArray<float> jointWeights;
pxr::VtArray<int> jointIndices;

const bool hasIndices = prim.HasAttribute(kFaceVertexIndices);
if ((numSubsets <= 1) && !hasIndices) {
Expand All @@ -670,6 +673,28 @@ void UsdMod::Impl::processPrim(Args& args, pxr::UsdPrim& prim) {
prim.GetAttribute(kPoints).Get(&points);
prim.GetAttribute(kNormals).Get(&normals);
prim.GetAttribute(kInvertedUvs).Get(&uvs);

size_t numBones = 0;
if (prim.HasAPI<pxr::UsdSkelBindingAPI>()) {
pxr::UsdSkelBindingAPI skelBinding(prim);
pxr::UsdGeomPrimvar jointIndicesPV = skelBinding.GetJointIndicesPrimvar();
pxr::UsdGeomPrimvar jointWeightsPV = skelBinding.GetJointWeightsPrimvar();
numBones = jointIndicesPV.GetElementSize();
if (numBones > 4) {
Logger::err(str::format("Prim: ", prim.GetPath().GetString(), ", has more than 4 bones per vertex. Falling back to 4 bones per vertex."));
// Should be safe to fall back to just 4 bones, though vertices with more bound bones will animate wrong.
numBones = 4;
}
if (!jointWeightsPV.HasValue()) {
Logger::err(str::format("Prim: ", prim.GetPath().GetString(), ", has Skeleton API but no joint weights."));
}
if (jointWeightsPV.GetElementSize() != numBones) {
Logger::err(str::format("Prim: ", prim.GetPath().GetString(), ", joint indices and joint weights must have matching element sizes."));
}
// TODO need to check that jointWeights exists.
jointIndicesPV.Get(&jointIndices);
jointWeightsPV.Get(&jointWeights);
}

if (points.size() == 0) {
Logger::err(str::format("Prim: ", prim.GetPath().GetString(), ", does not have positional vertices, this is currently a requirement."));
Expand All @@ -684,21 +709,35 @@ void UsdMod::Impl::processPrim(Args& args, pxr::UsdPrim& prim) {
Logger::warn(str::format("Prim: ", prim.GetPath().GetString(), "'s position array length doesn't match uv array's, skip uv data."));
}

if (!jointIndices.empty() && points.size() * numBones != jointIndices.size()) {
Logger::warn(str::format("Prim: ", prim.GetPath().GetString(), "'s num positions (", points.size(),") * bonesPerVertex (", numBones,") doesn't match num jointIndices (", jointIndices.size(),"), skip jointIndices data."));
}

if (!jointWeights.empty() && points.size() * numBones != jointWeights.size()) {
Logger::warn(str::format("Prim: ", prim.GetPath().GetString(), "'s num positions (", points.size(), ") * bonesPerVertex (", numBones, ") doesn't match num jointWeights (", jointIndices.size(), "), skip jointWeights data."));
}

bool isNormalValid = !normals.empty() && points.size() == normals.size();
bool isUVValid = !uvs.empty() && points.size() == uvs.size();
bool isJointIndicesValid = !jointIndices.empty() && points.size() * numBones == jointIndices.size();
bool isJointWeightsValid = !jointWeights.empty() && points.size() * numBones == jointWeights.size();

newGeomData.vertexCount = points.size();

const size_t indexSize = numSubsets <= 1 ? vecIndices.size() * sizeof(uint32_t) : 0; // allocate the worse case here (32-bit indices) - this leaves room for optimization but it should break the bank
const size_t pointsSize = sizeof(pxr::GfVec3f);
const size_t normalsSize = isNormalValid ? sizeof(pxr::GfVec3f) : 0;
const size_t uvSize = isUVValid ? sizeof(pxr::GfVec2f) : 0;
const size_t vertexStructureSize = pointsSize + normalsSize + uvSize;
const size_t jointIndicesSize = isJointIndicesValid ? sizeof(uint32_t): 0;
const size_t jointWeightsSize = isJointWeightsValid ? sizeof(float) * (numBones - 1) : 0; // last weight is 1 minus the other weights
const size_t vertexStructureSize = pointsSize + normalsSize + uvSize + jointIndicesSize + jointWeightsSize;

const size_t indexOffset = 0;
const size_t pointsOffset = dxvk::align(indexSize, CACHE_LINE_SIZE);
const size_t normalsOffset = pointsOffset + pointsSize;
const size_t uvOffset = normalsOffset + normalsSize;
const size_t jointIndicesOffset = uvOffset + uvSize;
const size_t jointWeightsOffset = jointIndicesOffset + jointIndicesSize;

const size_t indexSliceSize = dxvk::align(indexSize, CACHE_LINE_SIZE);
const size_t vertexSliceSize = dxvk::align(vertexStructureSize * newGeomData.vertexCount, CACHE_LINE_SIZE);
Expand Down Expand Up @@ -767,6 +806,21 @@ void UsdMod::Impl::processPrim(Args& args, pxr::UsdPrim& prim) {
(*pBaseVertexData++) = uvs[i][0];
(*pBaseVertexData++) = uvs[i][1];
}

if (isJointIndicesValid) {
uint32_t vertIndices = 0;
for (int j = 0; j < numBones; ++j) {
vertIndices |= jointIndices[i * numBones + j] << 8 * j;
}
memcpy(pBaseVertexData, &vertIndices, sizeof(float));
pBaseVertexData++;
}

if (isJointWeightsValid) {
for (int j = 0; j < numBones - 1; ++j) {
(*pBaseVertexData++) = jointWeights[i * numBones + j];
}
}
}

// Create the snapshots
Expand All @@ -780,6 +834,23 @@ void UsdMod::Impl::processPrim(Args& args, pxr::UsdPrim& prim) {
newGeomData.texcoordBuffer = RasterBuffer(vertexSlice, uvOffset - vertexSlice.offset(), vertexStructureSize, VK_FORMAT_R32G32B32_SFLOAT);
newGeomData.hashes[HashComponents::VertexTexcoord] = ++m_replacedCount;
}

if (isJointIndicesValid) {
newGeomData.blendIndicesBuffer = RasterBuffer(vertexSlice, jointIndicesOffset - vertexSlice.offset(), vertexStructureSize, VK_FORMAT_R8G8B8A8_USCALED);
}

if (isJointWeightsValid) {
VkFormat format = VK_FORMAT_R32_SFLOAT;
if (numBones == 3) {
format = VK_FORMAT_R32G32_SFLOAT;
} else if (numBones == 4) {
format = VK_FORMAT_R32G32B32_SFLOAT;
}
newGeomData.blendWeightBuffer = RasterBuffer(vertexSlice, jointWeightsOffset - vertexSlice.offset(), vertexStructureSize, format);

// Note: only want to set this when there are actually weights, as it triggers the replacement to be skinned.
newGeomData.numBonesPerVertex = numBones;
}

newGeomData.hashes[HashComponents::VertexPosition] = ++m_replacedCount;
if (!vecIndices.empty() || !points.empty()) {
Expand Down
6 changes: 4 additions & 2 deletions src/dxvk/rtx_render/rtx_scenemanager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -583,7 +583,7 @@ namespace dxvk {
*replacement.geometryData, // Note: Geometry Data replaced
input->getMaterialData(), // Note: Original legacy material data preserved
transforms,
SkinningData(), // We dont currently support skinning of replacements
input->getSkinningState(),
input->getFogState(),
input->getStencilEnabledState()
};
Expand Down Expand Up @@ -840,7 +840,9 @@ namespace dxvk {
// Update the input state, so we always have a reference to the original draw call state
pBlas->frameLastTouched = m_device->getCurrentFrameId();

if (drawCallState.getSkinningState().numBones > 0 && (result == ObjectCacheState::KBuildBVH || result == ObjectCacheState::kUpdateBVH)) {
if (drawCallState.getSkinningState().numBones > 0 &&
drawCallState.getGeometryData().numBonesPerVertex > 0 &&
(result == ObjectCacheState::KBuildBVH || result == ObjectCacheState::kUpdateBVH)) {
m_device->getCommon()->metaGeometryUtils().dispatchSkinning(cmd, ctx, drawCallState, pBlas->modifiedGeometryData);
pBlas->frameLastUpdated = pBlas->frameLastTouched;
}
Expand Down
4 changes: 2 additions & 2 deletions src/dxvk/rtx_render/rtx_texturemanager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -293,9 +293,9 @@ namespace dxvk {
const bool result = texture->mipCount == 1 && (extent.width * extent.height >= 512 * 512);

if (result) {
ONCE(Logger::warn(str::format("A suboptimal replacement texture detected: ",
Logger::warn(str::format("A suboptimal replacement texture detected: ",
texture->assetData->info().filename,
"! Please make sure all replacement textures have mip-maps.")));
"! Please make sure all replacement textures have mip-maps."));
}

return result;
Expand Down
4 changes: 4 additions & 0 deletions src/dxvk/rtx_render/rtx_types.h
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,10 @@ struct RasterGeometry {
uint32_t vertexCount = 0;
uint32_t indexCount = 0;

// Copy of the bones per vertex from SkinningState.
// This allows replacements to have different values from the original.
uint32_t numBonesPerVertex = 0;

// Hashed values
VkPrimitiveTopology topology = VkPrimitiveTopology(0);
VkCullModeFlags cullMode = VkCullModeFlags(0);
Expand Down

0 comments on commit 657a328

Please sign in to comment.