Revision control
Copy as Markdown
// Copyright (C) 2025 The Qt Company Ltd.
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
// Qt-Security score:significant reason:default
#include <ssg/qssgrendercontextcore.h>
#include <QtQuick3DRuntimeRender/private/qssgrenderlayer_p.h>
#include <QtQuick3DRuntimeRender/private/qssglayerrenderdata_p.h>
#include <QtQuick3DRuntimeRender/private/qssgrendermotionvectormap_p.h>
QT_BEGIN_NAMESPACE
QSSGRenderMotionVectorMap::QSSGRenderMotionVectorMap(const QSSGRenderContextInterface &inContext)
: m_context(inContext)
{
}
QSSGRenderMotionVectorMap::~QSSGRenderMotionVectorMap()
{
releaseCachedResources();
}
void QSSGRenderMotionVectorMap::endFrame()
{
cleanupStaleEntries();
}
static void copyToTextureData(QSSGRenderTextureData *dst, QSSGRenderTextureData *src) {
dst->setTextureData(src->textureData());
dst->setDepth(src->depth());
dst->setFormat(src->format());
dst->setHasTransparency(src->hasTransparency());
dst->setSize(src->size());
}
static void copyToTextureData(QSSGRenderTextureData *dst, QSSGRenderInstanceTable *src) {
dst->setDepth(0);
dst->setFormat(QSSGRenderTextureFormat::RGBA32F);
dst->setHasTransparency(false);
auto instanceCount = src->count();
int totalPixels = qMax(instanceCount * 4, 1);
int width = qCeil(qSqrt(static_cast<float>(totalPixels)));
int height = qCeil(static_cast<float>(totalPixels) / width);
dst->setSize(QSize(width, height));
QByteArray instanceBuffer(width * height * 4 * sizeof(float), Qt::Uninitialized);
float *ptr = reinterpret_cast<float*>(instanceBuffer.data());
for (int i = 0; i < instanceCount; ++i) {
memcpy(ptr + i * 16, src->getTransform(i).constData(), 16 * sizeof(float));
}
dst->setTextureData(instanceBuffer);
}
static void copyToTextureData(QSSGRenderTextureData *dst, const QVector<float> &src) {
dst->setDepth(0);
dst->setFormat(QSSGRenderTextureFormat::R32F);
dst->setHasTransparency(false);
dst->setSize(QSize(src.count(), 1));
QByteArray textureData(reinterpret_cast<const char*>(src.constData()), src.count() * sizeof(float));
dst->setTextureData(textureData);
}
QSSGRenderImageTexture QSSGRenderMotionVectorMap::loadAndReleaseIfNeeded(const std::shared_ptr<QSSGRenderTextureData>& textureData, QSize lastSize)
{
if (!textureData)
return QSSGRenderImageTexture();
auto texture = m_context.bufferManager()->loadTextureData(textureData.get(), QSSGBufferManager::MipModeDisable);
// the only thing might to force to recreate new texture is changing the texture data size
// so we should release last one
if (lastSize != textureData->size()) {
m_context.bufferManager()->releaseTextureData(
QSSGBufferManager::CustomImageCacheKey{
textureData.get(),
lastSize,
QSSGBufferManager::MipMode::MipModeDisable});
}
return texture;
}
QSSGRenderMotionVectorMap::MotionResultData QSSGRenderMotionVectorMap::trackMotionData(
const void *modelKey,
const QMatrix4x4 ¤tModelViewProjection,
const QMatrix4x4 ¤tInstanceLocal,
const QMatrix4x4 ¤tInstanceGlobal,
QSSGRenderTextureData *currentBoneTextureData,
QSSGRenderInstanceTable *currentInstanceTable,
const QVector<float> ¤tMorphWeights)
{
auto lastFrameDataIterator = m_cache.find(modelKey);
if (lastFrameDataIterator == m_cache.end()) {
MotionStoreData dataToStore;
dataToStore.prevModelViewProjection = currentModelViewProjection;
dataToStore.prevInstanceLocal = currentInstanceLocal;
dataToStore.prevInstanceGlobal = currentInstanceGlobal;
dataToStore.frameAge = 0;
dataToStore.visitedThisFrame = true;
if (currentBoneTextureData) {
dataToStore.prevBoneTextureData = std::make_shared<QSSGRenderTextureData>();
copyToTextureData(dataToStore.prevBoneTextureData.get(), currentBoneTextureData);
dataToStore.lastPrevBoneTextureDataSize = dataToStore.prevBoneTextureData->size();
}
if (currentInstanceTable) {
dataToStore.prevInstanceTextureData = std::make_shared<QSSGRenderTextureData>();
copyToTextureData(dataToStore.prevInstanceTextureData.get(), currentInstanceTable);
dataToStore.lastPrevInstanceTextureDataSize = dataToStore.prevInstanceTextureData->size();
}
if (currentMorphWeights.size()) {
dataToStore.currentMorphWeightTextureData = std::make_shared<QSSGRenderTextureData>();
copyToTextureData(dataToStore.currentMorphWeightTextureData.get(), currentMorphWeights);
dataToStore.lastCurrentMorphWeightTextureDataSize = dataToStore.currentMorphWeightTextureData->size();
dataToStore.prevMorphWeightTextureData = std::make_shared<QSSGRenderTextureData>();
copyToTextureData(dataToStore.prevMorphWeightTextureData.get(), currentMorphWeights);
dataToStore.lastPrevMorphWeightTextureDataSize = dataToStore.prevMorphWeightTextureData->size();
}
MotionResultData firstFrameResult = { currentModelViewProjection, currentInstanceLocal, currentInstanceGlobal,
loadAndReleaseIfNeeded(dataToStore.prevBoneTextureData, dataToStore.lastPrevBoneTextureDataSize),
loadAndReleaseIfNeeded(dataToStore.prevInstanceTextureData, dataToStore.lastPrevInstanceTextureDataSize),
loadAndReleaseIfNeeded(dataToStore.currentMorphWeightTextureData, dataToStore.lastCurrentMorphWeightTextureDataSize),
loadAndReleaseIfNeeded(dataToStore.prevMorphWeightTextureData, dataToStore.lastPrevMorphWeightTextureDataSize) };
dataToStore.cachedResult = firstFrameResult;
m_cache[modelKey] = dataToStore;
return firstFrameResult;
}
if (lastFrameDataIterator->visitedThisFrame)
return lastFrameDataIterator->cachedResult;
MotionResultData motionResult;
motionResult.prevModelViewProjection = lastFrameDataIterator->prevModelViewProjection;
lastFrameDataIterator->prevModelViewProjection = currentModelViewProjection;
motionResult.prevInstanceLocal = lastFrameDataIterator->prevInstanceLocal;
lastFrameDataIterator->prevInstanceLocal = currentInstanceLocal;
motionResult.prevInstanceGlobal = lastFrameDataIterator->prevInstanceGlobal;
lastFrameDataIterator->prevInstanceGlobal = currentInstanceGlobal;
motionResult.prevBoneTexture = loadAndReleaseIfNeeded(
lastFrameDataIterator->prevBoneTextureData,
lastFrameDataIterator->lastPrevBoneTextureDataSize);
motionResult.prevInstanceTexture = loadAndReleaseIfNeeded(
lastFrameDataIterator->prevInstanceTextureData,
lastFrameDataIterator->lastPrevInstanceTextureDataSize);
motionResult.prevMorphWeightTexture = loadAndReleaseIfNeeded(
lastFrameDataIterator->prevMorphWeightTextureData,
lastFrameDataIterator->lastPrevMorphWeightTextureDataSize);
motionResult.currentMorphWeightTexture = loadAndReleaseIfNeeded(
lastFrameDataIterator->currentMorphWeightTextureData,
lastFrameDataIterator->lastCurrentMorphWeightTextureDataSize);
if (currentBoneTextureData) {
if (!lastFrameDataIterator->prevBoneTextureData)
lastFrameDataIterator->prevBoneTextureData = std::make_shared<QSSGRenderTextureData>();
lastFrameDataIterator->lastPrevBoneTextureDataSize = lastFrameDataIterator->prevBoneTextureData->size();
copyToTextureData(lastFrameDataIterator->prevBoneTextureData.get(), currentBoneTextureData);
}
if (currentInstanceTable) {
if (!lastFrameDataIterator->prevInstanceTextureData)
lastFrameDataIterator->prevInstanceTextureData = std::make_shared<QSSGRenderTextureData>();
lastFrameDataIterator->lastPrevInstanceTextureDataSize = lastFrameDataIterator->prevInstanceTextureData->size();
copyToTextureData(lastFrameDataIterator->prevInstanceTextureData.get(), currentInstanceTable);
}
if (currentMorphWeights.size()) {
if (!lastFrameDataIterator->prevMorphWeightTextureData)
lastFrameDataIterator->prevMorphWeightTextureData = std::make_shared<QSSGRenderTextureData>();
lastFrameDataIterator->lastPrevMorphWeightTextureDataSize = lastFrameDataIterator->prevMorphWeightTextureData->size();
if (lastFrameDataIterator->currentMorphWeightTextureData)
copyToTextureData(lastFrameDataIterator->prevMorphWeightTextureData.get(), lastFrameDataIterator->currentMorphWeightTextureData.get());
else
copyToTextureData(lastFrameDataIterator->prevMorphWeightTextureData.get(), currentMorphWeights);
if (!lastFrameDataIterator->currentMorphWeightTextureData)
lastFrameDataIterator->currentMorphWeightTextureData = std::make_shared<QSSGRenderTextureData>();
lastFrameDataIterator->lastCurrentMorphWeightTextureDataSize = lastFrameDataIterator->currentMorphWeightTextureData->size();
copyToTextureData(lastFrameDataIterator->currentMorphWeightTextureData.get(), currentMorphWeights);
}
lastFrameDataIterator->frameAge = 0;
lastFrameDataIterator->visitedThisFrame = true;
lastFrameDataIterator->cachedResult = motionResult;
return motionResult;
}
void QSSGRenderMotionVectorMap::releaseEntryResources(MotionStoreData& entry)
{
if (entry.prevBoneTextureData)
m_context.bufferManager()->releaseTextureData(entry.prevBoneTextureData.get());
if (entry.prevInstanceTextureData)
m_context.bufferManager()->releaseTextureData(entry.prevInstanceTextureData.get());
if (entry.prevMorphWeightTextureData)
m_context.bufferManager()->releaseTextureData(entry.prevMorphWeightTextureData.get());
if (entry.currentMorphWeightTextureData)
m_context.bufferManager()->releaseTextureData(entry.currentMorphWeightTextureData.get());
}
void QSSGRenderMotionVectorMap::releaseCachedResources()
{
for (auto& entry : m_cache)
releaseEntryResources(entry);
m_cache.clear();
}
void QSSGRenderMotionVectorMap::cleanupStaleEntries()
{
constexpr int maxFrameAge = 2;
auto it = m_cache.begin();
while (it != m_cache.end()) {
it->visitedThisFrame = false;
it->frameAge++;
if (it->frameAge > maxFrameAge) {
releaseEntryResources(*it);
it = m_cache.erase(it);
} else {
++it;
}
}
}
QT_END_NAMESPACE