diff --git a/filament/backend/src/opengl/OpenGLProgram.cpp b/filament/backend/src/opengl/OpenGLProgram.cpp index 69ba1b6a3e7..e699149d1da 100644 --- a/filament/backend/src/opengl/OpenGLProgram.cpp +++ b/filament/backend/src/opengl/OpenGLProgram.cpp @@ -208,7 +208,8 @@ void OpenGLProgram::updateSamplers(OpenGLDriver* const gld) const noexcept { auto const& UTILS_RESTRICT usedBindingPoints = mUsedSamplerBindingPoints; for (uint8_t i = 0, tmu = 0, n = mUsedBindingsCount; i < n; i++) { - auto const binding = usedBindingPoints[i]; + size_t const binding = usedBindingPoints[i]; + assert_invariant(binding < Program::SAMPLER_BINDING_COUNT); auto const * const sb = samplerBindings[binding]; assert_invariant(sb); for (uint8_t j = 0, m = sb->textureUnitEntries.size(); j < m; ++j, ++tmu) { // "<=" on purpose here diff --git a/filament/src/RenderPass.cpp b/filament/src/RenderPass.cpp index a9fa1dc4cac..55a66a75d30 100644 --- a/filament/src/RenderPass.cpp +++ b/filament/src/RenderPass.cpp @@ -875,14 +875,12 @@ void RenderPass::Executor::execute(backend::DriverApi& driver, info.skinningHandle, info.skinningOffset * sizeof(PerRenderableBoneUib::BoneData), sizeof(PerRenderableBoneUib)); + // note: always bind the skinningTexture because the shader needs it. + driver.bindSamplers(+SamplerBindingPoints::PER_RENDERABLE_SKINNING, + info.skinningTexture); // note: even if only skinning is enabled, binding morphTargetBuffer is needed. driver.bindSamplers(+SamplerBindingPoints::PER_RENDERABLE_MORPHING, info.morphTargetBuffer); - - if (UTILS_UNLIKELY(info.skinningTexture)) { - driver.bindSamplers(+SamplerBindingPoints::PER_RENDERABLE_SKINNING, - info.skinningTexture); - } } if (UTILS_UNLIKELY(info.morphWeightBuffer)) { @@ -892,6 +890,9 @@ void RenderPass::Executor::execute(backend::DriverApi& driver, info.morphWeightBuffer); driver.bindSamplers(+SamplerBindingPoints::PER_RENDERABLE_MORPHING, info.morphTargetBuffer); + // note: even if only morphing is enabled, binding skinningTexture is needed. + driver.bindSamplers(+SamplerBindingPoints::PER_RENDERABLE_SKINNING, + info.skinningTexture); } driver.draw(pipeline, info.primitiveHandle, instanceCount); diff --git a/filament/src/components/RenderableManager.cpp b/filament/src/components/RenderableManager.cpp index 7314f705d53..d43fdb59d58 100644 --- a/filament/src/components/RenderableManager.cpp +++ b/filament/src/components/RenderableManager.cpp @@ -596,30 +596,30 @@ void FRenderableManager::create( } } - if (UTILS_UNLIKELY(boneCount > 0) && (builder->mBoneIndicesAndWeightsCount > 0)){ - // create and set texture for bone indices and weights - Bones& bones = manager[ci].bones; - FSkinningBuffer::HandleIndicesAndWeights handle = downcast(builder->mSkinningBuffer)-> - createIndicesAndWeightsHandle(downcast(engine), builder->mBoneIndicesAndWeightsCount); - bones.handleSamplerGroup = handle.sampler; - bones.handleTexture = handle.texture; - downcast(builder->mSkinningBuffer)-> - setIndicesAndWeightsData(downcast(engine), handle.texture, - builder->mBoneIndicesAndWeights, builder->mBoneIndicesAndWeightsCount); - } - // Create and initialize all needed MorphTargets. // It's required to avoid branches in hot loops. - MorphTargets* morphTargets = new MorphTargets[entryCount]; - for (size_t i = 0; i < entryCount; ++i) { - morphTargets[i] = { mEngine.getDummyMorphTargetBuffer(), 0, 0 }; - } + MorphTargets* const morphTargets = new MorphTargets[entryCount]; + std::generate_n(morphTargets, entryCount, + [dummy = mEngine.getDummyMorphTargetBuffer()]() -> MorphTargets { + return { dummy, 0, 0 }; + }); + mManager[ci].morphTargets = { morphTargets, size_type(entryCount) }; - // Even morphing isn't enabled, we should create morphig resources. - // Because morphing shader code is generated when skinning is enabled. - // You can see more detail at Variant::SKINNING_OR_MORPHING. + // Always create skinning and morphing resources if one of them is enabled because + // the shader always handles both. See Variant::SKINNING_OR_MORPHING. if (UTILS_UNLIKELY(boneCount > 0 || targetCount > 0)) { + + auto [sampler, texture] = FSkinningBuffer::createIndicesAndWeightsHandle( + downcast(engine), builder->mBoneIndicesAndWeightsCount); + if (builder->mBoneIndicesAndWeightsCount > 0) { + FSkinningBuffer::setIndicesAndWeightsData(downcast(engine), texture, + builder->mBoneIndicesAndWeights, builder->mBoneIndicesAndWeightsCount); + } + Bones& bones = manager[ci].bones; + bones.handleSamplerGroup = sampler; + bones.handleTexture = texture; + // Instead of using a UBO per primitive, we could also have a single UBO for all primitives // and use bindUniformBufferRange which might be more efficient. MorphWeights& morphWeights = manager[ci].morphWeights; @@ -643,7 +643,7 @@ void FRenderableManager::create( // morphWeights uniform array to avoid crash on adreno gpu. if (UTILS_UNLIKELY(targetCount == 0 && driver.isWorkaroundNeeded(Workaround::ADRENO_UNIFORM_ARRAY_CRASH))) { - float initWeights[1] = {0}; + float initWeights[1] = { 0 }; setMorphWeights(ci, initWeights, 1, 0); } } diff --git a/filament/src/details/SkinningBuffer.cpp b/filament/src/details/SkinningBuffer.cpp index 323a66ee97e..9bbf7a4302b 100644 --- a/filament/src/details/SkinningBuffer.cpp +++ b/filament/src/details/SkinningBuffer.cpp @@ -169,11 +169,12 @@ void FSkinningBuffer::setBones(FEngine& engine, Handle constexpr size_t MAX_SKINNING_BUFFER_WIDTH = 2048; static inline size_t getSkinningBufferWidth(size_t pairCount) noexcept { - return std::min(pairCount, MAX_SKINNING_BUFFER_WIDTH); + return std::clamp(pairCount, size_t(1), MAX_SKINNING_BUFFER_WIDTH); } static inline size_t getSkinningBufferHeight(size_t pairCount) noexcept { - return (pairCount + MAX_SKINNING_BUFFER_WIDTH - 1) / MAX_SKINNING_BUFFER_WIDTH; + return std::max(size_t(1), + (pairCount + MAX_SKINNING_BUFFER_WIDTH - 1) / MAX_SKINNING_BUFFER_WIDTH); } inline size_t getSkinningBufferSize(size_t pairCount) noexcept { @@ -191,62 +192,63 @@ void updateDataAt(backend::DriverApi& driver, const utils::FixedCapacityVector& pairs, size_t count) { - size_t elementSize = sizeof(float2); - size_t size = getSkinningBufferSize(count); - auto* out = (float2*) malloc(size); + size_t const elementSize = sizeof(float2); + size_t const size = getSkinningBufferSize(count); + auto* out = (float2*)malloc(size); std::memcpy(out, pairs.begin(), size); - size_t const textureWidth = getSkinningBufferWidth( count); - size_t const lineCount = count / textureWidth; - size_t const lastLineCount = count % textureWidth; + size_t const textureWidth = getSkinningBufferWidth(count); + size_t const lineCount = count / textureWidth; + size_t const lastLineCount = count % textureWidth; // 'out' buffer is going to be used up to 2 times, so for simplicity we use a shared_buffer // to manage its lifetime. One side effect of this is that the callbacks below will allocate // a small object on the heap. (inspired by MorphTargetBuffered) - std::shared_ptr allocation((void*)out, ::free); + std::shared_ptr const allocation((void*)out, ::free); if (lineCount) { // update the full-width lines if any driver.update3DImage(handle, 0, 0, 0, 0, - textureWidth, lineCount, 1, - PixelBufferDescriptor::make( - out, textureWidth * lineCount * elementSize, - format, type, [allocation](void const*, size_t) {} - )); + textureWidth, lineCount, 1, + PixelBufferDescriptor::make( + out, textureWidth * lineCount * elementSize, + format, type, [allocation](void const*, size_t) {} + )); out += lineCount * textureWidth; } if (lastLineCount) { // update the last partial line if any driver.update3DImage(handle, 0, 0, lineCount, 0, - lastLineCount, 1, 1, - PixelBufferDescriptor::make( - out, lastLineCount * elementSize, - format, type, [allocation](void const*, size_t) {} - )); + lastLineCount, 1, 1, + PixelBufferDescriptor::make( + out, lastLineCount * elementSize, + format, type, [allocation](void const*, size_t) {} + )); } } -FSkinningBuffer::HandleIndicesAndWeights FSkinningBuffer::createIndicesAndWeightsHandle(FEngine& engine, size_t count) { +FSkinningBuffer::HandleIndicesAndWeights FSkinningBuffer::createIndicesAndWeightsHandle( + FEngine& engine, size_t count) { backend::Handle samplerHandle; backend::Handle textureHandle; FEngine::DriverApi& driver = engine.getDriverApi(); // create a texture for skinning pairs data (bone index and weight) textureHandle = driver.createTexture(SamplerType::SAMPLER_2D, 1, - TextureFormat::RG32F, 1, - getSkinningBufferWidth(count), - getSkinningBufferHeight(count), 1, - TextureUsage::DEFAULT); + TextureFormat::RG32F, 1, + getSkinningBufferWidth(count), + getSkinningBufferHeight(count), 1, + TextureUsage::DEFAULT); samplerHandle = driver.createSamplerGroup(PerRenderPrimitiveSkinningSib::SAMPLER_COUNT); SamplerGroup samplerGroup(PerRenderPrimitiveSkinningSib::SAMPLER_COUNT); samplerGroup.setSampler(PerRenderPrimitiveSkinningSib::BONE_INDICES_AND_WEIGHTS, - {textureHandle, {}}); + { textureHandle, {}}); driver.updateSamplerGroup(samplerHandle, - samplerGroup.toBufferDescriptor(driver)); + samplerGroup.toBufferDescriptor(driver)); return { - .sampler = samplerHandle, - .texture = textureHandle + .sampler = samplerHandle, + .texture = textureHandle }; } @@ -257,7 +259,7 @@ void FSkinningBuffer::setIndicesAndWeightsData(FEngine& engine, FEngine::DriverApi& driver = engine.getDriverApi(); updateDataAt(driver, textureHandle, Texture::Format::RG, Texture::Type::FLOAT, - pairs, count); + pairs, count); } } // namespace filament diff --git a/filament/src/details/SkinningBuffer.h b/filament/src/details/SkinningBuffer.h index 23e6a48ce84..8fa056a5796 100644 --- a/filament/src/details/SkinningBuffer.h +++ b/filament/src/details/SkinningBuffer.h @@ -79,9 +79,9 @@ class FSkinningBuffer : public SkinningBuffer { backend::Handle sampler; backend::Handle texture; }; - HandleIndicesAndWeights createIndicesAndWeightsHandle(FEngine& engine, + static HandleIndicesAndWeights createIndicesAndWeightsHandle(FEngine& engine, size_t count); - void setIndicesAndWeightsData(FEngine& engine, + static void setIndicesAndWeightsData(FEngine& engine, backend::Handle textureHandle, const utils::FixedCapacityVector& pairs, size_t count);