From 5572097fb3334aea832b1da8c665c49d051f88ed Mon Sep 17 00:00:00 2001 From: Benjamin Doherty Date: Mon, 2 Oct 2023 16:55:52 -0700 Subject: [PATCH 01/63] Fix build failures due to filamat lite removal --- build.sh | 4 ++-- build/common/test_list.txt | 1 - 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/build.sh b/build.sh index daaa419f7e..1dce7d2931 100755 --- a/build.sh +++ b/build.sh @@ -495,7 +495,7 @@ function build_android { if [[ "${INSTALL_COMMAND}" ]]; then echo "Installing out/filamat-android-debug.aar..." - cp filamat-android/build/outputs/aar/filamat-android-full-debug.aar ../out/filamat-android-debug.aar + cp filamat-android/build/outputs/aar/filamat-android-debug.aar ../out/filamat-android-debug.aar echo "Installing out/filament-android-debug.aar..." cp filament-android/build/outputs/aar/filament-android-debug.aar ../out/ @@ -544,7 +544,7 @@ function build_android { if [[ "${INSTALL_COMMAND}" ]]; then echo "Installing out/filamat-android-release.aar..." - cp filamat-android/build/outputs/aar/filamat-android-full-release.aar ../out/filamat-android-release.aar + cp filamat-android/build/outputs/aar/filamat-android-release.aar ../out/filamat-android-release.aar echo "Installing out/filament-android-release.aar..." cp filament-android/build/outputs/aar/filament-android-release.aar ../out/ diff --git a/build/common/test_list.txt b/build/common/test_list.txt index 39a71c5f4f..3121fccfd0 100644 --- a/build/common/test_list.txt +++ b/build/common/test_list.txt @@ -5,7 +5,6 @@ libs/math/test_math libs/image/test_image compare libs/image/tests/reference/ libs/utils/test_utils libs/filamat/test_filamat -libs/filamat/test_filamat_lite tools/matc/test_matc tools/cmgen/test_cmgen compare tools/glslminifier/test_glslminifier From 2cf86454bb3b4494a653fa1afc865df45742ff15 Mon Sep 17 00:00:00 2001 From: Powei Feng Date: Tue, 3 Oct 2023 10:49:45 -0700 Subject: [PATCH 02/63] vk: refactor VulkanProgram (#7221) --- filament/backend/src/vulkan/VulkanBlitter.cpp | 9 +- filament/backend/src/vulkan/VulkanDriver.cpp | 109 +++++++-------- filament/backend/src/vulkan/VulkanHandles.cpp | 130 ++++++++++-------- filament/backend/src/vulkan/VulkanHandles.h | 58 +++++++- .../src/vulkan/VulkanPipelineCache.cpp | 17 ++- .../backend/src/vulkan/VulkanPipelineCache.h | 5 +- 6 files changed, 199 insertions(+), 129 deletions(-) diff --git a/filament/backend/src/vulkan/VulkanBlitter.cpp b/filament/backend/src/vulkan/VulkanBlitter.cpp index 9f20b11ac8..bfda73fa32 100644 --- a/filament/backend/src/vulkan/VulkanBlitter.cpp +++ b/filament/backend/src/vulkan/VulkanBlitter.cpp @@ -237,11 +237,12 @@ void VulkanBlitter::lazyInit() noexcept { VkShaderModule vertexShader = decode(VKSHADERS_BLITDEPTHVS_DATA, VKSHADERS_BLITDEPTHVS_SIZE); VkShaderModule fragmentShader = decode(VKSHADERS_BLITDEPTHFS_DATA, VKSHADERS_BLITDEPTHFS_SIZE); - mDepthResolveProgram = new VulkanProgram(mDevice, vertexShader, fragmentShader); // Allocate one anonymous sampler at slot 0. - mDepthResolveProgram->samplerGroupInfo[0].samplers.reserve(1); - mDepthResolveProgram->samplerGroupInfo[0].samplers.resize(1); + VulkanProgram::CustomSamplerInfoList samplers = { + {0, 0, ShaderStageFlags::FRAGMENT}, + }; + mDepthResolveProgram = new VulkanProgram(mDevice, vertexShader, fragmentShader, samplers); #if FVK_ENABLED(FVK_DEBUG_BLITTER) utils::slog.d << "Created Shader Module for VulkanBlitter " @@ -359,7 +360,7 @@ void VulkanBlitter::blitSlowDepth(VkFilter filter, const VkExtent2D srcExtent, V // DRAW THE TRIANGLE // ----------------- - mPipelineCache.bindProgram(*mDepthResolveProgram); + mPipelineCache.bindProgram(mDepthResolveProgram); mPipelineCache.bindPrimitiveTopology(VK_PRIMITIVE_TOPOLOGY_TRIANGLE_STRIP); auto vkraster = mPipelineCache.getCurrentRasterState(); diff --git a/filament/backend/src/vulkan/VulkanDriver.cpp b/filament/backend/src/vulkan/VulkanDriver.cpp index 217aab5690..b8381330e4 100644 --- a/filament/backend/src/vulkan/VulkanDriver.cpp +++ b/filament/backend/src/vulkan/VulkanDriver.cpp @@ -1548,7 +1548,7 @@ void VulkanDriver::draw(PipelineState pipelineState, Handle r VkDeviceSize const* offsets = prim.vertexBuffer->getOffsets(); // Push state changes to the VulkanPipelineCache instance. This is fast and does not make VK calls. - mPipelineCache.bindProgram(*program); + mPipelineCache.bindProgram(program); mPipelineCache.bindRasterState(mPipelineCache.getCurrentRasterState()); mPipelineCache.bindPrimitiveTopology(prim.primitiveTopology); mPipelineCache.bindVertexArray(attribDesc, bufferDesc, bufferCount); @@ -1560,68 +1560,69 @@ void VulkanDriver::draw(PipelineState pipelineState, Handle r VkDescriptorImageInfo samplerInfo[VulkanPipelineCache::SAMPLER_BINDING_COUNT] = {}; VulkanTexture* samplerTextures[VulkanPipelineCache::SAMPLER_BINDING_COUNT] = {nullptr}; - VulkanPipelineCache::UsageFlags usage; + auto const& bindingToSamplerIndex = program->getBindingToSamplerIndex(); + VulkanPipelineCache::UsageFlags usage = program->getUsage(); UTILS_NOUNROLL - for (uint8_t samplerGroupIdx = 0; samplerGroupIdx < Program::SAMPLER_BINDING_COUNT; samplerGroupIdx++) { - const auto& samplerGroup = program->samplerGroupInfo[samplerGroupIdx]; - const auto& samplers = samplerGroup.samplers; - if (samplers.empty()) { + for (uint8_t binding = 0; binding < VulkanPipelineCache::SAMPLER_BINDING_COUNT; binding++) { + uint16_t const indexPair = bindingToSamplerIndex[binding]; + + if (indexPair == 0xffff) { + usage = VulkanPipelineCache::disableUsageFlags(binding, usage); continue; } - VulkanSamplerGroup* vksb = mSamplerBindings[samplerGroupIdx]; + + uint16_t const samplerGroupInd = (indexPair >> 8) & 0xff; + uint16_t const samplerInd = (indexPair & 0xff); + + VulkanSamplerGroup* vksb = mSamplerBindings[samplerGroupInd]; if (!vksb) { + usage = VulkanPipelineCache::disableUsageFlags(binding, usage); continue; } - SamplerGroup* sb = vksb->sb.get(); - assert_invariant(sb->getSize() == samplers.size()); - size_t samplerIdx = 0; - for (auto& sampler : samplers) { - const SamplerDescriptor* boundSampler = sb->data() + samplerIdx; - samplerIdx++; + SamplerDescriptor const* boundSampler = ((SamplerDescriptor*) vksb->sb->data()) + samplerInd; - if (UTILS_LIKELY(boundSampler->t)) { - VulkanTexture* texture = mResourceAllocator.handle_cast(boundSampler->t); - VkImageViewType const expectedType = texture->getViewType(); - - // TODO: can this uninitialized check be checked in a higher layer? - // This fallback path is very flaky because the dummy texture might not have - // matching characteristics. (e.g. if the missing texture is a 3D texture) - if (UTILS_UNLIKELY(texture->getPrimaryImageLayout() == VulkanLayout::UNDEFINED)) { -#if FVK_ENABLED(FVK_DEBUG_TEXTURE) - utils::slog.w << "Uninitialized texture bound to '" << sampler.name.c_str() << "'"; - utils::slog.w << " in material '" << program->name.c_str() << "'"; - utils::slog.w << " at binding point " << +sampler.binding << utils::io::endl; -#endif - texture = mEmptyTexture.get(); - } - - const SamplerParams& samplerParams = boundSampler->s; - VkSampler vksampler = mSamplerCache.getSampler(samplerParams); - - usage = VulkanPipelineCache::getUsageFlags(sampler.binding, samplerGroup.stageFlags, usage); - - VkImageView imageView = VK_NULL_HANDLE; - VkImageSubresourceRange const range = texture->getPrimaryViewRange(); - if (any(texture->usage & TextureUsage::DEPTH_ATTACHMENT) - && expectedType == VK_IMAGE_VIEW_TYPE_2D) { - // If the sampler is part of a mipmapped depth texture, where one of the level - // *can* be an attachment, then the sampler for this texture has the same view - // properties as a view for an attachment. Therefore, we can use - // getAttachmentView to get a corresponding VkImageView. - imageView = texture->getAttachmentView(range); - } else { - imageView = texture->getViewForType(range, expectedType); - } - - samplerInfo[sampler.binding] = { - .sampler = vksampler, - .imageView = imageView, - .imageLayout = ImgUtil::getVkLayout(texture->getPrimaryImageLayout()) - }; - samplerTextures[sampler.binding] = texture; - } + if (UTILS_UNLIKELY(!boundSampler->t)) { + usage = VulkanPipelineCache::disableUsageFlags(binding, usage); + continue; } + + VulkanTexture* texture = mResourceAllocator.handle_cast(boundSampler->t); + VkImageViewType const expectedType = texture->getViewType(); + + // TODO: can this uninitialized check be checked in a higher layer? + // This fallback path is very flaky because the dummy texture might not have + // matching characteristics. (e.g. if the missing texture is a 3D texture) + if (UTILS_UNLIKELY(texture->getPrimaryImageLayout() == VulkanLayout::UNDEFINED)) { +#if FVK_ENABLED(FVK_DEBUG_TEXTURE) + utils::slog.w << "Uninitialized texture bound to '" << sampler.name.c_str() << "'"; + utils::slog.w << " in material '" << program->name.c_str() << "'"; + utils::slog.w << " at binding point " << +sampler.binding << utils::io::endl; +#endif + texture = mEmptyTexture.get(); + } + + SamplerParams const& samplerParams = boundSampler->s; + VkSampler const vksampler = mSamplerCache.getSampler(samplerParams); + VkImageView imageView = VK_NULL_HANDLE; + VkImageSubresourceRange const range = texture->getPrimaryViewRange(); + if (any(texture->usage & TextureUsage::DEPTH_ATTACHMENT) && + expectedType == VK_IMAGE_VIEW_TYPE_2D) { + // If the sampler is part of a mipmapped depth texture, where one of the level *can* be + // an attachment, then the sampler for this texture has the same view properties as a + // view for an attachment. Therefore, we can use getAttachmentView to get a + // corresponding VkImageView. + imageView = texture->getAttachmentView(range); + } else { + imageView = texture->getViewForType(range, expectedType); + } + + samplerInfo[binding] = { + .sampler = vksampler, + .imageView = imageView, + .imageLayout = ImgUtil::getVkLayout(texture->getPrimaryImageLayout()) + }; + samplerTextures[binding] = texture; } mPipelineCache.bindSamplers(samplerInfo, samplerTextures, usage); diff --git a/filament/backend/src/vulkan/VulkanHandles.cpp b/filament/backend/src/vulkan/VulkanHandles.cpp index 963ca70f0f..ac6b4ccea1 100644 --- a/filament/backend/src/vulkan/VulkanHandles.cpp +++ b/filament/backend/src/vulkan/VulkanHandles.cpp @@ -49,88 +49,100 @@ static void clampToFramebuffer(VkRect2D* rect, uint32_t fbWidth, uint32_t fbHeig VulkanProgram::VulkanProgram(VkDevice device, const Program& builder) noexcept : HwProgram(builder.getName()), VulkanResource(VulkanResourceType::PROGRAM), + mInfo(new PipelineInfo(builder.getSpecializationConstants().size())), mDevice(device) { - auto const& blobs = builder.getShadersSource(); - VkShaderModule* modules[2] = {&bundle.vertex, &bundle.fragment}; - // TODO: handle compute shaders. - for (size_t i = 0; i < 2; i++) { + auto& blobs = builder.getShadersSource(); + auto& modules = mInfo->shaders; + for (size_t i = 0; i < MAX_SHADER_MODULES; i++) { const auto& blob = blobs[i]; - VkShaderModule* module = modules[i]; - VkShaderModuleCreateInfo moduleInfo = {}; - moduleInfo.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; - moduleInfo.codeSize = blob.size(); - moduleInfo.pCode = (uint32_t*) blob.data(); - VkResult result = vkCreateShaderModule(mDevice, &moduleInfo, VKALLOC, module); + uint32_t* data = (uint32_t*)blob.data(); + VkShaderModule& module = modules[i]; + VkShaderModuleCreateInfo moduleInfo = { + .sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO, + .codeSize = blob.size(), + .pCode = data, + }; + VkResult result = vkCreateShaderModule(mDevice, &moduleInfo, VKALLOC, &module); ASSERT_POSTCONDITION(result == VK_SUCCESS, "Unable to create shader module."); } + // Note that bools are 4-bytes in Vulkan + // https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkBool32.html + constexpr uint32_t const CONSTANT_SIZE = 4; + // populate the specialization constants requirements right now auto const& specializationConstants = builder.getSpecializationConstants(); - if (!specializationConstants.empty()) { - // Allocate a single heap block to store all the specialization constants structures - // our supported types are int32, float and bool, so we use 4 bytes per data. bool will - // just use the first byte. - char* pStorage = (char*)malloc( - sizeof(VkSpecializationInfo) + - specializationConstants.size() * sizeof(VkSpecializationMapEntry) + - specializationConstants.size() * 4); - - VkSpecializationInfo* const pInfo = (VkSpecializationInfo*)pStorage; - VkSpecializationMapEntry* const pEntries = - (VkSpecializationMapEntry*)(pStorage + sizeof(VkSpecializationInfo)); - void* pData = pStorage + sizeof(VkSpecializationInfo) + - specializationConstants.size() * sizeof(VkSpecializationMapEntry); - - *pInfo = { - .mapEntryCount = specializationConstants.size(), - .pMapEntries = pEntries, - .dataSize = specializationConstants.size() * 4, - .pData = pData, + uint32_t const specConstCount = static_cast(specializationConstants.size()); + char* specData = mInfo->specConstData.get(); + if (specConstCount > 0) { + mInfo->specializationInfo = { + .mapEntryCount = specConstCount, + .pMapEntries = mInfo->specConsts.data(), + .dataSize = specConstCount * CONSTANT_SIZE, + .pData = specData, }; + } + for (uint32_t i = 0; i < specConstCount; ++i) { + uint32_t const offset = i * CONSTANT_SIZE; + mInfo->specConsts[i] = { + .constantID = specializationConstants[i].id, + .offset = offset, + .size = CONSTANT_SIZE, + }; + using SpecConstant = Program::SpecializationConstant::Type; + char const* addr = (char*)specData + offset; + SpecConstant const& arg = specializationConstants[i].value; + if (std::holds_alternative(arg)) { + *((VkBool32*)addr) = std::get(arg) ? VK_TRUE : VK_FALSE; + } else if (std::holds_alternative(arg)) { + *((float*)addr) = std::get(arg); + } else { + *((int32_t*)addr) = std::get(arg); + } + } - for (size_t i = 0; i < specializationConstants.size(); i++) { - uint32_t const offset = uint32_t(i) * 4; - pEntries[i] = { - .constantID = specializationConstants[i].id, - .offset = offset, - // Note that bools are 4-bytes in Vulkan - // https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkBool32.html - .size = 4, - }; - - using SpecConstant = Program::SpecializationConstant::Type; - char const* addr = (char*)pData + offset; - SpecConstant const& arg = specializationConstants[i].value; - if (std::holds_alternative(arg)) { - *((VkBool32*)addr) = std::get(arg) ? VK_TRUE : VK_FALSE; - } else if (std::holds_alternative(arg)) { - *((float*)addr) = std::get(arg); - } else { - *((int32_t*)addr) = std::get(arg); - } + auto& groupInfo = builder.getSamplerGroupInfo(); + auto& bindingToSamplerIndex = mInfo->bindingToSamplerIndex; + auto& usage = mInfo->usage; + for (uint8_t groupInd = 0; groupInd < Program::SAMPLER_BINDING_COUNT; groupInd++) { + auto const& group = groupInfo[groupInd]; + auto const& samplers = group.samplers; + for (size_t i = 0; i < samplers.size(); ++i) { + uint32_t const binding = samplers[i].binding; + bindingToSamplerIndex[binding] = (groupInd << 8) | (0xff & i); + usage = VulkanPipelineCache::getUsageFlags(binding, group.stageFlags, usage); } - bundle.specializationInfos = pInfo; } - // Make a copy of the binding map - samplerGroupInfo = builder.getSamplerGroupInfo(); #if FVK_ENABLED(FVK_DEBUG_SHADER_MODULE) utils::slog.d << "Created VulkanProgram " << builder << ", shaders = (" << bundle.vertex << ", " << bundle.fragment << ")" << utils::io::endl; #endif } -VulkanProgram::VulkanProgram(VkDevice device, VkShaderModule vs, VkShaderModule fs) noexcept +VulkanProgram::VulkanProgram(VkDevice device, VkShaderModule vs, VkShaderModule fs, + CustomSamplerInfoList const& samplerInfo) noexcept : VulkanResource(VulkanResourceType::PROGRAM), + mInfo(new PipelineInfo(0)), mDevice(device) { - bundle.vertex = vs; - bundle.fragment = fs; + mInfo->shaders[0] = vs; + mInfo->shaders[1] = fs; + auto& bindingToSamplerIndex = mInfo->bindingToSamplerIndex; + auto& usage = mInfo->usage; + bindingToSamplerIndex.resize(samplerInfo.size()); + for (uint16_t binding = 0; binding < samplerInfo.size(); ++binding) { + auto const& sampler = samplerInfo[binding]; + bindingToSamplerIndex[binding] + = (sampler.groupIndex << 8) | (0xff & sampler.samplerIndex); + usage = VulkanPipelineCache::getUsageFlags(binding, sampler.flags, usage); + } } VulkanProgram::~VulkanProgram() { - vkDestroyShaderModule(mDevice, bundle.vertex, VKALLOC); - vkDestroyShaderModule(mDevice, bundle.fragment, VKALLOC); - free(bundle.specializationInfos); + for (auto shader: mInfo->shaders) { + vkDestroyShaderModule(mDevice, shader, VKALLOC); + } + delete mInfo; } // Creates a special "default" render target (i.e. associated with the swap chain) diff --git a/filament/backend/src/vulkan/VulkanHandles.h b/filament/backend/src/vulkan/VulkanHandles.h index 223a13dbd6..f60d73ca55 100644 --- a/filament/backend/src/vulkan/VulkanHandles.h +++ b/filament/backend/src/vulkan/VulkanHandles.h @@ -37,14 +37,64 @@ namespace filament::backend { class VulkanTimestamps; struct VulkanProgram : public HwProgram, VulkanResource { + VulkanProgram(VkDevice device, const Program& builder) noexcept; - VulkanProgram(VkDevice device, VkShaderModule vs, VkShaderModule fs) noexcept; + + struct CustomSamplerInfo { + uint8_t groupIndex; + uint8_t samplerIndex; + ShaderStageFlags flags; + }; + using CustomSamplerInfoList = utils::FixedCapacityVector; + + // We allow custom descriptor of the samplers within shaders. This is needed if we want to use + // a program that exists only in the backend - for example, for shader-based bliting. + VulkanProgram(VkDevice device, VkShaderModule vs, VkShaderModule fs, + CustomSamplerInfoList const& samplerInfo) noexcept; ~VulkanProgram(); - VulkanPipelineCache::ProgramBundle bundle; - Program::SamplerGroupInfo samplerGroupInfo; + + inline VkShaderModule getVertexShader() const { + return mInfo->shaders[0]; + } + + inline VkShaderModule getFragmentShader() const { return mInfo->shaders[1]; } + + inline VulkanPipelineCache::UsageFlags getUsage() const { return mInfo->usage; } + + inline utils::FixedCapacityVector const& getBindingToSamplerIndex() const { + return mInfo->bindingToSamplerIndex; + } + + inline VkSpecializationInfo const& getSpecConstInfo() const { + return mInfo->specializationInfo; + } private: - VkDevice mDevice; + // TODO: handle compute shaders. + // The expected order of shaders - from frontend to backend - is vertex, fragment, compute. + static constexpr uint8_t MAX_SHADER_MODULES = 2; + + struct PipelineInfo { + PipelineInfo(size_t specConstsCount) : + bindingToSamplerIndex(MAX_SAMPLER_COUNT, 0xffff), + specConsts(specConstsCount, VkSpecializationMapEntry{}), + specConstData(new char[specConstsCount * 4]) + {} + + // This bitset maps to each of the sampler in the sampler groups associated with this + // program, and whether each sampler is used in which shader (i.e. vert, frag, compute). + VulkanPipelineCache::UsageFlags usage; + + // We store the samplerGroupIndex as the top 8-bit and the index within each group as the lower 8-bit. + utils::FixedCapacityVector bindingToSamplerIndex; + VkShaderModule shaders[MAX_SHADER_MODULES] = {VK_NULL_HANDLE}; + VkSpecializationInfo specializationInfo = {}; + utils::FixedCapacityVector specConsts; + std::unique_ptr specConstData; + }; + + PipelineInfo* mInfo; + VkDevice mDevice = VK_NULL_HANDLE; }; // The render target bundles together a set of attachments, each of which can have one of the diff --git a/filament/backend/src/vulkan/VulkanPipelineCache.cpp b/filament/backend/src/vulkan/VulkanPipelineCache.cpp index fb0ef037c4..000f8c755c 100644 --- a/filament/backend/src/vulkan/VulkanPipelineCache.cpp +++ b/filament/backend/src/vulkan/VulkanPipelineCache.cpp @@ -65,6 +65,13 @@ VulkanPipelineCache::getUsageFlags(uint16_t binding, ShaderStageFlags flags, Usa return src; } +VulkanPipelineCache::UsageFlags VulkanPipelineCache::disableUsageFlags(uint16_t binding, + UsageFlags src) { + src.unset(binding); + src.unset(MAX_SAMPLER_COUNT + binding); + return src; +} + VulkanPipelineCache::VulkanPipelineCache(VulkanResourceAllocator* allocator) : mCurrentRasterState(createDefaultRasterState()), mResourceAllocator(allocator), @@ -558,12 +565,10 @@ VulkanPipelineCache::PipelineLayoutCacheEntry* VulkanPipelineCache::getOrCreateP return &mPipelineLayouts.emplace(mPipelineRequirements.layout, cacheEntry).first.value(); } -void VulkanPipelineCache::bindProgram(const VulkanProgram& program) noexcept { - const VkShaderModule shaders[2] = { program.bundle.vertex, program.bundle.fragment }; - for (uint32_t ssi = 0; ssi < SHADER_MODULE_COUNT; ssi++) { - mPipelineRequirements.shaders[ssi] = shaders[ssi]; - } - mSpecializationRequirements = program.bundle.specializationInfos; +void VulkanPipelineCache::bindProgram(VulkanProgram* program) noexcept { + mPipelineRequirements.shaders[0] = program->getVertexShader(); + mPipelineRequirements.shaders[1] = program->getFragmentShader(); + mSpecializationRequirements = &program->getSpecConstInfo(); } void VulkanPipelineCache::bindRasterState(const RasterState& rasterState) noexcept { diff --git a/filament/backend/src/vulkan/VulkanPipelineCache.h b/filament/backend/src/vulkan/VulkanPipelineCache.h index b7248420f8..0e092e9555 100644 --- a/filament/backend/src/vulkan/VulkanPipelineCache.h +++ b/filament/backend/src/vulkan/VulkanPipelineCache.h @@ -91,6 +91,7 @@ public: using UsageFlags = utils::bitset128; static UsageFlags getUsageFlags(uint16_t binding, ShaderStageFlags stages, UsageFlags src = {}); + static UsageFlags disableUsageFlags(uint16_t binding, UsageFlags src); #pragma clang diagnostic push #pragma clang diagnostic warning "-Wpadded" @@ -150,7 +151,7 @@ public: void bindScissor(VkCommandBuffer cmdbuffer, VkRect2D scissor) noexcept; // Each of the following methods are fast and do not make Vulkan calls. - void bindProgram(const VulkanProgram& program) noexcept; + void bindProgram(VulkanProgram* program) noexcept; void bindRasterState(const RasterState& rasterState) noexcept; void bindRenderPass(VkRenderPass renderPass, int subpassIndex) noexcept; void bindPrimitiveTopology(VkPrimitiveTopology topology) noexcept; @@ -415,7 +416,7 @@ private: RasterState mCurrentRasterState; PipelineKey mPipelineRequirements = {}; DescriptorKey mDescriptorRequirements = {}; - VkSpecializationInfo* mSpecializationRequirements = {}; + VkSpecializationInfo const* mSpecializationRequirements = nullptr; // Current bindings for the pipeline and descriptor sets. PipelineKey mBoundPipeline = {}; From b4d6f975c1a6ff22b7e3cdb0bf39438fd7789b5a Mon Sep 17 00:00:00 2001 From: Powei Feng Date: Tue, 3 Oct 2023 12:39:50 -0700 Subject: [PATCH 03/63] Fix build failures due to filamat lite removal #2 (#7224) --- build.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.sh b/build.sh index 1dce7d2931..f64dcd7bf2 100755 --- a/build.sh +++ b/build.sh @@ -501,7 +501,7 @@ function build_android { cp filament-android/build/outputs/aar/filament-android-debug.aar ../out/ echo "Installing out/gltfio-android-debug.aar..." - cp gltfio-android/build/outputs/aar/gltfio-android-full-debug.aar ../out/gltfio-android-debug.aar + cp gltfio-android/build/outputs/aar/gltfio-android-debug.aar ../out/gltfio-android-debug.aar echo "Installing out/filament-utils-android-debug.aar..." cp filament-utils-android/build/outputs/aar/filament-utils-android-debug.aar ../out/filament-utils-android-debug.aar From df935b75e5bdc9ba8de82ee8268d0a72592155d9 Mon Sep 17 00:00:00 2001 From: Powei Feng Date: Tue, 3 Oct 2023 15:22:46 -0700 Subject: [PATCH 04/63] engine: add job system thread count configuration (#7223) - Plumb Engine::Config in Java - Add Engine::Builder for Java - Add jobSystemThreadCount to Engine::Config BUG=303129581 --- NEW_RELEASE_NOTES.md | 3 + .../filament-android/src/main/cpp/Engine.cpp | 57 ++++- .../com/google/android/filament/Engine.java | 214 ++++++++++++++++-- filament/include/filament/Engine.h | 13 ++ filament/src/details/Engine.cpp | 8 +- filament/src/details/Engine.h | 2 +- 6 files changed, 271 insertions(+), 26 deletions(-) diff --git a/NEW_RELEASE_NOTES.md b/NEW_RELEASE_NOTES.md index 4a1a9c7fa7..4a42fb94ef 100644 --- a/NEW_RELEASE_NOTES.md +++ b/NEW_RELEASE_NOTES.md @@ -7,3 +7,6 @@ for next branch cut* header. appropriate header in [RELEASE_NOTES.md](./RELEASE_NOTES.md). ## Release notes for next branch cut + +engine: Added parameter for configuring JobSystem thread count +engine: In Java, decpreate Engine.create() and recommend Engine.Builder instead \ No newline at end of file diff --git a/android/filament-android/src/main/cpp/Engine.cpp b/android/filament-android/src/main/cpp/Engine.cpp index e530e7bb55..e5bb4e95fa 100644 --- a/android/filament-android/src/main/cpp/Engine.cpp +++ b/android/filament-android/src/main/cpp/Engine.cpp @@ -25,15 +25,8 @@ using namespace filament; using namespace utils; -extern "C" JNIEXPORT jlong JNICALL -Java_com_google_android_filament_Engine_nCreateEngine(JNIEnv*, jclass, jlong backend, - jlong sharedContext) { - return (jlong) Engine::create((Engine::Backend) backend, nullptr, (void*) sharedContext); -} - extern "C" JNIEXPORT void JNICALL -Java_com_google_android_filament_Engine_nDestroyEngine(JNIEnv*, jclass, - jlong nativeEngine) { +Java_com_google_android_filament_Engine_nDestroyEngine(JNIEnv*, jclass, jlong nativeEngine) { Engine* engine = (Engine*) nativeEngine; Engine::destroy(&engine); } @@ -454,4 +447,50 @@ Java_com_google_android_filament_Engine_nGetActiveFeatureLevel(JNIEnv *, jclass, jlong nativeEngine) { Engine* engine = (Engine*) nativeEngine; return (jint)engine->getActiveFeatureLevel(); -} \ No newline at end of file +} + +extern "C" JNIEXPORT jlong JNICALL Java_com_google_android_filament_Engine_nCreateBuilder(JNIEnv*, + jclass) { + Engine::Builder* builder = new Engine::Builder{}; + return (jlong) builder; +} + +extern "C" JNIEXPORT void JNICALL Java_com_google_android_filament_Engine_nDestroyBuilder(JNIEnv*, + jclass, jlong nativeBuilder) { + Engine::Builder* builder = (Engine::Builder*) nativeBuilder; + delete builder; +} + +extern "C" JNIEXPORT void JNICALL Java_com_google_android_filament_Engine_nSetBuilderBackend( + JNIEnv*, jclass, jlong nativeBuilder, jlong backend) { + Engine::Builder* builder = (Engine::Builder*) nativeBuilder; + builder->backend((Engine::Backend) backend); +} + +extern "C" JNIEXPORT void JNICALL Java_com_google_android_filament_Engine_nSetBuilderConfig(JNIEnv*, + jclass, jlong nativeBuilder, jlong commandBufferSizeMB, jlong perRenderPassArenaSizeMB, + jlong driverHandleArenaSizeMB, jlong minCommandBufferSizeMB, jlong perFrameCommandsSizeMB, + jlong jobSystemThreadCount) { + Engine::Builder* builder = (Engine::Builder*) nativeBuilder; + Engine::Config config = { + .commandBufferSizeMB = (uint32_t) commandBufferSizeMB, + .perRenderPassArenaSizeMB = (uint32_t) perRenderPassArenaSizeMB, + .driverHandleArenaSizeMB = (uint32_t) driverHandleArenaSizeMB, + .minCommandBufferSizeMB = (uint32_t) minCommandBufferSizeMB, + .perFrameCommandsSizeMB = (uint32_t) perFrameCommandsSizeMB, + .jobSystemThreadCount = (uint32_t) jobSystemThreadCount, + }; + builder->config(&config); +} + +extern "C" JNIEXPORT void JNICALL Java_com_google_android_filament_Engine_nSetBuilderSharedContext( + JNIEnv*, jclass, jlong nativeBuilder, jlong sharedContext) { + Engine::Builder* builder = (Engine::Builder*) nativeBuilder; + builder->sharedContext((void*) sharedContext); +} + +extern "C" JNIEXPORT jlong JNICALL +Java_com_google_android_filament_Engine_nBuilderBuild(JNIEnv*, jclass, jlong nativeBuilder) { + Engine::Builder* builder = (Engine::Builder*) nativeBuilder; + return (jlong) builder->build(); +} diff --git a/android/filament-android/src/main/java/com/google/android/filament/Engine.java b/android/filament-android/src/main/java/com/google/android/filament/Engine.java index e6bc4d9243..291200adca 100644 --- a/android/filament-android/src/main/java/com/google/android/filament/Engine.java +++ b/android/filament-android/src/main/java/com/google/android/filament/Engine.java @@ -154,6 +154,184 @@ public class Engine { FEATURE_LEVEL_2 }; + /** + * Constructs Engine objects using a builder pattern. + */ + public static class Builder { + @SuppressWarnings({"FieldCanBeLocal", "UnusedDeclaration"}) + private final BuilderFinalizer mFinalizer; + private final long mNativeBuilder; + + public Builder() { + mNativeBuilder = nCreateBuilder(); + mFinalizer = new BuilderFinalizer(mNativeBuilder); + } + + /** + * Sets the {@link Backend} for the Engine. + * + * @param backend Driver backend to use + * @return A reference to this Builder for chaining calls. + */ + Builder backend(Backend backend) { + nSetBuilderBackend(mNativeBuilder, backend.ordinal()); + return this; + } + + /** + * Sets a sharedContext for the Engine. + * + * @param sharedContext A platform-dependant OpenGL context used as a shared context + * when creating filament's internal context. On Android this parameter + * must be an instance of {@link android.opengl.EGLContext}. + * @return A reference to this Builder for chaining calls. + */ + Builder sharedContext(Object sharedContext) { + if (Platform.get().validateSharedContext(sharedContext)) { + nSetBuilderSharedContext(mNativeBuilder, + Platform.get().getSharedContextNativeHandle(sharedContext)); + return this; + } + throw new IllegalArgumentException("Invalid shared context " + sharedContext); + } + + /** + * Configure the Engine with custom parameters. + * + * @param config A {@link Config} object + * @return A reference to this Builder for chaining calls. + */ + Builder config(Config config) { + nSetBuilderConfig(mNativeBuilder, config.commandBufferSizeMB, + config.perRenderPassArenaSizeMB, config.driverHandleArenaSizeMB, + config.minCommandBufferSizeMB, config.perFrameCommandsSizeMB, + config.jobSystemThreadCount); + return this; + } + + /** + * Creates an instance of Engine + * + * @return A newly created Engine, or null if the GPU driver couldn't + * be initialized, for instance if it doesn't support the right version of OpenGL or + * OpenGL ES. + * + * @exception IllegalStateException can be thrown if there isn't enough memory to + * allocate the command buffer. + */ + Engine build() { + long nativeEngine = nBuilderBuild(mNativeBuilder); + if (nativeEngine == 0) throw new IllegalStateException("Couldn't create Engine"); + return new Engine(nativeEngine); + } + + private static class BuilderFinalizer { + private final long mNativeObject; + + BuilderFinalizer(long nativeObject) { + mNativeObject = nativeObject; + } + + @Override + public void finalize() { + try { + super.finalize(); + } catch (Throwable t) { // Ignore + } finally { + nDestroyBuilder(mNativeObject); + } + } + } + } + + /** + * Parameters for customizing the initialization of {@link Engine}. + */ + public static class Config { + + // #defines in Engine.h + private static final long FILAMENT_PER_RENDER_PASS_ARENA_SIZE_IN_MB = 3; + private static final long FILAMENT_PER_FRAME_COMMANDS_SIZE_IN_MB = 2; + private static final long FILAMENT_MIN_COMMAND_BUFFERS_SIZE_IN_MB = 1; + private static final long FILAMENT_COMMAND_BUFFER_SIZE_IN_MB = + FILAMENT_MIN_COMMAND_BUFFERS_SIZE_IN_MB * 3; + + /** + * Size in MiB of the low-level command buffer arena. + * + * Each new command buffer is allocated from here. If this buffer is too small the program + * might terminate or rendering errors might occur. + * + * This is typically set to minCommandBufferSizeMB * 3, so that up to 3 frames can be + * batched-up at once. + * + * This value affects the application's memory usage. + */ + public long commandBufferSizeMB = FILAMENT_COMMAND_BUFFER_SIZE_IN_MB; + + /** + * Size in MiB of the per-frame data arena. + * + * This is the main arena used for allocations when preparing a frame. + * e.g.: Froxel data and high-level commands are allocated from this arena. + * + * If this size is too small, the program will abort on debug builds and have undefined + * behavior otherwise. + * + * This value affects the application's memory usage. + */ + public long perRenderPassArenaSizeMB = FILAMENT_PER_RENDER_PASS_ARENA_SIZE_IN_MB; + + /** + * Size in MiB of the backend's handle arena. + * + * Backends will fallback to slower heap-based allocations when running out of space and + * log this condition. + * + * If 0, then the default value for the given platform is used + * + * This value affects the application's memory usage. + */ + public long driverHandleArenaSizeMB = 0; + + /** + * Minimum size in MiB of a low-level command buffer. + * + * This is how much space is guaranteed to be available for low-level commands when a new + * buffer is allocated. If this is too small, the engine might have to stall to wait for + * more space to become available, this situation is logged. + * + * This value does not affect the application's memory usage. + */ + public long minCommandBufferSizeMB = FILAMENT_MIN_COMMAND_BUFFERS_SIZE_IN_MB; + + /** + * Size in MiB of the per-frame high level command buffer. + * + * This buffer is related to the number of draw calls achievable within a frame, if it is + * too small, the program will abort on debug builds and have undefined behavior otherwise. + * + * It is allocated from the 'per-render-pass arena' above. Make sure that at least 1 MiB is + * left in the per-render-pass arena when deciding the size of this buffer. + * + * This value does not affect the application's memory usage. + */ + public long perFrameCommandsSizeMB = FILAMENT_PER_FRAME_COMMANDS_SIZE_IN_MB; + + /** + * Number of threads to use in Engine's JobSystem. + * + * Engine uses a utils::JobSystem to carry out paralleization of Engine workloads. This + * value sets the number of threads allocated for JobSystem. Configuring this value can be + * helpful in CPU-constrained environments where too many threads can cause contention of + * CPU and reduce performance. + * + * The default value is 0, which implies that the Engine will use a heuristic to determine + * the number of threads to use. + */ + public long jobSystemThreadCount = 0; + } + private Engine(long nativeEngine) { mNativeObject = nativeEngine; mTransformManager = new TransformManager(nGetTransformManager(nativeEngine)); @@ -174,12 +352,12 @@ public class Engine { * @exception IllegalStateException can be thrown if there isn't enough memory to * allocate the command buffer. * + * @deprecated use {@link Builder} */ + @Deprecated @NonNull public static Engine create() { - long nativeEngine = nCreateEngine(0, 0); - if (nativeEngine == 0) throw new IllegalStateException("Couldn't create Engine"); - return new Engine(nativeEngine); + return new Builder().build(); } /** @@ -196,12 +374,14 @@ public class Engine { * @exception IllegalStateException can be thrown if there isn't enough memory to * allocate the command buffer. * + * @deprecated use {@link Builder} */ + @Deprecated @NonNull public static Engine create(@NonNull Backend backend) { - long nativeEngine = nCreateEngine(backend.ordinal(), 0); - if (nativeEngine == 0) throw new IllegalStateException("Couldn't create Engine"); - return new Engine(nativeEngine); + return new Builder() + .backend(backend) + .build(); } /** @@ -220,16 +400,14 @@ public class Engine { * @exception IllegalStateException can be thrown if there isn't enough memory to * allocate the command buffer. * + * @deprecated use {@link Builder} */ + @Deprecated @NonNull public static Engine create(@NonNull Object sharedContext) { - if (Platform.get().validateSharedContext(sharedContext)) { - long nativeEngine = nCreateEngine(0, - Platform.get().getSharedContextNativeHandle(sharedContext)); - if (nativeEngine == 0) throw new IllegalStateException("Couldn't create Engine"); - return new Engine(nativeEngine); - } - throw new IllegalArgumentException("Invalid shared context " + sharedContext); + return new Builder() + .sharedContext(sharedContext) + .build(); } /** @@ -914,7 +1092,6 @@ public class Engine { } } - private static native long nCreateEngine(long backend, long sharedContext); private static native void nDestroyEngine(long nativeEngine); private static native long nGetBackend(long nativeEngine); private static native long nCreateSwapChain(long nativeEngine, Object nativeWindow, long flags); @@ -971,4 +1148,13 @@ public class Engine { private static native int nGetSupportedFeatureLevel(long nativeEngine); private static native int nSetActiveFeatureLevel(long nativeEngine, int ordinal); private static native int nGetActiveFeatureLevel(long nativeEngine); + + private static native long nCreateBuilder(); + private static native void nDestroyBuilder(long nativeBuilder); + private static native void nSetBuilderBackend(long nativeBuilder, long backend); + private static native void nSetBuilderConfig(long nativeBuilder, long commandBufferSizeMB, + long perRenderPassArenaSizeMB, long driverHandleArenaSizeMB, + long minCommandBufferSizeMB, long perFrameCommandsSizeMB, long jobSystemThreadCount); + private static native void nSetBuilderSharedContext(long nativeBuilder, long sharedContext); + private static native long nBuilderBuild(long nativeBuilder); } diff --git a/filament/include/filament/Engine.h b/filament/include/filament/Engine.h index f4173f2614..a7b4e3c048 100644 --- a/filament/include/filament/Engine.h +++ b/filament/include/filament/Engine.h @@ -267,6 +267,19 @@ public: * This value does not affect the application's memory usage. */ uint32_t perFrameCommandsSizeMB = FILAMENT_PER_FRAME_COMMANDS_SIZE_IN_MB; + + /** + * Number of threads to use in Engine's JobSystem. + * + * Engine uses a utils::JobSystem to carry out paralleization of Engine workloads. This + * value sets the number of threads allocated for JobSystem. Configuring this value can be + * helpful in CPU-constrained environments where too many threads can cause contention of + * CPU and reduce performance. + * + * The default value is 0, which implies that the Engine will use a heuristic to determine + * the number of threads to use. + */ + uint32_t jobSystemThreadCount = 0; }; diff --git a/filament/src/details/Engine.cpp b/filament/src/details/Engine.cpp index 3e50fc7135..2b33a564ec 100644 --- a/filament/src/details/Engine.cpp +++ b/filament/src/details/Engine.cpp @@ -200,7 +200,7 @@ FEngine::FEngine(Engine::Builder const& builder) : "FEngine::mPerRenderPassAllocator", builder->mConfig.perRenderPassArenaSizeMB * MiB), mHeapAllocator("FEngine::mHeapAllocator", AreaPolicy::NullArea{}), - mJobSystem(getJobSystemThreadPoolSize()), + mJobSystem(getJobSystemThreadPoolSize(builder->mConfig)), mEngineEpoch(std::chrono::steady_clock::now()), mDriverBarrier(1), mMainThreadId(ThreadUtils::getThreadId()), @@ -214,7 +214,11 @@ FEngine::FEngine(Engine::Builder const& builder) : << "(threading is " << (UTILS_HAS_THREADING ? "enabled)" : "disabled)") << io::endl; } -uint32_t FEngine::getJobSystemThreadPoolSize() noexcept { +uint32_t FEngine::getJobSystemThreadPoolSize(Engine::Config const& config) noexcept { + if (config.jobSystemThreadCount > 0) { + return config.jobSystemThreadCount; + } + // 1 thread for the user, 1 thread for the backend int threadCount = (int)std::thread::hardware_concurrency() - 2; // make sure we have at least 1 thread though diff --git a/filament/src/details/Engine.h b/filament/src/details/Engine.h index c690619378..bca0346001 100644 --- a/filament/src/details/Engine.h +++ b/filament/src/details/Engine.h @@ -508,7 +508,7 @@ private: HeapAllocatorArena mHeapAllocator; utils::JobSystem mJobSystem; - static uint32_t getJobSystemThreadPoolSize() noexcept; + static uint32_t getJobSystemThreadPoolSize(Engine::Config const& config) noexcept; std::default_random_engine mRandomEngine; From 5ab526cbb0a229583c534e0b85c25bddbcbf246d Mon Sep 17 00:00:00 2001 From: Powei Feng Date: Tue, 3 Oct 2023 15:23:03 -0700 Subject: [PATCH 05/63] Fix build failures due to filamat lite removal try 3 (#7226) --- build.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.sh b/build.sh index f64dcd7bf2..7c9bba84a2 100755 --- a/build.sh +++ b/build.sh @@ -550,7 +550,7 @@ function build_android { cp filament-android/build/outputs/aar/filament-android-release.aar ../out/ echo "Installing out/gltfio-android-release.aar..." - cp gltfio-android/build/outputs/aar/gltfio-android-full-release.aar ../out/gltfio-android-release.aar + cp gltfio-android/build/outputs/aar/gltfio-android-release.aar ../out/gltfio-android-release.aar echo "Installing out/filament-utils-android-release.aar..." cp filament-utils-android/build/outputs/aar/filament-utils-android-release.aar ../out/filament-utils-android-release.aar From 07975868fec54cbc7adeb9538a00e7cfbc9ee685 Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Tue, 3 Oct 2023 13:56:57 -0700 Subject: [PATCH 06/63] update cgltf to latest v1.13 FIXES=[239321615] --- third_party/cgltf/cgltf.h | 235 +++++++--------------- third_party/cgltf/cgltf_write.h | 121 ++--------- third_party/cgltf/test/CMakeLists.txt | 13 -- third_party/cgltf/test/test_all.py | 1 - third_party/cgltf/test/test_write_glb.cpp | 58 ------ third_party/cgltf/tnt/README | 8 +- 6 files changed, 100 insertions(+), 336 deletions(-) delete mode 100644 third_party/cgltf/test/test_write_glb.cpp diff --git a/third_party/cgltf/cgltf.h b/third_party/cgltf/cgltf.h index a534cefb4f..95b56ceda5 100644 --- a/third_party/cgltf/cgltf.h +++ b/third_party/cgltf/cgltf.h @@ -80,9 +80,13 @@ * `cgltf_accessor_read_index` is similar to its floating-point counterpart, but it returns size_t * and only works with single-component data types. * - * `cgltf_copy_extras_json` allows users to retrieve the "extras" data that can be attached to many - * glTF objects (which can be arbitrary JSON data). This is a legacy function, consider using - * cgltf_extras::data directly instead. You can parse this data using your own JSON parser + * `cgltf_result cgltf_copy_extras_json(const cgltf_data*, const cgltf_extras*, + * char* dest, cgltf_size* dest_size)` allows users to retrieve the "extras" data that + * can be attached to many glTF objects (which can be arbitrary JSON data). The + * `cgltf_extras` struct stores the offsets of the start and end of the extras JSON data + * as it appears in the complete glTF JSON data. This function copies the extras data + * into the provided buffer. If `dest` is NULL, the length of the data is written into + * `dest_size`. You can then parse this data using your own JSON parser * or, if you've included the cgltf implementation using the integrated JSMN JSON parser. */ #ifndef CGLTF_H_INCLUDED__ @@ -252,10 +256,8 @@ typedef enum cgltf_data_free_method { } cgltf_data_free_method; typedef struct cgltf_extras { - cgltf_size start_offset; /* this field is deprecated and will be removed in the future; use data instead */ - cgltf_size end_offset; /* this field is deprecated and will be removed in the future; use data instead */ - - char* data; + cgltf_size start_offset; + cgltf_size end_offset; } cgltf_extras; typedef struct cgltf_extension { @@ -430,6 +432,8 @@ typedef struct cgltf_pbr_metallic_roughness cgltf_float base_color_factor[4]; cgltf_float metallic_factor; cgltf_float roughness_factor; + + cgltf_extras extras; } cgltf_pbr_metallic_roughness; typedef struct cgltf_pbr_specular_glossiness @@ -837,7 +841,6 @@ cgltf_size cgltf_num_components(cgltf_type type); cgltf_size cgltf_accessor_unpack_floats(const cgltf_accessor* accessor, cgltf_float* out, cgltf_size float_count); -/* this function is deprecated and will be removed in the future; use cgltf_extras::data instead */ cgltf_result cgltf_copy_extras_json(const cgltf_data* data, const cgltf_extras* extras, char* dest, cgltf_size* dest_size); #ifdef __cplusplus @@ -921,15 +924,12 @@ static int jsmn_parse(jsmn_parser *parser, const char *js, size_t len, jsmntok_t */ -#ifndef CGLTF_CONSTS static const cgltf_size GlbHeaderSize = 12; static const cgltf_size GlbChunkHeaderSize = 8; static const uint32_t GlbVersion = 2; static const uint32_t GlbMagic = 0x46546C67; static const uint32_t GlbMagicJsonChunk = 0x4E4F534A; static const uint32_t GlbMagicBinChunk = 0x004E4942; -#define CGLTF_CONSTS -#endif #ifndef CGLTF_MALLOC #define CGLTF_MALLOC(size) malloc(size) @@ -1745,12 +1745,7 @@ cgltf_result cgltf_copy_extras_json(const cgltf_data* data, const cgltf_extras* return cgltf_result_success; } -static void cgltf_free_extras(cgltf_data* data, cgltf_extras* extras) -{ - data->memory.free_func(data->memory.user_data, extras->data); -} - -static void cgltf_free_extensions(cgltf_data* data, cgltf_extension* extensions, cgltf_size extensions_count) +void cgltf_free_extensions(cgltf_data* data, cgltf_extension* extensions, cgltf_size extensions_count) { for (cgltf_size i = 0; i < extensions_count; ++i) { @@ -1760,12 +1755,6 @@ static void cgltf_free_extensions(cgltf_data* data, cgltf_extension* extensions, data->memory.free_func(data->memory.user_data, extensions); } -static void cgltf_free_texture_view(cgltf_data* data, cgltf_texture_view* view) -{ - cgltf_free_extensions(data, view->extensions, view->extensions_count); - cgltf_free_extras(data, &view->extras); -} - void cgltf_free(cgltf_data* data) { if (!data) @@ -1781,7 +1770,6 @@ void cgltf_free(cgltf_data* data) data->memory.free_func(data->memory.user_data, data->asset.min_version); cgltf_free_extensions(data, data->asset.extensions, data->asset.extensions_count); - cgltf_free_extras(data, &data->asset.extras); for (cgltf_size i = 0; i < data->accessors_count; ++i) { @@ -1792,12 +1780,8 @@ void cgltf_free(cgltf_data* data) cgltf_free_extensions(data, data->accessors[i].sparse.extensions, data->accessors[i].sparse.extensions_count); cgltf_free_extensions(data, data->accessors[i].sparse.indices_extensions, data->accessors[i].sparse.indices_extensions_count); cgltf_free_extensions(data, data->accessors[i].sparse.values_extensions, data->accessors[i].sparse.values_extensions_count); - cgltf_free_extras(data, &data->accessors[i].sparse.extras); - cgltf_free_extras(data, &data->accessors[i].sparse.indices_extras); - cgltf_free_extras(data, &data->accessors[i].sparse.values_extras); } cgltf_free_extensions(data, data->accessors[i].extensions, data->accessors[i].extensions_count); - cgltf_free_extras(data, &data->accessors[i].extras); } data->memory.free_func(data->memory.user_data, data->accessors); @@ -1807,7 +1791,6 @@ void cgltf_free(cgltf_data* data) data->memory.free_func(data->memory.user_data, data->buffer_views[i].data); cgltf_free_extensions(data, data->buffer_views[i].extensions, data->buffer_views[i].extensions_count); - cgltf_free_extras(data, &data->buffer_views[i].extras); } data->memory.free_func(data->memory.user_data, data->buffer_views); @@ -1827,8 +1810,8 @@ void cgltf_free(cgltf_data* data) data->memory.free_func(data->memory.user_data, data->buffers[i].uri); cgltf_free_extensions(data, data->buffers[i].extensions, data->buffers[i].extensions_count); - cgltf_free_extras(data, &data->buffers[i].extras); } + data->memory.free_func(data->memory.user_data, data->buffers); for (cgltf_size i = 0; i < data->meshes_count; ++i) @@ -1866,15 +1849,9 @@ void cgltf_free(cgltf_data* data) data->memory.free_func(data->memory.user_data, data->meshes[i].primitives[j].draco_mesh_compression.attributes); } - for (cgltf_size k = 0; k < data->meshes[i].primitives[j].mappings_count; ++k) - { - cgltf_free_extras(data, &data->meshes[i].primitives[j].mappings[k].extras); - } - data->memory.free_func(data->memory.user_data, data->meshes[i].primitives[j].mappings); cgltf_free_extensions(data, data->meshes[i].primitives[j].extensions, data->meshes[i].primitives[j].extensions_count); - cgltf_free_extras(data, &data->meshes[i].primitives[j].extras); } data->memory.free_func(data->memory.user_data, data->meshes[i].primitives); @@ -1886,7 +1863,6 @@ void cgltf_free(cgltf_data* data) } cgltf_free_extensions(data, data->meshes[i].extensions, data->meshes[i].extensions_count); - cgltf_free_extras(data, &data->meshes[i].extras); data->memory.free_func(data->memory.user_data, data->meshes[i].target_names); } @@ -1899,50 +1875,49 @@ void cgltf_free(cgltf_data* data) if(data->materials[i].has_pbr_metallic_roughness) { - cgltf_free_texture_view(data, &data->materials[i].pbr_metallic_roughness.metallic_roughness_texture); - cgltf_free_texture_view(data, &data->materials[i].pbr_metallic_roughness.base_color_texture); + cgltf_free_extensions(data, data->materials[i].pbr_metallic_roughness.metallic_roughness_texture.extensions, data->materials[i].pbr_metallic_roughness.metallic_roughness_texture.extensions_count); + cgltf_free_extensions(data, data->materials[i].pbr_metallic_roughness.base_color_texture.extensions, data->materials[i].pbr_metallic_roughness.base_color_texture.extensions_count); } if(data->materials[i].has_pbr_specular_glossiness) { - cgltf_free_texture_view(data, &data->materials[i].pbr_specular_glossiness.diffuse_texture); - cgltf_free_texture_view(data, &data->materials[i].pbr_specular_glossiness.specular_glossiness_texture); + cgltf_free_extensions(data, data->materials[i].pbr_specular_glossiness.diffuse_texture.extensions, data->materials[i].pbr_specular_glossiness.diffuse_texture.extensions_count); + cgltf_free_extensions(data, data->materials[i].pbr_specular_glossiness.specular_glossiness_texture.extensions, data->materials[i].pbr_specular_glossiness.specular_glossiness_texture.extensions_count); } if(data->materials[i].has_clearcoat) { - cgltf_free_texture_view(data, &data->materials[i].clearcoat.clearcoat_texture); - cgltf_free_texture_view(data, &data->materials[i].clearcoat.clearcoat_roughness_texture); - cgltf_free_texture_view(data, &data->materials[i].clearcoat.clearcoat_normal_texture); + cgltf_free_extensions(data, data->materials[i].clearcoat.clearcoat_texture.extensions, data->materials[i].clearcoat.clearcoat_texture.extensions_count); + cgltf_free_extensions(data, data->materials[i].clearcoat.clearcoat_roughness_texture.extensions, data->materials[i].clearcoat.clearcoat_roughness_texture.extensions_count); + cgltf_free_extensions(data, data->materials[i].clearcoat.clearcoat_normal_texture.extensions, data->materials[i].clearcoat.clearcoat_normal_texture.extensions_count); } if(data->materials[i].has_specular) { - cgltf_free_texture_view(data, &data->materials[i].specular.specular_texture); - cgltf_free_texture_view(data, &data->materials[i].specular.specular_color_texture); + cgltf_free_extensions(data, data->materials[i].specular.specular_texture.extensions, data->materials[i].specular.specular_texture.extensions_count); + cgltf_free_extensions(data, data->materials[i].specular.specular_color_texture.extensions, data->materials[i].specular.specular_color_texture.extensions_count); } if(data->materials[i].has_transmission) { - cgltf_free_texture_view(data, &data->materials[i].transmission.transmission_texture); + cgltf_free_extensions(data, data->materials[i].transmission.transmission_texture.extensions, data->materials[i].transmission.transmission_texture.extensions_count); } if (data->materials[i].has_volume) { - cgltf_free_texture_view(data, &data->materials[i].volume.thickness_texture); + cgltf_free_extensions(data, data->materials[i].volume.thickness_texture.extensions, data->materials[i].volume.thickness_texture.extensions_count); } if(data->materials[i].has_sheen) { - cgltf_free_texture_view(data, &data->materials[i].sheen.sheen_color_texture); - cgltf_free_texture_view(data, &data->materials[i].sheen.sheen_roughness_texture); + cgltf_free_extensions(data, data->materials[i].sheen.sheen_color_texture.extensions, data->materials[i].sheen.sheen_color_texture.extensions_count); + cgltf_free_extensions(data, data->materials[i].sheen.sheen_roughness_texture.extensions, data->materials[i].sheen.sheen_roughness_texture.extensions_count); } if(data->materials[i].has_iridescence) { - cgltf_free_texture_view(data, &data->materials[i].iridescence.iridescence_texture); - cgltf_free_texture_view(data, &data->materials[i].iridescence.iridescence_thickness_texture); + cgltf_free_extensions(data, data->materials[i].iridescence.iridescence_texture.extensions, data->materials[i].iridescence.iridescence_texture.extensions_count); + cgltf_free_extensions(data, data->materials[i].iridescence.iridescence_thickness_texture.extensions, data->materials[i].iridescence.iridescence_thickness_texture.extensions_count); } - cgltf_free_texture_view(data, &data->materials[i].normal_texture); - cgltf_free_texture_view(data, &data->materials[i].occlusion_texture); - cgltf_free_texture_view(data, &data->materials[i].emissive_texture); + cgltf_free_extensions(data, data->materials[i].normal_texture.extensions, data->materials[i].normal_texture.extensions_count); + cgltf_free_extensions(data, data->materials[i].occlusion_texture.extensions, data->materials[i].occlusion_texture.extensions_count); + cgltf_free_extensions(data, data->materials[i].emissive_texture.extensions, data->materials[i].emissive_texture.extensions_count); cgltf_free_extensions(data, data->materials[i].extensions, data->materials[i].extensions_count); - cgltf_free_extras(data, &data->materials[i].extras); } data->memory.free_func(data->memory.user_data, data->materials); @@ -1954,7 +1929,6 @@ void cgltf_free(cgltf_data* data) data->memory.free_func(data->memory.user_data, data->images[i].mime_type); cgltf_free_extensions(data, data->images[i].extensions, data->images[i].extensions_count); - cgltf_free_extras(data, &data->images[i].extras); } data->memory.free_func(data->memory.user_data, data->images); @@ -1962,9 +1936,7 @@ void cgltf_free(cgltf_data* data) for (cgltf_size i = 0; i < data->textures_count; ++i) { data->memory.free_func(data->memory.user_data, data->textures[i].name); - cgltf_free_extensions(data, data->textures[i].extensions, data->textures[i].extensions_count); - cgltf_free_extras(data, &data->textures[i].extras); } data->memory.free_func(data->memory.user_data, data->textures); @@ -1972,9 +1944,7 @@ void cgltf_free(cgltf_data* data) for (cgltf_size i = 0; i < data->samplers_count; ++i) { data->memory.free_func(data->memory.user_data, data->samplers[i].name); - cgltf_free_extensions(data, data->samplers[i].extensions, data->samplers[i].extensions_count); - cgltf_free_extras(data, &data->samplers[i].extras); } data->memory.free_func(data->memory.user_data, data->samplers); @@ -1985,7 +1955,6 @@ void cgltf_free(cgltf_data* data) data->memory.free_func(data->memory.user_data, data->skins[i].joints); cgltf_free_extensions(data, data->skins[i].extensions, data->skins[i].extensions_count); - cgltf_free_extras(data, &data->skins[i].extras); } data->memory.free_func(data->memory.user_data, data->skins); @@ -1993,18 +1962,7 @@ void cgltf_free(cgltf_data* data) for (cgltf_size i = 0; i < data->cameras_count; ++i) { data->memory.free_func(data->memory.user_data, data->cameras[i].name); - - if (data->cameras[i].type == cgltf_camera_type_perspective) - { - cgltf_free_extras(data, &data->cameras[i].data.perspective.extras); - } - else if (data->cameras[i].type == cgltf_camera_type_orthographic) - { - cgltf_free_extras(data, &data->cameras[i].data.orthographic.extras); - } - cgltf_free_extensions(data, data->cameras[i].extensions, data->cameras[i].extensions_count); - cgltf_free_extras(data, &data->cameras[i].extras); } data->memory.free_func(data->memory.user_data, data->cameras); @@ -2012,8 +1970,6 @@ void cgltf_free(cgltf_data* data) for (cgltf_size i = 0; i < data->lights_count; ++i) { data->memory.free_func(data->memory.user_data, data->lights[i].name); - - cgltf_free_extras(data, &data->lights[i].extras); } data->memory.free_func(data->memory.user_data, data->lights); @@ -2023,19 +1979,7 @@ void cgltf_free(cgltf_data* data) data->memory.free_func(data->memory.user_data, data->nodes[i].name); data->memory.free_func(data->memory.user_data, data->nodes[i].children); data->memory.free_func(data->memory.user_data, data->nodes[i].weights); - - if (data->nodes[i].has_mesh_gpu_instancing) - { - for (cgltf_size j = 0; j < data->nodes[i].mesh_gpu_instancing.attributes_count; ++j) - { - data->memory.free_func(data->memory.user_data, data->nodes[i].mesh_gpu_instancing.attributes[j].name); - } - - data->memory.free_func(data->memory.user_data, data->nodes[i].mesh_gpu_instancing.attributes); - } - cgltf_free_extensions(data, data->nodes[i].extensions, data->nodes[i].extensions_count); - cgltf_free_extras(data, &data->nodes[i].extras); } data->memory.free_func(data->memory.user_data, data->nodes); @@ -2046,7 +1990,6 @@ void cgltf_free(cgltf_data* data) data->memory.free_func(data->memory.user_data, data->scenes[i].nodes); cgltf_free_extensions(data, data->scenes[i].extensions, data->scenes[i].extensions_count); - cgltf_free_extras(data, &data->scenes[i].extras); } data->memory.free_func(data->memory.user_data, data->scenes); @@ -2057,19 +2000,16 @@ void cgltf_free(cgltf_data* data) for (cgltf_size j = 0; j < data->animations[i].samplers_count; ++j) { cgltf_free_extensions(data, data->animations[i].samplers[j].extensions, data->animations[i].samplers[j].extensions_count); - cgltf_free_extras(data, &data->animations[i].samplers[j].extras); } data->memory.free_func(data->memory.user_data, data->animations[i].samplers); for (cgltf_size j = 0; j < data->animations[i].channels_count; ++j) { cgltf_free_extensions(data, data->animations[i].channels[j].extensions, data->animations[i].channels[j].extensions_count); - cgltf_free_extras(data, &data->animations[i].channels[j].extras); } data->memory.free_func(data->memory.user_data, data->animations[i].channels); cgltf_free_extensions(data, data->animations[i].extensions, data->animations[i].extensions_count); - cgltf_free_extras(data, &data->animations[i].extras); } data->memory.free_func(data->memory.user_data, data->animations); @@ -2077,14 +2017,11 @@ void cgltf_free(cgltf_data* data) for (cgltf_size i = 0; i < data->variants_count; ++i) { data->memory.free_func(data->memory.user_data, data->variants[i].name); - - cgltf_free_extras(data, &data->variants[i].extras); } data->memory.free_func(data->memory.user_data, data->variants); cgltf_free_extensions(data, data->data_extensions, data->data_extensions_count); - cgltf_free_extras(data, &data->extras); for (cgltf_size i = 0; i < data->extensions_used_count; ++i) { @@ -2746,27 +2683,11 @@ static int cgltf_parse_json_attribute_list(cgltf_options* options, jsmntok_t con return i; } -static int cgltf_parse_json_extras(cgltf_options* options, jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_extras* out_extras) +static int cgltf_parse_json_extras(jsmntok_t const* tokens, int i, const uint8_t* json_chunk, cgltf_extras* out_extras) { - if (out_extras->data) - { - return CGLTF_ERROR_JSON; - } - - /* fill deprecated fields for now, this will be removed in the future */ + (void)json_chunk; out_extras->start_offset = tokens[i].start; out_extras->end_offset = tokens[i].end; - - size_t start = tokens[i].start; - size_t size = tokens[i].end - start; - out_extras->data = (char*)options->memory.alloc_func(options->memory.user_data, size + 1); - if (!out_extras->data) - { - return CGLTF_ERROR_NOMEM; - } - strncpy(out_extras->data, (const char*)json_chunk + start, size); - out_extras->data[size] = '\0'; - i = cgltf_skip_json(tokens, i); return i; } @@ -2921,7 +2842,7 @@ static int cgltf_parse_json_material_mapping_data(cgltf_options* options, jsmnto int material = -1; int variants_tok = -1; - int extras_tok = -1; + cgltf_extras extras = {0, 0}; for (int k = 0; k < obj_size; ++k) { @@ -2942,8 +2863,7 @@ static int cgltf_parse_json_material_mapping_data(cgltf_options* options, jsmnto } else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) { - extras_tok = i + 1; - i = cgltf_skip_json(tokens, extras_tok); + i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &extras); } else { @@ -2971,13 +2891,7 @@ static int cgltf_parse_json_material_mapping_data(cgltf_options* options, jsmnto out_mappings[*offset].material = CGLTF_PTRINDEX(cgltf_material, material); out_mappings[*offset].variant = variant; - - if (extras_tok >= 0) - { - int e = cgltf_parse_json_extras(options, tokens, extras_tok, json_chunk, &out_mappings[*offset].extras); - if (e < 0) - return e; - } + out_mappings[*offset].extras = extras; (*offset)++; } @@ -3092,7 +3006,7 @@ static int cgltf_parse_json_primitive(cgltf_options* options, jsmntok_t const* t } else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) { - i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_prim->extras); + i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_prim->extras); } else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0) { @@ -3339,7 +3253,7 @@ static int cgltf_parse_json_accessor_sparse(cgltf_options* options, jsmntok_t co } else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) { - i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_sparse->indices_extras); + i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_sparse->indices_extras); } else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0) { @@ -3382,7 +3296,7 @@ static int cgltf_parse_json_accessor_sparse(cgltf_options* options, jsmntok_t co } else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) { - i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_sparse->values_extras); + i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_sparse->values_extras); } else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0) { @@ -3401,7 +3315,7 @@ static int cgltf_parse_json_accessor_sparse(cgltf_options* options, jsmntok_t co } else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) { - i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_sparse->extras); + i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_sparse->extras); } else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0) { @@ -3524,7 +3438,7 @@ static int cgltf_parse_json_accessor(cgltf_options* options, jsmntok_t const* to } else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) { - i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_accessor->extras); + i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_accessor->extras); } else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0) { @@ -3630,7 +3544,7 @@ static int cgltf_parse_json_texture_view(cgltf_options* options, jsmntok_t const } else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) { - i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_texture_view->extras); + i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_texture_view->extras); } else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0) { @@ -3726,6 +3640,10 @@ static int cgltf_parse_json_pbr_metallic_roughness(cgltf_options* options, jsmnt i = cgltf_parse_json_texture_view(options, tokens, i + 1, json_chunk, &out_pbr->metallic_roughness_texture); } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) + { + i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_pbr->extras); + } else { i = cgltf_skip_json(tokens, i+1); @@ -4158,7 +4076,7 @@ static int cgltf_parse_json_image(cgltf_options* options, jsmntok_t const* token } else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) { - i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_image->extras); + i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_image->extras); } else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0) { @@ -4227,7 +4145,7 @@ static int cgltf_parse_json_sampler(cgltf_options* options, jsmntok_t const* tok } else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) { - i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_sampler->extras); + i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_sampler->extras); } else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0) { @@ -4276,7 +4194,7 @@ static int cgltf_parse_json_texture(cgltf_options* options, jsmntok_t const* tok } else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) { - i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_texture->extras); + i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_texture->extras); } else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0) { @@ -4439,7 +4357,7 @@ static int cgltf_parse_json_material(cgltf_options* options, jsmntok_t const* to } else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) { - i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_material->extras); + i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_material->extras); } else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0) { @@ -4792,7 +4710,7 @@ static int cgltf_parse_json_buffer_view(cgltf_options* options, jsmntok_t const* } else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) { - i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_buffer_view->extras); + i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_buffer_view->extras); } else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0) { @@ -4895,7 +4813,7 @@ static int cgltf_parse_json_buffer(cgltf_options* options, jsmntok_t const* toke } else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) { - i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_buffer->extras); + i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_buffer->extras); } else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0) { @@ -4979,7 +4897,7 @@ static int cgltf_parse_json_skin(cgltf_options* options, jsmntok_t const* tokens } else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) { - i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_skin->extras); + i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_skin->extras); } else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0) { @@ -5033,6 +4951,19 @@ static int cgltf_parse_json_camera(cgltf_options* options, jsmntok_t const* toke { i = cgltf_parse_json_string(options, tokens, i + 1, json_chunk, &out_camera->name); } + else if (cgltf_json_strcmp(tokens+i, json_chunk, "type") == 0) + { + ++i; + if (cgltf_json_strcmp(tokens + i, json_chunk, "perspective") == 0) + { + out_camera->type = cgltf_camera_type_perspective; + } + else if (cgltf_json_strcmp(tokens + i, json_chunk, "orthographic") == 0) + { + out_camera->type = cgltf_camera_type_orthographic; + } + ++i; + } else if (cgltf_json_strcmp(tokens+i, json_chunk, "perspective") == 0) { ++i; @@ -5042,11 +4973,6 @@ static int cgltf_parse_json_camera(cgltf_options* options, jsmntok_t const* toke int data_size = tokens[i].size; ++i; - if (out_camera->type != cgltf_camera_type_invalid) - { - return CGLTF_ERROR_JSON; - } - out_camera->type = cgltf_camera_type_perspective; for (int k = 0; k < data_size; ++k) @@ -5081,7 +5007,7 @@ static int cgltf_parse_json_camera(cgltf_options* options, jsmntok_t const* toke } else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) { - i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_camera->data.perspective.extras); + i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_camera->data.perspective.extras); } else { @@ -5103,11 +5029,6 @@ static int cgltf_parse_json_camera(cgltf_options* options, jsmntok_t const* toke int data_size = tokens[i].size; ++i; - if (out_camera->type != cgltf_camera_type_invalid) - { - return CGLTF_ERROR_JSON; - } - out_camera->type = cgltf_camera_type_orthographic; for (int k = 0; k < data_size; ++k) @@ -5140,7 +5061,7 @@ static int cgltf_parse_json_camera(cgltf_options* options, jsmntok_t const* toke } else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) { - i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_camera->data.orthographic.extras); + i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_camera->data.orthographic.extras); } else { @@ -5155,7 +5076,7 @@ static int cgltf_parse_json_camera(cgltf_options* options, jsmntok_t const* toke } else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) { - i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_camera->extras); + i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_camera->extras); } else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0) { @@ -5288,7 +5209,7 @@ static int cgltf_parse_json_light(cgltf_options* options, jsmntok_t const* token } else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) { - i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_light->extras); + i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_light->extras); } else { @@ -5414,7 +5335,7 @@ static int cgltf_parse_json_node(cgltf_options* options, jsmntok_t const* tokens } else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) { - i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_node->extras); + i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_node->extras); } else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0) { @@ -5552,7 +5473,7 @@ static int cgltf_parse_json_scene(cgltf_options* options, jsmntok_t const* token } else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) { - i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_scene->extras); + i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_scene->extras); } else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0) { @@ -5634,7 +5555,7 @@ static int cgltf_parse_json_animation_sampler(cgltf_options* options, jsmntok_t } else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) { - i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_sampler->extras); + i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_sampler->extras); } else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0) { @@ -5714,7 +5635,7 @@ static int cgltf_parse_json_animation_channel(cgltf_options* options, jsmntok_t } else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) { - i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_channel->extras); + i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_channel->extras); } else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0) { @@ -5796,7 +5717,7 @@ static int cgltf_parse_json_animation(cgltf_options* options, jsmntok_t const* t } else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) { - i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_animation->extras); + i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_animation->extras); } else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0) { @@ -5852,7 +5773,7 @@ static int cgltf_parse_json_variant(cgltf_options* options, jsmntok_t const* tok } else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) { - i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_variant->extras); + i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_variant->extras); } else { @@ -5916,7 +5837,7 @@ static int cgltf_parse_json_asset(cgltf_options* options, jsmntok_t const* token } else if (cgltf_json_strcmp(tokens + i, json_chunk, "extras") == 0) { - i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_asset->extras); + i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_asset->extras); } else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0) { @@ -6072,7 +5993,7 @@ static int cgltf_parse_json_root(cgltf_options* options, jsmntok_t const* tokens } else if (cgltf_json_strcmp(tokens+i, json_chunk, "extras") == 0) { - i = cgltf_parse_json_extras(options, tokens, i + 1, json_chunk, &out_data->extras); + i = cgltf_parse_json_extras(tokens, i + 1, json_chunk, &out_data->extras); } else if (cgltf_json_strcmp(tokens + i, json_chunk, "extensions") == 0) { diff --git a/third_party/cgltf/cgltf_write.h b/third_party/cgltf/cgltf_write.h index 033b0d1cca..5c4edaf72d 100644 --- a/third_party/cgltf/cgltf_write.h +++ b/third_party/cgltf/cgltf_write.h @@ -15,17 +15,19 @@ * * Reference: * `cgltf_result cgltf_write_file(const cgltf_options* options, const char* - * path, const cgltf_data* data)` writes a glTF data to the given file path. - * If `options->type` is `cgltf_file_type_glb`, both JSON content and binary - * buffer of the given glTF data will be written in a GLB format. - * Otherwise, only the JSON part will be written. - * External buffers and images are not written out. `data` is not deallocated. + * path, const cgltf_data* data)` writes JSON to the given file path. Buffer + * files and external images are not written out. `data` is not deallocated. * * `cgltf_size cgltf_write(const cgltf_options* options, char* buffer, * cgltf_size size, const cgltf_data* data)` writes JSON into the given memory * buffer. Returns the number of bytes written to `buffer`, including a null * terminator. If buffer is null, returns the number of bytes that would have * been written. `data` is not deallocated. + * + * To write custom JSON into the `extras` field, aggregate all the custom JSON + * into a single buffer, then set `file_data` to this buffer. By supplying + * start_offset and end_offset values for various objects, you can select a + * range of characters within the aggregated buffer. */ #ifndef CGLTF_WRITE_H_INCLUDED__ #define CGLTF_WRITE_H_INCLUDED__ @@ -145,17 +147,6 @@ typedef struct { context->needs_comma = 1; } #define CGLTF_WRITE_TEXTURE_INFO(label, info) if (info.texture) { \ - cgltf_write_line(context, "\"" label "\": {"); \ - CGLTF_WRITE_IDXPROP("index", info.texture, context->data->textures); \ - cgltf_write_intprop(context, "texCoord", info.texcoord, 0); \ - if (info.has_transform) { \ - context->extension_flags |= CGLTF_EXTENSION_FLAG_TEXTURE_TRANSFORM; \ - cgltf_write_texture_transform(context, &info.transform); \ - } \ - cgltf_write_extras(context, &info.extras); \ - cgltf_write_line(context, "}"); } - -#define CGLTF_WRITE_NORMAL_TEXTURE_INFO(label, info) if (info.texture) { \ cgltf_write_line(context, "\"" label "\": {"); \ CGLTF_WRITE_IDXPROP("index", info.texture, context->data->textures); \ cgltf_write_intprop(context, "texCoord", info.texcoord, 0); \ @@ -167,28 +158,6 @@ typedef struct { cgltf_write_extras(context, &info.extras); \ cgltf_write_line(context, "}"); } -#define CGLTF_WRITE_OCCLUSION_TEXTURE_INFO(label, info) if (info.texture) { \ - cgltf_write_line(context, "\"" label "\": {"); \ - CGLTF_WRITE_IDXPROP("index", info.texture, context->data->textures); \ - cgltf_write_intprop(context, "texCoord", info.texcoord, 0); \ - cgltf_write_floatprop(context, "strength", info.scale, 1.0f); \ - if (info.has_transform) { \ - context->extension_flags |= CGLTF_EXTENSION_FLAG_TEXTURE_TRANSFORM; \ - cgltf_write_texture_transform(context, &info.transform); \ - } \ - cgltf_write_extras(context, &info.extras); \ - cgltf_write_line(context, "}"); } - -#ifndef CGLTF_CONSTS -static const cgltf_size GlbHeaderSize = 12; -static const cgltf_size GlbChunkHeaderSize = 8; -static const uint32_t GlbVersion = 2; -static const uint32_t GlbMagic = 0x46546C67; -static const uint32_t GlbMagicJsonChunk = 0x4E4F534A; -static const uint32_t GlbMagicBinChunk = 0x004E4942; -#define CGLTF_CONSTS -#endif - static void cgltf_write_indent(cgltf_write_context* context) { if (context->needs_comma) @@ -239,24 +208,15 @@ static void cgltf_write_strprop(cgltf_write_context* context, const char* label, static void cgltf_write_extras(cgltf_write_context* context, const cgltf_extras* extras) { - if (extras->data) + cgltf_size length = extras->end_offset - extras->start_offset; + if (length > 0 && context->data->file_data) { + char* json_string = ((char*) context->data->file_data) + extras->start_offset; cgltf_write_indent(context); - CGLTF_SPRINTF("\"extras\": %s", extras->data); + CGLTF_SPRINTF("%s", "\"extras\": "); + CGLTF_SNPRINTF(length, "%.*s", (int)(extras->end_offset - extras->start_offset), json_string); context->needs_comma = 1; } - else - { - cgltf_size length = extras->end_offset - extras->start_offset; - if (length > 0 && context->data->json) - { - char* json_string = ((char*) context->data->json) + extras->start_offset; - cgltf_write_indent(context); - CGLTF_SPRINTF("%s", "\"extras\": "); - CGLTF_SNPRINTF(length, "%.*s", (int)(extras->end_offset - extras->start_offset), json_string); - context->needs_comma = 1; - } - } } static void cgltf_write_stritem(cgltf_write_context* context, const char* item) @@ -653,6 +613,7 @@ static void cgltf_write_material(cgltf_write_context* context, const cgltf_mater { cgltf_write_floatarrayprop(context, "baseColorFactor", params->base_color_factor, 4); } + cgltf_write_extras(context, ¶ms->extras); cgltf_write_line(context, "}"); } @@ -665,7 +626,7 @@ static void cgltf_write_material(cgltf_write_context* context, const cgltf_mater cgltf_write_line(context, "\"KHR_materials_clearcoat\": {"); CGLTF_WRITE_TEXTURE_INFO("clearcoatTexture", params->clearcoat_texture); CGLTF_WRITE_TEXTURE_INFO("clearcoatRoughnessTexture", params->clearcoat_roughness_texture); - CGLTF_WRITE_NORMAL_TEXTURE_INFO("clearcoatNormalTexture", params->clearcoat_normal_texture); + CGLTF_WRITE_TEXTURE_INFO("clearcoatNormalTexture", params->clearcoat_normal_texture); cgltf_write_floatprop(context, "clearcoatFactor", params->clearcoat_factor, 0.0f); cgltf_write_floatprop(context, "clearcoatRoughnessFactor", params->clearcoat_roughness_factor, 0.0f); cgltf_write_line(context, "}"); @@ -770,8 +731,8 @@ static void cgltf_write_material(cgltf_write_context* context, const cgltf_mater cgltf_write_line(context, "}"); } - CGLTF_WRITE_NORMAL_TEXTURE_INFO("normalTexture", material->normal_texture); - CGLTF_WRITE_OCCLUSION_TEXTURE_INFO("occlusionTexture", material->occlusion_texture); + CGLTF_WRITE_TEXTURE_INFO("normalTexture", material->normal_texture); + CGLTF_WRITE_TEXTURE_INFO("occlusionTexture", material->occlusion_texture); CGLTF_WRITE_TEXTURE_INFO("emissiveTexture", material->emissive_texture); if (cgltf_check_floatarray(material->emissive_factor, 3, 0.0f)) { @@ -1136,47 +1097,6 @@ static void cgltf_write_variant(cgltf_write_context* context, const cgltf_materi cgltf_write_line(context, "}"); } -static void cgltf_write_glb(FILE* file, const void* json_buf, const cgltf_size json_size, const void* bin_buf, const cgltf_size bin_size) -{ - char header[GlbHeaderSize]; - char chunk_header[GlbChunkHeaderSize]; - char json_pad[3] = { 0x20, 0x20, 0x20 }; - char bin_pad[3] = { 0, 0, 0 }; - - cgltf_size json_padsize = (json_size % 4 != 0) ? 4 - json_size % 4 : 0; - cgltf_size bin_padsize = (bin_size % 4 != 0) ? 4 - bin_size % 4 : 0; - cgltf_size total_size = GlbHeaderSize + GlbChunkHeaderSize + json_size + json_padsize; - if (bin_buf != NULL && bin_size > 0) { - total_size += GlbChunkHeaderSize + bin_size + bin_padsize; - } - - // Write a GLB header - memcpy(header, &GlbMagic, 4); - memcpy(header + 4, &GlbVersion, 4); - memcpy(header + 8, &total_size, 4); - fwrite(header, 1, GlbHeaderSize, file); - - // Write a JSON chunk (header & data) - uint32_t json_chunk_size = (uint32_t)(json_size + json_padsize); - memcpy(chunk_header, &json_chunk_size, 4); - memcpy(chunk_header + 4, &GlbMagicJsonChunk, 4); - fwrite(chunk_header, 1, GlbChunkHeaderSize, file); - - fwrite(json_buf, 1, json_size, file); - fwrite(json_pad, 1, json_padsize, file); - - if (bin_buf != NULL && bin_size > 0) { - // Write a binary chunk (header & data) - uint32_t bin_chunk_size = (uint32_t)(bin_size + bin_padsize); - memcpy(chunk_header, &bin_chunk_size, 4); - memcpy(chunk_header + 4, &GlbMagicBinChunk, 4); - fwrite(chunk_header, 1, GlbChunkHeaderSize, file); - - fwrite(bin_buf, 1, bin_size, file); - fwrite(bin_pad, 1, bin_padsize, file); - } -} - cgltf_result cgltf_write_file(const cgltf_options* options, const char* path, const cgltf_data* data) { cgltf_size expected = cgltf_write(options, NULL, 0, data); @@ -1185,18 +1105,13 @@ cgltf_result cgltf_write_file(const cgltf_options* options, const char* path, co if (expected != actual) { fprintf(stderr, "Error: expected %zu bytes but wrote %zu bytes.\n", expected, actual); } - FILE* file = fopen(path, "wb"); + FILE* file = fopen(path, "wt"); if (!file) { return cgltf_result_file_not_found; } // Note that cgltf_write() includes a null terminator, which we omit from the file content. - if (options->type == cgltf_file_type_glb) { - cgltf_write_glb(file, buffer, actual - 1, data->bin, data->bin_size); - } else { - // Write a plain JSON file. - fwrite(buffer, actual - 1, 1, file); - } + fwrite(buffer, actual - 1, 1, file); fclose(file); free(buffer); return cgltf_result_success; diff --git a/third_party/cgltf/test/CMakeLists.txt b/third_party/cgltf/test/CMakeLists.txt index d62d01d6cc..aa31730524 100644 --- a/third_party/cgltf/test/CMakeLists.txt +++ b/third_party/cgltf/test/CMakeLists.txt @@ -41,19 +41,6 @@ else() endif() install( TARGETS ${EXE_NAME} RUNTIME DESTINATION bin ) -set( EXE_NAME test_write_glb ) -add_executable( ${EXE_NAME} test_write_glb.cpp ) -set_property( TARGET ${EXE_NAME} PROPERTY CXX_STANDARD 11 ) -if(MSVC) - target_compile_options(${EXE_NAME} PRIVATE /W4 /WX) - add_definitions( -D_CRT_SECURE_NO_WARNINGS) -else() - target_compile_options(${EXE_NAME} PRIVATE -Wall -Wextra -pedantic -Werror) - target_compile_options(${EXE_NAME} PUBLIC -fsanitize=address) - target_link_options(${EXE_NAME} PUBLIC -fsanitize=address) -endif() -install( TARGETS ${EXE_NAME} RUNTIME DESTINATION bin ) - set( EXE_NAME test_math ) add_executable( ${EXE_NAME} test_math.cpp ) set_property( TARGET ${EXE_NAME} PROPERTY CXX_STANDARD 11 ) diff --git a/third_party/cgltf/test/test_all.py b/third_party/cgltf/test/test_all.py index ac0882f5d8..ec9b9f0164 100755 --- a/third_party/cgltf/test/test_all.py +++ b/third_party/cgltf/test/test_all.py @@ -59,7 +59,6 @@ if __name__ == "__main__": collect_files("glTF-Sample-Models/2.0/", ".glb", "test_conversion") collect_files("glTF-Sample-Models/2.0/", ".gltf", "test_conversion") collect_files("glTF-Sample-Models/2.0/", ".gltf", "test_write") - collect_files("glTF-Sample-Models/2.0/", ".glb", "test_write_glb") result = os.system(get_executable_path("test_math")) if result != 0: diff --git a/third_party/cgltf/test/test_write_glb.cpp b/third_party/cgltf/test/test_write_glb.cpp deleted file mode 100644 index c3c5b4430e..0000000000 --- a/third_party/cgltf/test/test_write_glb.cpp +++ /dev/null @@ -1,58 +0,0 @@ -#define CGLTF_IMPLEMENTATION -#define CGLTF_WRITE_IMPLEMENTATION -#include "../cgltf_write.h" - -#include -#include -#include -#include -#include - -int main(int argc, char** argv) -{ - if (argc < 2) - { - printf("err\n"); - return -1; - } - - cgltf_options options = {}; - cgltf_data* data0 = NULL; - cgltf_result result = cgltf_parse_file(&options, argv[1], &data0); - - // Silently skip over files that are unreadable since this is a writing test. - if (result != cgltf_result_success) - { - return cgltf_result_success; - } - - options.type = cgltf_file_type_glb; // Write back in a GLB format - result = cgltf_write_file(&options, "out.glb", data0); - if (result != cgltf_result_success) - { - return result; - } - - cgltf_data* data1 = NULL; - result = cgltf_parse_file(&options, "out.glb", &data1); - if (result != cgltf_result_success) - { - return result; - } - - if (data0->meshes_count != data1->meshes_count) { - return -1; - } - - // Compare binary buffers - if (data0->bin_size != data1->bin_size) { - return -1; - } - if (memcmp(data0->bin, data1->bin, data0->bin_size) != 0) { - return -1; - } - - cgltf_free(data1); - cgltf_free(data0); - return cgltf_result_success; -} diff --git a/third_party/cgltf/tnt/README b/third_party/cgltf/tnt/README index 1e992ce14c..fbfc97e770 100644 --- a/third_party/cgltf/tnt/README +++ b/third_party/cgltf/tnt/README @@ -1,10 +1,10 @@ This folder was last updated as follows: - export sha=280ab89 + export tag=v1.13 cd third_party - curl -L -O https://github.com/jkuhlmann/cgltf/archive/${sha}.zip - unzip ${sha}.zip + curl -L -O https://github.com/jkuhlmann/cgltf/archive/refs/tags/${tag}.zip + unzip ${tag}.zip mv cgltf-* cgltf_new rsync -r cgltf_new/ cgltf/ --delete --exclude tnt - rm -rf ${sha}.zip cgltf_new + rm -rf ${tag}.zip cgltf_new git add cgltf ; git status From 2cd492ed38c140494e0695dc598a3e5b3e7567c5 Mon Sep 17 00:00:00 2001 From: Powei Feng Date: Wed, 4 Oct 2023 10:25:15 -0700 Subject: [PATCH 07/63] Fix build failures due to filamat lite removal try 4 (#7228) --- .github/workflows/android-continuous.yml | 4 ---- .github/workflows/release.yml | 1 - 2 files changed, 5 deletions(-) diff --git a/.github/workflows/android-continuous.yml b/.github/workflows/android-continuous.yml index 326ceb6546..e9aebe8e76 100644 --- a/.github/workflows/android-continuous.yml +++ b/.github/workflows/android-continuous.yml @@ -29,10 +29,6 @@ jobs: with: name: filamat-android-full path: out/filamat-android-release.aar - - uses: actions/upload-artifact@v1.0.0 - with: - name: filamat-android-lite - path: out/filamat-android-lite-release.aar - uses: actions/upload-artifact@v1.0.0 with: name: gltfio-android-release diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 6f2422b1b6..fa12f3d322 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -124,7 +124,6 @@ jobs: cd ../.. mv out/filament-android-release.aar out/filament-${TAG}-android.aar mv out/filamat-android-release.aar out/filamat-${TAG}-android.aar - mv out/filamat-android-lite-release.aar out/filamat-${TAG}-lite-android.aar mv out/gltfio-android-release.aar out/gltfio-${TAG}-android.aar mv out/filament-utils-android-release.aar out/filament-utils-${TAG}-android.aar - name: Sign sample-gltf-viewer From 5190b03f891b8a4be1e10e1774e95fa199b36195 Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Wed, 4 Oct 2023 22:10:49 -0700 Subject: [PATCH 08/63] the visibility type can be 8 bits instead of 16. It was changed to 16 a while back to handle more shadows, but since then we changed the culling algorithm and 8 bits is enough again. --- filament/src/Culler.h | 4 ++-- filament/src/ShadowMap.h | 13 +++++++------ filament/src/details/View.h | 2 -- 3 files changed, 9 insertions(+), 10 deletions(-) diff --git a/filament/src/Culler.h b/filament/src/Culler.h index ad5fd4f3c8..6f3a0bf9f9 100644 --- a/filament/src/Culler.h +++ b/filament/src/Culler.h @@ -37,12 +37,12 @@ namespace filament { class Culler { public: // Culler can only process buffers with a size multiple of MODULO - static constexpr size_t MODULO = 4u; + static constexpr size_t MODULO = 8u; static inline size_t round(size_t count) noexcept { return (count + (MODULO - 1)) & ~(MODULO - 1); } - using result_type = uint16_t; + using result_type = uint8_t; /* * returns whether each AABB in an array intersects with the frustum diff --git a/filament/src/ShadowMap.h b/filament/src/ShadowMap.h index af78e271a4..3849710c3b 100644 --- a/filament/src/ShadowMap.h +++ b/filament/src/ShadowMap.h @@ -39,12 +39,12 @@ class RenderPass; // The value of the 'VISIBLE_MASK' after culling. Each bit represents visibility in a frustum // (either camera or light). // -// 1 -// bits 5 ... 7 6 5 4 3 2 1 0 -// +------------------------------------------------------+ -// VISIBLE_RENDERABLE X -// VISIBLE_DIR_SHADOW_RENDERABLE X -// VISIBLE_DYN_SHADOW_RENDERABLE X +// +// bits 7 6 5 4 3 2 1 0 +// +---------------------------------------------+ +// VISIBLE_RENDERABLE X +// VISIBLE_DIR_SHADOW_RENDERABLE X +// VISIBLE_DYN_SHADOW_RENDERABLE X // A "shadow renderable" is a renderable rendered to the shadow map during a shadow pass: // PCF shadows: only shadow casters @@ -54,6 +54,7 @@ static constexpr size_t VISIBLE_RENDERABLE_BIT = 0u; static constexpr size_t VISIBLE_DIR_SHADOW_RENDERABLE_BIT = 1u; static constexpr size_t VISIBLE_DYN_SHADOW_RENDERABLE_BIT = 2u; +static constexpr Culler::result_type VISIBLE_RENDERABLE = 1u << VISIBLE_RENDERABLE_BIT; static constexpr Culler::result_type VISIBLE_DIR_SHADOW_RENDERABLE = 1u << VISIBLE_DIR_SHADOW_RENDERABLE_BIT; static constexpr Culler::result_type VISIBLE_DYN_SHADOW_RENDERABLE = 1u << VISIBLE_DYN_SHADOW_RENDERABLE_BIT; diff --git a/filament/src/details/View.h b/filament/src/details/View.h index f452ce22f1..50a6c6bb52 100644 --- a/filament/src/details/View.h +++ b/filament/src/details/View.h @@ -73,8 +73,6 @@ class FMaterialInstance; class FRenderer; class FScene; -static constexpr Culler::result_type VISIBLE_RENDERABLE = 1u << VISIBLE_RENDERABLE_BIT; - // ------------------------------------------------------------------------------------------------ class FView : public View { From f1f7aeb14fea40899b0c8efd5e7bf57dd7b7c911 Mon Sep 17 00:00:00 2001 From: Jacob Su Date: Thu, 5 Oct 2023 00:33:27 +0800 Subject: [PATCH 09/63] fix java Engine.Builder method access modifiers. --- .../java/com/google/android/filament/Engine.java | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/android/filament-android/src/main/java/com/google/android/filament/Engine.java b/android/filament-android/src/main/java/com/google/android/filament/Engine.java index 291200adca..3423e87d05 100644 --- a/android/filament-android/src/main/java/com/google/android/filament/Engine.java +++ b/android/filament-android/src/main/java/com/google/android/filament/Engine.java @@ -173,7 +173,7 @@ public class Engine { * @param backend Driver backend to use * @return A reference to this Builder for chaining calls. */ - Builder backend(Backend backend) { + public Builder backend(Backend backend) { nSetBuilderBackend(mNativeBuilder, backend.ordinal()); return this; } @@ -186,7 +186,7 @@ public class Engine { * must be an instance of {@link android.opengl.EGLContext}. * @return A reference to this Builder for chaining calls. */ - Builder sharedContext(Object sharedContext) { + public Builder sharedContext(Object sharedContext) { if (Platform.get().validateSharedContext(sharedContext)) { nSetBuilderSharedContext(mNativeBuilder, Platform.get().getSharedContextNativeHandle(sharedContext)); @@ -201,7 +201,7 @@ public class Engine { * @param config A {@link Config} object * @return A reference to this Builder for chaining calls. */ - Builder config(Config config) { + public Builder config(Config config) { nSetBuilderConfig(mNativeBuilder, config.commandBufferSizeMB, config.perRenderPassArenaSizeMB, config.driverHandleArenaSizeMB, config.minCommandBufferSizeMB, config.perFrameCommandsSizeMB, @@ -219,7 +219,7 @@ public class Engine { * @exception IllegalStateException can be thrown if there isn't enough memory to * allocate the command buffer. */ - Engine build() { + public Engine build() { long nativeEngine = nBuilderBuild(mNativeBuilder); if (nativeEngine == 0) throw new IllegalStateException("Couldn't create Engine"); return new Engine(nativeEngine); @@ -352,9 +352,7 @@ public class Engine { * @exception IllegalStateException can be thrown if there isn't enough memory to * allocate the command buffer. * - * @deprecated use {@link Builder} */ - @Deprecated @NonNull public static Engine create() { return new Builder().build(); @@ -374,9 +372,7 @@ public class Engine { * @exception IllegalStateException can be thrown if there isn't enough memory to * allocate the command buffer. * - * @deprecated use {@link Builder} */ - @Deprecated @NonNull public static Engine create(@NonNull Backend backend) { return new Builder() @@ -400,9 +396,7 @@ public class Engine { * @exception IllegalStateException can be thrown if there isn't enough memory to * allocate the command buffer. * - * @deprecated use {@link Builder} */ - @Deprecated @NonNull public static Engine create(@NonNull Object sharedContext) { return new Builder() From 6590f62052149b36cba15be2df22d339bf254e19 Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Thu, 5 Oct 2023 10:38:18 -0700 Subject: [PATCH 10/63] handle more generic projections for shadowFar When we update the Far plane in the projection matrix, we assumed the shape of the matrix. This fell appart when the projection matrix was (for instance) a blend between an ortho and perspective projection. We now do this more generally, that is, with less assumptions on the projection matrix shape. --- filament/src/ShadowMapManager.cpp | 64 ++++++++++++++++++++++++++----- 1 file changed, 54 insertions(+), 10 deletions(-) diff --git a/filament/src/ShadowMapManager.cpp b/filament/src/ShadowMapManager.cpp index 34c47519e6..30880c96a9 100644 --- a/filament/src/ShadowMapManager.cpp +++ b/filament/src/ShadowMapManager.cpp @@ -446,21 +446,65 @@ ShadowMapManager::ShadowTechnique ShadowMapManager::updateCascadeShadowMaps(FEng FLightManager::ShadowParams const& params = lcm.getShadowParams(directionalLight); // Adjust the camera's projection for the light's shadowFar - cameraInfo.zf = params.options.shadowFar > 0.0f ? params.options.shadowFar : cameraInfo.zf; if (UTILS_UNLIKELY(params.options.shadowFar > 0.0f)) { cameraInfo.zf = params.options.shadowFar; float const n = cameraInfo.zn; float const f = cameraInfo.zf; - if (std::abs(cameraInfo.cullingProjection[2].w) > std::numeric_limits::epsilon()) { - // perspective projection - cameraInfo.cullingProjection[2].z = (f + n) / (n - f); - cameraInfo.cullingProjection[3].z = (2 * f * n) / (n - f); - } else { - // orthographic projection - cameraInfo.cullingProjection[2].z = 2.0f / (n - f); - cameraInfo.cullingProjection[3].z = (f + n) / (n - f); - } + + /* + * Updating a projection matrix near and far planes: + * + * We assume that the near and far plane equations are of the form: + * N = { 0, 0, 1, n } + * F = { 0, 0, -1, -f } + * + * We also assume that the lower-left 2x2 of the projection is all 0: + * P = A 0 C 0 + * 0 B D 0 + * 0 0 E F + * 0 0 G H + * + * It result that we need to calculate E and F while leaving all other parameter unchanged. + * + * We know that: + * with N, F the near/far normalized plane equation parameters + * sn, sf arbitrary scale factors (they don't affect the planes) + * m is the transpose of projection (see Frustum.cpp) + * + * sn * N == -m[3] - m[2] + * sf * F == -m[3] + m[2] + * + * sn * N + sf * F == -2 * m[3] + * sn * N - sf * F == -2 * m[2] + * + * sn * N.z + sf * F.z == -2 * m[3].z + * sn * N.w + sf * F.w == -2 * m[3].w + * sn * N.z - sf * F.z == -2 * m[2].z + * sn * N.w - sf * F.w == -2 * m[2].w + * + * sn * N.z + sf * F.z == -2 * p[2].w + * sn * N.w + sf * F.w == -2 * p[3].w + * sn * N.z - sf * F.z == -2 * p[2].z + * sn * N.w - sf * F.w == -2 * p[3].z + * + * We now need to solve for { p[2].z, p[3].z, sn, sf } : + * + * sn == -2 * (p[2].w * F.w - p[3].w * F.z) / (N.z * F.w - N.w * F.z) + * sf == -2 * (p[2].w * N.w - p[3].w * N.z) / (F.z * N.w - F.w * N.z) + * p[2].z == (sf * F.z - sn * N.z) / 2 + * p[3].z == (sf * F.w - sn * N.w) / 2 + */ + auto& p = cameraInfo.cullingProjection; + float4 const N = { 0, 0, 1, n }; // near plane equation + float4 const F = { 0, 0, -1, -f }; // far plane equation + // near plane equation scale factor + float const sn = -2.0f * (p[2].w * F.w - p[3].w * F.z) / (N.z * F.w - N.w * F.z); + // far plane equation scale factor + float const sf = -2.0f * (p[2].w * N.w - p[3].w * N.z) / (F.z * N.w - F.w * N.z); + // New values for the projection + p[2].z = (sf * F.z - sn * N.z) * 0.5f; + p[3].z = (sf * F.w - sn * N.w) * 0.5f; } const ShadowMap::ShadowMapInfo shadowMapInfo{ From 78d433cafad46dfa2b27a86408dab397b652bd93 Mon Sep 17 00:00:00 2001 From: Powei Feng Date: Thu, 5 Oct 2023 22:43:39 -0700 Subject: [PATCH 11/63] Update NEW_RELEASE_NOTES.md --- NEW_RELEASE_NOTES.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/NEW_RELEASE_NOTES.md b/NEW_RELEASE_NOTES.md index 4a42fb94ef..5b74000c76 100644 --- a/NEW_RELEASE_NOTES.md +++ b/NEW_RELEASE_NOTES.md @@ -8,5 +8,5 @@ appropriate header in [RELEASE_NOTES.md](./RELEASE_NOTES.md). ## Release notes for next branch cut -engine: Added parameter for configuring JobSystem thread count -engine: In Java, decpreate Engine.create() and recommend Engine.Builder instead \ No newline at end of file +- engine: Added parameter for configuring JobSystem thread count +- engine: In Java, introduce Engine.Builder From 6279613b79bcf003edd9781f1a53f833b0ce147d Mon Sep 17 00:00:00 2001 From: Ben Doherty Date: Mon, 9 Oct 2023 14:15:07 -0700 Subject: [PATCH 12/63] Metal: support float16 operations in --optimize-size mode (#7249) --- libs/filamat/src/GLSLPostProcessor.cpp | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/libs/filamat/src/GLSLPostProcessor.cpp b/libs/filamat/src/GLSLPostProcessor.cpp index 73605eb258..6d92153d5f 100644 --- a/libs/filamat/src/GLSLPostProcessor.cpp +++ b/libs/filamat/src/GLSLPostProcessor.cpp @@ -587,13 +587,14 @@ std::shared_ptr GLSLPostProcessor::createOptimizer( registerSizePasses(*optimizer, config); } else if (optimization == MaterialBuilder::Optimization::PERFORMANCE) { registerPerformancePasses(*optimizer, config); - // Metal doesn't support relaxed precision, but does have support for float16 math operations. - if (config.targetApi == MaterialBuilder::TargetApi::METAL) { - optimizer->RegisterPass(CreateConvertRelaxedToHalfPass()); - optimizer->RegisterPass(CreateSimplificationPass()); - optimizer->RegisterPass(CreateRedundancyEliminationPass()); - optimizer->RegisterPass(CreateAggressiveDCEPass()); - } + } + + // Metal doesn't support relaxed precision, but does have support for float16 math operations. + if (config.targetApi == MaterialBuilder::TargetApi::METAL) { + optimizer->RegisterPass(CreateConvertRelaxedToHalfPass()); + optimizer->RegisterPass(CreateSimplificationPass()); + optimizer->RegisterPass(CreateRedundancyEliminationPass()); + optimizer->RegisterPass(CreateAggressiveDCEPass()); } return optimizer; From e78a06797ccd01149d0d561085e7c52a31e6f81a Mon Sep 17 00:00:00 2001 From: Powei Feng Date: Tue, 10 Oct 2023 13:06:24 -0700 Subject: [PATCH 13/63] doc: pin viewer lit to a specific version (#7253) - Pin lit to version 2.8.0 (to fix a breakage caused by new release). - Update viewer filament version to latest - Use symbolic link instead of having two copies of the same file. (Could we consider removing `filament-viewer.js` in `web/filament-js/` ?) - Update `web/filament-js/README.md` --- docs/viewer/filament-viewer.js | 456 +---------------------------- docs/viewer/index.html | 2 +- web/filament-js/README.md | 4 +- web/filament-js/filament-viewer.js | 2 +- 4 files changed, 4 insertions(+), 460 deletions(-) mode change 100644 => 120000 docs/viewer/filament-viewer.js diff --git a/docs/viewer/filament-viewer.js b/docs/viewer/filament-viewer.js deleted file mode 100644 index 01439952f4..0000000000 --- a/docs/viewer/filament-viewer.js +++ /dev/null @@ -1,455 +0,0 @@ -/* -* Copyright (C) 2021 The Android Open Source Project -* -* Licensed under the Apache License, Version 2.0 (the "License"); -* you may not use this file except in compliance with the License. -* You may obtain a copy of the License at -* -* http://www.apache.org/licenses/LICENSE-2.0 -* -* Unless required by applicable law or agreed to in writing, software -* distributed under the License is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* See the License for the specific language governing permissions and -* limitations under the License. -*/ - -// If you are bundling this with rollup, webpack, or esbuild, the following URL should be trimmed. -import { LitElement, html, css } from "https://unpkg.com/lit?module"; - -// This little utility checks if the Filament module is ready for action. -// If so, it immediately calls the given function. If not, it asks the Filament -// loader to call it as soon as the module becomes ready. -class FilamentTasks { - add(callback) { - if (Filament.isReady) { - callback(); - } else { - Filament.init([], callback); - } - } -} - -// FilamentViewer is a fairly limited web component for showing glTF models with Filament. -// -// To embed a 3D viewer in your web page, simply add something like the following to your HTML, -// similar to an element. -// -// -// -// In addition to the src URL, attributes can be used to set up an optional IBL and skybox. -// The documentation for these attributes is in the FilamentViewer constructor. -// -// MISSING FEATURES -// ---------------- -// None of the following features are implemented. They would be easy to add. -// - Replace gltumble and Trackball with camutils Manipulator (i.e. enable scroll-to-zoom) -// - Write a documentation page (might be neat if the doc page has instances of the actual viewer) -// - Fix the import at the top of the file to support webpack / rollup / esbuild -// - Expose more animation properties (e.g. enable / disable, selected index) -// - Expose camera properties, glTF camera selection, and clear color -// - Expose more IBL properties -// - Expose directional light properties -// - Optional turntable animation -// -class FilamentViewer extends LitElement { - constructor() { - super(); - - // LitElement properties: - this.src = null; // Path to glTF file. - this.alt = null; // Alternate canvas content. - this.ibl = null; // Path to image based light ktx. - this.sky = null; // Path to skybox ktx. - this.enableDrop = null; // Enables drag and drop. - this.intensity = 30000; // Intensity of the image based light. - this.materialVariant = 0; // Index of material variant. - - // Private properties: - this.filamentTasks = new FilamentTasks(); - this.canvasId = "filament-viewer-canvas"; - this.overlayId = "filament-viewer-overlay"; - this.srcBlob = null; - } - - static get properties() { - return { - src: { type: String }, - alt: { type: String }, - ibl: { type: String }, - sky: { type: String }, - enableDrop: { type: Boolean }, - intensity: { type: Number }, - materialVariant: { type: Number }, - } - } - - firstUpdated() { - // At this point in the lit-element lifecycle, the "render" has taken place, which simply - // means the canvas element now exists. However the Filament wasm module may or may not be - // fully loaded, which is why we use the task manager. - const canvas = this.shadowRoot.getElementById(this.canvasId); - if (canvas.parentNode.host.parentElement.tagName === "FILAMENT-VIEWER") { - console.error("Do not nest FilamentViewer, this is unsupported."); - console.error("Try placing each viewer in a wrapper element."); - return; - } - this.filamentTasks.add(this._startFilament.bind(this)); - - const overlay = this.shadowRoot.getElementById(this.overlayId); - ["dragenter", "dragover", "dragleave", "drop"].forEach(eventName => { - overlay.addEventListener(eventName, e => { e.preventDefault(); e.stopPropagation() }, false) - }); - if (this.enableDrop) { - overlay.addEventListener("drop", this._dropHandler.bind(this), false); - } - } - - updated(props) { - if (props.has("src")) this.filamentTasks.add(this._loadAsset.bind(this)); - if (props.has("ibl")) this.filamentTasks.add(this._loadIbl.bind(this)); - if (props.has("sky")) this.filamentTasks.add(this._loadSky.bind(this)); - if (props.has("enableDrop")) this._updateOverlay(); - if (props.has("intensity") && this.indirectLight) { - this.indirectLight.setIntensity(this.intensity); - } - if (props.has("materialVariant") && this.asset) this._applyMaterialVariant(); - } - - static get styles() { - return css` - :host { - display: inline-block; - width: 300px; - height: 300px; - border: solid 1px black; - position: relative; - } - canvas { - background: #D9D9D9; /* This is consistent with the default clear color. */ - } - canvas, .overlay { - width: 100%; - height: 100%; - position: absolute; - } - .overlay { - text-align: center; - padding: 10px; - }`; - } - - render() { - return html` - -
- `; - } - - _dropHandler(dragEvent) { - if (!dragEvent.dataTransfer) return; - this.srcBlob = null; - this.srcBlobResources = {}; - for (const file of dragEvent.dataTransfer.files) { - if (file.name.endsWith(".glb") || file.name.endsWith(".gltf")) { - this.srcBlob = file; - } else { - this.srcBlobResources[file.name] = file; - } - } - if (this.srcBlob) { - this._loadAsset(); - } else { - console.error("Please include a glTF file."); - } - }; - - _updateOverlay() { - const overlay = this.shadowRoot.getElementById(this.overlayId); - if (!this.enableDrop || this.asset) { - overlay.innerHTML = ""; - } else { - overlay.innerHTML = "Drop glb or file set here."; - } - } - - _startFilament() { - const LightType = Filament.LightManager$Type; - - const canvas = this.shadowRoot.getElementById(this.canvasId); - const overlay = this.shadowRoot.getElementById(this.overlayId); - - this.engine = Filament.Engine.create(canvas); - this.scene = this.engine.createScene(); - this.sunlight = Filament.EntityManager.get().create(); - this.scene.addEntity(this.sunlight); - this.loader = this.engine.createAssetLoader(); - this.cameraEntity = Filament.EntityManager.get().create(); - this.camera = this.engine.createCamera(this.cameraEntity); - this.swapChain = this.engine.createSwapChain(); - this.renderer = this.engine.createRenderer(); - this.view = this.engine.createView(); - this.view.setVignetteOptions({ midPoint: 0.7, enabled: true }); - this.view.setCamera(this.camera); - this.view.setScene(this.scene); - - // If gltumble has been loaded, use it. - if (window.Trackball) { - this.trackball = new Trackball(overlay, { startSpin: 0.0 }); - } - - // This color is consistent with the default CSS background color. - this.renderer.setClearOptions({ clearColor: [0.8, 0.8, 0.8, 1.0], clear: true }); - - Filament.LightManager.Builder(LightType.SUN) - .direction([0, -.7, -.7]) - .sunAngularRadius(1.9) - .castShadows(true) - .sunHaloSize(10.0) - .sunHaloFalloff(80.0) - .build(this.engine, this.sunlight); - - // TODO: if ResizeObserver is not supported, then we should call _onResized and - // pass (canvas.clientWidth * window.devicePixelRatio) for the dimensions. - var ro = new ResizeObserver(entries => { - const width = entries[0].devicePixelContentBoxSize[0].inlineSize; - const height = entries[0].devicePixelContentBoxSize[0].blockSize; - this._onResized(width, height); - }); - ro.observe(canvas); - - window.requestAnimationFrame(this._renderFrame.bind(this)); - } - - _onResized(width, height) { - const Fov = Filament.Camera$Fov; - const canvas = this.shadowRoot.getElementById(this.canvasId); - canvas.width = width; - canvas.height = height; - this.view.setViewport([0, 0, width, height]); - const y = -0.125, eye = [0, y, 1.5], center = [0, y, 0], up = [0, 1, 0]; - this.camera.lookAt(eye, center, up); - const aspect = width / height; - const fov = aspect < 1 ? Fov.HORIZONTAL : Fov.VERTICAL; - this.camera.setProjectionFov(25, aspect, 1.0, 10.0, fov); - } - - _loadIbl() { - if (!this.ibl) { - return; - } - if (this.indirectLight) { - console.info("FilamentViewer does not allow the IBL to be changed."); - return; - } - fetch(this.ibl).then(response => { - return response.arrayBuffer(); - }).then(arrayBuffer => { - const ktxData = new Uint8Array(arrayBuffer); - this.indirectLight = this.engine.createIblFromKtx1(ktxData); - this.indirectLight.setIntensity(this.intensity); - this.scene.setIndirectLight(this.indirectLight); - }); - } - - _loadSky() { - if (!this.sky) { - return; - } - if (this.skybox) { - console.info("FilamentViewer does not allow the skybox to be changed."); - return; - } - fetch(this.sky).then(response => { - return response.arrayBuffer(); - }).then(arrayBuffer => { - const ktxData = new Uint8Array(arrayBuffer); - this.skybox = this.engine.createSkyFromKtx1(ktxData); - this.scene.setSkybox(this.skybox); - }); - } - - _loadAsset() { - const zoffset = 4; - - if (this.asset) { - this.scene.removeEntities(this.asset.getEntities()); - this.animator = null; - this.asset = null; - } - - // If we have neither a URL nor a dropped file, leave early. - if (!this.src && !this.srcBlob) { - this._updateOverlay(); - return; - } - - // Dropping a glb file is simple because there are no external resources. - if (this.srcBlob && this.srcBlob.name.endsWith(".glb")) { - this.srcBlob.arrayBuffer().then(buffer => { - this.asset = this.loader.createAssetFromBinary(new Uint8Array(buffer)); - const aabb = this.asset.getBoundingBox(); - this.assetRoot = this.asset.getRoot(); - this.unitCubeTransform = Filament.fitIntoUnitCube(aabb, zoffset); - this.asset.loadResources(); - this.animator = this.asset.getAnimator(); - this.animationStartTime = Date.now(); - this._updateOverlay(); - }); - return; - } - - // Dropping a fileset requires pushing each resource to ResourceLoader. - if (this.srcBlob && this.srcBlob.name.endsWith(".gltf")) { - - const config = { - normalizeSkinningWeights: true, - recomputeBoundingBoxes: false, - ignoreBindTransform: false, - asyncInterval: 30 - }; - - const doneAddingResources = (resourceLoader, stbProvider, ktx2Provider) => { - this.srcBlobResources = {}; - resourceLoader.asyncBeginLoad(this.asset); - const timer = setInterval(() => { - resourceLoader.asyncUpdateLoad(); - const progress = resourceLoader.asyncGetLoadProgress(); - if (progress >= 1) { - clearInterval(timer); - resourceLoader.delete(); - stbProvider.delete(); - ktx2Provider.delete(); - this.animator = this.asset.getAnimator(); - this.animationStartTime = Date.now(); - } - }, config.asyncInterval); - }; - - this.srcBlob.arrayBuffer().then(buffer => { - this.asset = this.loader.createAssetFromJson(new Uint8Array(buffer)); - const aabb = this.asset.getBoundingBox(); - this.assetRoot = this.asset.getRoot(); - this.unitCubeTransform = Filament.fitIntoUnitCube(aabb, zoffset); - - const resourceLoader = new Filament.gltfio$ResourceLoader(this.engine, - config.normalizeSkinningWeights, - config.recomputeBoundingBoxes, - config.ignoreBindTransform); - - const stbProvider = new Filament.gltfio$StbProvider(this.engine); - const ktx2Provider = new Filament.gltfio$Ktx2Provider(this.engine); - - resourceLoader.addStbProvider("image/jpeg", stbProvider); - resourceLoader.addStbProvider("image/png", stbProvider); - resourceLoader.addKtx2Provider("image/ktx2", ktx2Provider); - - let remaining = Object.keys(this.srcBlobResources).length; - for (const name in this.srcBlobResources) { - this.srcBlobResources[name].arrayBuffer().then(buffer => { - const desc = getBufferDescriptor(new Uint8Array(buffer)); - resourceLoader.addResourceData(name, getBufferDescriptor(desc)); - if (--remaining === 0) { - doneAddingResources(resourceLoader, stbProvider, ktx2Provider); - } - }); - } - - this._updateOverlay(); - }); - - return; - } - - // If we reach this point, we are loading from a src URL rather than drag-and-drop. - - fetch(this.src).then(response => { - return response.arrayBuffer(); - }).then(arrayBuffer => { - const modelData = new Uint8Array(arrayBuffer); - if (this.src.endsWith(".glb")) { - this.asset = this.loader.createAssetFromBinary(modelData); - } else { - this.asset = this.loader.createAssetFromJson(modelData); - } - - const aabb = this.asset.getBoundingBox(); - this.assetRoot = this.asset.getRoot(); - this.unitCubeTransform = Filament.fitIntoUnitCube(aabb, zoffset); - - const basePath = '' + new URL(this.src, document.location); - - this.asset.loadResources(() => { - this.animator = this.asset.getAnimator(); - this.animationStartTime = Date.now(); - this._applyMaterialVariant(); - }, null, basePath); - - this._updateOverlay(); - }); - } - - _updateAsset() { - // Invoke the first glTF animation if it exists. - if (this.animator) { - if (this.animator.getAnimationCount() > 0) { - const ms = Date.now() - this.animationStartTime; - this.animator.applyAnimation(0, ms / 1000); - } - this.animator.updateBoneMatrices(); - } - - // Apply the root transform of the model. - const tcm = this.engine.getTransformManager(); - const inst = tcm.getInstance(this.assetRoot); - let rootTransform = this.unitCubeTransform; - if (this.trackball) { - rootTransform = Filament.multiplyMatrices(rootTransform, this.trackball.getMatrix()); - } - tcm.setTransform(inst, rootTransform); - inst.delete(); - - // Add renderable entities to the scene as they become ready. - while (true) { - const entity = this.asset.popRenderable(); - if (entity.getId() == 0) { - entity.delete(); - break; - } - this.scene.addEntity(entity); - entity.delete(); - } - } - - _renderFrame() { - // Apply transforms and add entities to the scene. - if (this.asset) { - this._updateAsset(); - } - - // Render the view and update the Filament engine. - if (this.renderer.beginFrame(this.swapChain)) { - this.renderer.renderView(this.view); - this.renderer.endFrame(); - } - this.engine.execute(); - - window.requestAnimationFrame(this._renderFrame.bind(this)); - } - - _applyMaterialVariant() { - if (!this.hasAttribute("materialVariant")) { - return; - } - const names = this.asset.getMaterialVariantNames(); - const index = this.materialVariant; - if (index < 0 || index >= names.length) { - console.error(`Material variant ${index} does not exist in this asset.`); - return; - } - console.info(this.src, `Applying material variant: ${names[index]}`); - this.asset.applyMaterialVariant(index); - } -} - -customElements.define("filament-viewer", FilamentViewer); diff --git a/docs/viewer/filament-viewer.js b/docs/viewer/filament-viewer.js new file mode 120000 index 0000000000..bf4479c2be --- /dev/null +++ b/docs/viewer/filament-viewer.js @@ -0,0 +1 @@ +../../web/filament-js/filament-viewer.js \ No newline at end of file diff --git a/docs/viewer/index.html b/docs/viewer/index.html index 8d1ca5a760..2bbc6b187d 100644 --- a/docs/viewer/index.html +++ b/docs/viewer/index.html @@ -43,7 +43,7 @@ filament-viewer::part(canvas) {

- + diff --git a/web/filament-js/README.md b/web/filament-js/README.md index f83fcab2c1..bee62b75ef 100644 --- a/web/filament-js/README.md +++ b/web/filament-js/README.md @@ -49,6 +49,4 @@ do `npm publish`. 9. Update the live drag-and-drop viewer as follows: 1. Edit the pinned Filament version in the ` + + + + + + + diff --git a/libs/matdbg/web/script.js b/libs/matdbg/web/script.js index 9477ab05b7..5a7df5b557 100644 --- a/libs/matdbg/web/script.js +++ b/libs/matdbg/web/script.js @@ -234,9 +234,9 @@ function statusLoop() { // A first-time request returned successfully if (matid === '0') { onConnected(); - } else { + } else if (matid != '1') { fetchMaterial(matid); - } + } // else matid == '1' and it's a no-op, we just loop again. statusLoop(); }) .catch(err => { From 7b7dfad5527f0d6bf7e1aad0a632a69f484797fe Mon Sep 17 00:00:00 2001 From: Powei Feng Date: Tue, 24 Oct 2023 15:43:24 -0700 Subject: [PATCH 51/63] filamat: Fix MaterialInfo::userMaterialHasCustomDepth init (#7292) Leaving it uninitialized leads to msan failure. --- libs/filamat/src/MaterialBuilder.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/libs/filamat/src/MaterialBuilder.cpp b/libs/filamat/src/MaterialBuilder.cpp index 01e645c687..7f00a1b4f2 100644 --- a/libs/filamat/src/MaterialBuilder.cpp +++ b/libs/filamat/src/MaterialBuilder.cpp @@ -629,6 +629,9 @@ void MaterialBuilder::prepareToBuild(MaterialInfo& info) noexcept { info.vertexDomainDeviceJittered = mVertexDomainDeviceJittered; info.featureLevel = mFeatureLevel; info.groupSize = mGroupSize; + + // This is determined via static analysis of the glsl after prepareToBuild(). + info.userMaterialHasCustomDepth = false; } bool MaterialBuilder::findProperties(backend::ShaderStage type, From af0c6a7fe96f6dbfdd59eae1174081a4bffd2679 Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Tue, 24 Oct 2023 15:30:12 -0700 Subject: [PATCH 52/63] OpenGLBlobCache: be more robust when shader fails to compile - don't call BlobCache if link status false - don't assume glGetProgramiv never fails - don't assume malloc never fails FIXES=[307549547] --- .../backend/src/opengl/OpenGLBlobCache.cpp | 25 +++++++++++-------- .../src/opengl/ShaderCompilerService.cpp | 4 +-- 2 files changed, 17 insertions(+), 12 deletions(-) diff --git a/filament/backend/src/opengl/OpenGLBlobCache.cpp b/filament/backend/src/opengl/OpenGLBlobCache.cpp index 48155dc23f..4f65c60586 100644 --- a/filament/backend/src/opengl/OpenGLBlobCache.cpp +++ b/filament/backend/src/opengl/OpenGLBlobCache.cpp @@ -90,18 +90,21 @@ void OpenGLBlobCache::insert(Platform& platform, if (platform.hasBlobFunc()) { SYSTRACE_CONTEXT(); GLenum format; - GLint programBinarySize; + GLint programBinarySize = 0; SYSTRACE_NAME("glGetProgramiv"); glGetProgramiv(program, GL_PROGRAM_BINARY_LENGTH, &programBinarySize); if (programBinarySize) { size_t const size = sizeof(Blob) + programBinarySize; std::unique_ptr blob{ (Blob*)malloc(size), &::free }; - SYSTRACE_NAME("glGetProgramBinary"); - glGetProgramBinary(program, programBinarySize, &programBinarySize, &format, blob->data); - GLenum const error = glGetError(); - if (error == GL_NO_ERROR) { - blob->format = format; - platform.insertBlob(key.data(), key.size(), blob.get(), size); + if (UTILS_LIKELY(blob)) { + SYSTRACE_NAME("glGetProgramBinary"); + glGetProgramBinary(program, programBinarySize, &programBinarySize, &format, + blob->data); + GLenum const error = glGetError(); + if (error == GL_NO_ERROR) { + blob->format = format; + platform.insertBlob(key.data(), key.size(), blob.get(), size); + } } } } @@ -115,9 +118,11 @@ void OpenGLBlobCache::insert(Platform& platform, BlobCacheKey const& key, if (programBinarySize) { size_t const size = sizeof(Blob) + programBinarySize; std::unique_ptr blob{ (Blob*)malloc(size), &::free }; - blob->format = format; - memcpy(blob->data, data, programBinarySize); - platform.insertBlob(key.data(), key.size(), blob.get(), size); + if (UTILS_LIKELY(blob)) { + blob->format = format; + memcpy(blob->data, data, programBinarySize); + platform.insertBlob(key.data(), key.size(), blob.get(), size); + } } } } diff --git a/filament/backend/src/opengl/ShaderCompilerService.cpp b/filament/backend/src/opengl/ShaderCompilerService.cpp index ad6914d2f6..4d49cca9bf 100644 --- a/filament/backend/src/opengl/ShaderCompilerService.cpp +++ b/filament/backend/src/opengl/ShaderCompilerService.cpp @@ -249,7 +249,7 @@ ShaderCompilerService::program_token_t ShaderCompilerService::createProgram( // We need to query the link status here to guarantee that the // program is compiled and linked now (we don't want this to be // deferred to later). We don't care about the result at this point. - GLint status; + GLint status = GL_FALSE; glGetProgramiv(glProgram, GL_LINK_STATUS, &status); programData.program = glProgram; @@ -262,7 +262,7 @@ ShaderCompilerService::program_token_t ShaderCompilerService::createProgram( mCallbackManager.put(token->handle); // caching must be the last thing we do - if (token->key) { + if (token->key && status == GL_TRUE) { // Attempt to cache. This calls glGetProgramBinary. OpenGLBlobCache::insert(mDriver.mPlatform, token->key, glProgram); } From 6e249c4c1b290f8a37b692e70ad8dbeeea8b3b3a Mon Sep 17 00:00:00 2001 From: Powei Feng Date: Tue, 24 Oct 2023 21:56:42 -0700 Subject: [PATCH 53/63] matdbg: material info and fix resizing (#7295) --- libs/matdbg/web/experiment/app.js | 194 +++++++++++++++++++++++++++--- 1 file changed, 178 insertions(+), 16 deletions(-) diff --git a/libs/matdbg/web/experiment/app.js b/libs/matdbg/web/experiment/app.js index 05815cf89d..cf9d518c14 100644 --- a/libs/matdbg/web/experiment/app.js +++ b/libs/matdbg/web/experiment/app.js @@ -25,6 +25,25 @@ const LANGUAGE_CHOICES = { 'metal': ['msl'], }; +const MATERIAL_INFO_KEY_TO_STRING = { + 'model': 'shading model', + 'vertex_domain': 'vertex domain', + 'interpolation': 'interpolation', + 'shadow_multiply': 'shadow multiply', + 'specular_antialiasing': 'specular antialiasing', + 'variance': 'variance', + 'threshold': 'threshold', + 'clear_coat_IOR_change': 'clear coat IOR change', + 'blending': 'blending', + 'mask_threshold': 'mask threshold', + 'color_write': 'color write', + 'depth_write': 'depth write', + 'depth_test': 'depth test', + 'double_sided': 'double sided', + 'culling': 'culling', + 'transparency': 'transparency', +}; + // CSS constants const FOREGROUND_COLOR = '#fafafa'; const INACTIVE_COLOR = '#9a9a9a'; @@ -33,6 +52,21 @@ const LIGHTER_INACTIVE_COLOR = '#d9d9d9'; const UNSELECTED_COLOR = '#dfdfdf'; const BACKGROUND_COLOR = '#5362e5'; const HOVER_BACKGROUND_COLOR = '#b3c2ff'; +const CODE_VIEWER_BOTTOM_ROW_HEIGHT = 60; +const REGULAR_FONT_SIZE = 12; +const MENU_HR = ` + display: block; + height: 1px; + border: 0px; + border-top: 1px solid ${UNSELECTED_COLOR}; + padding: 0; + width: 100%; + margin: 3px 0 8px 0; +`; +const MENU_SECTION_TITLE = ` + font-size: 16px; + color: ${UNSELECTED_COLOR}; +`; // Set up the Monaco editor. See also CodeViewer const kMonacoBaseUrl = 'https://cdnjs.cloudflare.com/ajax/libs/monaco-editor/0.25.2/min/'; @@ -136,7 +170,7 @@ class CodeViewer extends LitElement { #bottom-row { width: 100%; display: flex; - height: 60px; + height: ${unsafeCSS(CODE_VIEWER_BOTTOM_ROW_HEIGHT)}px; flex-direction: column; align-items: flex-end; justify-content: center; @@ -168,6 +202,8 @@ class CodeViewer extends LitElement { code: {type: String, state: true}, active: {type: Boolean, attribute: 'active'}, modified: {type: Boolean, attribute: 'modified'}, + expectedWidth: {type: Number, attribute: 'expected-width'}, + expectedHeight: {type: Number, attribute: 'expected-height'}, } } @@ -216,6 +252,12 @@ class CodeViewer extends LitElement { if (props.has('code') && this.code.length > 0) { this.editor.setValue(this.code); } + if ((props.has('expectedWidth') || props.has('expectedHeight')) && + (this.expectedWidth > 0 && (this.expectedHeight - CODE_VIEWER_BOTTOM_ROW_HEIGHT) > 0)) { + this._editorDiv.style.width = Math.floor(this.expectedWidth) + 'px'; + this._editorDiv.style.height = + (Math.floor(this.expectedHeight) - CODE_VIEWER_BOTTOM_ROW_HEIGHT) + 'px'; + } } constructor() { @@ -224,6 +266,8 @@ class CodeViewer extends LitElement { this.active = false; this.modified = false; this.addEventListener('button-clicked', this._rebuild.bind(this)); + this.expectedWidth = 0; + this.expectedHeight = 0; } render() { @@ -252,7 +296,10 @@ class CodeViewer extends LitElement {
${stateDiv ?? nothing} - + +
`; @@ -260,6 +307,99 @@ class CodeViewer extends LitElement { } customElements.define("code-viewer", CodeViewer); +class MaterialInfo extends LitElement { + static get properties() { + return { + info: {type: Object, state: true}, + showing: {type: Boolean, state: true}, + } + } + + static get styles() { + return css` + :host { + font-size: ${unsafeCSS(REGULAR_FONT_SIZE)}px; + color: ${unsafeCSS(UNSELECTED_COLOR)}; + margin-bottom: 20px; + } + .section-title { + ${unsafeCSS(MENU_SECTION_TITLE)} + cursor: pointer; + } + hr { + ${unsafeCSS(MENU_HR)} + } + .hide { + display: none; + flex-direction: column; + } + .expander { + display: flex; + flex-direction: row; + align-items: center; + justify-content: space-between; + } + `; + } + + constructor() { + super(); + this.showing = true; + this.info = null; + } + + _hasInfo() { + return this.info && Object.keys(this.info).length > 0; + } + + _showClick() { + this.showing = !this.showing; + } + + render() { + const expandedIcon = this._hasInfo() ? (this.showing ? '-' : '+') : ''; + let infoDivs = []; + if (this._hasInfo()) { + if (this.info.shading && this.info.shading.material_domain === 'surface') { + infoDivs = infoDivs.concat( + Object.keys(this.info.shading) + .filter((propKey) => (propKey in MATERIAL_INFO_KEY_TO_STRING)) + .map((propKey) => html` +
+ ${MATERIAL_INFO_KEY_TO_STRING[propKey]} = ${this.info.shading[propKey]} +
+ `) + ); + } + if (this.info.raster) { + infoDivs = infoDivs.concat( + Object.keys(this.info.raster) + .filter((propKey) => (propKey in MATERIAL_INFO_KEY_TO_STRING)) + .map((propKey) => html` +
+ ${MATERIAL_INFO_KEY_TO_STRING[propKey]} = ${this.info.raster[propKey]} +
+ `) + ); + } + } + let divClass = 'container'; + if (infoDivs.length == 0) { + divClass += ' hide'; + } + return html` +
+
+ Material Details ${expandedIcon} +
+
+ ${this.showing ? infoDivs : []} +
+ `; + } +} +customElements.define('material-info', MaterialInfo); + class MaterialSidePanel extends LitElement { // Setting the style in render() has poor performance implications. We use it simply to avoid // having another container descending from the root to host the background color. @@ -281,15 +421,13 @@ class MaterialSidePanel extends LitElement { font-size: 20px; } .material-section { - font-size: 16px; - color: ${UNSELECTED_COLOR}; - margin-top: 5px; + ${MENU_SECTION_TITLE} } .materials { display: flex; flex-direction: column; - margin-bottom: 10px; - font-size: 12px; + margin-bottom: 20px; + font-size: ${REGULAR_FONT_SIZE}px; color: ${UNSELECTED_COLOR}; } .material_variant_language:hover { @@ -309,7 +447,7 @@ class MaterialSidePanel extends LitElement { padding-left: 20px; } .language { - margin: 0 4px; + margin: 0 8px 0 0; } .languages { padding-left: 20px; @@ -317,13 +455,7 @@ class MaterialSidePanel extends LitElement { display: flex; } hr { - display: block; - height: 1px; - border: 0px; - border-top: 1px solid ${UNSELECTED_COLOR}; - padding: 0; - width: 100%; - margin: 3px 0 8px 0; + ${MENU_HR} } `; } @@ -344,6 +476,10 @@ class MaterialSidePanel extends LitElement { } } + get _materialInfo() { + return this.renderRoot.querySelector('#material-info'); + } + constructor() { super(); this.connected = false; @@ -402,6 +538,11 @@ class MaterialSidePanel extends LitElement { } this.variants = variants; } + + if (this.currentMaterial && this.database) { + const material = this.database[this.currentMaterial]; + this._materialInfo.info = material; + } } } @@ -521,6 +662,7 @@ class MaterialSidePanel extends LitElement {
matdbg
${sections("Surface", "surface")} ${sections("Post-processing", "postpro")} + `; } @@ -585,6 +727,16 @@ class MatdbgViewer extends LitElement { return shaders[this.currentShaderIndex]; } + _onResize() { + const rect = this._sidepanel.getBoundingClientRect(); + this.codeViewerExpectedWidth = window.innerWidth - rect.width - 1; + this.codeViewerExpectedHeight = window.innerHeight; + } + + firstUpdated() { + this._onResize(); + } + constructor() { super(); this.connected = false; @@ -637,6 +789,8 @@ class MatdbgViewer extends LitElement { } } ); + + addEventListener('resize', this._onResize.bind(this)); } static get properties() { @@ -649,6 +803,8 @@ class MatdbgViewer extends LitElement { // Each material has a list of variants compiled for it, this index tracks a position in the list. currentShaderIndex: {type: Number, state: true}, currentBackend: {type: String, state: true}, + codeViewerExpectedWidth: {type: Number, state: true}, + codeViewerExpectedHeight: {type: Number, state: true}, } } @@ -692,6 +848,10 @@ class MatdbgViewer extends LitElement { shader.modified = false; this.database = this.database; } + + // Size of the editor will be adjusted due to the code being loaded, we try to + // fit the editor again by calling the resize signal. + setTimeout(this._onResize.bind(this), 700); })(); } if (props.has('activeShaders') || props.has('database')) { @@ -754,7 +914,9 @@ class MatdbgViewer extends LitElement { + ?connected="${this.connected}" + expected-width="${this.codeViewerExpectedWidth}" + expected-height="${this.codeViewerExpectedHeight}" > `; } From d76cf643c5da85fc29383a64d5cb59df8e4d206a Mon Sep 17 00:00:00 2001 From: Ben Doherty Date: Wed, 25 Oct 2023 12:14:02 -0400 Subject: [PATCH 54/63] Improve Metal vertex buffer bindings (#7293) --- .../include/private/backend/HandleAllocator.h | 4 +- filament/backend/src/metal/MetalDriver.mm | 25 +++--- filament/backend/src/metal/MetalHandles.h | 14 ++++ filament/backend/src/metal/MetalHandles.mm | 83 +++++++++++++------ 4 files changed, 84 insertions(+), 42 deletions(-) diff --git a/filament/backend/include/private/backend/HandleAllocator.h b/filament/backend/include/private/backend/HandleAllocator.h index 95481f5484..610ad3729e 100644 --- a/filament/backend/include/private/backend/HandleAllocator.h +++ b/filament/backend/include/private/backend/HandleAllocator.h @@ -33,7 +33,7 @@ #define HandleAllocatorGL HandleAllocator<16, 64, 208> #define HandleAllocatorVK HandleAllocator<16, 64, 880> -#define HandleAllocatorMTL HandleAllocator<16, 64, 576> +#define HandleAllocatorMTL HandleAllocator<16, 64, 584> namespace filament::backend { @@ -256,6 +256,7 @@ private: HandleBase::HandleId allocateHandle() noexcept { if constexpr (SIZE <= P0) { return allocateHandleInPool(); } if constexpr (SIZE <= P1) { return allocateHandleInPool(); } + static_assert(SIZE <= P2); return allocateHandleInPool(); } @@ -266,6 +267,7 @@ private: } else if constexpr (SIZE <= P1) { deallocateHandleFromPool(id); } else { + static_assert(SIZE <= P2); deallocateHandleFromPool(id); } } diff --git a/filament/backend/src/metal/MetalDriver.mm b/filament/backend/src/metal/MetalDriver.mm index c88abe34d1..1666edf8cf 100644 --- a/filament/backend/src/metal/MetalDriver.mm +++ b/filament/backend/src/metal/MetalDriver.mm @@ -1686,26 +1686,23 @@ void MetalDriver::draw(PipelineState ps, Handle rph, uint32_t // Bind the user vertex buffers. - MetalBuffer* buffers[MAX_VERTEX_BUFFER_COUNT] = {}; + MetalBuffer* vertexBuffers[MAX_VERTEX_BUFFER_COUNT] = {}; size_t vertexBufferOffsets[MAX_VERTEX_BUFFER_COUNT] = {}; - size_t bufferIndex = 0; + size_t maxBufferIndex = 0; auto vb = primitive->vertexBuffer; - for (uint32_t attributeIndex = 0; attributeIndex < vb->attributes.size(); attributeIndex++) { - const auto& attribute = vb->attributes[attributeIndex]; - if (attribute.buffer == Attribute::BUFFER_UNUSED) { - continue; - } - - assert_invariant(vb->buffers[attribute.buffer]); - buffers[bufferIndex] = vb->buffers[attribute.buffer]; - vertexBufferOffsets[bufferIndex] = attribute.offset; - bufferIndex++; + for (auto m : primitive->bufferMapping) { + assert_invariant( + m.bufferArgumentIndex >= USER_VERTEX_BUFFER_BINDING_START && + m.bufferArgumentIndex < USER_VERTEX_BUFFER_BINDING_START + MAX_VERTEX_BUFFER_COUNT); + size_t vertexBufferIndex = m.bufferArgumentIndex - USER_VERTEX_BUFFER_BINDING_START; + vertexBuffers[vertexBufferIndex] = vb->buffers[m.sourceBufferIndex]; + maxBufferIndex = std::max(maxBufferIndex, vertexBufferIndex); } - const auto bufferCount = bufferIndex; + const auto bufferCount = maxBufferIndex + 1; MetalBuffer::bindBuffers(getPendingCommandBuffer(mContext), mContext->currentRenderPassEncoder, - USER_VERTEX_BUFFER_BINDING_START, MetalBuffer::Stage::VERTEX, buffers, + USER_VERTEX_BUFFER_BINDING_START, MetalBuffer::Stage::VERTEX, vertexBuffers, vertexBufferOffsets, bufferCount); // Bind the zero buffer, used for missing vertex attributes. diff --git a/filament/backend/src/metal/MetalHandles.h b/filament/backend/src/metal/MetalHandles.h index 97a7085c03..fdc4a5e6a9 100644 --- a/filament/backend/src/metal/MetalHandles.h +++ b/filament/backend/src/metal/MetalHandles.h @@ -160,6 +160,7 @@ struct MetalIndexBuffer : public HwIndexBuffer { }; struct MetalRenderPrimitive : public HwRenderPrimitive { + MetalRenderPrimitive(); void setBuffers(MetalVertexBuffer* vertexBuffer, MetalIndexBuffer* indexBuffer); // The pointers to MetalVertexBuffer and MetalIndexBuffer are "weak". // The MetalVertexBuffer and MetalIndexBuffer must outlive the MetalRenderPrimitive. @@ -169,6 +170,19 @@ struct MetalRenderPrimitive : public HwRenderPrimitive { // This struct is used to create the pipeline description to describe vertex assembly. VertexDescription vertexDescription = {}; + + struct Entry { + uint8_t sourceBufferIndex = 0; + uint8_t stride = 0; + // maps to -> + uint8_t bufferArgumentIndex = 0; + + Entry(uint8_t sourceBufferIndex, uint8_t stride, uint8_t bufferArgumentIndex) + : sourceBufferIndex(sourceBufferIndex), + stride(stride), + bufferArgumentIndex(bufferArgumentIndex) {} + }; + utils::FixedCapacityVector bufferMapping; }; class MetalProgram : public HwProgram { diff --git a/filament/backend/src/metal/MetalHandles.mm b/filament/backend/src/metal/MetalHandles.mm index 9f4625123f..b866053e16 100644 --- a/filament/backend/src/metal/MetalHandles.mm +++ b/filament/backend/src/metal/MetalHandles.mm @@ -299,6 +299,9 @@ MetalIndexBuffer::MetalIndexBuffer(MetalContext& context, BufferUsage usage, uin uint32_t indexCount) : HwIndexBuffer(elementSize, indexCount), buffer(context, BufferObjectBinding::VERTEX, usage, elementSize * indexCount, true) { } +MetalRenderPrimitive::MetalRenderPrimitive() + : bufferMapping(utils::FixedCapacityVector::with_capacity(MAX_VERTEX_BUFFER_COUNT)) {} + void MetalRenderPrimitive::setBuffers(MetalVertexBuffer* vertexBuffer, MetalIndexBuffer* indexBuffer) { this->vertexBuffer = vertexBuffer; @@ -306,44 +309,70 @@ void MetalRenderPrimitive::setBuffers(MetalVertexBuffer* vertexBuffer, MetalInde const size_t attributeCount = vertexBuffer->attributes.size(); + auto& mapping = bufferMapping; + mapping.clear(); vertexDescription = {}; - // Each attribute gets its own vertex buffer, starting at logical buffer 1. - uint32_t bufferIndex = 1; + // Set the layout for the zero buffer, which unused attributes are mapped to. + vertexDescription.layouts[ZERO_VERTEX_BUFFER_LOGICAL_INDEX] = { + .step = MTLVertexStepFunctionConstant, .stride = 16 + }; + + // Here we map each source buffer to a Metal buffer argument. + // Each attribute has a source buffer, offset, and stride. + // Two source buffers with the same index and stride can share the same Metal buffer argument + // index. + // + // The source buffer is the buffer index that the Filament client sets. + // * source buffer + // .attribute(VertexAttribute::POSITION, 0, VertexBuffer::AttributeType::FLOAT2, 0, 12) + // .attribute(VertexAttribute::UV, 0, VertexBuffer::AttributeType::HALF2, 8, 12) + // .attribute(VertexAttribute::COLOR, 1, VertexBuffer::AttributeType::UBYTE4, 0, 4) + + auto allocateOrGetBufferArgumentIndex = + [&mapping, currentBufferArgumentIndex = USER_VERTEX_BUFFER_BINDING_START, this]( + auto sourceBuffer, auto sourceBufferStride) mutable -> uint8_t { + auto match = [&](const auto& e) { + return e.sourceBufferIndex == sourceBuffer && e.stride == sourceBufferStride; + }; + if (auto it = std::find_if(mapping.begin(), mapping.end(), match); it != mapping.end()) { + return it->bufferArgumentIndex; + } else { + auto bufferArgumentIndex = currentBufferArgumentIndex++; + mapping.emplace_back(sourceBuffer, sourceBufferStride, bufferArgumentIndex); + vertexDescription.layouts[bufferArgumentIndex] = { + .step = MTLVertexStepFunctionPerVertex, .stride = sourceBufferStride + }; + return bufferArgumentIndex; + } + }; + for (uint32_t attributeIndex = 0; attributeIndex < attributeCount; attributeIndex++) { const auto& attribute = vertexBuffer->attributes[attributeIndex]; - if (attribute.buffer == Attribute::BUFFER_UNUSED) { - const uint8_t flags = attribute.flags; - const MTLVertexFormat format = (flags & Attribute::FLAG_INTEGER_TARGET) ? - MTLVertexFormatUInt4 : MTLVertexFormatFloat4; - // If the attribute is not enabled, bind it to the zero buffer. It's a Metal error for a - // shader to read from missing vertex attributes. + // If the attribute is unused, bind it to the zero buffer. It's a Metal error for a shader + // to read from missing vertex attributes. + if (attribute.buffer == Attribute::BUFFER_UNUSED) { + const MTLVertexFormat format = (attribute.flags & Attribute::FLAG_INTEGER_TARGET) + ? MTLVertexFormatUInt4 + : MTLVertexFormatFloat4; vertexDescription.attributes[attributeIndex] = { - .format = format, - .buffer = ZERO_VERTEX_BUFFER_LOGICAL_INDEX, - .offset = 0 - }; - vertexDescription.layouts[ZERO_VERTEX_BUFFER_LOGICAL_INDEX] = { - .step = MTLVertexStepFunctionConstant, - .stride = 16 + .format = format, .buffer = ZERO_VERTEX_BUFFER_LOGICAL_INDEX, .offset = 0 }; continue; } - vertexDescription.attributes[attributeIndex] = { - .format = getMetalFormat(attribute.type, - attribute.flags & Attribute::FLAG_NORMALIZED), - .buffer = bufferIndex, - .offset = 0 - }; - vertexDescription.layouts[bufferIndex] = { - .step = MTLVertexStepFunctionPerVertex, - .stride = attribute.stride - }; + // Map the source buffer and stride of this attribute to a Metal buffer argument. + auto bufferArgumentIndex = + allocateOrGetBufferArgumentIndex(attribute.buffer, attribute.stride); - bufferIndex++; - }; + vertexDescription.attributes[attributeIndex] = { + .format = getMetalFormat( + attribute.type, attribute.flags & Attribute::FLAG_NORMALIZED), + .buffer = uint32_t(bufferArgumentIndex), + .offset = attribute.offset + }; + } } MetalProgram::MetalProgram(MetalContext& context, Program&& program) noexcept From cc95a4a7a3a8bf6eaa886728a63de0f40ed71d1d Mon Sep 17 00:00:00 2001 From: Powei Feng Date: Wed, 25 Oct 2023 11:00:35 -0700 Subject: [PATCH 55/63] vk: remove unused platform GGP (#7298) --- .../VulkanPlatformAndroidLinuxWindows.cpp | 18 ------------------ 1 file changed, 18 deletions(-) diff --git a/filament/backend/src/vulkan/platform/VulkanPlatformAndroidLinuxWindows.cpp b/filament/backend/src/vulkan/platform/VulkanPlatformAndroidLinuxWindows.cpp index af4fce092e..e25d5f4af0 100644 --- a/filament/backend/src/vulkan/platform/VulkanPlatformAndroidLinuxWindows.cpp +++ b/filament/backend/src/vulkan/platform/VulkanPlatformAndroidLinuxWindows.cpp @@ -30,8 +30,6 @@ // Platform specific includes and defines #if defined(__ANDROID__) #include -#elif defined(__linux__) && defined(FILAMENT_SUPPORTS_GGP) - #include #elif defined(__linux__) && defined(FILAMENT_SUPPORTS_WAYLAND) #include namespace { @@ -86,8 +84,6 @@ VulkanPlatform::ExtensionSet VulkanPlatform::getRequiredInstanceExtensions() { VulkanPlatform::ExtensionSet ret; #if defined(__ANDROID__) ret.insert("VK_KHR_android_surface"); - #elif defined(__linux__) && defined(FILAMENT_SUPPORTS_GGP) - ret.insert(VK_GGP_STREAM_DESCRIPTOR_SURFACE_EXTENSION_NAME); #elif defined(__linux__) && defined(FILAMENT_SUPPORTS_WAYLAND) ret.insert("VK_KHR_wayland_surface"); #elif LINUX_OR_FREEBSD && defined(FILAMENT_SUPPORTS_X11) @@ -121,20 +117,6 @@ VulkanPlatform::SurfaceBundle VulkanPlatform::createVkSurfaceKHR(void* nativeWin VkResult const result = vkCreateAndroidSurfaceKHR(instance, &createInfo, VKALLOC, (VkSurfaceKHR*) &surface); ASSERT_POSTCONDITION(result == VK_SUCCESS, "vkCreateAndroidSurfaceKHR error."); - #elif defined(__linux__) && defined(FILAMENT_SUPPORTS_GGP) - VkStreamDescriptorSurfaceCreateInfoGGP const surface_create_info = { - .sType = VK_STRUCTURE_TYPE_STREAM_DESCRIPTOR_SURFACE_CREATE_INFO_GGP, - .streamDescriptor = kGgpPrimaryStreamDescriptor, - }; - PFN_vkCreateStreamDescriptorSurfaceGGP fpCreateStreamDescriptorSurfaceGGP - = reinterpret_cast( - vkGetInstanceProcAddr(instance, "vkCreateStreamDescriptorSurfaceGGP")); - ASSERT_PRECONDITION(fpCreateStreamDescriptorSurfaceGGP != nullptr, - "Error getting VkInstance " - "function vkCreateStreamDescriptorSurfaceGGP"); - VkResult const result = fpCreateStreamDescriptorSurfaceGGP(instance, &surface_create_info, - nullptr, (VkSurfaceKHR*) &surface); - ASSERT_POSTCONDITION(result == VK_SUCCESS, "vkCreateStreamDescriptorSurfaceGGP error."); #elif defined(__linux__) && defined(FILAMENT_SUPPORTS_WAYLAND) wl* ptrval = reinterpret_cast(nativeWindow); extent.width = ptrval->width; From 73b0751ccfcc8855603aa04d2c7ef733893959bc Mon Sep 17 00:00:00 2001 From: Powei Feng Date: Wed, 25 Oct 2023 15:14:40 -0700 Subject: [PATCH 56/63] Release Filament 1.45.0 --- NEW_RELEASE_NOTES.md | 8 -------- README.md | 4 ++-- RELEASE_NOTES.md | 10 ++++++++++ android/gradle.properties | 2 +- ios/CocoaPods/Filament.podspec | 4 ++-- web/filament-js/package.json | 2 +- 6 files changed, 16 insertions(+), 14 deletions(-) diff --git a/NEW_RELEASE_NOTES.md b/NEW_RELEASE_NOTES.md index f0b7719bd3..4a1a9c7fa7 100644 --- a/NEW_RELEASE_NOTES.md +++ b/NEW_RELEASE_NOTES.md @@ -7,11 +7,3 @@ for next branch cut* header. appropriate header in [RELEASE_NOTES.md](./RELEASE_NOTES.md). ## Release notes for next branch cut - -- engine: Added parameter for configuring JobSystem thread count -- engine: In Java, introduce Engine.Builder -- gltfio: fix ubershader index for transmission&volume material -- engine: New tone mapper: `AgXTonemapper`. -- matinfo: Add support for viewing ESSL 1.0 shaders -- engine: Add `Renderer::getClearOptions()` [b/243846268] -- engine: Fix stable shadows (again) when an IBL rotation is used diff --git a/README.md b/README.md index e35994aa51..3da3ee8f71 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,7 @@ repositories { } dependencies { - implementation 'com.google.android.filament:filament-android:1.44.0' + implementation 'com.google.android.filament:filament-android:1.45.0' } ``` @@ -51,7 +51,7 @@ Here are all the libraries available in the group `com.google.android.filament`: iOS projects can use CocoaPods to install the latest release: ```shell -pod 'Filament', '~> 1.44.0' +pod 'Filament', '~> 1.45.0' ``` ### Snapshots diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 7d657e90cd..52ba23eeaf 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -7,6 +7,16 @@ A new header is inserted each time a *tag* is created. Instead, if you are authoring a PR for the main branch, add your release note to [NEW_RELEASE_NOTES.md](./NEW_RELEASE_NOTES.md). +## v1.45.1 + +- engine: Added parameter for configuring JobSystem thread count +- engine: In Java, introduce Engine.Builder +- gltfio: fix ubershader index for transmission&volume material +- engine: New tone mapper: `AgXTonemapper`. +- matinfo: Add support for viewing ESSL 1.0 shaders +- engine: Add `Renderer::getClearOptions()` [b/243846268] +- engine: Fix stable shadows (again) when an IBL rotation is used + ## v1.45.0 - materials: fix alpha masked materials when MSAA is turned on [⚠️ **Recompile materials**] diff --git a/android/gradle.properties b/android/gradle.properties index 9437d5af28..61ffc823bb 100644 --- a/android/gradle.properties +++ b/android/gradle.properties @@ -1,5 +1,5 @@ GROUP=com.google.android.filament -VERSION_NAME=1.44.0 +VERSION_NAME=1.45.0 POM_DESCRIPTION=Real-time physically based rendering engine for Android. diff --git a/ios/CocoaPods/Filament.podspec b/ios/CocoaPods/Filament.podspec index b83e7f0e02..723561db30 100644 --- a/ios/CocoaPods/Filament.podspec +++ b/ios/CocoaPods/Filament.podspec @@ -1,12 +1,12 @@ Pod::Spec.new do |spec| spec.name = "Filament" - spec.version = "1.44.0" + spec.version = "1.45.0" spec.license = { :type => "Apache 2.0", :file => "LICENSE" } spec.homepage = "https://google.github.io/filament" spec.authors = "Google LLC." spec.summary = "Filament is a real-time physically based rendering engine for Android, iOS, Windows, Linux, macOS, and WASM/WebGL." spec.platform = :ios, "11.0" - spec.source = { :http => "https://github.com/google/filament/releases/download/v1.44.0/filament-v1.44.0-ios.tgz" } + spec.source = { :http => "https://github.com/google/filament/releases/download/v1.45.0/filament-v1.45.0-ios.tgz" } # Fix linking error with Xcode 12; we do not yet support the simulator on Apple silicon. spec.pod_target_xcconfig = { diff --git a/web/filament-js/package.json b/web/filament-js/package.json index b832762146..78ac20872c 100644 --- a/web/filament-js/package.json +++ b/web/filament-js/package.json @@ -1,6 +1,6 @@ { "name": "filament", - "version": "1.44.0", + "version": "1.45.0", "description": "Real-time physically based rendering engine", "main": "filament.js", "module": "filament.js", From cb3e808e8d4544519af418131e96b74fb92dd598 Mon Sep 17 00:00:00 2001 From: Powei Feng Date: Wed, 25 Oct 2023 15:18:59 -0700 Subject: [PATCH 57/63] Bump version to 1.45.1 --- README.md | 4 ++-- android/gradle.properties | 2 +- ios/CocoaPods/Filament.podspec | 4 ++-- web/filament-js/package.json | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 3da3ee8f71..1c9b9c0105 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,7 @@ repositories { } dependencies { - implementation 'com.google.android.filament:filament-android:1.45.0' + implementation 'com.google.android.filament:filament-android:1.45.1' } ``` @@ -51,7 +51,7 @@ Here are all the libraries available in the group `com.google.android.filament`: iOS projects can use CocoaPods to install the latest release: ```shell -pod 'Filament', '~> 1.45.0' +pod 'Filament', '~> 1.45.1' ``` ### Snapshots diff --git a/android/gradle.properties b/android/gradle.properties index 61ffc823bb..0fdca3754e 100644 --- a/android/gradle.properties +++ b/android/gradle.properties @@ -1,5 +1,5 @@ GROUP=com.google.android.filament -VERSION_NAME=1.45.0 +VERSION_NAME=1.45.1 POM_DESCRIPTION=Real-time physically based rendering engine for Android. diff --git a/ios/CocoaPods/Filament.podspec b/ios/CocoaPods/Filament.podspec index 723561db30..e6ca3b3dbe 100644 --- a/ios/CocoaPods/Filament.podspec +++ b/ios/CocoaPods/Filament.podspec @@ -1,12 +1,12 @@ Pod::Spec.new do |spec| spec.name = "Filament" - spec.version = "1.45.0" + spec.version = "1.45.1" spec.license = { :type => "Apache 2.0", :file => "LICENSE" } spec.homepage = "https://google.github.io/filament" spec.authors = "Google LLC." spec.summary = "Filament is a real-time physically based rendering engine for Android, iOS, Windows, Linux, macOS, and WASM/WebGL." spec.platform = :ios, "11.0" - spec.source = { :http => "https://github.com/google/filament/releases/download/v1.45.0/filament-v1.45.0-ios.tgz" } + spec.source = { :http => "https://github.com/google/filament/releases/download/v1.45.1/filament-v1.45.1-ios.tgz" } # Fix linking error with Xcode 12; we do not yet support the simulator on Apple silicon. spec.pod_target_xcconfig = { diff --git a/web/filament-js/package.json b/web/filament-js/package.json index 78ac20872c..3f5afa8743 100644 --- a/web/filament-js/package.json +++ b/web/filament-js/package.json @@ -1,6 +1,6 @@ { "name": "filament", - "version": "1.45.0", + "version": "1.45.1", "description": "Real-time physically based rendering engine", "main": "filament.js", "module": "filament.js", From 7abdea5a2e92fd7c89027519d37b3fa236f870d4 Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Wed, 25 Oct 2023 11:19:57 -0700 Subject: [PATCH 58/63] improve BlobCache API and compatibility - the insert and retrieve handlers can now be set/unset independently. this could be useful for debugging. - program caching is disabled if the GL implementation doesn't support it. - removed unused code FIXES=[307549547] --- filament/backend/include/backend/Platform.h | 17 ++++- filament/backend/src/Platform.cpp | 14 ++-- .../backend/src/opengl/OpenGLBlobCache.cpp | 75 +++++++++---------- filament/backend/src/opengl/OpenGLBlobCache.h | 13 ++-- filament/backend/src/opengl/OpenGLContext.cpp | 56 ++++++++------ filament/backend/src/opengl/OpenGLContext.h | 7 +- .../src/opengl/ShaderCompilerService.cpp | 9 ++- .../src/opengl/ShaderCompilerService.h | 2 + 8 files changed, 108 insertions(+), 85 deletions(-) diff --git a/filament/backend/include/backend/Platform.h b/filament/backend/include/backend/Platform.h index a84a8ba01f..dac1e37630 100644 --- a/filament/backend/include/backend/Platform.h +++ b/filament/backend/include/backend/Platform.h @@ -114,6 +114,7 @@ public: * Platform. The and Invocables may be called at any time and * from any thread from the time at which setBlobFunc is called until the time that Platform * is destroyed. Concurrent calls to these functions from different threads is also allowed. + * Either function can be null. * * @param insertBlob an Invocable that inserts a new value into the cache and associates * it with the given key @@ -123,9 +124,21 @@ public: void setBlobFunc(InsertBlobFunc&& insertBlob, RetrieveBlobFunc&& retrieveBlob) noexcept; /** - * @return true if setBlobFunc was called. + * @return true if insertBlob is valid. */ - bool hasBlobFunc() const noexcept; + bool hasInsertBlobFunc() const noexcept; + + /** + * @return true if retrieveBlob is valid. + */ + bool hasRetrieveBlobFunc() const noexcept; + + /** + * @return true if either of insertBlob or retrieveBlob are valid. + */ + bool hasBlobFunc() const noexcept { + return hasInsertBlobFunc() || hasRetrieveBlobFunc(); + } /** * To insert a new binary value into the cache and associate it with a given diff --git a/filament/backend/src/Platform.cpp b/filament/backend/src/Platform.cpp index ece36477ba..db2fd0eafd 100644 --- a/filament/backend/src/Platform.cpp +++ b/filament/backend/src/Platform.cpp @@ -28,14 +28,16 @@ bool Platform::pumpEvents() noexcept { } void Platform::setBlobFunc(InsertBlobFunc&& insertBlob, RetrieveBlobFunc&& retrieveBlob) noexcept { - if (!mInsertBlob && !mRetrieveBlob) { - mInsertBlob = std::move(insertBlob); - mRetrieveBlob = std::move(retrieveBlob); - } + mInsertBlob = std::move(insertBlob); + mRetrieveBlob = std::move(retrieveBlob); } -bool Platform::hasBlobFunc() const noexcept { - return mInsertBlob && mRetrieveBlob; +bool Platform::hasInsertBlobFunc() const noexcept { + return bool(mInsertBlob); +} + +bool Platform::hasRetrieveBlobFunc() const noexcept { + return bool(mRetrieveBlob); } void Platform::insertBlob(void const* key, size_t keySize, void const* value, size_t valueSize) { diff --git a/filament/backend/src/opengl/OpenGLBlobCache.cpp b/filament/backend/src/opengl/OpenGLBlobCache.cpp index 4f65c60586..f9e68384da 100644 --- a/filament/backend/src/opengl/OpenGLBlobCache.cpp +++ b/filament/backend/src/opengl/OpenGLBlobCache.cpp @@ -16,6 +16,8 @@ #include "OpenGLBlobCache.h" +#include "OpenGLContext.h" + #include #include @@ -28,17 +30,18 @@ struct OpenGLBlobCache::Blob { char data[]; }; -GLuint OpenGLBlobCache::retrieve(BlobCacheKey* outKey, Platform& platform, - Program const& program) noexcept { - SYSTRACE_CALL(); +OpenGLBlobCache::OpenGLBlobCache(OpenGLContext& gl) noexcept + : mCachingSupported(gl.gets.num_program_binary_formats >= 1) { +} - if (!platform.hasBlobFunc()) { +GLuint OpenGLBlobCache::retrieve(BlobCacheKey* outKey, Platform& platform, + Program const& program) const noexcept { + SYSTRACE_CALL(); + if (!mCachingSupported || !platform.hasRetrieveBlobFunc()) { // the key is never updated in that case return 0; } - SYSTRACE_CONTEXT(); - GLuint programId = 0; #ifndef FILAMENT_SILENCE_NOT_SUPPORTED_BY_ES2 @@ -64,8 +67,10 @@ GLuint OpenGLBlobCache::retrieve(BlobCacheKey* outKey, Platform& platform, programId = glCreateProgram(); - SYSTRACE_NAME("glProgramBinary"); - glProgramBinary(programId, blob->format, blob->data, programBinarySize); + { // scope for systrace + SYSTRACE_NAME("glProgramBinary"); + glProgramBinary(programId, blob->format, blob->data, programBinarySize); + } if (UTILS_UNLIKELY(glGetError() != GL_NO_ERROR)) { // glProgramBinary can fail if for instance the driver has been updated @@ -85,46 +90,36 @@ GLuint OpenGLBlobCache::retrieve(BlobCacheKey* outKey, Platform& platform, void OpenGLBlobCache::insert(Platform& platform, BlobCacheKey const& key, GLuint program) noexcept { -#ifndef FILAMENT_SILENCE_NOT_SUPPORTED_BY_ES2 SYSTRACE_CALL(); - if (platform.hasBlobFunc()) { - SYSTRACE_CONTEXT(); - GLenum format; - GLint programBinarySize = 0; + if (!mCachingSupported || !platform.hasInsertBlobFunc()) { + // the key is never updated in that case + return; + } + +#ifndef FILAMENT_SILENCE_NOT_SUPPORTED_BY_ES2 + GLenum format; + GLint programBinarySize = 0; + { // scope for systrace SYSTRACE_NAME("glGetProgramiv"); glGetProgramiv(program, GL_PROGRAM_BINARY_LENGTH, &programBinarySize); - if (programBinarySize) { - size_t const size = sizeof(Blob) + programBinarySize; - std::unique_ptr blob{ (Blob*)malloc(size), &::free }; - if (UTILS_LIKELY(blob)) { + } + if (programBinarySize) { + size_t const size = sizeof(Blob) + programBinarySize; + std::unique_ptr blob{ (Blob*)malloc(size), &::free }; + if (UTILS_LIKELY(blob)) { + { // scope for systrace SYSTRACE_NAME("glGetProgramBinary"); - glGetProgramBinary(program, programBinarySize, &programBinarySize, &format, - blob->data); - GLenum const error = glGetError(); - if (error == GL_NO_ERROR) { - blob->format = format; - platform.insertBlob(key.data(), key.size(), blob.get(), size); - } + glGetProgramBinary(program, programBinarySize, + &programBinarySize, &format, blob->data); + } + GLenum const error = glGetError(); + if (error == GL_NO_ERROR) { + blob->format = format; + platform.insertBlob(key.data(), key.size(), blob.get(), size); } } } #endif } -void OpenGLBlobCache::insert(Platform& platform, BlobCacheKey const& key, - GLenum format, void* data, GLsizei programBinarySize) noexcept { - SYSTRACE_CALL(); - if (platform.hasBlobFunc()) { - if (programBinarySize) { - size_t const size = sizeof(Blob) + programBinarySize; - std::unique_ptr blob{ (Blob*)malloc(size), &::free }; - if (UTILS_LIKELY(blob)) { - blob->format = format; - memcpy(blob->data, data, programBinarySize); - platform.insertBlob(key.data(), key.size(), blob.get(), size); - } - } - } -} - } // namespace filament::backend diff --git a/filament/backend/src/opengl/OpenGLBlobCache.h b/filament/backend/src/opengl/OpenGLBlobCache.h index 5569fa2034..fa98f0a449 100644 --- a/filament/backend/src/opengl/OpenGLBlobCache.h +++ b/filament/backend/src/opengl/OpenGLBlobCache.h @@ -25,20 +25,21 @@ namespace filament::backend { class Platform; class Program; +class OpenGLContext; class OpenGLBlobCache { public: - static GLuint retrieve(BlobCacheKey* key, Platform& platform, - Program const& program) noexcept; + explicit OpenGLBlobCache(OpenGLContext& gl) noexcept; - static void insert(Platform& platform, + GLuint retrieve(BlobCacheKey* key, Platform& platform, + Program const& program) const noexcept; + + void insert(Platform& platform, BlobCacheKey const& key, GLuint program) noexcept; - static void insert(Platform& platform, BlobCacheKey const& key, - GLenum format, void* data, GLsizei programBinarySize) noexcept; - private: struct Blob; + bool mCachingSupported = false; }; } // namespace filament::backend diff --git a/filament/backend/src/opengl/OpenGLContext.cpp b/filament/backend/src/opengl/OpenGLContext.cpp index 072096718b..bf4c7e1f13 100644 --- a/filament/backend/src/opengl/OpenGLContext.cpp +++ b/filament/backend/src/opengl/OpenGLContext.cpp @@ -99,38 +99,41 @@ OpenGLContext::OpenGLContext() noexcept { if (mFeatureLevel >= FeatureLevel::FEATURE_LEVEL_1) { #ifndef FILAMENT_SILENCE_NOT_SUPPORTED_BY_ES2 - glGetIntegerv(GL_MAX_UNIFORM_BLOCK_SIZE, - &gets.max_uniform_block_size); - glGetIntegerv(GL_MAX_UNIFORM_BUFFER_BINDINGS, - &gets.max_uniform_buffer_bindings); - glGetIntegerv(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT, - &gets.uniform_buffer_offset_alignment); - glGetIntegerv(GL_MAX_SAMPLES, - &gets.max_samples); - glGetIntegerv(GL_MAX_DRAW_BUFFERS, - &gets.max_draw_buffers); - glGetIntegerv(GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS, - &gets.max_transform_feedback_separate_attribs); #ifdef GL_EXT_texture_filter_anisotropic if (ext.EXT_texture_filter_anisotropic) { glGetFloatv(GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT, &gets.max_anisotropy); } #endif + glGetIntegerv(GL_MAX_DRAW_BUFFERS, + &gets.max_draw_buffers); + glGetIntegerv(GL_MAX_SAMPLES, + &gets.max_samples); + glGetIntegerv(GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS, + &gets.max_transform_feedback_separate_attribs); + glGetIntegerv(GL_MAX_UNIFORM_BLOCK_SIZE, + &gets.max_uniform_block_size); + glGetIntegerv(GL_MAX_UNIFORM_BUFFER_BINDINGS, + &gets.max_uniform_buffer_bindings); + glGetIntegerv(GL_NUM_PROGRAM_BINARY_FORMATS, + &gets.num_program_binary_formats); + glGetIntegerv(GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT, + &gets.uniform_buffer_offset_alignment); #endif } + #ifdef BACKEND_OPENGL_VERSION_GLES else { + gets.max_anisotropy = 1; + gets.max_draw_buffers = 1; + gets.max_samples = 1; + gets.max_transform_feedback_separate_attribs = 0; gets.max_uniform_block_size = 0; gets.max_uniform_buffer_bindings = 0; + gets.num_program_binary_formats = 0; gets.uniform_buffer_offset_alignment = 0; - gets.max_samples = 1; - gets.max_draw_buffers = 1; - gets.max_transform_feedback_separate_attribs = 0; - gets.max_anisotropy = 1; } #endif - slog.v << "Feature level: " << +mFeatureLevel << '\n'; slog.v << "Active workarounds: " << '\n'; UTILS_NOUNROLL @@ -143,13 +146,18 @@ OpenGLContext::OpenGLContext() noexcept { #ifndef NDEBUG // this is useful for development - slog.v << "GL_MAX_DRAW_BUFFERS = " << gets.max_draw_buffers << '\n' - << "GL_MAX_RENDERBUFFER_SIZE = " << gets.max_renderbuffer_size << '\n' - << "GL_MAX_SAMPLES = " << gets.max_samples << '\n' - << "GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT = " << gets.max_anisotropy << '\n' - << "GL_MAX_UNIFORM_BLOCK_SIZE = " << gets.max_uniform_block_size << '\n' - << "GL_MAX_TEXTURE_IMAGE_UNITS = " << gets.max_texture_image_units << '\n' - << "GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT = " << gets.uniform_buffer_offset_alignment << '\n' + slog.v + << "GL_MAX_TEXTURE_MAX_ANISOTROPY_EXT" << gets.max_anisotropy << '\n' + << "GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS" << gets.max_combined_texture_image_units << '\n' + << "GL_MAX_DRAW_BUFFERS" << gets.max_draw_buffers << '\n' + << "GL_MAX_RENDERBUFFER_SIZE" << gets.max_renderbuffer_size << '\n' + << "GL_MAX_SAMPLES" << gets.max_samples << '\n' + << "GL_MAX_TEXTURE_IMAGE_UNITS" << gets.max_texture_image_units << '\n' + << "GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS" << gets.max_transform_feedback_separate_attribs << '\n' + << "GL_MAX_UNIFORM_BLOCK_SIZE" << gets.max_uniform_block_size << '\n' + << "GL_MAX_UNIFORM_BUFFER_BINDINGS" << gets.max_uniform_buffer_bindings << '\n' + << "GL_NUM_PROGRAM_BINARY_FORMATS" << gets.num_program_binary_formats << '\n' + << "GL_UNIFORM_BUFFER_OFFSET_ALIGNMENT" << gets.uniform_buffer_offset_alignment << '\n' ; flush(slog.v); #endif diff --git a/filament/backend/src/opengl/OpenGLContext.h b/filament/backend/src/opengl/OpenGLContext.h index ff8b29cbd5..4ba559c78c 100644 --- a/filament/backend/src/opengl/OpenGLContext.h +++ b/filament/backend/src/opengl/OpenGLContext.h @@ -153,14 +153,15 @@ public: // glGet*() values struct Gets { GLfloat max_anisotropy; + GLint max_combined_texture_image_units; GLint max_draw_buffers; GLint max_renderbuffer_size; GLint max_samples; - GLint max_uniform_block_size; GLint max_texture_image_units; - GLint max_combined_texture_image_units; GLint max_transform_feedback_separate_attribs; - GLint max_uniform_buffer_bindings; + GLint max_uniform_block_size; + GLint max_uniform_buffer_bindings; + GLint num_program_binary_formats; GLint uniform_buffer_offset_alignment; } gets = {}; diff --git a/filament/backend/src/opengl/ShaderCompilerService.cpp b/filament/backend/src/opengl/ShaderCompilerService.cpp index 4d49cca9bf..9ab30a9bf1 100644 --- a/filament/backend/src/opengl/ShaderCompilerService.cpp +++ b/filament/backend/src/opengl/ShaderCompilerService.cpp @@ -140,6 +140,7 @@ void* ShaderCompilerService::getUserData(const program_token_t& token) noexcept ShaderCompilerService::ShaderCompilerService(OpenGLDriver& driver) : mDriver(driver), + mBlobCache(driver.getContext()), mCallbackManager(driver), KHR_parallel_shader_compile(driver.getContext().ext.KHR_parallel_shader_compile) { } @@ -219,7 +220,7 @@ ShaderCompilerService::program_token_t ShaderCompilerService::createProgram( token->attributes = std::move(program.getAttributes()); } - token->gl.program = OpenGLBlobCache::retrieve(&token->key, mDriver.mPlatform, program); + token->gl.program = mBlobCache.retrieve(&token->key, mDriver.mPlatform, program); if (token->gl.program) { return token; } @@ -264,7 +265,7 @@ ShaderCompilerService::program_token_t ShaderCompilerService::createProgram( // caching must be the last thing we do if (token->key && status == GL_TRUE) { // Attempt to cache. This calls glGetProgramBinary. - OpenGLBlobCache::insert(mDriver.mPlatform, token->key, glProgram); + mBlobCache.insert(mDriver.mPlatform, token->key, glProgram); } }); @@ -317,7 +318,7 @@ ShaderCompilerService::program_token_t ShaderCompilerService::createProgram( // do this later, maybe depending on CPU usage? // attempt to cache if we don't have a thread pool (otherwise it's done // by the pool). - OpenGLBlobCache::insert(mDriver.mPlatform, token->key, token->gl.program); + mBlobCache.insert(mDriver.mPlatform, token->key, token->gl.program); } return true; @@ -431,7 +432,7 @@ GLuint ShaderCompilerService::initialize(program_token_t& token) noexcept { mCallbackManager.put(token->handle); if (token->key) { - OpenGLBlobCache::insert(mDriver.mPlatform, token->key, token->gl.program); + mBlobCache.insert(mDriver.mPlatform, token->key, token->gl.program); } } else { // if we don't have a program yet, block until we get it. diff --git a/filament/backend/src/opengl/ShaderCompilerService.h b/filament/backend/src/opengl/ShaderCompilerService.h index bbce6a5c23..0ff9e493cb 100644 --- a/filament/backend/src/opengl/ShaderCompilerService.h +++ b/filament/backend/src/opengl/ShaderCompilerService.h @@ -21,6 +21,7 @@ #include "CallbackManager.h" #include "CompilerThreadPool.h" +#include "OpenGLBlobCache.h" #include #include @@ -95,6 +96,7 @@ public: private: OpenGLDriver& mDriver; + OpenGLBlobCache mBlobCache; CallbackManager mCallbackManager; CompilerThreadPool mCompilerThreadPool; From f8b70e8ec585548475c1592de407222e7924e7a6 Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Wed, 25 Oct 2023 22:10:51 -0700 Subject: [PATCH 59/63] fix a Transform component leak in CameraManager CameraManager creates a Transform component for each Camera component is not already present. However, it didn't destroy the transform component when it's itself destroyed. the leaked transform component would eventually be garbage collected, but caused significant slow down and memory pressure. This is because camera components are created every frame for the shadow maps. FIXES=[303914944] --- filament/src/components/CameraManager.cpp | 68 +++++++++++++---------- filament/src/components/CameraManager.h | 15 +++-- filament/src/details/Engine.cpp | 10 ++-- 3 files changed, 52 insertions(+), 41 deletions(-) diff --git a/filament/src/components/CameraManager.cpp b/filament/src/components/CameraManager.cpp index 21dd9e81d0..a34b376d48 100644 --- a/filament/src/components/CameraManager.cpp +++ b/filament/src/components/CameraManager.cpp @@ -23,70 +23,82 @@ #include #include -#include - using namespace utils; using namespace filament::math; namespace filament { -FCameraManager::FCameraManager(FEngine& engine) noexcept - : mEngine(engine) { +FCameraManager::FCameraManager(FEngine&) noexcept { } -FCameraManager::~FCameraManager() noexcept { -} +FCameraManager::~FCameraManager() noexcept = default; -void FCameraManager::terminate() noexcept { +void FCameraManager::terminate(FEngine& engine) noexcept { auto& manager = mManager; if (!manager.empty()) { #ifndef NDEBUG slog.d << "cleaning up " << manager.getComponentCount() << " leaked Camera components" << io::endl; #endif - while (!manager.empty()) { - Instance const ci = manager.end() - 1; - destroy(manager.getEntity(ci)); + utils::Slice const entities{ manager.getEntities(), manager.getComponentCount() }; + for (Entity const e : entities) { + destroy(engine, e); } } } -void FCameraManager::gc(utils::EntityManager& em) noexcept { +void FCameraManager::gc(FEngine& engine, utils::EntityManager& em) noexcept { auto& manager = mManager; - manager.gc(em, 4, [this](Entity e) { - destroy(e); + manager.gc(em, 4, [this, &engine](Entity e) { + destroy(engine, e); }); } -FCamera* FCameraManager::create(Entity entity) { - FEngine& engine = mEngine; +FCamera* FCameraManager::create(FEngine& engine, Entity entity) { auto& manager = mManager; + // if this entity already has Camera component, destroy it. if (UTILS_UNLIKELY(manager.hasComponent(entity))) { - destroy(entity); + destroy(engine, entity); } + + // add the Camera component to the entity Instance const i = manager.addComponent(entity); - FCamera* camera = engine.getHeapAllocator().make(engine, entity); + // For historical reasons, FCamera must not move. So the CameraManager stores a pointer. + FCamera* const camera = engine.getHeapAllocator().make(engine, entity); manager.elementAt(i) = camera; + manager.elementAt(i) = false; // Make sure we have a transform component - FTransformManager& transformManager = engine.getTransformManager(); - if (!transformManager.hasComponent(entity)) { - transformManager.create(entity); + FTransformManager& tcm = engine.getTransformManager(); + if (!tcm.hasComponent(entity)) { + tcm.create(entity); + manager.elementAt(i) = true; } return camera; } -void FCameraManager::destroy(Entity e) noexcept { +void FCameraManager::destroy(FEngine& engine, Entity e) noexcept { auto& manager = mManager; - Instance const i = manager.getInstance(e); - if (i) { - FCamera* camera = manager.elementAt(i); - assert_invariant(camera); - camera->terminate(mEngine); - mEngine.getHeapAllocator().destroy(camera); - manager.removeComponent(e); + if (Instance const i = manager.getInstance(e) ; i) { + // destroy the FCamera object + bool const ownsTransformComponent = manager.elementAt(i); + + { // scope for camera -- it's invalid after this scope. + FCamera* const camera = manager.elementAt(i); + assert_invariant(camera); + camera->terminate(engine); + engine.getHeapAllocator().destroy(camera); + + // Remove the camera component + manager.removeComponent(e); + } + + // if we added the transform component, remove it. + if (ownsTransformComponent) { + engine.getTransformManager().destroy(e); + } } } diff --git a/filament/src/components/CameraManager.h b/filament/src/components/CameraManager.h index c6753f9553..239790a399 100644 --- a/filament/src/components/CameraManager.h +++ b/filament/src/components/CameraManager.h @@ -44,9 +44,9 @@ public: ~FCameraManager() noexcept; // free-up all resources - void terminate() noexcept; + void terminate(FEngine& engine) noexcept; - void gc(utils::EntityManager& em) noexcept; + void gc(FEngine& engine, utils::EntityManager& em) noexcept; /* * Component Manager APIs @@ -64,25 +64,24 @@ public: return mManager.elementAt(i); } - FCamera* create(utils::Entity entity); + FCamera* create(FEngine& engine, utils::Entity entity); - void destroy(utils::Entity e) noexcept; + void destroy(FEngine& engine, utils::Entity e) noexcept; private: enum { - CAMERA + CAMERA, + OWNS_TRANSFORM_COMPONENT }; - using Base = utils::SingleInstanceComponentManager; + using Base = utils::SingleInstanceComponentManager; struct CameraManagerImpl : public Base { using Base::gc; using Base::swap; using Base::hasComponent; } mManager; - - FEngine& mEngine; }; } // namespace filament diff --git a/filament/src/details/Engine.cpp b/filament/src/details/Engine.cpp index a02e524539..84bb55f4a0 100644 --- a/filament/src/details/Engine.cpp +++ b/filament/src/details/Engine.cpp @@ -424,7 +424,7 @@ void FEngine::shutdown() { mDFG.terminate(*this); // free-up the DFG mRenderableManager.terminate(); // free-up all renderables mLightManager.terminate(); // free-up all lights - mCameraManager.terminate(); // free-up all cameras + mCameraManager.terminate(*this); // free-up all cameras driver.destroyRenderPrimitive(mFullScreenTriangleRph); destroy(mFullScreenTriangleIb); @@ -537,7 +537,7 @@ void FEngine::gc() { mRenderableManager.gc(em); mLightManager.gc(em); mTransformManager.gc(em); - mCameraManager.gc(em); + mCameraManager.gc(*this, em); } void FEngine::flush() { @@ -828,7 +828,7 @@ FSwapChain* FEngine::createSwapChain(uint32_t width, uint32_t height, uint64_t f FCamera* FEngine::createCamera(Entity entity) noexcept { - return mCameraManager.create(entity); + return mCameraManager.create(*this, entity); } FCamera* FEngine::getCameraComponent(Entity entity) noexcept { @@ -837,7 +837,7 @@ FCamera* FEngine::getCameraComponent(Entity entity) noexcept { } void FEngine::destroyCameraComponent(utils::Entity entity) noexcept { - mCameraManager.destroy(entity); + mCameraManager.destroy(*this, entity); } @@ -1053,7 +1053,7 @@ void FEngine::destroy(Entity e) { mRenderableManager.destroy(e); mLightManager.destroy(e); mTransformManager.destroy(e); - mCameraManager.destroy(e); + mCameraManager.destroy(*this, e); } bool FEngine::isValid(const FBufferObject* p) { From 0f2c89b1403ced89a5d37a6e5af61bd111f825aa Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Thu, 26 Oct 2023 13:10:43 -0700 Subject: [PATCH 60/63] improvements to EntityManagers and Filament APIs (#7302) * prevent public classes from being created on the stack - we used to to this by deleting operator delete, but this prevented the internal "F" classes from being virtual; which can be useful when using EntityManger::Listener. now we just make the destructor protected in each class. - EntityManger::Listener now has a virtual destructor so that objects could be correctly destroyed from Listener* * improve EntityManger and Component managers - all component managers now have the same "base" API - getComponentCount() - empty() - getEntity() - getEntities() - Scene now has getEntityCount() - EntityManager now has getEntityCount() - all component manager implement gc() the same way, by calling destroy() - SingleInstanceComponentManager::gc() that calls removeComponent() has been removed because it's dangerous. removeComponent() is often not enough, some additional cleanup might be needed. --- .../filament-android/src/main/cpp/Scene.cpp | 7 +++ .../com/google/android/filament/Scene.java | 20 +++++++-- filament/include/filament/BufferObject.h | 4 ++ filament/include/filament/Camera.h | 4 ++ filament/include/filament/ColorGrading.h | 4 ++ filament/include/filament/DebugRegistry.h | 4 ++ filament/include/filament/Fence.h | 4 ++ filament/include/filament/FilamentAPI.h | 2 - filament/include/filament/IndexBuffer.h | 4 ++ filament/include/filament/IndirectLight.h | 4 ++ filament/include/filament/InstanceBuffer.h | 4 ++ filament/include/filament/LightManager.h | 43 ++++++++++--------- filament/include/filament/Material.h | 4 ++ filament/include/filament/MaterialInstance.h | 4 ++ filament/include/filament/MorphTargetBuffer.h | 4 ++ filament/include/filament/RenderTarget.h | 4 ++ filament/include/filament/RenderableManager.h | 27 ++++++++++++ filament/include/filament/Renderer.h | 4 ++ filament/include/filament/Scene.h | 18 ++++++-- filament/include/filament/SkinningBuffer.h | 4 ++ filament/include/filament/Skybox.h | 4 ++ filament/include/filament/Stream.h | 4 ++ filament/include/filament/SwapChain.h | 4 ++ filament/include/filament/Texture.h | 4 ++ filament/include/filament/TransformManager.h | 31 ++++++++++++- filament/include/filament/VertexBuffer.h | 4 ++ filament/include/filament/View.h | 4 ++ filament/src/LightManager.cpp | 16 +++++-- filament/src/RenderableManager.cpp | 16 +++++++ filament/src/Scene.cpp | 4 ++ filament/src/TransformManager.cpp | 38 +++++++++++----- filament/src/components/CameraManager.cpp | 2 +- filament/src/components/CameraManager.h | 18 +++++++- filament/src/components/LightManager.cpp | 4 +- filament/src/components/LightManager.h | 28 ++++++++---- filament/src/components/RenderableManager.cpp | 4 +- filament/src/components/RenderableManager.h | 22 +++++++--- filament/src/components/TransformManager.cpp | 3 +- filament/src/components/TransformManager.h | 22 ++++++++-- filament/src/details/Scene.h | 3 +- libs/gltfio/src/FNodeManager.h | 6 ++- libs/gltfio/src/FTrsTransformManager.h | 6 ++- libs/utils/include/utils/EntityManager.h | 19 ++++---- .../include/utils/NameComponentManager.h | 17 +++----- .../utils/SingleInstanceComponentManager.h | 36 ++++++++-------- libs/utils/src/EntityManager.cpp | 8 +++- libs/utils/src/EntityManagerImpl.h | 10 +++++ libs/utils/src/NameComponentManager.cpp | 14 +----- libs/viewer/src/Settings.cpp | 6 ++- libs/viewer/src/ViewerGui.cpp | 16 +++++-- web/filament-js/jsbindings.cpp | 1 + 51 files changed, 414 insertions(+), 133 deletions(-) diff --git a/android/filament-android/src/main/cpp/Scene.cpp b/android/filament-android/src/main/cpp/Scene.cpp index 25e6be09ba..b1d3d00348 100644 --- a/android/filament-android/src/main/cpp/Scene.cpp +++ b/android/filament-android/src/main/cpp/Scene.cpp @@ -71,6 +71,13 @@ Java_com_google_android_filament_Scene_nRemoveEntities(JNIEnv *env, jclass type, env->ReleaseIntArrayElements(entities, (jint*) nativeEntities, JNI_ABORT); } +extern "C" JNIEXPORT jint JNICALL +Java_com_google_android_filament_Scene_nGetEntityCount(JNIEnv *env, jclass type, + jlong nativeScene) { + Scene* scene = (Scene*) nativeScene; + return (jint) scene->getEntityCount(); +} + extern "C" JNIEXPORT jint JNICALL Java_com_google_android_filament_Scene_nGetRenderableCount(JNIEnv *env, jclass type, jlong nativeScene) { diff --git a/android/filament-android/src/main/java/com/google/android/filament/Scene.java b/android/filament-android/src/main/java/com/google/android/filament/Scene.java index 283b7024e7..fd425fbeaa 100644 --- a/android/filament-android/src/main/java/com/google/android/filament/Scene.java +++ b/android/filament-android/src/main/java/com/google/android/filament/Scene.java @@ -146,18 +146,29 @@ public class Scene { } /** - * Returns the number of {@link RenderableManager} components in the Scene. + * Returns the total number of Entities in the Scene, whether alive or not. * - * @return number of {@link RenderableManager} components in the Scene.. + * @return the total number of Entities in the Scene. + */ + public int getEntityCount() { + return nGetEntityCount(getNativeObject()); + } + + /** + * Returns the number of active (alive) {@link RenderableManager} components in the + * Scene. + * + * @return number of {@link RenderableManager} components in the Scene. */ public int getRenderableCount() { return nGetRenderableCount(getNativeObject()); } /** - * Returns the number of {@link LightManager} components in the Scene. + * Returns the number of active (alive) {@link LightManager} components in the + * Scene. * - * @return number of {@link LightManager} components in the Scene.. + * @return number of {@link LightManager} components in the Scene. */ public int getLightCount() { return nGetLightCount(getNativeObject()); @@ -189,6 +200,7 @@ public class Scene { private static native void nAddEntities(long nativeScene, int[] entities); private static native void nRemove(long nativeScene, int entity); private static native void nRemoveEntities(long nativeScene, int[] entities); + private static native int nGetEntityCount(long nativeScene); private static native int nGetRenderableCount(long nativeScene); private static native int nGetLightCount(long nativeScene); private static native boolean nHasEntity(long nativeScene, int entity); diff --git a/filament/include/filament/BufferObject.h b/filament/include/filament/BufferObject.h index 1ede31b8cb..edabba83fa 100644 --- a/filament/include/filament/BufferObject.h +++ b/filament/include/filament/BufferObject.h @@ -110,6 +110,10 @@ public: * @return The maximum capacity of the BufferObject. */ size_t getByteCount() const noexcept; + +protected: + // prevent heap allocation + ~BufferObject() = default; }; } // namespace filament diff --git a/filament/include/filament/Camera.h b/filament/include/filament/Camera.h index 1c52a47cae..ab06a413b5 100644 --- a/filament/include/filament/Camera.h +++ b/filament/include/filament/Camera.h @@ -565,6 +565,10 @@ public: * @return effective full field of view in degrees */ static double computeEffectiveFov(double fovInDegrees, double focusDistance) noexcept; + +protected: + // prevent heap allocation + ~Camera() = default; }; } // namespace filament diff --git a/filament/include/filament/ColorGrading.h b/filament/include/filament/ColorGrading.h index db709600ef..0cd4e8618a 100644 --- a/filament/include/filament/ColorGrading.h +++ b/filament/include/filament/ColorGrading.h @@ -478,6 +478,10 @@ public: private: friend class FColorGrading; }; + +protected: + // prevent heap allocation + ~ColorGrading() = default; }; } // namespace filament diff --git a/filament/include/filament/DebugRegistry.h b/filament/include/filament/DebugRegistry.h index 3fa8c5a432..f4df9e07a5 100644 --- a/filament/include/filament/DebugRegistry.h +++ b/filament/include/filament/DebugRegistry.h @@ -142,6 +142,10 @@ public: float pid_i = 0.0f; float pid_d = 0.0f; }; + +protected: + // prevent heap allocation + ~DebugRegistry() = default; }; diff --git a/filament/include/filament/Fence.h b/filament/include/filament/Fence.h index bcfd2871fd..56cb33e66f 100644 --- a/filament/include/filament/Fence.h +++ b/filament/include/filament/Fence.h @@ -75,6 +75,10 @@ public: * FenceStatus::ERROR otherwise. */ static FenceStatus waitAndDestroy(Fence* fence, Mode mode = Mode::FLUSH); + +protected: + // prevent heap allocation + ~Fence() = default; }; } // namespace filament diff --git a/filament/include/filament/FilamentAPI.h b/filament/include/filament/FilamentAPI.h index 2925aca4a5..19d6ba246a 100644 --- a/filament/include/filament/FilamentAPI.h +++ b/filament/include/filament/FilamentAPI.h @@ -49,8 +49,6 @@ public: // prevent heap allocation static void *operator new (size_t) = delete; static void *operator new[] (size_t) = delete; - static void operator delete (void*) = delete; - static void operator delete[](void*) = delete; }; template diff --git a/filament/include/filament/IndexBuffer.h b/filament/include/filament/IndexBuffer.h index 09b479296d..09e433a36d 100644 --- a/filament/include/filament/IndexBuffer.h +++ b/filament/include/filament/IndexBuffer.h @@ -118,6 +118,10 @@ public: * @return The number of indices the IndexBuffer holds. */ size_t getIndexCount() const noexcept; + +protected: + // prevent heap allocation + ~IndexBuffer() = default; }; } // namespace filament diff --git a/filament/include/filament/IndirectLight.h b/filament/include/filament/IndirectLight.h index 70448523b7..4c3c6e2c38 100644 --- a/filament/include/filament/IndirectLight.h +++ b/filament/include/filament/IndirectLight.h @@ -342,6 +342,10 @@ public: /** @deprecated use static versions instead */ UTILS_DEPRECATED math::float4 getColorEstimate(math::float3 direction) const noexcept; + +protected: + // prevent heap allocation + ~IndirectLight() = default; }; } // namespace filament diff --git a/filament/include/filament/InstanceBuffer.h b/filament/include/filament/InstanceBuffer.h index d1ad29a908..671114a93f 100644 --- a/filament/include/filament/InstanceBuffer.h +++ b/filament/include/filament/InstanceBuffer.h @@ -91,6 +91,10 @@ public: * @param offset index of the first instance to set local transforms */ void setLocalTransforms(math::mat4f const* localTransforms, size_t count, size_t offset = 0); + +protected: + // prevent heap allocation + ~InstanceBuffer() = default; }; } // namespace filament diff --git a/filament/include/filament/LightManager.h b/filament/include/filament/LightManager.h index b7cb62e16a..fa0bdee71c 100644 --- a/filament/include/filament/LightManager.h +++ b/filament/include/filament/LightManager.h @@ -143,20 +143,13 @@ public: using Instance = utils::EntityInstance; /** - * Returns the number of component in the LightManager, not that component are not + * Returns the number of component in the LightManager, note that component are not * guaranteed to be active. Use the EntityManager::isAlive() before use if needed. * * @return number of component in the LightManager */ size_t getComponentCount() const noexcept; - /** - * Returns the list of Entity for all components. Use getComponentCount() to know the size - * of the list. - * @return a pointer to Entity - */ - utils::Entity const* getEntities() const noexcept; - /** * Returns whether a particular Entity is associated with a component of this LightManager * @param e An Entity. @@ -164,6 +157,24 @@ public: */ bool hasComponent(utils::Entity e) const noexcept; + /** + * @return true if the this manager has no components + */ + bool empty() const noexcept; + + /** + * Retrieve the `Entity` of the component from its `Instance`. + * @param i Instance of the component obtained from getInstance() + * @return + */ + utils::Entity getEntity(Instance i) const noexcept; + + /** + * Retrieve the Entities of all the components of this manager. + * @return A list, in no particular order, of all the entities managed by this manager. + */ + utils::Entity const* getEntities() const noexcept; + /** * Gets an Instance representing the Light component associated with the given Entity. * @param e An Entity. @@ -953,19 +964,9 @@ public: */ bool isShadowCaster(Instance i) const noexcept; - /** - * Helper to process all components with a given function - * @tparam F a void(Entity entity, Instance instance) - * @param func a function of type F - */ - template - void forEachComponent(F func) noexcept { - utils::Entity const* const pEntity = getEntities(); - for (size_t i = 0, c = getComponentCount(); i < c; i++) { - // Instance 0 is the invalid instance - func(pEntity[i], Instance(i + 1)); - } - } +protected: + // prevent heap allocation + ~LightManager() = default; }; } // namespace filament diff --git a/filament/include/filament/Material.h b/filament/include/filament/Material.h index 123d8599f8..e6f374be91 100644 --- a/filament/include/filament/Material.h +++ b/filament/include/filament/Material.h @@ -375,6 +375,10 @@ public: //! Returns this material's default instance. MaterialInstance const* getDefaultInstance() const noexcept; + +protected: + // prevent heap allocation + ~Material() = default; }; } // namespace filament diff --git a/filament/include/filament/MaterialInstance.h b/filament/include/filament/MaterialInstance.h index ee7a8e252f..0c6ef20573 100644 --- a/filament/include/filament/MaterialInstance.h +++ b/filament/include/filament/MaterialInstance.h @@ -479,6 +479,10 @@ public: */ void setStencilWriteMask(uint8_t writeMask, StencilFace face = StencilFace::FRONT_AND_BACK) noexcept; + +protected: + // prevent heap allocation + ~MaterialInstance() = default; }; } // namespace filament diff --git a/filament/include/filament/MorphTargetBuffer.h b/filament/include/filament/MorphTargetBuffer.h index d080d0da67..e94854643e 100644 --- a/filament/include/filament/MorphTargetBuffer.h +++ b/filament/include/filament/MorphTargetBuffer.h @@ -136,6 +136,10 @@ public: * @return The number of targets the MorphTargetBuffer holds. */ size_t getCount() const noexcept; + +protected: + // prevent heap allocation + ~MorphTargetBuffer() = default; }; } // namespace filament diff --git a/filament/include/filament/RenderTarget.h b/filament/include/filament/RenderTarget.h index 508e1c246f..faeee80cf6 100644 --- a/filament/include/filament/RenderTarget.h +++ b/filament/include/filament/RenderTarget.h @@ -180,6 +180,10 @@ public: * @return Number of color attachments usable in a render target. */ uint8_t getSupportedColorAttachmentsCount() const noexcept; + +protected: + // prevent heap allocation + ~RenderTarget() = default; }; } // namespace filament diff --git a/filament/include/filament/RenderableManager.h b/filament/include/filament/RenderableManager.h index 30705b8d62..8383a94748 100644 --- a/filament/include/filament/RenderableManager.h +++ b/filament/include/filament/RenderableManager.h @@ -102,6 +102,29 @@ public: */ Instance getInstance(utils::Entity e) const noexcept; + /** + * @return the number of Components + */ + size_t getComponentCount() const noexcept; + + /** + * @return true if the this manager has no components + */ + bool empty() const noexcept; + + /** + * Retrieve the `Entity` of the component from its `Instance`. + * @param i Instance of the component obtained from getInstance() + * @return + */ + utils::Entity getEntity(Instance i) const noexcept; + + /** + * Retrieve the Entities of all the components of this manager. + * @return A list, in no particular order, of all the entities managed by this manager. + */ + utils::Entity const* getEntities() const noexcept; + /** * The transformation associated with a skinning joint. * @@ -829,6 +852,10 @@ public: typename = typename is_supported_index_type::type> static Box computeAABB(VECTOR const* vertices, INDEX const* indices, size_t count, size_t stride = sizeof(VECTOR)) noexcept; + +protected: + // prevent heap allocation + ~RenderableManager() = default; }; RenderableManager::Builder& RenderableManager::Builder::morphing(uint8_t level, size_t primitiveIndex, diff --git a/filament/include/filament/Renderer.h b/filament/include/filament/Renderer.h index 999f31a965..8295a06fed 100644 --- a/filament/include/filament/Renderer.h +++ b/filament/include/filament/Renderer.h @@ -579,6 +579,10 @@ public: * getUserTime() */ void resetUserTime(); + +protected: + // prevent heap allocation + ~Renderer() = default; }; } // namespace filament diff --git a/filament/include/filament/Scene.h b/filament/include/filament/Scene.h index 3e6a63c4b3..28361278a2 100644 --- a/filament/include/filament/Scene.h +++ b/filament/include/filament/Scene.h @@ -140,16 +140,22 @@ public: void removeEntities(const utils::Entity* entities, size_t count); /** - * Returns the number of Renderable objects in the Scene. + * Returns the total number of Entities in the Scene, whether alive or not. + * @return Total number of Entities in the Scene. + */ + size_t getEntityCount() const noexcept; + + /** + * Returns the number of active (alive) Renderable objects in the Scene. * - * @return number of Renderable objects in the Scene. + * @return The number of active (alive) Renderable objects in the Scene. */ size_t getRenderableCount() const noexcept; /** - * Returns the total number of Light objects in the Scene. + * Returns the number of active (alive) Light objects in the Scene. * - * @return The total number of Light objects in the Scene. + * @return The number of active (alive) Light objects in the Scene. */ size_t getLightCount() const noexcept; @@ -168,6 +174,10 @@ public: * @param functor User provided functor called for each entity in the scene */ void forEach(utils::Invocable&& functor) const noexcept; + +protected: + // prevent heap allocation + ~Scene() = default; }; } // namespace filament diff --git a/filament/include/filament/SkinningBuffer.h b/filament/include/filament/SkinningBuffer.h index 007feb8508..5c33456360 100644 --- a/filament/include/filament/SkinningBuffer.h +++ b/filament/include/filament/SkinningBuffer.h @@ -115,6 +115,10 @@ public: * @return The number of bones the SkinningBuffer holds. */ size_t getBoneCount() const noexcept; + +protected: + // prevent heap allocation + ~SkinningBuffer() = default; }; } // namespace filament diff --git a/filament/include/filament/Skybox.h b/filament/include/filament/Skybox.h index 5e9f2c6063..150c9d3e4c 100644 --- a/filament/include/filament/Skybox.h +++ b/filament/include/filament/Skybox.h @@ -174,6 +174,10 @@ public: * @return the associated texture, or null if it does not exist */ Texture const* getTexture() const noexcept; + +protected: + // prevent heap allocation + ~Skybox() = default; }; } // namespace filament diff --git a/filament/include/filament/Stream.h b/filament/include/filament/Stream.h index 7fccecfe3e..83f898acae 100644 --- a/filament/include/filament/Stream.h +++ b/filament/include/filament/Stream.h @@ -207,6 +207,10 @@ public: * @return timestamp in nanosecond. */ int64_t getTimestamp() const noexcept; + +protected: + // prevent heap allocation + ~Stream() = default; }; } // namespace filament diff --git a/filament/include/filament/SwapChain.h b/filament/include/filament/SwapChain.h index 26c3da4a04..c17523acd5 100644 --- a/filament/include/filament/SwapChain.h +++ b/filament/include/filament/SwapChain.h @@ -286,6 +286,10 @@ public: void setFrameCompletedCallback(backend::CallbackHandler* handler = nullptr, FrameCompletedCallback&& callback = {}) noexcept; + +protected: + // prevent heap allocation + ~SwapChain() = default; }; } // namespace filament diff --git a/filament/include/filament/Texture.h b/filament/include/filament/Texture.h index c005c2b165..bb011cc978 100644 --- a/filament/include/filament/Texture.h +++ b/filament/include/filament/Texture.h @@ -541,6 +541,10 @@ public: return *this; } }; + +protected: + // prevent heap allocation + ~Texture() = default; }; } // namespace filament diff --git a/filament/include/filament/TransformManager.h b/filament/include/filament/TransformManager.h index 9afa689712..73ac2c9900 100644 --- a/filament/include/filament/TransformManager.h +++ b/filament/include/filament/TransformManager.h @@ -118,6 +118,29 @@ public: */ Instance getInstance(utils::Entity e) const noexcept; + /** + * @return the number of Components + */ + size_t getComponentCount() const noexcept; + + /** + * @return true if the this manager has no components + */ + bool empty() const noexcept; + + /** + * Retrieve the `Entity` of the component from its `Instance`. + * @param i Instance of the component obtained from getInstance() + * @return + */ + utils::Entity getEntity(Instance i) const noexcept; + + /** + * Retrieve the Entities of all the components of this manager. + * @return A list, in no particular order, of all the entities managed by this manager. + */ + utils::Entity const* getEntities() const noexcept; + /** * Enables or disable the accurate translation mode. Disabled by default. * @@ -261,7 +284,7 @@ public: * returns the value set by setTransform(). * @see setTransform() */ - const math::mat4 getTransformAccurate(Instance ci) const noexcept; + math::mat4 getTransformAccurate(Instance ci) const noexcept; /** * Return the world transform of a transform component. @@ -279,7 +302,7 @@ public: * composition of this component's local transform with its parent's world transform. * @see setTransform() */ - const math::mat4 getWorldTransformAccurate(Instance ci) const noexcept; + math::mat4 getWorldTransformAccurate(Instance ci) const noexcept; /** * Opens a local transform transaction. During a transaction, getWorldTransform() can @@ -308,6 +331,10 @@ public: * @see openLocalTransformTransaction(), setTransform() */ void commitLocalTransformTransaction() noexcept; + +protected: + // prevent heap allocation + ~TransformManager() = default; }; } // namespace filament diff --git a/filament/include/filament/VertexBuffer.h b/filament/include/filament/VertexBuffer.h index dd844c375a..317d5a7368 100644 --- a/filament/include/filament/VertexBuffer.h +++ b/filament/include/filament/VertexBuffer.h @@ -207,6 +207,10 @@ public: * @param bufferObject The handle to the GPU data that will be used in this buffer slot. */ void setBufferObjectAt(Engine& engine, uint8_t bufferIndex, BufferObject const* bufferObject); + +protected: + // prevent heap allocation + ~VertexBuffer() = default; }; } // namespace filament diff --git a/filament/include/filament/View.h b/filament/include/filament/View.h index 4bba745d1f..5eddb99143 100644 --- a/filament/include/filament/View.h +++ b/filament/include/filament/View.h @@ -894,6 +894,10 @@ public: */ UTILS_DEPRECATED AmbientOcclusion getAmbientOcclusion() const noexcept; + +protected: + // prevent heap allocation + ~View() = default; }; } // namespace filament diff --git a/filament/src/LightManager.cpp b/filament/src/LightManager.cpp index be0252c123..cc6bd083e5 100644 --- a/filament/src/LightManager.cpp +++ b/filament/src/LightManager.cpp @@ -22,16 +22,24 @@ namespace filament { using namespace math; +bool LightManager::hasComponent(Entity e) const noexcept { + return downcast(this)->hasComponent(e); +} + size_t LightManager::getComponentCount() const noexcept { return downcast(this)->getComponentCount(); } -utils::Entity const* LightManager::getEntities() const noexcept { - return downcast(this)->getEntities(); +bool LightManager::empty() const noexcept { + return downcast(this)->empty(); } -bool LightManager::hasComponent(Entity e) const noexcept { - return downcast(this)->hasComponent(e); +utils::Entity LightManager::getEntity(LightManager::Instance i) const noexcept { + return downcast(this)->getEntity(i); +} + +utils::Entity const* LightManager::getEntities() const noexcept { + return downcast(this)->getEntities(); } LightManager::Instance LightManager::getInstance(Entity e) const noexcept { diff --git a/filament/src/RenderableManager.cpp b/filament/src/RenderableManager.cpp index bc1240415d..71a83bf544 100644 --- a/filament/src/RenderableManager.cpp +++ b/filament/src/RenderableManager.cpp @@ -31,6 +31,22 @@ bool RenderableManager::hasComponent(utils::Entity e) const noexcept { return downcast(this)->hasComponent(e); } +size_t RenderableManager::getComponentCount() const noexcept { + return downcast(this)->getComponentCount(); +} + +bool RenderableManager::empty() const noexcept { + return downcast(this)->empty(); +} + +utils::Entity RenderableManager::getEntity(RenderableManager::Instance i) const noexcept { + return downcast(this)->getEntity(i); +} + +utils::Entity const* RenderableManager::getEntities() const noexcept { + return downcast(this)->getEntities(); +} + RenderableManager::Instance RenderableManager::getInstance(utils::Entity e) const noexcept { return downcast(this)->getInstance(e); diff --git a/filament/src/Scene.cpp b/filament/src/Scene.cpp index e686824ed9..1cc7496723 100644 --- a/filament/src/Scene.cpp +++ b/filament/src/Scene.cpp @@ -55,6 +55,10 @@ void Scene::removeEntities(const Entity* entities, size_t count) { downcast(this)->removeEntities(entities, count); } +size_t Scene::getEntityCount() const noexcept { + return downcast(this)->getEntityCount(); +} + size_t Scene::getRenderableCount() const noexcept { return downcast(this)->getRenderableCount(); } diff --git a/filament/src/TransformManager.cpp b/filament/src/TransformManager.cpp index 4a6b672fe7..384b930c50 100644 --- a/filament/src/TransformManager.cpp +++ b/filament/src/TransformManager.cpp @@ -22,6 +22,30 @@ namespace filament { using namespace math; +bool TransformManager::hasComponent(Entity e) const noexcept { + return downcast(this)->hasComponent(e); +} + +size_t TransformManager::getComponentCount() const noexcept { + return downcast(this)->getComponentCount(); +} + +bool TransformManager::empty() const noexcept { + return downcast(this)->empty(); +} + +utils::Entity TransformManager::getEntity(TransformManager::Instance i) const noexcept { + return downcast(this)->getEntity(i); +} + +utils::Entity const* TransformManager::getEntities() const noexcept { + return downcast(this)->getEntities(); +} + +TransformManager::Instance TransformManager::getInstance(Entity e) const noexcept { + return downcast(this)->getInstance(e); +} + void TransformManager::create(Entity entity, Instance parent, const mat4f& worldTransform) { downcast(this)->create(entity, parent, worldTransform); } @@ -38,14 +62,6 @@ void TransformManager::destroy(Entity e) noexcept { downcast(this)->destroy(e); } -bool TransformManager::hasComponent(Entity e) const noexcept { - return downcast(this)->hasComponent(e); -} - -TransformManager::Instance TransformManager::getInstance(Entity e) const noexcept { - return downcast(this)->getInstance(e); -} - void TransformManager::setTransform(Instance ci, const mat4f& model) noexcept { downcast(this)->setTransform(ci, model); } @@ -58,7 +74,7 @@ const mat4f& TransformManager::getTransform(Instance ci) const noexcept { return downcast(this)->getTransform(ci); } -const mat4 TransformManager::getTransformAccurate(Instance ci) const noexcept { +mat4 TransformManager::getTransformAccurate(Instance ci) const noexcept { return downcast(this)->getTransformAccurate(ci); } @@ -66,7 +82,7 @@ const mat4f& TransformManager::getWorldTransform(Instance ci) const noexcept { return downcast(this)->getWorldTransform(ci); } -const mat4 TransformManager::getWorldTransformAccurate(Instance ci) const noexcept { +mat4 TransformManager::getWorldTransformAccurate(Instance ci) const noexcept { return downcast(this)->getWorldTransformAccurate(ci); } @@ -110,7 +126,7 @@ void TransformManager::setAccurateTranslationsEnabled(bool enable) noexcept { } bool TransformManager::isAccurateTranslationsEnabled() const noexcept { - return downcast(this)->isAccurateTranslationsEnabled();; + return downcast(this)->isAccurateTranslationsEnabled(); } } // namespace filament diff --git a/filament/src/components/CameraManager.cpp b/filament/src/components/CameraManager.cpp index a34b376d48..b48742eae6 100644 --- a/filament/src/components/CameraManager.cpp +++ b/filament/src/components/CameraManager.cpp @@ -49,7 +49,7 @@ void FCameraManager::terminate(FEngine& engine) noexcept { void FCameraManager::gc(FEngine& engine, utils::EntityManager& em) noexcept { auto& manager = mManager; - manager.gc(em, 4, [this, &engine](Entity e) { + manager.gc(em, [this, &engine](Entity e) { destroy(engine, e); }); } diff --git a/filament/src/components/CameraManager.h b/filament/src/components/CameraManager.h index 239790a399..67bbfcb9ae 100644 --- a/filament/src/components/CameraManager.h +++ b/filament/src/components/CameraManager.h @@ -57,7 +57,23 @@ public: } Instance getInstance(utils::Entity e) const noexcept { - return Instance(mManager.getInstance(e)); + return { mManager.getInstance(e) }; + } + + size_t getComponentCount() const noexcept { + return mManager.getComponentCount(); + } + + bool empty() const noexcept { + return mManager.empty(); + } + + utils::Entity getEntity(Instance i) const noexcept { + return mManager.getEntity(i); + } + + utils::Entity const* getEntities() const noexcept { + return mManager.getEntities(); } FCamera* getCamera(Instance i) noexcept { diff --git a/filament/src/components/LightManager.cpp b/filament/src/components/LightManager.cpp index 76e2beea67..6393caa10a 100644 --- a/filament/src/components/LightManager.cpp +++ b/filament/src/components/LightManager.cpp @@ -227,7 +227,9 @@ void FLightManager::terminate() noexcept { } } void FLightManager::gc(utils::EntityManager& em) noexcept { - mManager.gc(em); + mManager.gc(em, [this](Entity e) { + destroy(e); + }); } void FLightManager::setShadowOptions(Instance i, ShadowOptions const& options) noexcept { diff --git a/filament/src/components/LightManager.h b/filament/src/components/LightManager.h index 47f5cfcc27..317ec723f7 100644 --- a/filament/src/components/LightManager.h +++ b/filament/src/components/LightManager.h @@ -46,20 +46,32 @@ public: void gc(utils::EntityManager& em) noexcept; - size_t getComponentCount() const noexcept { - return mManager.getComponentCount(); - } - - utils::Entity const* getEntities() const noexcept { - return mManager.getEntities(); - } + /* + * Component Manager APIs + */ bool hasComponent(utils::Entity e) const noexcept { return mManager.hasComponent(e); } Instance getInstance(utils::Entity e) const noexcept { - return mManager.getInstance(e); + return { mManager.getInstance(e) }; + } + + size_t getComponentCount() const noexcept { + return mManager.getComponentCount(); + } + + bool empty() const noexcept { + return mManager.empty(); + } + + utils::Entity getEntity(Instance i) const noexcept { + return mManager.getEntity(i); + } + + utils::Entity const* getEntities() const noexcept { + return mManager.getEntities(); } void create(const FLightManager::Builder& builder, utils::Entity entity); diff --git a/filament/src/components/RenderableManager.cpp b/filament/src/components/RenderableManager.cpp index d43fdb59d5..11e4eba8f5 100644 --- a/filament/src/components/RenderableManager.cpp +++ b/filament/src/components/RenderableManager.cpp @@ -678,7 +678,9 @@ void FRenderableManager::terminate() noexcept { } void FRenderableManager::gc(utils::EntityManager& em) noexcept { - mManager.gc(em); + mManager.gc(em, [this](Entity e) { + destroy(e); + }); } // This is basically a Renderable's destructor. diff --git a/filament/src/components/RenderableManager.h b/filament/src/components/RenderableManager.h index d79d2ca3ee..746c46dbee 100644 --- a/filament/src/components/RenderableManager.h +++ b/filament/src/components/RenderableManager.h @@ -92,7 +92,23 @@ public: } Instance getInstance(utils::Entity e) const noexcept { - return mManager.getInstance(e); + return { mManager.getInstance(e) }; + } + + size_t getComponentCount() const noexcept { + return mManager.getComponentCount(); + } + + bool empty() const noexcept { + return mManager.empty(); + } + + utils::Entity getEntity(Instance i) const noexcept { + return mManager.getEntity(i); + } + + utils::Entity const* getEntities() const noexcept { + return mManager.getEntities(); } void create(const RenderableManager::Builder& builder, utils::Entity entity); @@ -176,10 +192,6 @@ public: static_assert(sizeof(InstancesInfo) == 16); inline InstancesInfo getInstancesInfo(Instance instance) const noexcept; - utils::Entity getEntity(Instance instance) const noexcept { - return mManager.getEntity(instance); - } - inline size_t getLevelCount(Instance) const noexcept { return 1u; } size_t getPrimitiveCount(Instance instance, uint8_t level) const noexcept; void setMaterialInstanceAt(Instance instance, uint8_t level, diff --git a/filament/src/components/TransformManager.cpp b/filament/src/components/TransformManager.cpp index 9324d5ac6e..7806e41f02 100644 --- a/filament/src/components/TransformManager.cpp +++ b/filament/src/components/TransformManager.cpp @@ -471,8 +471,7 @@ void FTransformManager::validateNode(UTILS_UNUSED_IN_RELEASE Instance i) noexcep } void FTransformManager::gc(utils::EntityManager& em) noexcept { - auto& manager = mManager; - manager.gc(em, 4, [this](Entity e) { + mManager.gc(em, [this](Entity e) { destroy(e); }); } diff --git a/filament/src/components/TransformManager.h b/filament/src/components/TransformManager.h index e7a674c8b0..6e42103370 100644 --- a/filament/src/components/TransformManager.h +++ b/filament/src/components/TransformManager.h @@ -50,7 +50,23 @@ public: } Instance getInstance(utils::Entity e) const noexcept { - return Instance(mManager.getInstance(e)); + return { mManager.getInstance(e) }; + } + + size_t getComponentCount() const noexcept { + return mManager.getComponentCount(); + } + + bool empty() const noexcept { + return mManager.empty(); + } + + utils::Entity getEntity(Instance i) const noexcept { + return mManager.getEntity(i); + } + + utils::Entity const* getEntities() const noexcept { + return mManager.getEntities(); } void setAccurateTranslationsEnabled(bool enable) noexcept; @@ -103,7 +119,7 @@ public: math::mat4 getTransformAccurate(Instance ci) const noexcept { math::mat4f const& local = mManager[ci].local; - math::float3 localTranslationLo = mManager[ci].localTranslationLo; + math::float3 const localTranslationLo = mManager[ci].localTranslationLo; math::mat4 r(local); r[3].xyz += localTranslationLo; return r; @@ -111,7 +127,7 @@ public: math::mat4 getWorldTransformAccurate(Instance ci) const noexcept { math::mat4f const& world = mManager[ci].world; - math::float3 worldTranslationLo = mManager[ci].worldTranslationLo; + math::float3 const worldTranslationLo = mManager[ci].worldTranslationLo; math::mat4 r(world); r[3].xyz += worldTranslationLo; return r; diff --git a/filament/src/details/Scene.h b/filament/src/details/Scene.h index 2e0aff2ae8..1882bb4dc3 100644 --- a/filament/src/details/Scene.h +++ b/filament/src/details/Scene.h @@ -55,7 +55,7 @@ class FSkybox; class FScene : public Scene { public: /* - * Filaments-scope Public API + * Filament-scope Public API */ FSkybox* getSkybox() const noexcept { return mSkybox; } @@ -197,6 +197,7 @@ private: void addEntities(const utils::Entity* entities, size_t count); void remove(utils::Entity entity); void removeEntities(const utils::Entity* entities, size_t count); + size_t getEntityCount() const noexcept { return mEntities.size(); } size_t getRenderableCount() const noexcept; size_t getLightCount() const noexcept; bool hasEntity(utils::Entity entity) const noexcept; diff --git a/libs/gltfio/src/FNodeManager.h b/libs/gltfio/src/FNodeManager.h index 5f8dc53edb..4e1c80d887 100644 --- a/libs/gltfio/src/FNodeManager.h +++ b/libs/gltfio/src/FNodeManager.h @@ -57,13 +57,15 @@ public: } void destroy(utils::Entity e) noexcept { - if (Instance ci = mManager.getInstance(e); ci) { + if (Instance const ci = mManager.getInstance(e); ci) { mManager.removeComponent(e); } } void gc(utils::EntityManager& em) noexcept { - mManager.gc(em); + mManager.gc(em, [this](Entity e) { + destroy(e); + }); } void setMorphTargetNames(Instance ci, utils::FixedCapacityVector names) noexcept { diff --git a/libs/gltfio/src/FTrsTransformManager.h b/libs/gltfio/src/FTrsTransformManager.h index c53342fcdb..a2cb53e06e 100644 --- a/libs/gltfio/src/FTrsTransformManager.h +++ b/libs/gltfio/src/FTrsTransformManager.h @@ -70,13 +70,15 @@ public: } void destroy(utils::Entity e) noexcept { - if (Instance ci = mManager.getInstance(e); ci) { + if (Instance const ci = mManager.getInstance(e); ci) { mManager.removeComponent(e); } } void gc(utils::EntityManager& em) noexcept { - mManager.gc(em); + mManager.gc(em, [this](Entity e) { + destroy(e); + }); } void setTranslation(Instance ci, const float3& translation) noexcept { diff --git a/libs/utils/include/utils/EntityManager.h b/libs/utils/include/utils/EntityManager.h index 9674cac255..8fac30cc53 100644 --- a/libs/utils/include/utils/EntityManager.h +++ b/libs/utils/include/utils/EntityManager.h @@ -44,23 +44,25 @@ public: public: virtual void onEntitiesDestroyed(size_t n, Entity const* entities) noexcept = 0; protected: - ~Listener() noexcept; + virtual ~Listener() noexcept; }; - // maximum number of entities that can exist at the same time static size_t getMaxEntityCount() noexcept { // because index 0 is reserved, we only have 2^GENERATION_SHIFT - 1 valid indices return RAW_INDEX_COUNT - 1; } - // create n entities. Thread safe. + // number of active Entities + size_t getEntityCount() const noexcept; + + // Create n entities. Thread safe. void create(size_t n, Entity* entities); // destroys n entities. Thread safe. void destroy(size_t n, Entity* entities) noexcept; - // create a new Entity. Thread safe. + // Create a new Entity. Thread safe. // Return Entity.isNull() if the entity cannot be allocated. Entity create() { Entity e; @@ -68,20 +70,20 @@ public: return e; } - // destroys an Entity. Thread safe. + // Destroys an Entity. Thread safe. void destroy(Entity e) noexcept { destroy(1, &e); } - // return whether the given Entity has been destroyed (false) or not (true). + // Return whether the given Entity has been destroyed (false) or not (true). // Thread safe. bool isAlive(Entity e) const noexcept { assert(getIndex(e) < RAW_INDEX_COUNT); return (!e.isNull()) && (getGeneration(e) == mGens[getIndex(e)]); } - // registers a listener to be called when an entity is destroyed. thread safe. - // if the listener is already register, this method has no effect. + // Registers a listener to be called when an entity is destroyed. Thread safe. + // If the listener is already registered, this method has no effect. void registerListener(Listener* l) noexcept; // unregisters a listener. @@ -94,6 +96,7 @@ public: uint8_t getGenerationForIndex(size_t index) const noexcept { return mGens[index]; } + // singleton, can't be copied EntityManager(const EntityManager& rhs) = delete; EntityManager& operator=(const EntityManager& rhs) = delete; diff --git a/libs/utils/include/utils/NameComponentManager.h b/libs/utils/include/utils/NameComponentManager.h index 9e31e4618d..a161b56ad7 100644 --- a/libs/utils/include/utils/NameComponentManager.h +++ b/libs/utils/include/utils/NameComponentManager.h @@ -48,7 +48,7 @@ class EntityManager; * printf("%s\n", names->getName(names->getInstance(myEntity)); * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */ -class UTILS_PUBLIC NameComponentManager : public SingleInstanceComponentManager { +class UTILS_PUBLIC NameComponentManager : private SingleInstanceComponentManager { public: using Instance = EntityInstance; @@ -75,15 +75,6 @@ public: return { SingleInstanceComponentManager::getInstance(e) }; } - /*! \cond PRIVATE */ - // these are implemented in SingleInstanceComponentManager<>, but we need to - // reimplement them in each manager, to ensure they are generated in an implementation file - // for backward binary compatibility reasons. - size_t getComponentCount() const noexcept; - Entity const* getEntities() const noexcept; - void gc(const EntityManager& em, size_t ratio = 4) noexcept; - /*! \endcond */ - /** * Adds a name component to the given entity if it doesn't already exist. */ @@ -105,6 +96,12 @@ public: * @return pointer to the copy that was made during setName() */ const char* getName(Instance instance) const noexcept; + + void gc(EntityManager& em) noexcept { + SingleInstanceComponentManager::gc(em, [this](Entity e) { + removeComponent(e); + }); + } }; } // namespace utils diff --git a/libs/utils/include/utils/SingleInstanceComponentManager.h b/libs/utils/include/utils/SingleInstanceComponentManager.h index c03ec5f100..ddd538f5e9 100644 --- a/libs/utils/include/utils/SingleInstanceComponentManager.h +++ b/libs/utils/include/utils/SingleInstanceComponentManager.h @@ -98,7 +98,7 @@ public: return pos != map.end() ? pos->second : 0; } - // returns the number of components (i.e. size of each arrays) + // Returns the number of components (i.e. size of each array) size_t getComponentCount() const noexcept { // The array as an extra dummy component at index 0, so the visible count is 1 less. return mData.size() - 1; @@ -108,11 +108,8 @@ public: return getComponentCount() == 0; } - // returns a pointer to the Entity array. This is basically the list - // of entities this component manager handles. - // The pointer becomes invalid when adding or removing a component. - Entity const* getEntities() const noexcept { - return begin(); + utils::Entity const* getEntities() const noexcept { + return data() + 1; } Entity getEntity(Instance i) const noexcept { @@ -128,14 +125,6 @@ public: // This invalidates all pointers components. inline Instance removeComponent(Entity e); - // trigger one round of garbage collection. this is intended to be called on a regular - // basis. This gc gives up after it cannot randomly free 'ratio' component in a row. - void gc(const EntityManager& em, size_t ratio = 4) noexcept { - gc(em, ratio, [this](Entity e) { - removeComponent(e); - }); - } - // return the first instance Instance begin() const noexcept { return 1u; } @@ -234,24 +223,33 @@ protected: } } + template + void gc(const EntityManager& em, + REMOVE&& removeComponent) noexcept { + gc(em, 4, std::forward(removeComponent)); + } + template void gc(const EntityManager& em, size_t ratio, - REMOVE removeComponent) noexcept { - Entity const* entities = getEntities(); + REMOVE&& removeComponent) noexcept { + Entity const* const pEntities = begin(); size_t count = getComponentCount(); size_t aliveInARow = 0; default_random_engine& rng = mRng; UTILS_NOUNROLL while (count && aliveInARow < ratio) { + assert_invariant(count == getComponentCount()); // note: using the modulo favorizes lower number - size_t i = rng() % count; - if (UTILS_LIKELY(em.isAlive(entities[i]))) { + size_t const i = rng() % count; + Entity const entity = pEntities[i]; + assert_invariant(entity); + if (UTILS_LIKELY(em.isAlive(entity))) { ++aliveInARow; continue; } + removeComponent(entity); aliveInARow = 0; count--; - removeComponent(entities[i]); } } diff --git a/libs/utils/src/EntityManager.cpp b/libs/utils/src/EntityManager.cpp index 30149c68ad..bdb12db325 100644 --- a/libs/utils/src/EntityManager.cpp +++ b/libs/utils/src/EntityManager.cpp @@ -22,6 +22,8 @@ namespace utils { +EntityManager::Listener::~Listener() noexcept = default; + EntityManager::EntityManager() : mGens(new uint8_t[RAW_INDEX_COUNT]) { // initialize all the generations to 0 @@ -32,8 +34,6 @@ EntityManager::~EntityManager() { delete [] mGens; } -EntityManager::Listener::~Listener() noexcept = default; - EntityManager& EntityManager::get() noexcept { // note: we leak the EntityManager because it's more important that it survives everything else // the leak is really not a problem because the process is terminating anyway. @@ -57,6 +57,10 @@ void EntityManager::unregisterListener(EntityManager::Listener* l) noexcept { static_cast(this)->unregisterListener(l); } +size_t EntityManager::getEntityCount() const noexcept { + return static_cast(this)->getEntityCount(); +} + #if FILAMENT_UTILS_TRACK_ENTITIES std::vector EntityManager::getActiveEntities() const { return static_cast(this)->getActiveEntities(); diff --git a/libs/utils/src/EntityManagerImpl.h b/libs/utils/src/EntityManagerImpl.h index bfda7dbe25..adf64a80c8 100644 --- a/libs/utils/src/EntityManagerImpl.h +++ b/libs/utils/src/EntityManagerImpl.h @@ -48,6 +48,16 @@ public: using EntityManager::create; using EntityManager::destroy; + UTILS_NOINLINE + size_t getEntityCount() const noexcept { + std::lock_guard const lock(mFreeListLock); + if (mCurrentIndex < RAW_INDEX_COUNT) { + return (mCurrentIndex - 1) - mFreeList.size(); + } else { + return getMaxEntityCount() - mFreeList.size(); + } + } + UTILS_NOINLINE void create(size_t n, Entity* entities) { Entity::Type index{}; diff --git a/libs/utils/src/NameComponentManager.cpp b/libs/utils/src/NameComponentManager.cpp index 98bb3839a3..eae5df4a16 100644 --- a/libs/utils/src/NameComponentManager.cpp +++ b/libs/utils/src/NameComponentManager.cpp @@ -21,7 +21,7 @@ namespace utils { static constexpr size_t NAME = 0; -NameComponentManager::NameComponentManager(EntityManager& em) { +NameComponentManager::NameComponentManager(EntityManager&) { } NameComponentManager::~NameComponentManager() = default; @@ -36,14 +36,6 @@ const char* NameComponentManager::getName(Instance instance) const noexcept { return elementAt(instance).c_str(); } -size_t NameComponentManager::getComponentCount() const noexcept { - return SingleInstanceComponentManager::getComponentCount(); -} - -Entity const* NameComponentManager::getEntities() const noexcept { - return SingleInstanceComponentManager::getEntities(); -} - void NameComponentManager::addComponent(Entity e) { SingleInstanceComponentManager::addComponent(e); } @@ -52,8 +44,4 @@ void NameComponentManager::removeComponent(Entity e) { SingleInstanceComponentManager::removeComponent(e); } -void NameComponentManager::gc(const EntityManager& em, size_t ratio) noexcept { - SingleInstanceComponentManager::gc(em, ratio); -} - } // namespace utils diff --git a/libs/viewer/src/Settings.cpp b/libs/viewer/src/Settings.cpp index 22b64588b0..febeb321fd 100644 --- a/libs/viewer/src/Settings.cpp +++ b/libs/viewer/src/Settings.cpp @@ -612,8 +612,10 @@ void applySettings(Engine* engine, const LightSettings& settings, IndirectLight* } for (size_t i = 0; i < sceneLightCount; i++) { auto const li = lm->getInstance(sceneLights[i]); - lm->setShadowCaster(li, settings.enableShadows); - lm->setShadowOptions(li, settings.shadowOptions); + if (li) { + lm->setShadowCaster(li, settings.enableShadows); + lm->setShadowOptions(li, settings.shadowOptions); + } } view->setSoftShadowOptions(settings.softShadowOptions); } diff --git a/libs/viewer/src/ViewerGui.cpp b/libs/viewer/src/ViewerGui.cpp index 8e0177538d..37d6420491 100644 --- a/libs/viewer/src/ViewerGui.cpp +++ b/libs/viewer/src/ViewerGui.cpp @@ -1045,7 +1045,7 @@ void ViewerGui::updateUserInterface() { std::vector names; names.reserve(cameraCount + 1); - names.push_back("Free camera"); + names.emplace_back("Free camera"); int c = 0; for (size_t i = 0; i < cameraCount; i++) { const char* n = mAsset->getName(cameras[i]); @@ -1060,8 +1060,8 @@ void ViewerGui::updateUserInterface() { std::vector cstrings; cstrings.reserve(names.size()); - for (size_t i = 0; i < names.size(); i++) { - cstrings.push_back(names[i].c_str()); + for (const auto & name : names) { + cstrings.push_back(name.c_str()); } ImGui::ListBox("Cameras", &mCurrentCamera, cstrings.data(), cstrings.size()); @@ -1083,8 +1083,16 @@ void ViewerGui::updateUserInterface() { // At this point, all View settings have been modified, // so we can now push them into the Filament View. applySettings(mEngine, mSettings.view, mView); + + auto lights = utils::FixedCapacityVector::with_capacity(mScene->getEntityCount()); + mScene->forEach([&](utils::Entity entity) { + if (lm.hasComponent(entity)) { + lights.push_back(entity); + } + }); + applySettings(mEngine, mSettings.lighting, mIndirectLight, mSunlight, - lm.getEntities(), lm.getComponentCount(), &lm, mScene, mView); + lights.data(), lights.size(), &lm, mScene, mView); // TODO(prideout): add support for hierarchy, animation and variant selection in remote mode. To // support these features, we will need to send a message (list of strings) from DebugServer to diff --git a/web/filament-js/jsbindings.cpp b/web/filament-js/jsbindings.cpp index 7e31c16786..90e4171b7a 100644 --- a/web/filament-js/jsbindings.cpp +++ b/web/filament-js/jsbindings.cpp @@ -688,6 +688,7 @@ class_("Scene") .function("getSkybox", &Scene::getSkybox, allow_raw_pointers()) .function("setIndirectLight", &Scene::setIndirectLight, allow_raw_pointers()) .function("getIndirectLight", &Scene::getIndirectLight, allow_raw_pointers()) + .function("getEntityCount", &Scene::getEntityCount) .function("getRenderableCount", &Scene::getRenderableCount) .function("getLightCount", &Scene::getLightCount); From 13571868de89dc44b38cfe124740ee71205f825a Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Fri, 27 Oct 2023 10:14:34 -0700 Subject: [PATCH 61/63] don't use a spinlock for the HandleArena We've seen hangs/ANR that are not well understood on that spinlock, so for now we're going back to mutexes, which, on android, are very efficient under low contention (no syscall). FIXES=[308029108] --- .../backend/include/private/backend/HandleAllocator.h | 8 +++++--- filament/src/details/Engine.h | 2 +- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/filament/backend/include/private/backend/HandleAllocator.h b/filament/backend/include/private/backend/HandleAllocator.h index 610ad3729e..f461da4336 100644 --- a/filament/backend/include/private/backend/HandleAllocator.h +++ b/filament/backend/include/private/backend/HandleAllocator.h @@ -239,14 +239,16 @@ private: } }; - +// FIXME: We should be using a Spinlock here, at least on platforms where mutexes are not +// efficient (i.e. non-Linux). However, we've seen some hangs on that spinlock, which +// we don't understand well (b/308029108). #ifndef NDEBUG using HandleArena = utils::Arena; #else using HandleArena = utils::Arena; + utils::LockingPolicy::Mutex>; #endif // allocateHandle()/deallocateHandle() selects the pool to use at compile-time based on the diff --git a/filament/src/details/Engine.h b/filament/src/details/Engine.h index e7d910002d..c6cfaee95f 100644 --- a/filament/src/details/Engine.h +++ b/filament/src/details/Engine.h @@ -487,7 +487,7 @@ private: ResourceList mRenderTargets{ "RenderTarget" }; // the fence list is accessed from multiple threads - utils::SpinLock mFenceListLock; + utils::Mutex mFenceListLock; ResourceList mFences{"Fence"}; mutable uint32_t mMaterialId = 0; From 763950ec1829bca914ce083f0ee8dcee4e4dd90c Mon Sep 17 00:00:00 2001 From: Benjamin Doherty Date: Wed, 8 Nov 2023 12:33:24 -0800 Subject: [PATCH 62/63] Revert "fix a couple shadow stability bugs" This reverts commit 1b0db0fca28982f796abc67b589d1b077ef0efc0. --- RELEASE_NOTES.md | 1 - filament/src/PerShadowMapUniforms.cpp | 2 +- filament/src/PerViewUniforms.cpp | 4 +- filament/src/ShadowMap.cpp | 185 ++++++++++++++++---------- filament/src/ShadowMap.h | 15 ++- filament/src/ShadowMapManager.cpp | 5 +- filament/src/details/Camera.cpp | 6 +- filament/src/details/Camera.h | 6 +- filament/src/details/Scene.cpp | 66 +++------ filament/src/details/Scene.h | 6 +- filament/src/details/View.cpp | 20 ++- filament/test/filament_test.cpp | 4 +- libs/math/include/math/mat3.h | 31 ----- libs/math/include/math/mat4.h | 29 ++-- libs/math/tests/test_mat.cpp | 112 +++++++--------- samples/gltf_viewer.cpp | 11 +- 16 files changed, 222 insertions(+), 281 deletions(-) diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 52ba23eeaf..453d4f4c16 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -15,7 +15,6 @@ Instead, if you are authoring a PR for the main branch, add your release note to - engine: New tone mapper: `AgXTonemapper`. - matinfo: Add support for viewing ESSL 1.0 shaders - engine: Add `Renderer::getClearOptions()` [b/243846268] -- engine: Fix stable shadows (again) when an IBL rotation is used ## v1.45.0 diff --git a/filament/src/PerShadowMapUniforms.cpp b/filament/src/PerShadowMapUniforms.cpp index 1c1ac24b72..87b78a9a9e 100644 --- a/filament/src/PerShadowMapUniforms.cpp +++ b/filament/src/PerShadowMapUniforms.cpp @@ -60,7 +60,7 @@ void PerShadowMapUniforms::prepareCamera(Transaction const& transaction, s.viewFromClipMatrix = viewFromClip; // 1/projection s.clipFromWorldMatrix[0] = clipFromWorld; // projection * view s.worldFromClipMatrix = worldFromClip; // 1/(projection * view) - s.userWorldFromWorldMatrix = mat4f(inverse(camera.worldTransform)); + s.userWorldFromWorldMatrix = mat4f(inverse(camera.worldOrigin)); s.clipTransform = camera.clipTransform; s.cameraFar = camera.zf; s.oneOverFarMinusNear = 1.0f / (camera.zf - camera.zn); diff --git a/filament/src/PerViewUniforms.cpp b/filament/src/PerViewUniforms.cpp index 2f68bdfd57..6ea6f4f629 100644 --- a/filament/src/PerViewUniforms.cpp +++ b/filament/src/PerViewUniforms.cpp @@ -75,7 +75,7 @@ void PerViewUniforms::prepareCamera(FEngine& engine, const CameraInfo& camera) n s.clipFromViewMatrix = clipFromView; // projection s.viewFromClipMatrix = viewFromClip; // 1/projection s.worldFromClipMatrix = worldFromClip; // 1/(projection * view) - s.userWorldFromWorldMatrix = mat4f(inverse(camera.worldTransform)); + s.userWorldFromWorldMatrix = mat4f(inverse(camera.worldOrigin)); s.clipTransform = camera.clipTransform; s.cameraFar = camera.zf; s.oneOverFarMinusNear = 1.0f / (camera.zf - camera.zn); @@ -151,7 +151,7 @@ void PerViewUniforms::prepareFog(FEngine& engine, const CameraInfo& cameraInfo, // why we store the cofactor matrix. mat4f const viewFromWorld = cameraInfo.view; - mat4 const worldFromUserWorld = cameraInfo.worldTransform; + mat4 const worldFromUserWorld = cameraInfo.worldOrigin; mat4 const worldFromFog = worldFromUserWorld * userWorldFromFog; mat4 const viewFromFog = viewFromWorld * worldFromFog; diff --git a/filament/src/ShadowMap.cpp b/filament/src/ShadowMap.cpp index e2b8dc769e..318a566cc3 100644 --- a/filament/src/ShadowMap.cpp +++ b/filament/src/ShadowMap.cpp @@ -77,13 +77,20 @@ void ShadowMap::initialize(size_t lightIndex, ShadowType shadowType, mFace = face; } -math::mat4f ShadowMap::getDirectionalLightViewMatrix(math::float3 direction, math::float3 up, - math::float3 position) noexcept { - // 1. we use the x-axis as the "up" reference so that the math is stable when the light - // is pointing down, which is a common case for lights. - // 2. we do the math in double to avoid some precision issues when the light is almost - // straight (i.e. parallel to the x-axis) - mat4f const Mm = mat4f{ mat4::lookTo(direction, position, up) }; +mat4f ShadowMap::getDirectionalLightViewMatrix(float3 direction, float3 position) noexcept { + auto z_axis = direction; + auto norm_up = float3{ 0, 1, 0 }; + if (UTILS_UNLIKELY(std::abs(dot(z_axis, norm_up)) > 0.999f)) { + // Fix up vector if we're degenerate (looking straight up, basically) + norm_up = { norm_up.z, norm_up.x, norm_up.y }; + } + auto x_axis = normalize(cross(z_axis, norm_up)); + auto y_axis = cross(x_axis, z_axis); + const mat4f Mm{ + float4{ x_axis, 0 }, + float4{ y_axis, 0 }, + float4{ -z_axis, 0 }, + float4{ position, 1 }}; return FCamera::rigidTransformInverse(Mm); } @@ -98,7 +105,7 @@ math::mat4f ShadowMap::getPointLightViewMatrix(backend::TextureCubemapFace face, case TextureCubemapFace::POSITIVE_Z: direction = { 0, 0, 1 }; break; case TextureCubemapFace::NEGATIVE_Z: direction = { 0, 0, -1 }; break; } - const mat4f Mv = getDirectionalLightViewMatrix(direction, { 0, 1, 0 }, position); + const mat4f Mv = getDirectionalLightViewMatrix(direction, position); return Mv; } @@ -113,7 +120,7 @@ ShadowMap::ShaderParameters ShadowMap::updateDirectional(FEngine& engine, FLightManager::ShadowParams const params = lcm.getShadowParams(li); // We can't use LISPSM in stable mode - const auto direction = lightData.elementAt(index); + const auto direction = params.options.transform * lightData.elementAt(index); auto [Mv, znear, zfar, lsClippedShadowVolume, vertexCount, visibleShadows] = computeDirectionalShadowBounds(engine, direction, params, camera, sceneInfo); @@ -159,22 +166,11 @@ ShadowMap::ShaderParameters ShadowMap::updateDirectional(FEngine& engine, // This is the most important step to increase the quality of the shadow map. // // In LiPSM mode, we're using the warped space here. - float4 f = computeFocusParams(LMpMv, WLMp, + const mat4f F = computeFocusMatrix(LMpMv, WLMp, + sceneInfo.wsShadowReceiversVolume, lsClippedShadowVolume, vertexCount, camera, sceneInfo.csNearFar, - params.options.shadowFar, params.options.stable); - - if (params.options.stable) { - const auto lsRef = lightData.elementAt(index); - snapLightFrustum(f.xy, f.zw, lsRef, shadowMapInfo.shadowDimension); - } - - const mat4f F(mat4f::row_major_init { - f.x, 0.0f, 0.0f, f.z, - 0.0f, f.y, 0.0f, f.w, - 0.0f, 0.0f, 1.0f, 0.0f, - 0.0f, 0.0f, 0.0f, 1.0f, - }); + shadowMapInfo.shadowDimension, params.options.stable); /* * Final shadow map transform @@ -226,7 +222,7 @@ ShadowMap::ShaderParameters ShadowMap::updateDirectional(FEngine& engine, mCamera->setCustomProjection(mat4(Mn * F * WLMp), znear, zfar); // for the debug camera, we need to undo the world origin - mDebugCamera->setCustomProjection(mat4(S * b * camera.worldTransform), znear, zfar); + mDebugCamera->setCustomProjection(mat4(S * b * camera.worldOrigin), znear, zfar); mHasVisibleShadows = true; @@ -301,7 +297,7 @@ ShadowMap::ShaderParameters ShadowMap::updateSpot(FEngine& engine, auto radius = lightData.elementAt(index).w; auto li = lightData.elementAt(index); const FLightManager::ShadowParams& params = lcm.getShadowParams(li); - const mat4f Mv = getDirectionalLightViewMatrix(direction, { 0, 1, 0 }, position); + const mat4f Mv = getDirectionalLightViewMatrix(direction, position); // We only keep this for reference. updateSceneInfoSpot() is quite expensive on large scenes // currently, and only needed to find a near/far. Instead, we just use a small near and the @@ -405,8 +401,7 @@ ShadowMap::DirectionalShadowBounds ShadowMap::computeDirectionalShadowBounds( // We compute the directional light's model matrix using the origin's as the light position. // The choice of the light's origin initially doesn't matter for a directional light. // This will be adjusted later because of how we compute the depth metric for VSM. - mat4f const MvAtOrigin = ShadowMap::getDirectionalLightViewMatrix(direction, - normalize(camera.worldTransform[0].xyz)); + mat4f const MvAtOrigin = ShadowMap::getDirectionalLightViewMatrix(direction); Aabb lsLightFrustumBounds = computeLightFrustumBounds( @@ -460,6 +455,10 @@ ShadowMap::DirectionalShadowBounds ShadowMap::computeDirectionalShadowBounds( std::max(lsLightFrustumBounds.min.z, sceneInfo.lsCastersNearFar[1]); } + // Now that we know the znear (-lsLightFrustumBounds.max.z), adjust the light's position such + // that znear = 0, this is only needed for VSM, but doesn't hurt PCF. + const mat4f Mv = getDirectionalLightViewMatrix(direction, direction * -lsLightFrustumBounds.max.z); + // near / far planes are specified relative to the direction the eye is looking at // i.e. the -z axis (see: ortho) const float znear = 0.0f; @@ -474,11 +473,6 @@ ShadowMap::DirectionalShadowBounds ShadowMap::computeDirectionalShadowBounds( v.z -= lsLightFrustumBounds.max.z; } - // Now that we know the znear (-lsLightFrustumBounds.max.z), adjust the light's position such - // that znear = 0, this is only needed for VSM, but doesn't hurt PCF. - const mat4f Mv = getDirectionalLightViewMatrix(direction, normalize(camera.worldTransform[0].xyz), - direction * -lsLightFrustumBounds.max.z); - return { Mv, znear, zfar, lsClippedShadowVolume, vertexCount, true }; } @@ -583,36 +577,65 @@ math::mat4f ShadowMap::computeLightRotation(math::float3 const& lsDirection) noe return L; } -math::float4 ShadowMap::computeFocusParams( - mat4f const& LMpMv, - mat4f const& WLMp, +math::mat4f ShadowMap::computeFocusMatrix( + const mat4f& LMpMv, const mat4f& WLMp, + Aabb const& wsShadowReceiversVolume, FrustumBoxIntersection const& lsShadowVolume, size_t vertexCount, filament::CameraInfo const& camera, float2 const& csNearFar, - float shadowFar, bool stable) noexcept { + uint16_t shadowDimension, bool stable) noexcept { + float2 s, o; + float4 wsViewVolumeBoundingSphere = {}; + if (stable) { - // In stable mode, the light frustum size must be fixed, so we choose the - // whole view frustum. - // We simply take the view volume bounding sphere, but we calculate it + // In stable mode, the light frustum size must be fixed, so we can choose either the + // whole view frustum, or the whole scene bounding volume. We simply pick whichever + // is smaller. + + // in stable mode we simply take the shadow receivers volume + const float4 shadowReceiverVolumeBoundingSphere = computeBoundingSphere( + wsShadowReceiversVolume.getCorners().data(), 8); + + // in stable mode we simply take the view volume bounding sphere, but we calculate it // in view space, so that it's perfectly stable. + mat4f const viewFromClip = inverse(camera.cullingProjection); + Corners const wsFrustumVertices = computeFrustumCorners(viewFromClip, csNearFar); + wsViewVolumeBoundingSphere = computeBoundingSphere(wsFrustumVertices.vertices, 8); - auto getViewVolumeBoundingSphere = [&]() { - if (shadowFar > 0) { - float4 const wsViewVolumeBoundingSphere = { camera.getPosition(), shadowFar }; - return wsViewVolumeBoundingSphere; - } else { - mat4f const viewFromClip = inverse(camera.cullingProjection); - Corners const wsFrustumVertices = computeFrustumCorners(viewFromClip, csNearFar); - float4 const wsViewVolumeBoundingSphere = - computeBoundingSphere(wsFrustumVertices.vertices, 8); - return wsViewVolumeBoundingSphere; - } - }; + if (shadowReceiverVolumeBoundingSphere.w < wsViewVolumeBoundingSphere.w) { + // When using the shadowReceiver volume, we don't have to use its enclosing sphere + // because (we assume) the scene volume doesn't change. Seen from the light it only + // changes when the light moves or rotates, and it is acceptable in that case to have + // non "stable" shadows (the shadow will never be stable when the light moves). + // + // On the other hand, when using the view volume, we must use a sphere because otherwise + // its projection's bounds in light space change with the camera, leading to unstable + // shadows with camera movement. - float4 const wsViewVolumeBoundingSphere = getViewVolumeBoundingSphere(); - s = 1.0f / wsViewVolumeBoundingSphere.w; - o = mat4f::project(LMpMv * camera.model, wsViewVolumeBoundingSphere.xyz).xy; - o = -s * o; + wsViewVolumeBoundingSphere.w = 0; + } + + if (wsViewVolumeBoundingSphere.w > 0) { + s = 1.0f / wsViewVolumeBoundingSphere.w; + o = mat4f::project(LMpMv * camera.model, wsViewVolumeBoundingSphere.xyz).xy; + } else { + // TODO: another options is the sphere around the intersections of receiver & casters + // FIXME: this is not stable with the global rotation because wsShadowReceiversVolume + // is not stable with it. + Aabb const bounds = compute2DBounds(LMpMv, + wsShadowReceiversVolume.getCorners().data(), + wsShadowReceiversVolume.getCorners().size()); + assert_invariant(bounds.min.x < bounds.max.x); + assert_invariant(bounds.min.y < bounds.max.y); + + s = 2.0f / float2(bounds.max.xy - bounds.min.xy); + o = float2(bounds.max.xy + bounds.min.xy) * 0.5f; + + // Quantize the scale in world-space units. This value can be very small because + // if it wasn't for floating-point imprecision, the scale would be a constant. + double2 const quantizer = 0.0625; + s = 1.0 / (ceil(1.0 / (s * quantizer)) * quantizer); + } } else { Aabb const bounds = compute2DBounds(WLMp, lsShadowVolume.data(), vertexCount); assert_invariant(bounds.min.x < bounds.max.x); @@ -620,11 +643,29 @@ math::float4 ShadowMap::computeFocusParams( s = 2.0f / float2(bounds.max.xy - bounds.min.xy); o = float2(bounds.max.xy + bounds.min.xy) * 0.5f; - o = -s * o; + + // TODO: we could quantize `s` here to give some stability when lispsm is disabled, + // however, the quantization paramater should probably be user settable. } - return { s, o }; + + // adjust offset for scale + o = -s * o; + + if (stable) { + snapLightFrustum(s, o, LMpMv, wsShadowReceiversVolume.center(), shadowDimension); + } + + const mat4f F(mat4f::row_major_init { + s.x, 0.0f, 0.0f, o.x, + 0.0f, s.y, 0.0f, o.y, + 0.0f, 0.0f, 1.0f, 0.0f, + 0.0f, 0.0f, 0.0f, 1.0f, + }); + + return F; } + // Apply these remapping in double to maintain a high precision for the depth axis ShadowMap::TextureCoordsMapping ShadowMap::getTextureCoordsMapping(ShadowMapInfo const& info, backend::Viewport const& viewport) noexcept { @@ -638,8 +679,6 @@ ShadowMap::TextureCoordsMapping ShadowMap::getTextureCoordsMapping(ShadowMapInfo 0.0f, 0.0f, 0.0f, 1.0f }}; - constexpr mat4f MtInverse = inverse(Mt); - // apply the viewport transform const float2 o = float2{ viewport.left, viewport.bottom } / float(info.atlasDimension); const float2 s = float2{ viewport.width, viewport.height } / float(info.atlasDimension); @@ -661,7 +700,7 @@ ShadowMap::TextureCoordsMapping ShadowMap::getTextureCoordsMapping(ShadowMapInfo }} : mat4f{}; // Compute shadow-map texture access and viewport transform - return { Mf * (Mv * Mt), MtInverse * (Mv * Mt) }; + return { Mf * (Mv * Mt), inverse(Mt) * (Mv * Mt) }; } mat4f ShadowMap::computeVsmLightSpaceMatrix(const mat4f& lightSpacePcf, @@ -802,8 +841,8 @@ ShadowMap::Corners ShadowMap::computeFrustumCorners( Aabb ShadowMap::computeLightFrustumBounds(mat4f const& lightView, Aabb const& wsShadowReceiversVolume, Aabb const& wsShadowCastersVolume, - ShadowMap::SceneInfo const& sceneInfo, - bool stable, bool focusShadowCasters, bool farUsesShadowCasters) noexcept { + ShadowMap::SceneInfo const& sceneInfo, bool stable, bool focusShadowCasters, + bool farUsesShadowCasters) noexcept { Aabb lsLightFrustumBounds{}; float const receiversFar = sceneInfo.lsReceiversNearFar[1]; @@ -834,13 +873,13 @@ Aabb ShadowMap::computeLightFrustumBounds(mat4f const& lightView, } void ShadowMap::snapLightFrustum(float2& s, float2& o, - double2 lsRef, int2 resolution) noexcept { + mat4f const& Mv, double3 wsSnapCoords, int2 resolution) noexcept { - auto proj2 = [](mat4 m, double2 v) -> double2 { - double2 p; - p.x = dot(double2{ m[0].x, m[1].x }, v) + m[3].x; - p.y = dot(double2{ m[0].y, m[1].y }, v) + m[3].y; - return p; + auto proj = [](mat4 m, double3 v) -> double3 { + // for directional light p.w == 1, exactly + auto p = m * v; + assert_invariant(p.w == 1.0); + return p.xyz; }; auto fract = [](auto v) { @@ -857,13 +896,13 @@ void ShadowMap::snapLightFrustum(float2& s, float2& o, }); // The (resolution * 0.5) comes from Mv having a NDC in the range -1,1 (so a range of 2). - // Another (resolution * 0.5) is there to snap on even texels, which helps with debugging + + // focused light-space + mat4 const FMv{ F * Mv }; // This offsets the texture coordinates, so it has a fixed offset w.r.t the world - // F * Mv * ref - - double2 const lsFocusedOrigin = proj2(F, lsRef); - double2 const d = fract(lsFocusedOrigin * (resolution * 0.25)) / (resolution * 0.25); + double2 const lsOrigin = proj(FMv, wsSnapCoords).xy; + double2 const d = (fract(lsOrigin * resolution * 0.5) * 2.0) / resolution; // adjust offset o -= d; diff --git a/filament/src/ShadowMap.h b/filament/src/ShadowMap.h index b1678503dd..3849710c3b 100644 --- a/filament/src/ShadowMap.h +++ b/filament/src/ShadowMap.h @@ -122,8 +122,8 @@ public: uint8_t visibleLayers; }; - static math::mat4f getDirectionalLightViewMatrix(math::float3 direction, math::float3 up, - math::float3 position = {}) noexcept; + static math::mat4f getDirectionalLightViewMatrix( + math::float3 direction, math::float3 position = {}) noexcept; static math::mat4f getPointLightViewMatrix(backend::TextureCubemapFace face, math::float3 position) noexcept; @@ -246,15 +246,16 @@ private: static inline math::mat4f computeLightRotation(math::float3 const& lsDirection) noexcept; - static inline math::float4 computeFocusParams( - math::mat4f const& LMpMv, - math::mat4f const& WLMp, + static inline math::mat4f computeFocusMatrix( + const math::mat4f& LMpMv, + const math::mat4f& WLMp, + Aabb const& wsShadowReceiversVolume, FrustumBoxIntersection const& lsShadowVolume, size_t vertexCount, filament::CameraInfo const& camera, math::float2 const& csNearFar, - float shadowFar, bool stable) noexcept; + uint16_t shadowDimension, bool stable) noexcept; static inline void snapLightFrustum(math::float2& s, math::float2& o, - math::double2 lsRef, math::int2 resolution) noexcept; + math::mat4f const& Mv, math::double3 wsSnapCoords, math::int2 resolution) noexcept; static inline Aabb computeLightFrustumBounds(const math::mat4f& lightView, Aabb const& wsShadowReceiversVolume, Aabb const& wsShadowCastersVolume, diff --git a/filament/src/ShadowMapManager.cpp b/filament/src/ShadowMapManager.cpp index 73a51b16c5..30880c96a9 100644 --- a/filament/src/ShadowMapManager.cpp +++ b/filament/src/ShadowMapManager.cpp @@ -527,8 +527,7 @@ ShadowMapManager::ShadowTechnique ShadowMapManager::updateCascadeShadowMaps(FEng // We compute the directional light's model matrix using the origin's as the light position. // The choice of the light's origin initially doesn't matter for a directional light. // This will be adjusted later because of how we compute the depth metric for VSM. - const mat4f MvAtOrigin = ShadowMap::getDirectionalLightViewMatrix(direction, - normalize(cameraInfo.worldTransform[0].xyz)); + const mat4f MvAtOrigin = ShadowMap::getDirectionalLightViewMatrix(direction); // Compute scene-dependent values shared across all cascades ShadowMap::updateSceneInfoDirectional(MvAtOrigin, *scene, sceneInfo); @@ -696,7 +695,7 @@ void ShadowMapManager::prepareSpotShadowMap(ShadowMap& shadowMap, const auto outerConeAngle = lcm.getSpotLightOuterCone(li); // compute shadow map frustum for culling - const mat4f Mv = ShadowMap::getDirectionalLightViewMatrix(direction, { 0, 1, 0 }, position); + const mat4f Mv = ShadowMap::getDirectionalLightViewMatrix(direction, position); const mat4f Mp = mat4f::perspective(outerConeAngle * f::RAD_TO_DEG * 2.0f, 1.0f, 0.01f, radius); const mat4f MpMv = math::highPrecisionMultiply(Mp, Mv); const Frustum frustum(MpMv); diff --git a/filament/src/details/Camera.cpp b/filament/src/details/Camera.cpp index 88bdb246e8..3ea6c5b3bc 100644 --- a/filament/src/details/Camera.cpp +++ b/filament/src/details/Camera.cpp @@ -266,8 +266,8 @@ CameraInfo::CameraInfo(FCamera const& camera) noexcept { d = std::max(zn, camera.getFocusDistance()); } -CameraInfo::CameraInfo(FCamera const& camera, math::mat4 const& inWorldTransform) noexcept { - const mat4 modelMatrix{ inWorldTransform * camera.getModelMatrix() }; +CameraInfo::CameraInfo(FCamera const& camera, const math::mat4& worldOriginCamera) noexcept { + const mat4 modelMatrix{ worldOriginCamera * camera.getModelMatrix() }; for (uint8_t i = 0; i < CONFIG_STEREOSCOPIC_EYES; i++) { eyeProjection[i] = mat4f{ camera.getProjectionMatrix(i) }; eyeFromView[i] = mat4f{ camera.getEyeFromViewMatrix(i) }; @@ -275,7 +275,7 @@ CameraInfo::CameraInfo(FCamera const& camera, math::mat4 const& inWorldTransform cullingProjection = mat4f{ camera.getCullingProjectionMatrix() }; model = mat4f{ modelMatrix }; view = mat4f{ inverse(modelMatrix) }; - worldTransform = inWorldTransform; + worldOrigin = worldOriginCamera; zn = (float)camera.getNear(); zf = (float)camera.getCullingFar(); ev100 = Exposure::ev100(camera); diff --git a/filament/src/details/Camera.h b/filament/src/details/Camera.h index bab9f11326..26e1297c56 100644 --- a/filament/src/details/Camera.h +++ b/filament/src/details/Camera.h @@ -223,7 +223,7 @@ private: struct CameraInfo { CameraInfo() noexcept {} explicit CameraInfo(FCamera const& camera) noexcept; - CameraInfo(FCamera const& camera, math::mat4 const& inWorldTransform) noexcept; + CameraInfo(FCamera const& camera, const math::mat4& worldOriginCamera) noexcept; union { // projection matrix for drawing (infinite zfar) @@ -239,7 +239,7 @@ struct CameraInfo { math::mat4f model; // camera model matrix math::mat4f view; // camera view matrix (inverse(model)) math::mat4f eyeFromView[CONFIG_STEREOSCOPIC_EYES]; // eye view matrix (only for stereoscopic) - math::mat4 worldTransform; // world transform (already applied + math::mat4 worldOrigin; // world origin transform (already applied // to model and view) math::float4 clipTransform{1, 1, 0, 0}; // clip-space transform, only for VERTEX_DOMAIN_DEVICE float zn{}; // distance (positive) to the near plane @@ -250,7 +250,7 @@ struct CameraInfo { float d{}; // focus distance [m] math::float3 const& getPosition() const noexcept { return model[3].xyz; } math::float3 getForwardVector() const noexcept { return normalize(-model[2].xyz); } - math::mat4 getUserViewMatrix() const noexcept { return view * worldTransform; } + math::mat4 getUserViewMatrix() const noexcept { return view * worldOrigin; } }; FILAMENT_DOWNCAST(Camera) diff --git a/filament/src/details/Scene.cpp b/filament/src/details/Scene.cpp index 2d2a99746b..bc2ce1d264 100644 --- a/filament/src/details/Scene.cpp +++ b/filament/src/details/Scene.cpp @@ -33,8 +33,6 @@ #include #include -#include - #include using namespace filament::backend; @@ -54,7 +52,7 @@ FScene::~FScene() noexcept = default; void FScene::prepare(utils::JobSystem& js, LinearAllocatorArena& allocator, - mat4 const& worldTransform, + const mat4& worldOriginTransform, bool shadowReceiversAreCasters) noexcept { // TODO: can we skip this in most cases? Since we rely on indices staying the same, // we could only skip, if nothing changed in the RCM. @@ -170,7 +168,7 @@ void FScene::prepare(utils::JobSystem& js, * Fill the SoA with the JobSystem */ - auto renderableWork = [first = renderableInstances.data(), &rcm, &tcm, &worldTransform, + auto renderableWork = [first = renderableInstances.data(), &rcm, &tcm, &worldOriginTransform, &sceneData, shadowReceiversAreCasters](auto* p, auto c) { SYSTRACE_NAME("renderableWork"); @@ -178,12 +176,12 @@ void FScene::prepare(utils::JobSystem& js, auto [ri, ti] = p[i]; // this is where we go from double to float for our transforms - const mat4f shaderWorldTransform{ - worldTransform * tcm.getWorldTransformAccurate(ti) }; - const bool reversedWindingOrder = det(shaderWorldTransform.upperLeft()) < 0; + const mat4f worldTransform{ + worldOriginTransform * tcm.getWorldTransformAccurate(ti) }; + const bool reversedWindingOrder = det(worldTransform.upperLeft()) < 0; // compute the world AABB so we can perform culling - const Box worldAABB = rigidTransform(rcm.getAABB(ri), shaderWorldTransform); + const Box worldAABB = rigidTransform(rcm.getAABB(ri), worldTransform); auto visibility = rcm.getVisibility(ri); visibility.reversedWindingOrder = reversedWindingOrder; @@ -201,7 +199,7 @@ void FScene::prepare(utils::JobSystem& js, assert_invariant(index < sceneData.size()); sceneData.elementAt(index) = ri; - sceneData.elementAt(index) = shaderWorldTransform; + sceneData.elementAt(index) = worldTransform; sceneData.elementAt(index) = visibility; sceneData.elementAt(index) = rcm.getSkinningBufferInfo(ri); sceneData.elementAt(index) = rcm.getMorphingBufferInfo(ri); @@ -218,20 +216,19 @@ void FScene::prepare(utils::JobSystem& js, } }; - auto lightWork = [first = lightInstances.data(), &lcm, &tcm, &worldTransform, + auto lightWork = [first = lightInstances.data(), &lcm, &tcm, &worldOriginTransform, &lightData](auto* p, auto c) { SYSTRACE_NAME("lightWork"); for (size_t i = 0; i < c; i++) { auto [li, ti] = p[i]; // this is where we go from double to float for our transforms - mat4f const shaderWorldTransform{ - worldTransform * tcm.getWorldTransformAccurate(ti) }; - float4 const position = shaderWorldTransform * float4{ lcm.getLocalPosition(li), 1 }; + const mat4f worldTransform{ worldOriginTransform * tcm.getWorldTransformAccurate(ti) }; + const float4 position = worldTransform * float4{ lcm.getLocalPosition(li), 1 }; float3 d = 0; if (!lcm.isPointLight(li) || lcm.isIESLight(li)) { d = lcm.getLocalDirection(li); // using mat3f::getTransformForNormals handles non-uniform scaling - d = normalize(mat3f::getTransformForNormals(shaderWorldTransform.upperLeft()) * d); + d = normalize(mat3f::getTransformForNormals(worldTransform.upperLeft()) * d); } size_t const index = DIRECTIONAL_LIGHTS_COUNT + std::distance(first, p) + i; assert_invariant(index < lightData.size()); @@ -264,43 +261,14 @@ void FScene::prepare(utils::JobSystem& js, */ if (auto [li, ti] = directionalLightInstances ; li) { - // in the code below, we only transform directions, so the translation of the - // world transform is irrelevant, and we don't need to use getWorldTransformAccurate() - - FLightManager::ShadowParams const params = lcm.getShadowParams(li); - float3 const localDirection = lcm.getLocalDirection(li); - float3 const shadowLocalDirection = params.options.transform * localDirection; - mat3 const worldDirectionTransform = tcm.getWorldTransformAccurate(ti).upperLeft(); - mat3 const shaderWorldTransform = worldTransform.upperLeft() * worldDirectionTransform; - - // using mat3::getTransformForNormals handles non-uniform scaling - // note: in the common case of the rigid-body transform, getTransformForNormals() returns - // identity. - mat3 const worlTransformNormals = mat3::getTransformForNormals(shaderWorldTransform); - double3 const d = worlTransformNormals * localDirection; - double3 const s = worlTransformNormals * shadowLocalDirection; - - // We compute the reference point for snapping shadowmaps without applying the - // rotation of `worldOriginTransform` on both sides, so that we don't have any instability - // due to the limited precision of the "light space" matrix (even at double precision). - - // getMv() Returns the world-to-lightspace transformation. See ShadowMap.cpp. - auto getMv = [](double3 direction) -> mat3 { - // We use the x-axis as the "up" reference so that the math is stable when the light - // is pointing down, which is a common case for lights. See ShadowMap.cpp. - return transpose(mat3::lookTo(direction, double3{ 1, 0, 0 })); - }; - double3 const worldDirection = - mat3::getTransformForNormals(worldDirectionTransform) * shadowLocalDirection; - double3 const worldOrigin = transpose(worldTransform.upperLeft()) * worldTransform[3].xyz; - mat3 const Mv = getMv(worldDirection); - double2 const lsReferencePoint = (Mv * worldOrigin).xy; - + const mat4f worldTransform{ + worldOriginTransform * tcm.getWorldTransformAccurate(ti) }; + // using mat3f::getTransformForNormals handles non-uniform scaling + float3 d = lcm.getLocalDirection(li); + d = normalize(mat3f::getTransformForNormals(worldTransform.upperLeft()) * d); constexpr float inf = std::numeric_limits::infinity(); lightData.elementAt(0) = float4{ 0, 0, 0, inf }; - lightData.elementAt(0) = normalize(d); - lightData.elementAt(0) = normalize(s); - lightData.elementAt(0) = lsReferencePoint; + lightData.elementAt(0) = d; lightData.elementAt(0) = li; } else { lightData.elementAt(0) = 0; diff --git a/filament/src/details/Scene.h b/filament/src/details/Scene.h index 1882bb4dc3..9009861762 100644 --- a/filament/src/details/Scene.h +++ b/filament/src/details/Scene.h @@ -71,7 +71,7 @@ public: void terminate(FEngine& engine); void prepare(utils::JobSystem& js, LinearAllocatorArena& allocator, - math::mat4 const& worldTransform, bool shadowReceiversAreCasters) noexcept; + math::mat4 const& worldOriginTransform, bool shadowReceiversAreCasters) noexcept; void prepareVisibleRenderables(utils::Range visibleRenderables) noexcept; @@ -162,8 +162,6 @@ public: enum { POSITION_RADIUS, DIRECTION, - SHADOW_DIRECTION, - SHADOW_REF, LIGHT_INSTANCE, VISIBILITY, SCREEN_SPACE_Z_RANGE, @@ -173,8 +171,6 @@ public: using LightSoa = utils::StructureOfArrays< math::float4, math::float3, - math::float3, - math::double2, FLightManager::Instance, Culler::result_type, math::float2, diff --git a/filament/src/details/View.cpp b/filament/src/details/View.cpp index b02d50c5c7..271c39a710 100644 --- a/filament/src/details/View.cpp +++ b/filament/src/details/View.cpp @@ -397,8 +397,8 @@ CameraInfo FView::computeCameraInfo(FEngine& engine) const noexcept { * The "world origin" is also used to keep the origin close to the camera position to * improve fp precision in the shader for large scenes. */ - double3 translation; - mat3 rotation; + mat4 translation; + mat4 rotation; /* * Calculate all camera parameters needed to render this View for this frame. @@ -409,18 +409,16 @@ CameraInfo FView::computeCameraInfo(FEngine& engine) const noexcept { // view-space, which improves floating point precision in the shader by staying around // zero, where fp precision is highest. This also ensures that when the camera is placed // very far from the origin, objects are still rendered and lit properly. - translation = -camera->getPosition(); + translation = mat4::translation( -camera->getPosition() ); } FIndirectLight const* const ibl = scene->getIndirectLight(); if (ibl) { // the IBL transformation must be a rigid transform - rotation = mat3{ transpose(scene->getIndirectLight()->getRotation()) }; - // it is important to orthogonalize the matrix when converting it to doubles, because - // as float, it only has about a 1e-8 precision on the size of the basis vectors - rotation = orthogonalize(rotation); + rotation = mat4{ transpose(scene->getIndirectLight()->getRotation()) }; } - return { *camera, mat4{ rotation } * mat4::translation(translation) }; + + return { *camera, rotation * translation }; } void FView::prepare(FEngine& engine, DriverApi& driver, ArenaScope& arena, @@ -448,7 +446,7 @@ void FView::prepare(FEngine& engine, DriverApi& driver, ArenaScope& arena, // intent of the code, which is that we should only depend on CameraInfo here. // This is an extremely uncommon case. const mat4 projection = mCullingCamera->getCullingProjectionMatrix(); - const mat4 view = inverse(cameraInfo.worldTransform * mCullingCamera->getModelMatrix()); + const mat4 view = inverse(cameraInfo.worldOrigin * mCullingCamera->getModelMatrix()); return Frustum{ mat4f{ projection * view }}; } }; @@ -461,9 +459,7 @@ void FView::prepare(FEngine& engine, DriverApi& driver, ArenaScope& arena, * Gather all information needed to render this scene. Apply the world origin to all * objects in the scene. */ - scene->prepare(js, arena.getAllocator(), - cameraInfo.worldTransform, - hasVSM()); + scene->prepare(js, arena.getAllocator(), cameraInfo.worldOrigin, hasVSM()); /* * Light culling: runs in parallel with Renderable culling (below) diff --git a/filament/test/filament_test.cpp b/filament/test/filament_test.cpp index 5f47b0abee..8244197fd5 100644 --- a/filament/test/filament_test.cpp +++ b/filament/test/filament_test.cpp @@ -702,8 +702,8 @@ TEST(FilamentTest, FroxelData) { LightManager::Instance instance = engine->getLightManager().getInstance(e); FScene::LightSoa lights; - lights.push_back({}, {}, {}, {}, {}, {}, {}, {}); // first one is always skipped - lights.push_back(float4{ 0, 0, -5, 1 }, {}, {}, {}, instance, 1, {}, {}); + lights.push_back({}, {}, {}, {}, {}, {}); // first one is always skipped + lights.push_back(float4{ 0, 0, -5, 1 }, {}, instance, 1, {}, {}); { froxelData.froxelizeLights(*engine, {}, lights); diff --git a/libs/math/include/math/mat3.h b/libs/math/include/math/mat3.h index 5ad06bdf4b..8ebb93df43 100644 --- a/libs/math/include/math/mat3.h +++ b/libs/math/include/math/mat3.h @@ -289,14 +289,6 @@ public: return matrix::cof(m); } - /* - * Returns a matrix representing the pose of a virtual camera looking towards -Z in its - * local Y-up coordinate system. "up" defines where the Y axis of the camera's local coordinate - * system is. - */ - template - static TMat33 lookTo(const TVec3& direction, const TVec3& up) noexcept; - /** * Packs the tangent frame represented by the specified matrix into a quaternion. * Reflection is preserved by encoding it as the sign of the w component in the @@ -414,29 +406,6 @@ constexpr TMat33::TMat33(const TQuaternion& q) noexcept : m_value{} { m_value[2] = col_type(xz + yw, yz - xw, 1 - xx - yy); // NOLINT } -template -constexpr T dot_tolerance() noexcept; - -template<> -constexpr float dot_tolerance() noexcept { return 0.999f; } - -template<> -constexpr double dot_tolerance() noexcept { return 0.9999; } - -template -template -TMat33 TMat33::lookTo(const TVec3& direction, const TVec3& up) noexcept { - auto const z_axis = direction; - auto norm_up = up; - if (std::abs(dot(z_axis, norm_up)) > dot_tolerance< arithmetic_result_t >()) { - // Fix up vector if we're degenerate (looking straight up, basically) - norm_up = { norm_up.z, norm_up.x, norm_up.y }; - } - auto const x_axis = normalize(cross(z_axis, norm_up)); - auto const y_axis = cross(x_axis, z_axis); - return { x_axis, y_axis, -z_axis }; -} - //------------------------------------------------------------------------------ template constexpr TQuaternion TMat33::packTangentFrame(const TMat33& m, size_t storageSize) noexcept { diff --git a/libs/math/include/math/mat4.h b/libs/math/include/math/mat4.h index 570585397c..d44081b264 100644 --- a/libs/math/include/math/mat4.h +++ b/libs/math/include/math/mat4.h @@ -285,9 +285,6 @@ public: template static TMat44 lookAt(const TVec3& eye, const TVec3& center, const TVec3& up) noexcept; - template - static TMat44 lookTo(const TVec3& direction, const TVec3& position, const TVec3& up) noexcept; - template static constexpr TVec3 project(const TMat44& projectionMatrix, TVec3 vertice) noexcept{ TVec4 r = projectionMatrix * TVec4{ vertice, 1 }; @@ -520,19 +517,19 @@ template template TMat44 TMat44::lookAt(const TVec3& eye, const TVec3& center, const TVec3& up) noexcept { - return lookTo(normalize(center - eye), eye, normalize(up)); -} - -template -template -TMat44 TMat44::lookTo(const TVec3& direction, const TVec3& position, - const TVec3& up) noexcept { - auto r = TMat33::lookTo(direction, up); - return TMat44{ - TVec4{ r[0], 0 }, - TVec4{ r[1], 0 }, - TVec4{ r[2], 0 }, - TVec4{ position, 1 } }; + TVec3 z_axis(normalize(center - eye)); + TVec3 norm_up(normalize(up)); + if (std::abs(dot(z_axis, norm_up)) > T(0.999)) { + // Fix up vector if we're degenerate (looking straight up, basically) + norm_up = { norm_up.z, norm_up.x, norm_up.y }; + } + TVec3 x_axis(normalize(cross(z_axis, norm_up))); + TVec3 y_axis(cross(x_axis, z_axis)); + return TMat44( + TVec4(x_axis, 0), + TVec4(y_axis, 0), + TVec4(-z_axis, 0), + TVec4(eye, 1)); } // ---------------------------------------------------------------------------------------- diff --git a/libs/math/tests/test_mat.cpp b/libs/math/tests/test_mat.cpp index 047a5629cd..d53e73a916 100644 --- a/libs/math/tests/test_mat.cpp +++ b/libs/math/tests/test_mat.cpp @@ -32,71 +32,6 @@ class MatTest : public testing::Test { protected: }; -//------------------------------------------------------------------------------ -// A macro to help with vector comparisons within floating point range. -#define EXPECT_VEC_EQ(VEC1, VEC2) \ -do { \ - const decltype(VEC1) v1 = VEC1; \ - const decltype(VEC2) v2 = VEC2; \ - if (std::is_same::value) { \ - for (int i = 0; i < v1.size(); ++i) { \ - EXPECT_FLOAT_EQ(v1[i], v2[i]); \ - } \ - } else if (std::is_same::value) { \ - for (int i = 0; i < v1.size(); ++i) { \ - EXPECT_DOUBLE_EQ(v1[i], v2[i]); \ - } \ - } else { \ - for (int i = 0; i < v1.size(); ++i) { \ - EXPECT_EQ(v1[i], v2[i]); \ - } \ - } \ -} while(0) - -//------------------------------------------------------------------------------ -// A macro to help with vector comparisons within a range. -#define EXPECT_VEC_NEAR(VEC1, VEC2, eps) \ -do { \ - const decltype(VEC1) v1 = VEC1; \ - const decltype(VEC2) v2 = VEC2; \ - for (int i = 0; i < v1.size(); ++i) { \ - EXPECT_NEAR(v1[i], v2[i], eps); \ - } \ -} while(0) - - -//------------------------------------------------------------------------------ -// A macro to help with type comparisons within floating point range. -#define ASSERT_TYPE_EQ(T1, T2) \ -do { \ - const decltype(T1) t1 = T1; \ - const decltype(T2) t2 = T2; \ - if (std::is_same::value) { \ - ASSERT_FLOAT_EQ(t1, t2); \ - } else if (std::is_same::value) { \ - ASSERT_DOUBLE_EQ(t1, t2); \ - } else { \ - ASSERT_EQ(t1, t2); \ - } \ -} while(0) - - - -TEST_F(MatTest, LargeFloatRotationsWithOrthogonalization) { - double3 const t = { 2304097.1410110965, -4688442.9915525438, -3639452.5611694567 }; - mat4 const T = mat4::translation(t); - for (float d = 0; d < 90; d = d + 1.0) { - mat3f const R = mat3f::rotation(d * f::DEG_TO_RAD, float3{ 0, 1, 0 }); - mat3 RR = orthogonalize(mat3{ R }); - ASSERT_NEAR(dot(RR[0], RR[0]), 1.0, 1e-12); - ASSERT_NEAR(dot(RR[1], RR[1]), 1.0, 1e-12); - ASSERT_NEAR(dot(RR[2], RR[2]), 1.0, 1e-12); - mat4 M = mat4{ RR } * T; - double3 const t2 = transpose(M.upperLeft()) * M[3].xyz; - EXPECT_VEC_NEAR(t, t2, 0.0001); // 0.1mm - } -} - TEST_F(MatTest, ConstexprMat2) { constexpr float a = F_PI; constexpr mat2f M; @@ -617,6 +552,53 @@ TYPED_TEST(MatTestT, Inverse2) { TEST_MATRIX_INVERSE(m4, 20.0 * std::numeric_limits::epsilon()); } +//------------------------------------------------------------------------------ +// A macro to help with vector comparisons within floating point range. +#define EXPECT_VEC_EQ(VEC1, VEC2) \ +do { \ + const decltype(VEC1) v1 = VEC1; \ + const decltype(VEC2) v2 = VEC2; \ + if (std::is_same::value) { \ + for (int i = 0; i < v1.size(); ++i) { \ + EXPECT_FLOAT_EQ(v1[i], v2[i]); \ + } \ + } else if (std::is_same::value) { \ + for (int i = 0; i < v1.size(); ++i) { \ + EXPECT_DOUBLE_EQ(v1[i], v2[i]); \ + } \ + } else { \ + for (int i = 0; i < v1.size(); ++i) { \ + EXPECT_EQ(v1[i], v2[i]); \ + } \ + } \ +} while(0) + +//------------------------------------------------------------------------------ +// A macro to help with vector comparisons within a range. +#define EXPECT_VEC_NEAR(VEC1, VEC2, eps) \ +do { \ + const decltype(VEC1) v1 = VEC1; \ + const decltype(VEC2) v2 = VEC2; \ + for (int i = 0; i < v1.size(); ++i) { \ + EXPECT_NEAR(v1[i], v2[i], eps); \ + } \ +} while(0) + + +//------------------------------------------------------------------------------ +// A macro to help with type comparisons within floating point range. +#define ASSERT_TYPE_EQ(T1, T2) \ +do { \ + const decltype(T1) t1 = T1; \ + const decltype(T2) t2 = T2; \ + if (std::is_same::value) { \ + ASSERT_FLOAT_EQ(t1, t2); \ + } else if (std::is_same::value) { \ + ASSERT_DOUBLE_EQ(t1, t2); \ + } else { \ + ASSERT_EQ(t1, t2); \ + } \ +} while(0) TYPED_TEST(MatTestT, NormalsNegativeScale) { typedef filament::math::details::TMat33 M33T; diff --git a/samples/gltf_viewer.cpp b/samples/gltf_viewer.cpp index cd46e0d76c..1fb250897e 100644 --- a/samples/gltf_viewer.cpp +++ b/samples/gltf_viewer.cpp @@ -101,7 +101,7 @@ struct App { bool actualSize = false; bool originIsFarAway = false; - float originDistance = 1.0f; + float originDistance = 6378137; // Earth's radius in [m] struct Scene { Entity groundPlane; @@ -762,7 +762,7 @@ int main(int argc, char** argv) { ImGui::Checkbox("Camera at origin", debug.getPropertyAddress("d.view.camera_at_origin")); ImGui::Checkbox("Far Origin", &app.originIsFarAway); - ImGui::SliderFloat("Origin", &app.originDistance, 0, 1); + ImGui::SliderFloat("Origin", &app.originDistance, 0, 10000000); ImGui::Checkbox("Far uses shadow casters", debug.getPropertyAddress("d.shadowmap.far_uses_shadowcasters")); ImGui::Checkbox("Focus shadow casters", @@ -981,12 +981,7 @@ int main(int argc, char** argv) { tcm.setParent(tcm.getInstance(camera.getEntity()), root); tcm.setParent(tcm.getInstance(app.asset->getRoot()), root); tcm.setParent(tcm.getInstance(view->getFogEntity()), root); - - // these values represent a point somewhere on Earth's surface - float const d = app.originIsFarAway ? app.originDistance : 0.0f; -// tcm.setTransform(root, mat4::translation(double3{ 67.0, -6366759.0, -21552.0 } * d)); - tcm.setTransform(root, mat4::translation( - double3{ 2304097.1410110965, -4688442.9915525438, -3639452.5611694567 } * d)); + tcm.setTransform(root, mat4f::translation(float3{ app.originIsFarAway ? app.originDistance : 0.0f })); // Check if color grading has changed. ColorGradingSettings const& options = app.viewer->getSettings().view.colorGrading; From 79116905aa4213a9f138ad8560d820f9401df135 Mon Sep 17 00:00:00 2001 From: Ben Doherty Date: Wed, 8 Nov 2023 12:38:23 -0800 Subject: [PATCH 63/63] Fix mix-precision quaternion conversions (#7339) --- libs/math/include/math/TQuatHelpers.h | 18 ++--- libs/math/tests/test_quat.cpp | 101 ++++++++++++++++++++++++++ 2 files changed, 110 insertions(+), 9 deletions(-) diff --git a/libs/math/include/math/TQuatHelpers.h b/libs/math/include/math/TQuatHelpers.h index e2a89dc0d7..0439b9712b 100644 --- a/libs/math/include/math/TQuatHelpers.h +++ b/libs/math/include/math/TQuatHelpers.h @@ -58,7 +58,7 @@ public: /* compound assignment products by a scalar */ - template + template>> constexpr QUATERNION& operator*=(U v) { QUATERNION& lhs = static_cast&>(*this); for (size_t i = 0; i < QUATERNION::size(); i++) { @@ -67,7 +67,7 @@ public: return lhs; } - template + template>> constexpr QUATERNION& operator/=(U v) { QUATERNION& lhs = static_cast&>(*this); for (size_t i = 0; i < QUATERNION::size(); i++) { @@ -125,25 +125,25 @@ public: * q.w*r.z + q.x*r.y - q.y*r.x + q.z*r.w); * */ - template + template>> friend inline constexpr QUATERNION> MATH_PURE operator*(QUATERNION q, U scalar) { // don't pass q by reference because we need a copy anyway - return q *= scalar; + return QUATERNION>(q *= scalar); } - template + template>> friend inline constexpr - QUATERNION> MATH_PURE operator*(T scalar, QUATERNION q) { + QUATERNION> MATH_PURE operator*(U scalar, QUATERNION q) { // don't pass q by reference because we need a copy anyway - return q *= scalar; + return QUATERNION>(q *= scalar); } - template + template>> friend inline constexpr QUATERNION> MATH_PURE operator/(QUATERNION q, U scalar) { // don't pass q by reference because we need a copy anyway - return q /= scalar; + return QUATERNION>(q /= scalar); } }; diff --git a/libs/math/tests/test_quat.cpp b/libs/math/tests/test_quat.cpp index 6a5849875e..044a272a7e 100644 --- a/libs/math/tests/test_quat.cpp +++ b/libs/math/tests/test_quat.cpp @@ -17,6 +17,7 @@ #include #include #include +#include #include @@ -324,3 +325,103 @@ TEST_F(QuatTest, NaN) { EXPECT_NEAR(qs[2], 0.5, 0.1); EXPECT_NEAR(qs[3], 0.5, 0.1); } + +TEST_F(QuatTest, Conversions) { + quat qd; + quatf qf; + float3 vf; + double3 vd; + double d = 0.0; + float f = 0.0f; + + static_assert(std::is_same, float>::value); + static_assert(std::is_same, double>::value); + static_assert(std::is_same, double>::value); + static_assert(std::is_same, double>::value); + + { + auto r1 = qd * d; + auto r2 = qd * f; + auto r3 = qf * d; + auto r4 = qf * f; + static_assert(std::is_same::value); + static_assert(std::is_same::value); + static_assert(std::is_same::value); + static_assert(std::is_same::value); + } + { + auto r1 = qd / d; + auto r2 = qd / f; + auto r3 = qf / d; + auto r4 = qf / f; + static_assert(std::is_same::value); + static_assert(std::is_same::value); + static_assert(std::is_same::value); + static_assert(std::is_same::value); + } + { + auto r1 = d * qd; + auto r2 = f * qd; + auto r3 = d * qf; + auto r4 = f * qf; + static_assert(std::is_same::value); + static_assert(std::is_same::value); + static_assert(std::is_same::value); + static_assert(std::is_same::value); + } + { + auto r1 = qd * vd; + auto r2 = qf * vd; + auto r3 = qd * vf; + auto r4 = qf * vf; + static_assert(std::is_same::value); + static_assert(std::is_same::value); + static_assert(std::is_same::value); + static_assert(std::is_same::value); + } + { + auto r1 = qd * qd; + auto r2 = qf * qd; + auto r3 = qd * qf; + auto r4 = qf * qf; + static_assert(std::is_same::value); + static_assert(std::is_same::value); + static_assert(std::is_same::value); + static_assert(std::is_same::value); + } + { + auto r1 = dot(qd, qd); + auto r2 = dot(qf, qd); + auto r3 = dot(qd, qf); + auto r4 = dot(qf, qf); + static_assert(std::is_same::value); + static_assert(std::is_same::value); + static_assert(std::is_same::value); + static_assert(std::is_same::value); + } + { + auto r1 = cross(qd, qd); + auto r2 = cross(qf, qd); + auto r3 = cross(qd, qf); + auto r4 = cross(qf, qf); + static_assert(std::is_same::value); + static_assert(std::is_same::value); + static_assert(std::is_same::value); + static_assert(std::is_same::value); + } +} + +template +struct has_divide_assign : std::false_type {}; + +template +struct has_divide_assign() /= std::declval(), void())> : std::true_type {}; + +// Static assertions to validate the availability of the /= operator for specific type +// combinations. The first static_assert checks that the quat does not have a /= operator with Foo. +// This ensures that quat does not provide an inappropriate overload that could be erroneously +// selected. +struct Foo {}; +static_assert(!has_divide_assign::value); +static_assert(has_divide_assign::value);