From 06e8b1d6890c096eb19de9f8c1c41fc8316b3967 Mon Sep 17 00:00:00 2001 From: Ben Doherty Date: Thu, 9 Nov 2023 15:34:50 -0800 Subject: [PATCH 01/23] Support up to 4 side-by-side stereoscopic eyes (#7328) --- NEW_RELEASE_NOTES.md | 2 + filament/backend/src/opengl/OpenGLContext.cpp | 1 + filament/backend/src/opengl/gl_headers.h | 2 + filament/include/filament/Camera.h | 17 ++++--- filament/include/filament/Engine.h | 8 +++ filament/src/PerViewUniforms.cpp | 3 +- filament/src/RenderPass.cpp | 23 +++++---- filament/src/RenderPass.h | 6 ++- filament/src/details/Camera.cpp | 32 ++++++++---- filament/src/details/Camera.h | 30 ++++++------ filament/src/details/Engine.cpp | 3 ++ filament/src/details/Material.cpp | 3 ++ filament/src/fg/FrameGraph.h | 2 +- .../include/private/filament/EngineEnums.h | 10 ++-- .../include/private/filament/UibStructs.h | 4 +- libs/filamat/src/GLSLPostProcessor.cpp | 2 +- libs/filamat/src/shaders/CodeGenerator.cpp | 7 ++- libs/filamat/src/shaders/UibGenerator.cpp | 2 +- libs/filamentapp/include/filamentapp/Config.h | 1 + .../include/filamentapp/FilamentApp.h | 4 ++ libs/filamentapp/src/FilamentApp.cpp | 27 ++++++++-- samples/gltf_viewer.cpp | 49 +++++++++++++------ shaders/src/common_getters.glsl | 2 +- shaders/src/main.vs | 37 +++++++------- shaders/src/varyings.glsl | 2 +- 25 files changed, 189 insertions(+), 90 deletions(-) diff --git a/NEW_RELEASE_NOTES.md b/NEW_RELEASE_NOTES.md index c3161a7700b..ec967554ed3 100644 --- a/NEW_RELEASE_NOTES.md +++ b/NEW_RELEASE_NOTES.md @@ -8,3 +8,5 @@ appropriate header in [RELEASE_NOTES.md](./RELEASE_NOTES.md). ## Release notes for next branch cut +- engine: Support up to 4 side-by-side stereoscopic eyes, configurable at Engine creation time. See + `Engine::Config::stereoscopicEyeCount`. [⚠️ **Recompile Materials**] diff --git a/filament/backend/src/opengl/OpenGLContext.cpp b/filament/backend/src/opengl/OpenGLContext.cpp index b183cd91d57..71305fffe33 100644 --- a/filament/backend/src/opengl/OpenGLContext.cpp +++ b/filament/backend/src/opengl/OpenGLContext.cpp @@ -284,6 +284,7 @@ void OpenGLContext::setDefaultState() noexcept { if (ext.EXT_clip_cull_distance) { glEnable(GL_CLIP_DISTANCE0); + glEnable(GL_CLIP_DISTANCE1); } } diff --git a/filament/backend/src/opengl/gl_headers.h b/filament/backend/src/opengl/gl_headers.h index c934cb7d837..2491e742f08 100644 --- a/filament/backend/src/opengl/gl_headers.h +++ b/filament/backend/src/opengl/gl_headers.h @@ -190,8 +190,10 @@ using namespace glext; #if defined(GL_EXT_clip_cull_distance) # define GL_CLIP_DISTANCE0 GL_CLIP_DISTANCE0_EXT +# define GL_CLIP_DISTANCE1 GL_CLIP_DISTANCE1_EXT #else # define GL_CLIP_DISTANCE0 0x3000 +# define GL_CLIP_DISTANCE1 0x3001 #endif #if defined(GL_KHR_debug) diff --git a/filament/include/filament/Camera.h b/filament/include/filament/Camera.h index ab06a413b58..2d8e6310fc9 100644 --- a/filament/include/filament/Camera.h +++ b/filament/include/filament/Camera.h @@ -286,17 +286,20 @@ class UTILS_PUBLIC Camera : public FilamentAPI { /** Sets a custom projection matrix for each eye. * * The projectionForCulling, near, and far parameters establish a "culling frustum" which must - * encompass anything either eye can see. + * encompass anything any eye can see. All projection matrices must be set simultaneously. The + * number of stereoscopic eyes is controlled by the stereoscopicEyeCount setting inside of + * Engine::Config. * - * @param projection an array of projection matrices, only the first - * CONFIG_STEREOSCOPIC_EYES (2) are read + * @param projection an array of projection matrices, only the first config.stereoscopicEyeCount + * are read * @param count size of the projection matrix array to set, must be - * >= CONFIG_STEREOSCOPIC_EYES (2) + * >= config.stereoscopicEyeCount * @param projectionForCulling custom projection matrix for culling, must encompass both eyes * @param near distance in world units from the camera to the culling near plane. \p near > 0. * @param far distance in world units from the camera to the culling far plane. \p far > \p * near. * @see setCustomProjection + * @see Engine::Config::stereoscopicEyeCount */ void setCustomEyeProjection(math::mat4 const* projection, size_t count, math::mat4 const& projectionForCulling, double near, double far); @@ -357,8 +360,8 @@ class UTILS_PUBLIC Camera : public FilamentAPI { * The projection matrix used for rendering always has its far plane set to infinity. This * is why it may differ from the matrix set through setProjection() or setLensProjection(). * - * @param eyeId the index of the eye to return the projection matrix for, must be < - * CONFIG_STEREOSCOPIC_EYES (2) + * @param eyeId the index of the eye to return the projection matrix for, must be + * < config.stereoscopicEyeCount * @return The projection matrix used for rendering * * @see setProjection, setLensProjection, setCustomProjection, getCullingProjectionMatrix, @@ -416,7 +419,7 @@ class UTILS_PUBLIC Camera : public FilamentAPI { * This method is not intended to be called every frame. Instead, to update the position of the * head, use Camera::setModelMatrix. * - * @param eyeId the index of the eye to set, must be < CONFIG_STEREOSCOPIC_EYES (2) + * @param eyeId the index of the eye to set, must be < config.stereoscopicEyeCount * @param model the model matrix for an individual eye */ void setEyeModelMatrix(uint8_t eyeId, math::mat4 const& model); diff --git a/filament/include/filament/Engine.h b/filament/include/filament/Engine.h index 2ab2e20d265..2442e6c2554 100644 --- a/filament/include/filament/Engine.h +++ b/filament/include/filament/Engine.h @@ -291,6 +291,14 @@ class UTILS_PUBLIC Engine { * Currently only respected by the Metal backend. */ size_t textureUseAfterFreePoolSize = 0; + + /* + * The number of eyes to render when stereoscopic rendering is enabled. Supported values are + * between 1 and CONFIG_MAX_STEREOSCOPIC_EYES (inclusive). + * + * @see View::setStereoscopicOptions + */ + uint8_t stereoscopicEyeCount = 2; }; diff --git a/filament/src/PerViewUniforms.cpp b/filament/src/PerViewUniforms.cpp index 07a2fa8202a..03d1898567e 100644 --- a/filament/src/PerViewUniforms.cpp +++ b/filament/src/PerViewUniforms.cpp @@ -82,7 +82,8 @@ void PerViewUniforms::prepareCamera(FEngine& engine, const CameraInfo& camera) n s.nearOverFarMinusNear = camera.zn / (camera.zf - camera.zn); mat4f const& headFromWorld = camera.view; - for (uint8_t i = 0; i < CONFIG_STEREOSCOPIC_EYES; i++) { + Engine::Config const& config = engine.getConfig(); + for (int i = 0; i < config.stereoscopicEyeCount; i++) { mat4f const& eyeFromHead = camera.eyeFromView[i]; // identity for monoscopic rendering mat4f const& clipFromEye = camera.eyeProjection[i]; // clipFromEye * eyeFromHead * headFromWorld diff --git a/filament/src/RenderPass.cpp b/filament/src/RenderPass.cpp index b602e52786a..30aff5ac0ff 100644 --- a/filament/src/RenderPass.cpp +++ b/filament/src/RenderPass.cpp @@ -116,14 +116,17 @@ void RenderPass::appendCommands(FEngine& engine, CommandTypeFlags const commandT commandCount += 1; // for the sentinel Command* const curr = append(commandCount); + auto stereoscopicEyeCount = + renderFlags & IS_STEREOSCOPIC ? engine.getConfig().stereoscopicEyeCount : 1; + const float3 cameraPosition(mCameraPosition); const float3 cameraForwardVector(mCameraForwardVector); auto work = [commandTypeFlags, curr, &soa, variant, renderFlags, visibilityMask, cameraPosition, - cameraForwardVector] + cameraForwardVector, stereoscopicEyeCount] (uint32_t startIndex, uint32_t indexCount) { RenderPass::generateCommands(commandTypeFlags, curr, soa, { startIndex, startIndex + indexCount }, variant, renderFlags, visibilityMask, - cameraPosition, cameraForwardVector); + cameraPosition, cameraForwardVector, stereoscopicEyeCount); }; if (vr.size() <= JOBS_PARALLEL_FOR_COMMANDS_COUNT) { @@ -374,8 +377,8 @@ UTILS_NOINLINE void RenderPass::generateCommands(uint32_t commandTypeFlags, Command* const commands, FScene::RenderableSoa const& soa, Range range, Variant variant, RenderFlags renderFlags, - FScene::VisibleMaskType visibilityMask, - float3 cameraPosition, float3 cameraForward) noexcept { + FScene::VisibleMaskType visibilityMask, float3 cameraPosition, float3 cameraForward, + uint8_t instancedStereoEyeCount) noexcept { SYSTRACE_CALL(); @@ -406,11 +409,13 @@ void RenderPass::generateCommands(uint32_t commandTypeFlags, Command* const comm switch (commandTypeFlags & (CommandTypeFlags::COLOR | CommandTypeFlags::DEPTH)) { case CommandTypeFlags::COLOR: curr = generateCommandsImpl(commandTypeFlags, curr, - soa, range, variant, renderFlags, visibilityMask, cameraPosition, cameraForward); + soa, range, variant, renderFlags, visibilityMask, cameraPosition, cameraForward, + instancedStereoEyeCount); break; case CommandTypeFlags::DEPTH: curr = generateCommandsImpl(commandTypeFlags, curr, - soa, range, variant, renderFlags, visibilityMask, cameraPosition, cameraForward); + soa, range, variant, renderFlags, visibilityMask, cameraPosition, cameraForward, + instancedStereoEyeCount); break; default: // we should never end-up here @@ -433,7 +438,7 @@ RenderPass::Command* RenderPass::generateCommandsImpl(uint32_t extraFlags, Command* UTILS_RESTRICT curr, FScene::RenderableSoa const& UTILS_RESTRICT soa, Range range, Variant const variant, RenderFlags renderFlags, FScene::VisibleMaskType visibilityMask, - float3 cameraPosition, float3 cameraForward) noexcept { + float3 cameraPosition, float3 cameraForward, uint8_t instancedStereoEyeCount) noexcept { // generateCommands() writes both the draw and depth commands simultaneously such that // we go throw the list of renderables just once. @@ -527,7 +532,7 @@ RenderPass::Command* RenderPass::generateCommandsImpl(uint32_t extraFlags, // eye count. if (UTILS_UNLIKELY(hasInstancedStereo)) { cmdColor.primitive.instanceCount = - (soaInstanceInfo[i].count * CONFIG_STEREOSCOPIC_EYES) | + (soaInstanceInfo[i].count * instancedStereoEyeCount) | PrimitiveInfo::USER_INSTANCE_MASK; } @@ -552,7 +557,7 @@ RenderPass::Command* RenderPass::generateCommandsImpl(uint32_t extraFlags, if (UTILS_UNLIKELY(hasInstancedStereo)) { cmdColor.primitive.instanceCount = - (soaInstanceInfo[i].count * CONFIG_STEREOSCOPIC_EYES) | + (soaInstanceInfo[i].count * instancedStereoEyeCount) | PrimitiveInfo::USER_INSTANCE_MASK; } } diff --git a/filament/src/RenderPass.h b/filament/src/RenderPass.h index fcbfba9d57b..3ef806365ab 100644 --- a/filament/src/RenderPass.h +++ b/filament/src/RenderPass.h @@ -407,13 +407,15 @@ class RenderPass { FScene::RenderableSoa const& soa, utils::Range range, Variant variant, RenderFlags renderFlags, FScene::VisibleMaskType visibilityMask, - math::float3 cameraPosition, math::float3 cameraForward) noexcept; + math::float3 cameraPosition, math::float3 cameraForward, + uint8_t instancedStereoEyeCount) noexcept; template static inline Command* generateCommandsImpl(uint32_t extraFlags, Command* curr, FScene::RenderableSoa const& soa, utils::Range range, Variant variant, RenderFlags renderFlags, FScene::VisibleMaskType visibilityMask, - math::float3 cameraPosition, math::float3 cameraForward) noexcept; + math::float3 cameraPosition, math::float3 cameraForward, + uint8_t instancedStereoEyeCount) noexcept; static void setupColorCommand(Command& cmdDraw, Variant variant, FMaterialInstance const* mi, bool invertedFrontFaces) noexcept; diff --git a/filament/src/details/Camera.cpp b/filament/src/details/Camera.cpp index 88bdb246e85..81e54d27b51 100644 --- a/filament/src/details/Camera.cpp +++ b/filament/src/details/Camera.cpp @@ -96,11 +96,11 @@ void UTILS_NOINLINE FCamera::setCustomProjection(mat4 const& p, void UTILS_NOINLINE FCamera::setCustomEyeProjection(math::mat4 const* projection, size_t count, math::mat4 const& projectionForCulling, double near, double far) { - ASSERT_PRECONDITION(count >= CONFIG_STEREOSCOPIC_EYES, + const Engine::Config& config = mEngine.getConfig(); + ASSERT_PRECONDITION(count >= config.stereoscopicEyeCount, "All eye projections must be supplied together, count must be >= " - "CONFIG_STEREOSCOPIC_EYES(%d)", - CONFIG_STEREOSCOPIC_EYES); - for (uint8_t i = 0; i < CONFIG_STEREOSCOPIC_EYES; i++) { + "config.stereoscopicEyeCount (%d)", config.stereoscopicEyeCount); + for (int i = 0; i < config.stereoscopicEyeCount; i++) { mEyeProjection[i] = projection[i]; } mProjectionForCulling = projectionForCulling; @@ -165,7 +165,8 @@ void UTILS_NOINLINE FCamera::setProjection(Camera::Projection projection, } math::mat4 FCamera::getProjectionMatrix(uint8_t eye) const noexcept { - assert_invariant(eye < CONFIG_STEREOSCOPIC_EYES); + UTILS_UNUSED_IN_RELEASE const Engine::Config& config = mEngine.getConfig(); + assert_invariant(eye < config.stereoscopicEyeCount); // This is where we transform the user clip-space (GL convention) to our virtual clip-space // (inverted DX convention) // Note that this math ends up setting the projection matrix' p33 to 0, which is where we're @@ -190,6 +191,13 @@ math::mat4 FCamera::getCullingProjectionMatrix() const noexcept { return m * mProjectionForCulling; } +const math::mat4& FCamera::getUserProjectionMatrix(uint8_t eyeId) const { + const Engine::Config& config = mEngine.getConfig(); + ASSERT_PRECONDITION(eyeId < config.stereoscopicEyeCount, + "eyeId must be < config.stereoscopicEyeCount (%d)", config.stereoscopicEyeCount); + return mEyeProjection[eyeId]; +} + void UTILS_NOINLINE FCamera::setModelMatrix(const mat4f& modelMatrix) noexcept { FTransformManager& transformManager = mEngine.getTransformManager(); transformManager.setTransform(transformManager.getInstance(mEntity), modelMatrix); @@ -201,8 +209,9 @@ void UTILS_NOINLINE FCamera::setModelMatrix(const mat4& modelMatrix) noexcept { } void UTILS_NOINLINE FCamera::setEyeModelMatrix(uint8_t eyeId, math::mat4 const& model) { - ASSERT_PRECONDITION(eyeId < CONFIG_STEREOSCOPIC_EYES, - "eyeId must be < CONFIG_STEREOSCOPIC_EYES(%d)", CONFIG_STEREOSCOPIC_EYES); + const Engine::Config& config = mEngine.getConfig(); + ASSERT_PRECONDITION(eyeId < config.stereoscopicEyeCount, + "eyeId must be < config.stereoscopicEyeCount (%d)", config.stereoscopicEyeCount); mEyeFromView[eyeId] = inverse(model); } @@ -249,10 +258,15 @@ double FCamera::computeEffectiveFov(double fovInDegrees, double focusDistance) n return fov * math::d::RAD_TO_DEG; } +uint8_t FCamera::getStereoscopicEyeCount() const noexcept { + const Engine::Config& config = mEngine.getConfig(); + return config.stereoscopicEyeCount; +} + // ------------------------------------------------------------------------------------------------ CameraInfo::CameraInfo(FCamera const& camera) noexcept { - for (uint8_t i = 0; i < CONFIG_STEREOSCOPIC_EYES; i++) { + for (int i = 0; i < camera.getStereoscopicEyeCount(); i++) { eyeProjection[i] = mat4f{ camera.getProjectionMatrix(i) }; } cullingProjection = mat4f{ camera.getCullingProjectionMatrix() }; @@ -268,7 +282,7 @@ CameraInfo::CameraInfo(FCamera const& camera) noexcept { CameraInfo::CameraInfo(FCamera const& camera, math::mat4 const& inWorldTransform) noexcept { const mat4 modelMatrix{ inWorldTransform * camera.getModelMatrix() }; - for (uint8_t i = 0; i < CONFIG_STEREOSCOPIC_EYES; i++) { + for (int i = 0; i < camera.getStereoscopicEyeCount(); i++) { eyeProjection[i] = mat4f{ camera.getProjectionMatrix(i) }; eyeFromView[i] = mat4f{ camera.getEyeFromViewMatrix(i) }; } diff --git a/filament/src/details/Camera.h b/filament/src/details/Camera.h index bab9f11326a..1989178772e 100644 --- a/filament/src/details/Camera.h +++ b/filament/src/details/Camera.h @@ -85,11 +85,7 @@ class FCamera : public Camera { math::mat4 getEyeFromViewMatrix(uint8_t eye) const noexcept { return mEyeFromView[eye]; } // viewing projection matrix set by the user - const math::mat4& getUserProjectionMatrix(uint8_t eyeId) const { - ASSERT_PRECONDITION(eyeId < CONFIG_STEREOSCOPIC_EYES, - "eyeId must be < CONFIG_STEREOSCOPIC_EYES(%d)", CONFIG_STEREOSCOPIC_EYES); - return mEyeProjection[eyeId]; - } + const math::mat4& getUserProjectionMatrix(uint8_t eyeId) const; // culling projection matrix set by the user math::mat4 getUserCullingProjectionMatrix() const noexcept { return mProjectionForCulling; } @@ -189,6 +185,8 @@ class FCamera : public Camera { static double computeEffectiveFov(double fovInDegrees, double focusDistance) noexcept; + uint8_t getStereoscopicEyeCount() const noexcept; + utils::Entity getEntity() const noexcept { return mEntity; } @@ -204,10 +202,10 @@ class FCamera : public Camera { utils::Entity mEntity; // For monoscopic cameras, mEyeProjection[0] == mEyeProjection[1]. - math::mat4 mEyeProjection[CONFIG_STEREOSCOPIC_EYES]; // projection matrix per eye (infinite far) - math::mat4 mProjectionForCulling; // projection matrix (with far plane) - math::mat4 mEyeFromView[CONFIG_STEREOSCOPIC_EYES]; // transforms from the main view (head) - // space to each eye's unique view space + math::mat4 mEyeProjection[CONFIG_MAX_STEREOSCOPIC_EYES]; // projection matrix per eye (infinite far) + math::mat4 mProjectionForCulling; // projection matrix (with far plane) + math::mat4 mEyeFromView[CONFIG_MAX_STEREOSCOPIC_EYES]; // transforms from the main view (head) + // space to each eye's unique view space math::double2 mScalingCS = {1.0}; // additional scaling applied to projection math::double2 mShiftCS = {0.0}; // additional translation applied to projection @@ -232,15 +230,15 @@ struct CameraInfo { math::mat4f projection; // for stereo rendering, one matrix per eye - math::mat4f eyeProjection[CONFIG_STEREOSCOPIC_EYES] = {}; + math::mat4f eyeProjection[CONFIG_MAX_STEREOSCOPIC_EYES] = {}; }; - math::mat4f cullingProjection; // projection matrix for culling - 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 - // to model and view) + math::mat4f cullingProjection; // projection matrix for culling + math::mat4f model; // camera model matrix + math::mat4f view; // camera view matrix (inverse(model)) + math::mat4f eyeFromView[CONFIG_MAX_STEREOSCOPIC_EYES]; // eye view matrix (only for stereoscopic) + math::mat4 worldTransform; // world 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 float zf{}; // distance (positive) to the far plane diff --git a/filament/src/details/Engine.cpp b/filament/src/details/Engine.cpp index 7ac6ec77dd4..13c9d851b63 100644 --- a/filament/src/details/Engine.cpp +++ b/filament/src/details/Engine.cpp @@ -1266,6 +1266,9 @@ Engine::Config Engine::BuilderDetails::validateConfig(const Config* const pConfi // This value gets validated during driver creation, so pass it through config.driverHandleArenaSizeMB = config.driverHandleArenaSizeMB; + config.stereoscopicEyeCount = + std::clamp(config.stereoscopicEyeCount, uint8_t(1), CONFIG_MAX_STEREOSCOPIC_EYES); + return config; } diff --git a/filament/src/details/Material.cpp b/filament/src/details/Material.cpp index 03cd2f8605a..91539a6db1f 100644 --- a/filament/src/details/Material.cpp +++ b/filament/src/details/Material.cpp @@ -260,6 +260,9 @@ FMaterial::FMaterial(FEngine& engine, const Material::Builder& builder) mSpecializationConstants.push_back({ +ReservedSpecializationConstants::CONFIG_POWER_VR_SHADER_WORKAROUNDS, (bool)powerVrShaderWorkarounds }); + mSpecializationConstants.push_back({ + +ReservedSpecializationConstants::CONFIG_STEREO_EYE_COUNT, + (int)engine.getConfig().stereoscopicEyeCount }); if (UTILS_UNLIKELY(engine.getShaderLanguage() == ShaderLanguage::ESSL1)) { // The actual value of this spec-constant is set in the OpenGLDriver backend. mSpecializationConstants.push_back({ diff --git a/filament/src/fg/FrameGraph.h b/filament/src/fg/FrameGraph.h index 4538801bed6..803e5c8088c 100644 --- a/filament/src/fg/FrameGraph.h +++ b/filament/src/fg/FrameGraph.h @@ -527,7 +527,7 @@ class FrameGraph { template FrameGraphPass& FrameGraph::addPass(char const* name, Setup setup, Execute&& execute) { - static_assert(sizeof(Execute) < 1024, "Execute() lambda is capturing too much data."); + static_assert(sizeof(Execute) < 2048, "Execute() lambda is capturing too much data."); // create the FrameGraph pass auto* const pass = mArena.make>(std::forward(execute)); diff --git a/libs/filabridge/include/private/filament/EngineEnums.h b/libs/filabridge/include/private/filament/EngineEnums.h index 70d736cb4d3..1b643735d50 100644 --- a/libs/filabridge/include/private/filament/EngineEnums.h +++ b/libs/filabridge/include/private/filament/EngineEnums.h @@ -66,6 +66,7 @@ enum class ReservedSpecializationConstants : uint8_t { CONFIG_POWER_VR_SHADER_WORKAROUNDS = 5, CONFIG_DEBUG_DIRECTIONAL_SHADOWMAP = 6, CONFIG_DEBUG_FROXEL_VISUALIZATION = 7, + CONFIG_STEREO_EYE_COUNT = 8, }; // This value is limited by UBO size, ES3.0 only guarantees 16 KiB. @@ -75,7 +76,8 @@ constexpr size_t CONFIG_MAX_LIGHT_INDEX = CONFIG_MAX_LIGHT_COUNT - 1; // The number of specialization constants that Filament reserves for its own use. These are always // the first constants (from 0 to CONFIG_MAX_RESERVED_SPEC_CONSTANTS - 1). -constexpr size_t CONFIG_MAX_RESERVED_SPEC_CONSTANTS = 8; +// Updating this value necessitates a material version bump. +constexpr size_t CONFIG_MAX_RESERVED_SPEC_CONSTANTS = 16; // The maximum number of shadowmaps. // There is currently a maximum limit of 128 shadowmaps. @@ -120,8 +122,10 @@ constexpr size_t CONFIG_MAX_BONE_COUNT = 256; // Furthermore, this is constrained by CONFIG_MINSPEC_UBO_SIZE (16 bytes per morph target). constexpr size_t CONFIG_MAX_MORPH_TARGET_COUNT = 256; -// The number of eyes in stereoscopic mode. -constexpr uint8_t CONFIG_STEREOSCOPIC_EYES = 2; +// The max number of eyes supported in stereoscopic mode. +// The number of eyes actually rendered is set at Engine creation time, see +// Engine::Config::stereoscopicEyeCount. +constexpr uint8_t CONFIG_MAX_STEREOSCOPIC_EYES = 4; } // namespace filament diff --git a/libs/filabridge/include/private/filament/UibStructs.h b/libs/filabridge/include/private/filament/UibStructs.h index 1def148334e..dba88fc1d6a 100644 --- a/libs/filabridge/include/private/filament/UibStructs.h +++ b/libs/filabridge/include/private/filament/UibStructs.h @@ -76,7 +76,7 @@ struct PerViewUib { // NOLINT(cppcoreguidelines-pro-type-member-init) math::mat4f worldFromViewMatrix; // clip view -> world : model matrix math::mat4f clipFromViewMatrix; // clip <- view world : projection matrix math::mat4f viewFromClipMatrix; // clip -> view world : inverse projection matrix - math::mat4f clipFromWorldMatrix[CONFIG_STEREOSCOPIC_EYES]; // clip <- view <- world + math::mat4f clipFromWorldMatrix[CONFIG_MAX_STEREOSCOPIC_EYES]; // clip <- view <- world math::mat4f worldFromClipMatrix; // clip -> view -> world math::mat4f userWorldFromWorldMatrix; // userWorld <- world math::float4 clipTransform; // [sx, sy, tx, ty] only used by VERTEX_DOMAIN_DEVICE @@ -203,7 +203,7 @@ struct PerViewUib { // NOLINT(cppcoreguidelines-pro-type-member-init) float es2Reserved2; // bring PerViewUib to 2 KiB - math::float4 reserved[48]; + math::float4 reserved[40]; }; // 2 KiB == 128 float4s diff --git a/libs/filamat/src/GLSLPostProcessor.cpp b/libs/filamat/src/GLSLPostProcessor.cpp index db36aae23d9..871bc298002 100644 --- a/libs/filamat/src/GLSLPostProcessor.cpp +++ b/libs/filamat/src/GLSLPostProcessor.cpp @@ -553,7 +553,7 @@ void GLSLPostProcessor::fullOptimization(const TShader& tShader, // According to EXT_clip_cull_distance, gl_ClipDistance can be // "implicitly sized by indexing it only with integral constant expressions". std::string& str = *internalConfig.glslOutput; - const std::string clipDistanceDefinition = "out float gl_ClipDistance[1];"; + const std::string clipDistanceDefinition = "out float gl_ClipDistance[2];"; size_t const found = str.find(clipDistanceDefinition); if (found != std::string::npos) { str.replace(found, clipDistanceDefinition.length(), ""); diff --git a/libs/filamat/src/shaders/CodeGenerator.cpp b/libs/filamat/src/shaders/CodeGenerator.cpp index e1dbd80ba20..b4818a50fde 100644 --- a/libs/filamat/src/shaders/CodeGenerator.cpp +++ b/libs/filamat/src/shaders/CodeGenerator.cpp @@ -253,10 +253,13 @@ utils::io::sstream& CodeGenerator::generateProlog(utils::io::sstream& out, Shade generateSpecializationConstant(out, "CONFIG_POWER_VR_SHADER_WORKAROUNDS", +ReservedSpecializationConstants::CONFIG_POWER_VR_SHADER_WORKAROUNDS, false); - // CONFIG_STEREOSCOPIC_EYES is used to size arrays and on Adreno GPUs + vulkan, this has to + generateSpecializationConstant(out, "CONFIG_STEREO_EYE_COUNT", + +ReservedSpecializationConstants::CONFIG_STEREO_EYE_COUNT, 2); + + // CONFIG_MAX_STEREOSCOPIC_EYES is used to size arrays and on Adreno GPUs + vulkan, this has to // be explicitly, statically defined (as in #define). Otherwise (using const int for // example), we'd run into a GPU crash. - out << "#define CONFIG_STEREOSCOPIC_EYES " << (int) CONFIG_STEREOSCOPIC_EYES << "\n"; + out << "#define CONFIG_MAX_STEREOSCOPIC_EYES " << (int) CONFIG_MAX_STEREOSCOPIC_EYES << "\n"; if (mFeatureLevel == FeatureLevel::FEATURE_LEVEL_0) { // On ES2 since we don't have post-processing, we need to emulate EGL_GL_COLORSPACE_KHR, diff --git a/libs/filamat/src/shaders/UibGenerator.cpp b/libs/filamat/src/shaders/UibGenerator.cpp index 31932145b47..d1dd2235d0e 100644 --- a/libs/filamat/src/shaders/UibGenerator.cpp +++ b/libs/filamat/src/shaders/UibGenerator.cpp @@ -39,7 +39,7 @@ BufferInterfaceBlock const& UibGenerator::getPerViewUib() noexcept { { "worldFromViewMatrix", 0, Type::MAT4, Precision::HIGH, FeatureLevel::FEATURE_LEVEL_0 }, { "clipFromViewMatrix", 0, Type::MAT4, Precision::HIGH, FeatureLevel::FEATURE_LEVEL_0 }, { "viewFromClipMatrix", 0, Type::MAT4, Precision::HIGH, FeatureLevel::FEATURE_LEVEL_0 }, - { "clipFromWorldMatrix", CONFIG_STEREOSCOPIC_EYES, + { "clipFromWorldMatrix", CONFIG_MAX_STEREOSCOPIC_EYES, Type::MAT4, Precision::HIGH, FeatureLevel::FEATURE_LEVEL_0 }, { "worldFromClipMatrix", 0, Type::MAT4, Precision::HIGH, FeatureLevel::FEATURE_LEVEL_0 }, { "userWorldFromWorldMatrix",0,Type::MAT4, Precision::HIGH, FeatureLevel::FEATURE_LEVEL_0 }, diff --git a/libs/filamentapp/include/filamentapp/Config.h b/libs/filamentapp/include/filamentapp/Config.h index 1343339d808..4f55cf866bb 100644 --- a/libs/filamentapp/include/filamentapp/Config.h +++ b/libs/filamentapp/include/filamentapp/Config.h @@ -34,6 +34,7 @@ struct Config { filament::camutils::Mode cameraMode = filament::camutils::Mode::ORBIT; bool resizeable = true; bool headless = false; + int stereoscopicEyeCount = 2; // Provided to indicate GPU preference for vulkan std::string vulkanGPUHint; diff --git a/libs/filamentapp/include/filamentapp/FilamentApp.h b/libs/filamentapp/include/filamentapp/FilamentApp.h index ce1466362ac..186d146d083 100644 --- a/libs/filamentapp/include/filamentapp/FilamentApp.h +++ b/libs/filamentapp/include/filamentapp/FilamentApp.h @@ -85,6 +85,8 @@ class FilamentApp { PostRenderCallback postRender = PostRenderCallback(), size_t width = 1024, size_t height = 640); + void reconfigureCameras() { mReconfigureCameras = true; } + filament::Material const* getDefaultMaterial() const noexcept { return mDefaultMaterial; } filament::Material const* getTransparentMaterial() const noexcept { return mTransparentMaterial; } IBL* getIBL() const noexcept { return mIBL.get(); } @@ -189,6 +191,7 @@ class FilamentApp { void fixupMouseCoordinatesForHdpi(ssize_t& x, ssize_t& y) const; FilamentApp* const mFilamentApp = nullptr; + Config mConfig; const bool mIsHeadless; SDL_Window* mWindow = nullptr; @@ -251,6 +254,7 @@ class FilamentApp { float mCameraFocalLength = 28.0f; float mCameraNear = 0.1f; float mCameraFar = 100.0f; + bool mReconfigureCameras = false; #if defined(FILAMENT_DRIVER_SUPPORTS_VULKAN) filament::backend::VulkanPlatform* mVulkanPlatform = nullptr; diff --git a/libs/filamentapp/src/FilamentApp.cpp b/libs/filamentapp/src/FilamentApp.cpp index 7970c61f3a5..108bb0ac535 100644 --- a/libs/filamentapp/src/FilamentApp.cpp +++ b/libs/filamentapp/src/FilamentApp.cpp @@ -448,6 +448,11 @@ void FilamentApp::run(const Config& config, SetupCallback setupCallback, preRender(mEngine, window->mViews[0]->getView(), mScene, renderer); } + if (mReconfigureCameras) { + window->configureCamerasForWindow(); + mReconfigureCameras = false; + } + if (renderer->beginFrame(window->getSwapChain())) { for (filament::View* offscreenView : mOffscreenViews) { renderer->render(offscreenView); @@ -568,7 +573,7 @@ void FilamentApp::initSDL() { FilamentApp::Window::Window(FilamentApp* filamentApp, const Config& config, std::string title, size_t w, size_t h) - : mFilamentApp(filamentApp), mIsHeadless(config.headless) { + : mFilamentApp(filamentApp), mConfig(config), mIsHeadless(config.headless) { const int x = SDL_WINDOWPOS_CENTERED; const int y = SDL_WINDOWPOS_CENTERED; uint32_t windowFlags = SDL_WINDOW_SHOWN | SDL_WINDOW_ALLOW_HIGHDPI; @@ -596,6 +601,9 @@ FilamentApp::Window::Window(FilamentApp* filamentApp, } #endif + Engine::Config engineConfig = {}; + engineConfig.stereoscopicEyeCount = config.stereoscopicEyeCount; + if (backend == Engine::Backend::VULKAN) { #if defined(FILAMENT_DRIVER_SUPPORTS_VULKAN) mFilamentApp->mVulkanPlatform = @@ -604,12 +612,14 @@ FilamentApp::Window::Window(FilamentApp* filamentApp, .backend(backend) .platform(mFilamentApp->mVulkanPlatform) .featureLevel(config.featureLevel) + .config(&engineConfig) .build(); #endif } return Engine::Builder() .backend(backend) .featureLevel(config.featureLevel) + .config(&engineConfig) .build(); }; @@ -875,12 +885,23 @@ void FilamentApp::Window::configureCamerasForWindow() { double near = mFilamentApp->mCameraNear; double far = mFilamentApp->mCameraFar; - mMainCamera->setLensProjection(mFilamentApp->mCameraFocalLength, 1.0, near, far); + if (mMainView->getView()->getStereoscopicOptions().enabled) { + mat4 projections[4]; + projections[0] = Camera::projection(mFilamentApp->mCameraFocalLength, 1.0, near, far); + projections[1] = projections[0]; + // simulate foveated rendering + projections[2] = Camera::projection(mFilamentApp->mCameraFocalLength * 2.0, 1.0, near, far); + projections[3] = projections[2]; + mMainCamera->setCustomEyeProjection(projections, 4, projections[0], near, far); + } else { + mMainCamera->setLensProjection(mFilamentApp->mCameraFocalLength, 1.0, near, far); + } mDebugCamera->setProjection(45.0, double(width) / height, 0.0625, 4096, Camera::Fov::VERTICAL); auto aspectRatio = double(mainWidth) / height; if (mMainView->getView()->getStereoscopicOptions().enabled) { - aspectRatio = double(mainWidth) / 2.0 / height; + const int ec = mConfig.stereoscopicEyeCount; + aspectRatio = double(mainWidth) / ec / height; } mMainCamera->setScaling({1.0 / aspectRatio, 1.0}); diff --git a/samples/gltf_viewer.cpp b/samples/gltf_viewer.cpp index 36ecdd8236e..a7861e04383 100644 --- a/samples/gltf_viewer.cpp +++ b/samples/gltf_viewer.cpp @@ -41,6 +41,8 @@ #include +#include + #include #include @@ -180,10 +182,13 @@ static void printUsage(char* name) { " Set the camera mode: orbit (default) or flight\n" " Flight mode uses the following controls:\n" " Click and drag the mouse to pan the camera\n" - " Use the scroll weel to adjust movement speed\n" + " Use the scroll wheel to adjust movement speed\n" " W / S: forward / backward\n" " A / D: left / right\n" " E / Q: up / down\n\n" + " --eyes=, -y \n" + " Sets the number of stereoscopic eyes (default: 2) when stereoscopic rendering is\n" + " enabled.\n\n" " --split-view, -v\n" " Splits the window into 4 views\n\n" " --vulkan-gpu-hint=, -g\n" @@ -215,6 +220,7 @@ static int handleCommandLineArguments(int argc, char* argv[], App* app) { { "ubershader", no_argument, nullptr, 'u' }, { "actual-size", no_argument, nullptr, 's' }, { "camera", required_argument, nullptr, 'c' }, + { "eyes", required_argument, nullptr, 'y' }, { "recompute-aabb", no_argument, nullptr, 'r' }, { "settings", required_argument, nullptr, 't' }, { "split-view", no_argument, nullptr, 'v' }, @@ -261,6 +267,19 @@ static int handleCommandLineArguments(int argc, char* argv[], App* app) { std::cerr << "Unrecognized camera mode. Must be 'flight'|'orbit'.\n"; } break; + case 'y': { + int eyeCount = 0; + try { + eyeCount = std::stoi(arg); + } catch (std::invalid_argument &e) { } + if (eyeCount >= 1 && eyeCount <= CONFIG_MAX_STEREOSCOPIC_EYES) { + app->config.stereoscopicEyeCount = eyeCount; + } else { + std::cerr << "Eye count must be between 1 and CONFIG_MAX_STEREOSCOPIC_EYES (" + << (int) CONFIG_MAX_STEREOSCOPIC_EYES << ") (inclusive).\n"; + } + break; + } case 'e': app->config.headless = true; break; @@ -987,25 +1006,27 @@ int main(int argc, char** argv) { if (view->getStereoscopicOptions().enabled) { Camera& c = view->getCamera(); auto od = app.viewer->getOcularDistance(); - // Eye 0 is always rendered to the left side of the screen; Eye 1, the right side. + // Eyes are rendered from left-to-right, i.e., eye 0 is rendered to the left side of the + // window. // For testing, we want to render a side-by-side layout so users can view with // "cross-eyed" stereo. // For cross-eyed stereo, Eye 0 is really the RIGHT eye, while Eye 1 is the LEFT eye. const mat4 rightEye = mat4::translation(double3{ od, 0.0, 0.0}); // right eye const mat4 leftEye = mat4::translation(double3{-od, 0.0, 0.0}); // left eye - c.setEyeModelMatrix(0, rightEye); - c.setEyeModelMatrix(1, leftEye); - mat4 projections[2]; - // Use an aspect ratio of 1.0. The viewport will be taken into account in - // FilamentApp.cpp. - projections[0] = mat4::perspective(70.0, 1.0, .1, 10.0); - projections[1] = mat4::perspective(70.0, 1.0, .1, 10.0); - c.setCustomEyeProjection(projections, 2, projections[0], .1, 10.0); - // FIXME: the aspect ratio will be incorrect until configureCamerasForWindow is - // triggered, which will happen the next time the window is resized. + const mat4 modelMatrices[4] = { rightEye, leftEye, rightEye, leftEye }; + for (int i = 0; i < std::min(app.config.stereoscopicEyeCount, 4); i++) { + c.setEyeModelMatrix(i, modelMatrices[i]); + } } else { - view->getCamera().setEyeModelMatrix(0, {}); - view->getCamera().setEyeModelMatrix(1, {}); + for (int i = 0; i < app.config.stereoscopicEyeCount; i++) { + view->getCamera().setEyeModelMatrix(i, {}); + } + } + static bool stereoscopicEnabled = false; + if (stereoscopicEnabled != view->getStereoscopicOptions().enabled) { + // Stereo was turned on/off. + FilamentApp::get().reconfigureCameras(); + stereoscopicEnabled = view->getStereoscopicOptions().enabled; } app.scene.groundMaterial->setDefaultParameter( diff --git a/shaders/src/common_getters.glsl b/shaders/src/common_getters.glsl index 54ee7be33dd..718ea05c5b4 100644 --- a/shaders/src/common_getters.glsl +++ b/shaders/src/common_getters.glsl @@ -25,7 +25,7 @@ highp mat4 getViewFromClipMatrix() { /** @public-api */ highp mat4 getClipFromWorldMatrix() { #if defined(VARIANT_HAS_INSTANCED_STEREO) - int eye = instance_index % CONFIG_STEREOSCOPIC_EYES; + int eye = instance_index % CONFIG_STEREO_EYE_COUNT; return frameUniforms.clipFromWorldMatrix[eye]; #else return frameUniforms.clipFromWorldMatrix[0]; diff --git a/shaders/src/main.vs b/shaders/src/main.vs index a52bc527d1d..6856d21a89f 100644 --- a/shaders/src/main.vs +++ b/shaders/src/main.vs @@ -27,9 +27,8 @@ void main() { #if !defined(FILAMENT_HAS_FEATURE_INSTANCING) #error Instanced stereo not supported at this feature level #endif - // The lowest bit of the instance index represents the eye. - // This logic must be updated if CONFIG_STEREOSCOPIC_EYES changes - logical_instance_index = instance_index >> 1; + // Calculate the logical instance index, which is the instance index within a single eye. + logical_instance_index = instance_index / CONFIG_STEREO_EYE_COUNT; #endif initObjectUniforms(); @@ -217,22 +216,26 @@ void main() { vertex_position = position; #if defined(VARIANT_HAS_INSTANCED_STEREO) - // This logic must be updated if CONFIG_STEREOSCOPIC_EYES changes // We're transforming a vertex whose x coordinate is within the range (-w to w). - // To move it to the correct half of the viewport, we need to modify the x coordinate: - // Eye 0 (left half): (-w to 0) - // Eye 1 (right half): ( 0 to w) + // To move it to the correct portion of the viewport, we need to modify the x coordinate. // It's important to do this after computing vertex_position. - int eyeIndex = instance_index % 2; - float eyeShift = float(eyeIndex) * 2.0f - 1.0f; // eye 0: -1.0, eye 1: 1.0 - position.x = position.x * 0.5f + (position.w * 0.5 * eyeShift); - - // A fragment is clipped when gl_ClipDistance is negative (outside the clip plane). So, - // Eye 0 should have a positive value when x is < 0 - // -position.x - // Eye 1 should have a positive value when x is > 0 - // position.x - FILAMENT_CLIPDISTANCE[0] = position.x * eyeShift; + int eyeIndex = instance_index % CONFIG_STEREO_EYE_COUNT; + + float ndcViewportWidth = 2.0 / float(CONFIG_STEREO_EYE_COUNT); // the width of ndc space is 2 + float eyeZeroMidpoint = -1.0f + ndcViewportWidth / 2.0; + + float transform = eyeZeroMidpoint + ndcViewportWidth * float(eyeIndex); + position.x *= 1.0 / float(CONFIG_STEREO_EYE_COUNT); + position.x += transform * position.w; + + // A fragment is clipped when gl_ClipDistance is negative (outside the clip plane). + + float leftClip = position.x + + (1.0 - ndcViewportWidth * float(eyeIndex)) * position.w; + float rightClip = position.x + + (1.0 - ndcViewportWidth * float(eyeIndex + 1)) * position.w; + FILAMENT_CLIPDISTANCE[0] = leftClip; + FILAMENT_CLIPDISTANCE[1] = -rightClip; #endif #if defined(TARGET_VULKAN_ENVIRONMENT) diff --git a/shaders/src/varyings.glsl b/shaders/src/varyings.glsl index 40eb3723504..fe5c24d2477 100644 --- a/shaders/src/varyings.glsl +++ b/shaders/src/varyings.glsl @@ -40,7 +40,7 @@ LAYOUT_LOCATION(11) VARYING highp vec4 vertex_lightSpacePosition; // However, this extension is not supported by glslang, so we instead write to // filament_gl_ClipDistance, which will get decorated at the SPIR-V stage to refer to the built-in. // The location here does not matter, so long as it doesn't conflict with others. -LAYOUT_LOCATION(100) out float filament_gl_ClipDistance[1]; +LAYOUT_LOCATION(100) out float filament_gl_ClipDistance[2]; #define FILAMENT_CLIPDISTANCE filament_gl_ClipDistance #else // If we're on Desktop GL (or not running shaders through glslang), we're free to use gl_ClipDistance From e8bed52b3fc6075377e75f065bb8fc7df670590a Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Thu, 9 Nov 2023 13:31:03 -0800 Subject: [PATCH 02/23] repair visible shadow status FIXES=[309519433] --- filament/src/ShadowMap.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/filament/src/ShadowMap.cpp b/filament/src/ShadowMap.cpp index e2b8dc769e7..3bc93e90b6a 100644 --- a/filament/src/ShadowMap.cpp +++ b/filament/src/ShadowMap.cpp @@ -108,6 +108,9 @@ ShadowMap::ShaderParameters ShadowMap::updateDirectional(FEngine& engine, ShadowMapInfo const& shadowMapInfo, SceneInfo const& sceneInfo) noexcept { + // reset the visible shadow status + mHasVisibleShadows = false; + FLightManager const& lcm = engine.getLightManager(); FLightManager::Instance const li = lightData.elementAt(index); FLightManager::ShadowParams const params = lcm.getShadowParams(li); From 1a0b5ddc14f96085ebbe9277efd3da8afb8d33eb Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Fri, 10 Nov 2023 10:40:18 -0800 Subject: [PATCH 03/23] fix [Pixel]BufferDescriptor functor callbacks and attempt to make functor<->callback code less confusing. --- .../include/backend/BufferDescriptor.h | 20 ++++++++-------- .../include/backend/PixelBufferDescriptor.h | 24 +++++++++---------- filament/include/filament/View.h | 24 +++++++++---------- libs/utils/include/utils/JobSystem.h | 19 ++++++++------- 4 files changed, 44 insertions(+), 43 deletions(-) diff --git a/filament/backend/include/backend/BufferDescriptor.h b/filament/backend/include/backend/BufferDescriptor.h index 80fe182abb2..0d3490afb7a 100644 --- a/filament/backend/include/backend/BufferDescriptor.h +++ b/filament/backend/include/backend/BufferDescriptor.h @@ -113,7 +113,7 @@ class UTILS_PUBLIC BufferDescriptor { /** * Helper to create a BufferDescriptor that uses a KNOWN method pointer w/ object passed * by pointer as the callback. e.g.: - * auto bd = BufferDescriptor::make(buffer, size, &Foo::method, foo); + * auto bd = BufferDescriptor::make(buffer, size, foo); * * @param buffer Memory address of the CPU buffer to reference * @param size Size of the CPU buffer in bytes @@ -121,12 +121,12 @@ class UTILS_PUBLIC BufferDescriptor { * @return a new BufferDescriptor */ template - static BufferDescriptor make( - void const* buffer, size_t size, T* data, CallbackHandler* handler = nullptr) noexcept { + static BufferDescriptor make(void const* buffer, size_t size, T* data, + CallbackHandler* handler = nullptr) noexcept { return { buffer, size, handler, [](void* b, size_t s, void* u) { - (*static_cast(u)->*method)(b, s); + (static_cast(u)->*method)(b, s); }, data }; } @@ -145,14 +145,14 @@ class UTILS_PUBLIC BufferDescriptor { * @return a new BufferDescriptor */ template - static BufferDescriptor make( - void const* buffer, size_t size, T&& functor, CallbackHandler* handler = nullptr) noexcept { + static BufferDescriptor make(void const* buffer, size_t size, T&& functor, + CallbackHandler* handler = nullptr) noexcept { return { buffer, size, handler, [](void* b, size_t s, void* u) { - T& that = *static_cast(u); - that(b, s); - delete &that; + T* const that = static_cast(u); + that->operator()(b, s); + delete that; }, new T(std::forward(functor)) }; @@ -201,7 +201,7 @@ class UTILS_PUBLIC BufferDescriptor { return mUser; } - //! CPU mempry-buffer virtual address + //! CPU memory-buffer virtual address void* buffer = nullptr; //! CPU memory-buffer size in bytes diff --git a/filament/backend/include/backend/PixelBufferDescriptor.h b/filament/backend/include/backend/PixelBufferDescriptor.h index 1b498032fdc..67564fcce46 100644 --- a/filament/backend/include/backend/PixelBufferDescriptor.h +++ b/filament/backend/include/backend/PixelBufferDescriptor.h @@ -141,7 +141,7 @@ class UTILS_PUBLIC PixelBufferDescriptor : public BufferDescriptor { CallbackHandler* handler = nullptr) noexcept { return { buffer, size, format, type, alignment, left, top, stride, handler, [](void* b, size_t s, void* u) { - (*static_cast(u)->*method)(b, s); }, data }; + (static_cast(u)->*method)(b, s); }, data }; } template @@ -149,7 +149,7 @@ class UTILS_PUBLIC PixelBufferDescriptor : public BufferDescriptor { PixelDataFormat format, PixelDataType type, T* data, CallbackHandler* handler = nullptr) noexcept { return { buffer, size, format, type, handler, [](void* b, size_t s, void* u) { - (*static_cast(u)->*method)(b, s); }, data }; + (static_cast(u)->*method)(b, s); }, data }; } template @@ -157,7 +157,7 @@ class UTILS_PUBLIC PixelBufferDescriptor : public BufferDescriptor { backend::CompressedPixelDataType format, uint32_t imageSize, T* data, CallbackHandler* handler = nullptr) noexcept { return { buffer, size, format, imageSize, handler, [](void* b, size_t s, void* u) { - (*static_cast(u)->*method)(b, s); }, data + (static_cast(u)->*method)(b, s); }, data }; } @@ -168,9 +168,9 @@ class UTILS_PUBLIC PixelBufferDescriptor : public BufferDescriptor { CallbackHandler* handler = nullptr) noexcept { return { buffer, size, format, type, alignment, left, top, stride, handler, [](void* b, size_t s, void* u) { - T& that = *static_cast(u); - that(b, s); - delete &that; + T* const that = static_cast(u); + that->operator()(b, s); + delete that; }, new T(std::forward(functor)) }; } @@ -181,9 +181,9 @@ class UTILS_PUBLIC PixelBufferDescriptor : public BufferDescriptor { CallbackHandler* handler = nullptr) noexcept { return { buffer, size, format, type, handler, [](void* b, size_t s, void* u) { - T& that = *static_cast(u); - that(b, s); - delete &that; + T* const that = static_cast(u); + that->operator()(b, s); + delete that; }, new T(std::forward(functor)) }; } @@ -194,9 +194,9 @@ class UTILS_PUBLIC PixelBufferDescriptor : public BufferDescriptor { CallbackHandler* handler = nullptr) noexcept { return { buffer, size, format, imageSize, handler, [](void* b, size_t s, void* u) { - T& that = *static_cast(u); - that(b, s); - delete &that; + T* const that = static_cast(u); + that->operator()(b, s); + delete that; }, new T(std::forward(functor)) }; } diff --git a/filament/include/filament/View.h b/filament/include/filament/View.h index e15fa8724e6..ffa15367b7f 100644 --- a/filament/include/filament/View.h +++ b/filament/include/filament/View.h @@ -742,10 +742,10 @@ class UTILS_PUBLIC View : public FilamentAPI { * @param handler Handler to dispatch the callback or nullptr for the default handler. */ template - void pick(uint32_t x, uint32_t y, T* instance, backend::CallbackHandler* handler = nullptr) noexcept { + void pick(uint32_t x, uint32_t y, T* instance, + backend::CallbackHandler* handler = nullptr) noexcept { PickingQuery& query = pick(x, y, [](PickingQueryResult const& result, PickingQuery* pq) { - void* user = pq->storage; - (*static_cast(user)->*method)(result); + (static_cast(pq->storage[0])->*method)(result); }, handler); query.storage[0] = instance; } @@ -762,11 +762,11 @@ class UTILS_PUBLIC View : public FilamentAPI { * @param handler Handler to dispatch the callback or nullptr for the default handler. */ template - void pick(uint32_t x, uint32_t y, T instance, backend::CallbackHandler* handler = nullptr) noexcept { + void pick(uint32_t x, uint32_t y, T instance, + backend::CallbackHandler* handler = nullptr) noexcept { static_assert(sizeof(instance) <= sizeof(PickingQuery::storage), "user data too large"); PickingQuery& query = pick(x, y, [](PickingQueryResult const& result, PickingQuery* pq) { - void* user = pq->storage; - T* that = static_cast(user); + T* const that = static_cast(reinterpret_cast(pq->storage)); (that->*method)(result); that->~T(); }, handler); @@ -783,15 +783,15 @@ class UTILS_PUBLIC View : public FilamentAPI { * @param handler Handler to dispatch the callback or nullptr for the default handler. */ template - void pick(uint32_t x, uint32_t y, T functor, backend::CallbackHandler* handler = nullptr) noexcept { + void pick(uint32_t x, uint32_t y, T functor, + backend::CallbackHandler* handler = nullptr) noexcept { static_assert(sizeof(functor) <= sizeof(PickingQuery::storage), "functor too large"); PickingQuery& query = pick(x, y, handler, (PickingQueryResultCallback)[](PickingQueryResult const& result, PickingQuery* pq) { - void* user = pq->storage; - T& that = *static_cast(user); - that(result); - that.~T(); - }); + T* const that = static_cast(reinterpret_cast(pq->storage)); + that->operator()(result); + that->~T(); + }); new(query.storage) T(std::move(functor)); } diff --git a/libs/utils/include/utils/JobSystem.h b/libs/utils/include/utils/JobSystem.h index 1c602740de1..041c6d9c55e 100644 --- a/libs/utils/include/utils/JobSystem.h +++ b/libs/utils/include/utils/JobSystem.h @@ -169,8 +169,9 @@ class JobSystem { // the caller must ensure the object will outlive the Job template Job* createJob(Job* parent, T* data) noexcept { - Job* job = create(parent, [](void* user, JobSystem& js, Job* job) { - (*static_cast(user)->*method)(js, job); + Job* job = create(parent, +[](void* storage, JobSystem& js, Job* job) { + T* const that = static_cast(reinterpret_cast(storage)[0]); + (that->*method)(js, job); }); if (job) { job->storage[0] = data; @@ -182,8 +183,8 @@ class JobSystem { template Job* createJob(Job* parent, T data) noexcept { static_assert(sizeof(data) <= sizeof(Job::storage), "user data too large"); - Job* job = create(parent, [](void* user, JobSystem& js, Job* job) { - T* that = static_cast(user); + Job* job = create(parent, [](void* storage, JobSystem& js, Job* job) { + T* const that = static_cast(storage); (that->*method)(js, job); that->~T(); }); @@ -197,10 +198,10 @@ class JobSystem { template Job* createJob(Job* parent, T functor) noexcept { static_assert(sizeof(functor) <= sizeof(Job::storage), "functor too large"); - Job* job = create(parent, [](void* user, JobSystem& js, Job* job){ - T& that = *static_cast(user); - that(js, job); - that.~T(); + Job* job = create(parent, [](void* storage, JobSystem& js, Job* job){ + T* const that = static_cast(storage); + that->operator()(js, job); + that->~T(); }); if (job) { new(job->storage) T(std::move(functor)); @@ -252,7 +253,7 @@ class JobSystem { void signal() noexcept; /* - * Add job to this thread's execution queue and and keep a reference to it. + * Add job to this thread's execution queue and keep a reference to it. * Current thread must be owned by JobSystem's thread pool. See adopt(). * * This job MUST BE waited on with wait(), or released with release(). From edbfefda0ab3d7a3503a0f7756d33407c50ac1b8 Mon Sep 17 00:00:00 2001 From: Ben Doherty Date: Fri, 10 Nov 2023 14:41:17 -0800 Subject: [PATCH 04/23] Fix Java FeatureLevel Enum (#7344) --- .../src/main/java/com/google/android/filament/Engine.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) 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 3f0130c3835..82fc77fc3d3 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 @@ -150,8 +150,10 @@ public enum FeatureLevel { FEATURE_LEVEL_0, /** OpenGL ES 3.0 features (default) */ FEATURE_LEVEL_1, + /** OpenGL ES 3.1 features + 16 textures units + cubemap arrays */ + FEATURE_LEVEL_2, /** OpenGL ES 3.1 features + 31 textures units + cubemap arrays */ - FEATURE_LEVEL_2 + FEATURE_LEVEL_3, }; /** From a900bc69fbbb19183832f2a47aa1733afc1cbdcb Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Fri, 10 Nov 2023 14:23:45 -0800 Subject: [PATCH 05/23] don't precompile variants in gltfio we recently added calls to Material::compile in gltfio to precompile materials are they are discovered. that wasn't a good call, because this should be the responsibility of the app, not of gltfio, at least not without an option. This is now done in gltf_viewer. We need something similar for Android. Bugs #7318, #7336 --- libs/gltfio/src/ArchiveCache.cpp | 15 ------------- samples/gltf_viewer.cpp | 36 ++++++++++++++++++++++++++++++++ 2 files changed, 36 insertions(+), 15 deletions(-) diff --git a/libs/gltfio/src/ArchiveCache.cpp b/libs/gltfio/src/ArchiveCache.cpp index 47798741fa3..c71065263a0 100644 --- a/libs/gltfio/src/ArchiveCache.cpp +++ b/libs/gltfio/src/ArchiveCache.cpp @@ -118,21 +118,6 @@ Material* ArchiveCache::getMaterial(const ArchiveRequirements& reqs) { mMaterials[i] = Material::Builder() .package(spec.package, spec.packageByteCount) .build(mEngine); - - // Don't attempt to precompile shaders on WebGL. - // Chrome already suffers from slow shader compilation: - // https://github.com/google/filament/issues/6615 - // Precompiling shaders exacerbates the problem. - #if !defined(__EMSCRIPTEN__) - // First compile high priority variants - mMaterials[i]->compile(Material::CompilerPriorityQueue::HIGH, - UserVariantFilterBit::DIRECTIONAL_LIGHTING | - UserVariantFilterBit::DYNAMIC_LIGHTING | - UserVariantFilterBit::SHADOW_RECEIVER); - - // and then, everything else at low priority - mMaterials[i]->compile(Material::CompilerPriorityQueue::LOW); - #endif } return mMaterials[i]; diff --git a/samples/gltf_viewer.cpp b/samples/gltf_viewer.cpp index a7861e04383..1a09e79f8d9 100644 --- a/samples/gltf_viewer.cpp +++ b/samples/gltf_viewer.cpp @@ -60,6 +60,7 @@ #include #include #include +#include #include #include "generated/resources/gltf_demo.h" @@ -597,6 +598,41 @@ int main(int argc, char** argv) { exit(1); } + // pre-compile all material variants + std::set materials; + RenderableManager const& rcm = app.engine->getRenderableManager(); + Slice const renderables{ + app.asset->getRenderableEntities(), app.asset->getRenderableEntityCount() }; + for (Entity const e: renderables) { + auto ri = rcm.getInstance(e); + size_t const c = rcm.getPrimitiveCount(ri); + for (size_t i = 0; i < c; i++) { + MaterialInstance* const mi = rcm.getMaterialInstanceAt(ri, i); + Material* ma = const_cast(mi->getMaterial()); + materials.insert(ma); + } + } + for (Material* ma : materials) { + // Don't attempt to precompile shaders on WebGL. + // Chrome already suffers from slow shader compilation: + // https://github.com/google/filament/issues/6615 + // Precompiling shaders exacerbates the problem. +#if !defined(__EMSCRIPTEN__) + // First compile high priority variants + ma->compile(Material::CompilerPriorityQueue::HIGH, + UserVariantFilterBit::DIRECTIONAL_LIGHTING | + UserVariantFilterBit::DYNAMIC_LIGHTING | + UserVariantFilterBit::SHADOW_RECEIVER); + + // and then, everything else at low priority, except STE, which is very uncommon. + ma->compile(Material::CompilerPriorityQueue::LOW, + UserVariantFilterBit::FOG | + UserVariantFilterBit::SKINNING | + UserVariantFilterBit::SSR | + UserVariantFilterBit::VSM); +#endif + } + app.instance = app.asset->getInstance(); buffer.clear(); buffer.shrink_to_fit(); From 0e9b2eda0aaeba3e69251616b5e849103c124a62 Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Fri, 10 Nov 2023 12:57:23 -0800 Subject: [PATCH 06/23] Fix (again) the shadow transform - we were not using the correct field in ShadowMapManager - we were not computing the transform correctly, it should applied after the local transform, not before. FIXES=[299310624] --- filament/src/ShadowMap.cpp | 2 +- filament/src/ShadowMapManager.cpp | 2 +- filament/src/details/Scene.cpp | 14 ++++++-------- 3 files changed, 8 insertions(+), 10 deletions(-) diff --git a/filament/src/ShadowMap.cpp b/filament/src/ShadowMap.cpp index 3bc93e90b6a..e4d7f67be01 100644 --- a/filament/src/ShadowMap.cpp +++ b/filament/src/ShadowMap.cpp @@ -115,7 +115,6 @@ ShadowMap::ShaderParameters ShadowMap::updateDirectional(FEngine& engine, FLightManager::Instance const li = lightData.elementAt(index); FLightManager::ShadowParams const params = lcm.getShadowParams(li); - // We can't use LISPSM in stable mode const auto direction = lightData.elementAt(index); auto [Mv, znear, zfar, lsClippedShadowVolume, vertexCount, visibleShadows] = @@ -140,6 +139,7 @@ ShadowMap::ShaderParameters ShadowMap::updateDirectional(FEngine& engine, */ mat4f W, Wp, L; + // We can't use LISPSM in stable mode const bool useLispsm = params.options.lispsm && !params.options.stable; if (useLispsm) { // Orient the shadow map in the direction of the view vector by constructing a diff --git a/filament/src/ShadowMapManager.cpp b/filament/src/ShadowMapManager.cpp index c24e18b4305..8f34fd5e5d5 100644 --- a/filament/src/ShadowMapManager.cpp +++ b/filament/src/ShadowMapManager.cpp @@ -522,7 +522,7 @@ ShadowMapManager::ShadowTechnique ShadowMapManager::updateCascadeShadowMaps(FEng // entire camera frustum, as if we only had a single cascade. ShadowMap& shadowMap = cascadedShadowMaps[0]; - const auto direction = options.transform * lightData.elementAt(0); + const auto direction = lightData.elementAt(0); // 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. diff --git a/filament/src/details/Scene.cpp b/filament/src/details/Scene.cpp index 2d2a99746b3..942fb1e24fc 100644 --- a/filament/src/details/Scene.cpp +++ b/filament/src/details/Scene.cpp @@ -267,16 +267,16 @@ void FScene::prepare(utils::JobSystem& js, // 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() + mat3 const worldDirectionTransform = + mat3::getTransformForNormals(tcm.getWorldTransformAccurate(ti).upperLeft()); 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; + float3 const localDirection = worldDirectionTransform * lcm.getLocalDirection(li); + double3 const shadowLocalDirection = params.options.transform * localDirection; // 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); + mat3 const worlTransformNormals = mat3::getTransformForNormals(worldTransform.upperLeft()); double3 const d = worlTransformNormals * localDirection; double3 const s = worlTransformNormals * shadowLocalDirection; @@ -290,10 +290,8 @@ void FScene::prepare(utils::JobSystem& js, // 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); + mat3 const Mv = getMv(shadowLocalDirection); double2 const lsReferencePoint = (Mv * worldOrigin).xy; constexpr float inf = std::numeric_limits::infinity(); From 75e8961109618145c7292495fc17e963e132b090 Mon Sep 17 00:00:00 2001 From: Powei Feng Date: Mon, 13 Nov 2023 10:37:16 -0800 Subject: [PATCH 07/23] Update Material_VERSION to 45 --- libs/filabridge/include/filament/MaterialEnums.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/filabridge/include/filament/MaterialEnums.h b/libs/filabridge/include/filament/MaterialEnums.h index 2c9fedc5648..b6d677a4c94 100644 --- a/libs/filabridge/include/filament/MaterialEnums.h +++ b/libs/filabridge/include/filament/MaterialEnums.h @@ -28,7 +28,7 @@ namespace filament { // update this when a new version of filament wouldn't work with older materials -static constexpr size_t MATERIAL_VERSION = 45; +static constexpr size_t MATERIAL_VERSION = 46; /** * Supported shading models From 485ae1b3244ac0836e1b3ac7f887430163838158 Mon Sep 17 00:00:00 2001 From: Mathias Agopian Date: Mon, 13 Nov 2023 10:43:52 -0800 Subject: [PATCH 08/23] add Material.compile and Engine.flush java bindings (#7348) * Add Material.compile() Java binding. * Add Engine.flush() java binding * Add Scene.forEach java binding update the Android gltf-viewer sample to precompile all variants of all materials in the scene, similarly to the desktop sample. --- .../filament-android/src/main/cpp/Engine.cpp | 7 ++ .../src/main/cpp/Material.cpp | 15 ++++ .../filament-android/src/main/cpp/Scene.cpp | 19 +++++ .../com/google/android/filament/Engine.java | 13 ++++ .../com/google/android/filament/Material.java | 76 +++++++++++++++++++ .../com/google/android/filament/Scene.java | 48 ++++++++++++ .../android/filament/gltf/MainActivity.kt | 31 ++++++++ .../filament/hellotriangle/MainActivity.kt | 10 +++ 8 files changed, 219 insertions(+) diff --git a/android/filament-android/src/main/cpp/Engine.cpp b/android/filament-android/src/main/cpp/Engine.cpp index 72b499e618e..b5fd507aa18 100644 --- a/android/filament-android/src/main/cpp/Engine.cpp +++ b/android/filament-android/src/main/cpp/Engine.cpp @@ -384,6 +384,13 @@ Java_com_google_android_filament_Engine_nFlushAndWait(JNIEnv*, jclass, engine->flushAndWait(); } +extern "C" JNIEXPORT void JNICALL +Java_com_google_android_filament_Engine_nFlush(JNIEnv*, jclass, + jlong nativeEngine) { + Engine* engine = (Engine*) nativeEngine; + engine->flush(); +} + // Managers... extern "C" JNIEXPORT jlong JNICALL diff --git a/android/filament-android/src/main/cpp/Material.cpp b/android/filament-android/src/main/cpp/Material.cpp index b6865cfaaf2..7ef3b0b5be2 100644 --- a/android/filament-android/src/main/cpp/Material.cpp +++ b/android/filament-android/src/main/cpp/Material.cpp @@ -19,6 +19,7 @@ #include #include "common/NioUtils.h" +#include "common/CallbackUtils.h" using namespace filament; @@ -271,3 +272,17 @@ Java_com_google_android_filament_Material_nHasParameter(JNIEnv* env, jclass, env->ReleaseStringUTFChars(name_, name); return (jboolean) hasParameter; } + +extern "C" +JNIEXPORT void JNICALL +Java_com_google_android_filament_Material_nCompile(JNIEnv *env, jclass clazz, + jlong nativeMaterial, jint priority, jint variants, jobject handler, jobject runnable) { + Material* material = (Material*) nativeMaterial; + JniCallback* jniCallback = JniCallback::make(env, handler, runnable); + material->compile( + (Material::CompilerPriorityQueue) priority, + (UserVariantFilterBit) variants, + jniCallback->getHandler(), [jniCallback](Material*){ + JniCallback::postToJavaAndDestroy(jniCallback); + }); +} diff --git a/android/filament-android/src/main/cpp/Scene.cpp b/android/filament-android/src/main/cpp/Scene.cpp index b1d3d003489..14b0b49a948 100644 --- a/android/filament-android/src/main/cpp/Scene.cpp +++ b/android/filament-android/src/main/cpp/Scene.cpp @@ -98,3 +98,22 @@ Java_com_google_android_filament_Scene_nHasEntity(JNIEnv *env, jclass type, jlon Entity entity = Entity::import(entityId); return (jboolean) scene->hasEntity(entity); } + +extern "C" +JNIEXPORT jboolean JNICALL +Java_com_google_android_filament_Scene_nGetEntities(JNIEnv *env, jclass , + jlong nativeScene, jintArray outArray, jint length) { + Scene const* const scene = (Scene*) nativeScene; + if (length < scene->getEntityCount()) { + // should not happen because we already checked on the java side + return JNI_FALSE; + } + jint *out = (jint *) env->GetIntArrayElements(outArray, nullptr); + scene->forEach([out, length, i = 0](Entity entity)mutable { + if (i < length) { // this is just paranoia here + out[i++] = (jint) entity.getId(); + } + }); + env->ReleaseIntArrayElements(outArray, (jint*) out, JNI_ABORT); + return JNI_TRUE; +} 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 82fc77fc3d3..26fd9e81c3a 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 @@ -1079,6 +1079,18 @@ public void flushAndWait() { nFlushAndWait(getNativeObject()); } + /** + * Kicks the hardware thread (e.g. the OpenGL, Vulkan or Metal thread) but does not wait + * for commands to be either executed or the hardware finished. + * + *

This is typically used after creating a lot of objects to start draining the command + * queue which has a limited size.

+ */ + public void flush() { + nFlush(getNativeObject()); + } + + @UsedByReflection("TextureHelper.java") public long getNativeObject() { if (mNativeObject == 0) { @@ -1151,6 +1163,7 @@ private static void assertDestroy(boolean success) { private static native boolean nIsValidSwapChain(long nativeEngine, long nativeSwapChain); private static native void nDestroyEntity(long nativeEngine, int entity); private static native void nFlushAndWait(long nativeEngine); + private static native void nFlush(long nativeEngine); private static native long nGetTransformManager(long nativeEngine); private static native long nGetLightManager(long nativeEngine); private static native long nGetRenderableManager(long nativeEngine); diff --git a/android/filament-android/src/main/java/com/google/android/filament/Material.java b/android/filament-android/src/main/java/com/google/android/filament/Material.java index 8f0d68a55cc..80b143ee80d 100644 --- a/android/filament-android/src/main/java/com/google/android/filament/Material.java +++ b/android/filament-android/src/main/java/com/google/android/filament/Material.java @@ -18,6 +18,7 @@ import androidx.annotation.IntRange; import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import androidx.annotation.Size; import com.google.android.filament.proguard.UsedByNative; @@ -238,6 +239,31 @@ public enum CullingMode { FRONT_AND_BACK } + public enum CompilerPriorityQueue { + HIGH, + LOW + } + + public static class UserVariantFilterBit { + /** Directional lighting */ + public static int DIRECTIONAL_LIGHTING = 0x01; + /** Dynamic lighting */ + public static int DYNAMIC_LIGHTING = 0x02; + /** Shadow receiver */ + public static int SHADOW_RECEIVER = 0x04; + /** Skinning */ + public static int SKINNING = 0x08; + /** Fog */ + public static int FOG = 0x10; + /** Variance shadow maps */ + public static int VSM = 0x20; + /** Screen-space reflections */ + public static int SSR = 0x40; + /** Instanced stereo rendering */ + public static int STE = 0x80; + public static int ALL = 0xFF; + } + @UsedByNative("Material.cpp") public static class Parameter { private static final Type[] sTypeValues = Type.values(); @@ -352,6 +378,55 @@ public Material build(@NonNull Engine engine) { } } + + /** + * Asynchronously ensures that a subset of this Material's variants are compiled. After issuing + * several compile() calls in a row, it is recommended to call {@link Engine#flush} + * such that the backend can start the compilation work as soon as possible. + * The provided callback is guaranteed to be called on the main thread after all specified + * variants of the material are compiled. This can take hundreds of milliseconds. + *

+ * If all the material's variants are already compiled, the callback will be scheduled as + * soon as possible, but this might take a few dozen millisecond, corresponding to how + * many previous frames are enqueued in the backend. This also varies by backend. Therefore, + * it is recommended to only call this method once per material shortly after creation. + *

+ *

+ * If the same variant is scheduled for compilation multiple times, the first scheduling + * takes precedence; later scheduling are ignored. + *

+ *

+ * caveat: A consequence is that if a variant is scheduled on the low priority queue and later + * scheduled again on the high priority queue, the later scheduling is ignored. + * Therefore, the second callback could be called before the variant is compiled. + * However, the first callback, if specified, will trigger as expected. + *

+ *

+ * The callback is guaranteed to be called. If the engine is destroyed while some material + * variants are still compiling or in the queue, these will be discarded and the corresponding + * callback will be called. In that case however the Material pointer passed to the callback + * is guaranteed to be invalid (either because it's been destroyed by the user already, or, + * because it's been cleaned-up by the Engine). + *

+ *

+ * {@link UserVariantFilterBit#ALL} should be used with caution. Only variants that an application + * needs should be included in the variants argument. For example, the STE variant is only used + * for stereoscopic rendering. If an application is not planning to render in stereo, this bit + * should be turned off to avoid unnecessary material compilations. + *

+ * @param priority Which priority queue to use, LOW or HIGH. + * @param variants Variants to include to the compile command. + * @param handler An {@link java.util.concurrent.Executor Executor}. On Android this can also be a {@link android.os.Handler Handler}. + * @param callback callback called on the main thread when the compilation is done on + * by backend. + */ + public void compile(@NonNull CompilerPriorityQueue priority, + int variants, + @Nullable Object handler, + @Nullable Runnable callback) { + nCompile(getNativeObject(), priority.ordinal(), variants, handler, callback); + } + /** * Creates a new instance of this material. Material instances should be freed using * {@link Engine#destroyMaterialInstance(MaterialInstance)}. @@ -953,6 +1028,7 @@ void clearNativeObject() { private static native long nCreateInstanceWithName(long nativeMaterial, @NonNull String name); private static native long nGetDefaultInstance(long nativeMaterial); + private static native void nCompile(long nativeMaterial, int priority, int variants, Object handler, Runnable runnable); private static native String nGetName(long nativeMaterial); private static native int nGetShading(long nativeMaterial); private static native int nGetInterpolation(long nativeMaterial); 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 fd425fbeaac..9a41a4f64e5 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 @@ -16,6 +16,7 @@ package com.google.android.filament; +import androidx.annotation.NonNull; import androidx.annotation.Nullable; /** @@ -190,6 +191,52 @@ public long getNativeObject() { return mNativeObject; } + /** + * Returns the list of all entities in the Scene. If outArray is provided and large enough, + * it is used to store the list and returned, otherwise a new array is allocated and returned. + * @param outArray an array to store the list of entities in the scene. + * @return outArray if it was used or a newly allocated array. + * @see #getEntityCount + */ + public int[] getEntities(@Nullable int[] outArray) { + int c = getEntityCount(); + if (outArray == null || outArray.length < c) { + outArray = new int[c]; + } + boolean success = nGetEntities(getNativeObject(), outArray, outArray.length); + if (!success) { + throw new IllegalStateException("Error retriving Scene's entities"); + } + return outArray; + } + + /** + * Returns the list of all entities in the Scene in a newly allocated array. + * @return an array containing the list of all entities in the scene. + * @see #getEntityCount + */ + public int[] getEntities() { + return getEntities(null); + } + + public interface EntityProcessor { + void process(@Entity int entity); + } + + /** + * Invokes user functor on each entity in the scene. + * + * It is not allowed to add or remove an entity from the scene within the functor. + * + * @param entityProcessor User provided functor called for each entity in the scene + */ + public void forEach(@NonNull EntityProcessor entityProcessor) { + int[] entities = getEntities(null); + for (int entity : entities) { + entityProcessor.process(entity); + } + } + void clearNativeObject() { mNativeObject = 0; } @@ -204,4 +251,5 @@ void clearNativeObject() { private static native int nGetRenderableCount(long nativeScene); private static native int nGetLightCount(long nativeScene); private static native boolean nHasEntity(long nativeScene, int entity); + private static native boolean nGetEntities(long nativeScene, int[] outArray, int length); } diff --git a/android/samples/sample-gltf-viewer/src/main/java/com/google/android/filament/gltf/MainActivity.kt b/android/samples/sample-gltf-viewer/src/main/java/com/google/android/filament/gltf/MainActivity.kt index 372cba57004..102c4944e6e 100644 --- a/android/samples/sample-gltf-viewer/src/main/java/com/google/android/filament/gltf/MainActivity.kt +++ b/android/samples/sample-gltf-viewer/src/main/java/com/google/android/filament/gltf/MainActivity.kt @@ -26,6 +26,7 @@ import android.widget.TextView import android.widget.Toast import com.google.android.filament.Fence import com.google.android.filament.IndirectLight +import com.google.android.filament.Material import com.google.android.filament.Skybox import com.google.android.filament.View import com.google.android.filament.View.OnPickCallback @@ -392,6 +393,36 @@ class MainActivity : Activity() { Log.i(TAG, "The Filament backend took $total ms to load the model geometry.") modelViewer.engine.destroyFence(it) loadStartFence = null + + val materials = mutableSetOf() + val rcm = modelViewer.engine.renderableManager + modelViewer.scene.forEach { + val entity = it + if (rcm.hasComponent(entity)) { + val ri = rcm.getInstance(entity) + val c = rcm.getPrimitiveCount(ri) + for (i in 0 until c) { + val mi = rcm.getMaterialInstanceAt(ri, i) + val ma = mi.material + materials.add(ma) + } + } + } + materials.forEach { + it.compile( + Material.CompilerPriorityQueue.HIGH, + Material.UserVariantFilterBit.DIRECTIONAL_LIGHTING or + Material.UserVariantFilterBit.DYNAMIC_LIGHTING or + Material.UserVariantFilterBit.SHADOW_RECEIVER, + null, null) + it.compile( + Material.CompilerPriorityQueue.LOW, + Material.UserVariantFilterBit.FOG or + Material.UserVariantFilterBit.SKINNING or + Material.UserVariantFilterBit.SSR or + Material.UserVariantFilterBit.VSM, + null, null) + } } } diff --git a/android/samples/sample-hello-triangle/src/main/java/com/google/android/filament/hellotriangle/MainActivity.kt b/android/samples/sample-hello-triangle/src/main/java/com/google/android/filament/hellotriangle/MainActivity.kt index 8571ee4d60a..f4b1e868178 100644 --- a/android/samples/sample-hello-triangle/src/main/java/com/google/android/filament/hellotriangle/MainActivity.kt +++ b/android/samples/sample-hello-triangle/src/main/java/com/google/android/filament/hellotriangle/MainActivity.kt @@ -20,6 +20,8 @@ import android.animation.ValueAnimator import android.app.Activity import android.opengl.Matrix import android.os.Bundle +import android.os.Handler +import android.os.Looper import android.view.Choreographer import android.view.Surface import android.view.SurfaceView @@ -157,6 +159,14 @@ class MainActivity : Activity() { private fun loadMaterial() { readUncompressedAsset("materials/baked_color.filamat").let { material = Material.Builder().payload(it, it.remaining()).build(engine) + material.compile( + Material.CompilerPriorityQueue.HIGH, + Material.UserVariantFilterBit.ALL, + Handler(Looper.getMainLooper())) { + android.util.Log.i("hellotriangle", + "Material " + material.name + " compiled.") + } + engine.flush() } } From d5bab43cebbba93a8af933a0b5ac457d21adfd8e Mon Sep 17 00:00:00 2001 From: Powei Feng Date: Mon, 13 Nov 2023 22:26:03 -0800 Subject: [PATCH 09/23] Revert "engine: move setFrontFaceWindingInverted from View to MaterialInstance (#7331)" (#7360) This reverts commit 038f07cb345702af9bda7bea21f6870c7e53c42f. --- RELEASE_NOTES.md | 1 - .../src/main/cpp/MaterialInstance.cpp | 34 ++++------- .../filament-android/src/main/cpp/View.cpp | 14 +++++ .../android/filament/MaterialInstance.java | 29 --------- .../com/google/android/filament/View.java | 28 +++++++++ filament/include/filament/MaterialInstance.h | 21 ------- filament/include/filament/View.h | 21 +++++++ filament/src/MaterialInstance.cpp | 8 --- filament/src/RenderPass.cpp | 17 +++--- filament/src/RenderPass.h | 11 ++-- filament/src/View.cpp | 8 +++ filament/src/details/MaterialInstance.cpp | 4 +- filament/src/details/MaterialInstance.h | 10 ---- filament/src/details/Renderer.cpp | 1 + filament/src/details/View.h | 5 ++ samples/rendertarget.cpp | 59 ++++++++++--------- web/filament-js/jsbindings.cpp | 5 +- 17 files changed, 136 insertions(+), 140 deletions(-) diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index c6a410af3b3..20a3c0fab4c 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -17,7 +17,6 @@ Instead, if you are authoring a PR for the main branch, add your release note to - engine: Add `Material::getFeatureLevel()` - engine: Add missing `Material::getReflectionMode()` method in Java - engine: Support basic usage of post-processing materials on feature level 0 -- engine: move `setFrontFaceWindingInverted` from `View` to `MaterialInstance` [**API CHANGE**] ## v1.45.1 diff --git a/android/filament-android/src/main/cpp/MaterialInstance.cpp b/android/filament-android/src/main/cpp/MaterialInstance.cpp index 1046c75a5a4..c0b0bcb7168 100644 --- a/android/filament-android/src/main/cpp/MaterialInstance.cpp +++ b/android/filament-android/src/main/cpp/MaterialInstance.cpp @@ -433,13 +433,6 @@ Java_com_google_android_filament_MaterialInstance_nSetStencilWriteMask(JNIEnv*, instance->setStencilWriteMask(writeMask, static_cast(face)); } -extern "C" JNIEXPORT void JNICALL -Java_com_google_android_filament_MaterialInstance_nSetFrontFaceWindingInverted(JNIEnv*, - jclass, jlong nativeMaterialInstance, jboolean inverted) { - MaterialInstance* instance = (MaterialInstance*) nativeMaterialInstance; - instance->setFrontFaceWindingInverted(inverted); -} - extern "C" JNIEXPORT jstring JNICALL Java_com_google_android_filament_MaterialInstance_nGetName(JNIEnv* env, jclass, @@ -476,7 +469,7 @@ extern "C" JNIEXPORT jfloat JNICALL Java_com_google_android_filament_MaterialInstance_nGetMaskThreshold(JNIEnv* env, jclass clazz, jlong nativeMaterialInstance) { - MaterialInstance* instance = (MaterialInstance*) nativeMaterialInstance; + MaterialInstance* instance = (MaterialInstance*)nativeMaterialInstance; return instance->getMaskThreshold(); } @@ -484,7 +477,7 @@ extern "C" JNIEXPORT jfloat JNICALL Java_com_google_android_filament_MaterialInstance_nGetSpecularAntiAliasingVariance(JNIEnv* env, jclass clazz, jlong nativeMaterialInstance) { - MaterialInstance* instance = (MaterialInstance*) nativeMaterialInstance; + MaterialInstance* instance = (MaterialInstance*)nativeMaterialInstance; return instance->getSpecularAntiAliasingVariance(); } @@ -492,7 +485,7 @@ extern "C" JNIEXPORT jfloat JNICALL Java_com_google_android_filament_MaterialInstance_nGetSpecularAntiAliasingThreshold(JNIEnv* env, jclass clazz, jlong nativeMaterialInstance) { - MaterialInstance* instance = (MaterialInstance*) nativeMaterialInstance; + MaterialInstance* instance = (MaterialInstance*)nativeMaterialInstance; return instance->getSpecularAntiAliasingThreshold(); } @@ -500,7 +493,7 @@ extern "C" JNIEXPORT jboolean JNICALL Java_com_google_android_filament_MaterialInstance_nIsDoubleSided(JNIEnv* env, jclass clazz, jlong nativeMaterialInstance) { - MaterialInstance* instance = (MaterialInstance*) nativeMaterialInstance; + MaterialInstance* instance = (MaterialInstance*)nativeMaterialInstance; return instance->isDoubleSided(); } @@ -508,7 +501,7 @@ extern "C" JNIEXPORT jint JNICALL Java_com_google_android_filament_MaterialInstance_nGetCullingMode(JNIEnv* env, jclass clazz, jlong nativeMaterialInstance) { - MaterialInstance* instance = (MaterialInstance*) nativeMaterialInstance; + MaterialInstance* instance = (MaterialInstance*)nativeMaterialInstance; return (jint)instance->getCullingMode(); } @@ -516,7 +509,7 @@ extern "C" JNIEXPORT jboolean JNICALL Java_com_google_android_filament_MaterialInstance_nIsColorWriteEnabled(JNIEnv* env, jclass clazz, jlong nativeMaterialInstance) { - MaterialInstance* instance = (MaterialInstance*) nativeMaterialInstance; + MaterialInstance* instance = (MaterialInstance*)nativeMaterialInstance; return instance->isColorWriteEnabled(); } @@ -524,7 +517,7 @@ extern "C" JNIEXPORT jboolean JNICALL Java_com_google_android_filament_MaterialInstance_nIsDepthWriteEnabled(JNIEnv* env, jclass clazz, jlong nativeMaterialInstance) { - MaterialInstance* instance = (MaterialInstance*) nativeMaterialInstance; + MaterialInstance* instance = (MaterialInstance*)nativeMaterialInstance; return instance->isDepthWriteEnabled(); } @@ -532,7 +525,7 @@ extern "C" JNIEXPORT jboolean JNICALL Java_com_google_android_filament_MaterialInstance_nIsStencilWriteEnabled(JNIEnv* env, jclass clazz, jlong nativeMaterialInstance) { - MaterialInstance* instance = (MaterialInstance*) nativeMaterialInstance; + MaterialInstance* instance = (MaterialInstance*)nativeMaterialInstance; return instance->isStencilWriteEnabled(); } @@ -540,7 +533,7 @@ extern "C" JNIEXPORT jboolean JNICALL Java_com_google_android_filament_MaterialInstance_nIsDepthCullingEnabled(JNIEnv* env, jclass clazz, jlong nativeMaterialInstance) { - MaterialInstance* instance = (MaterialInstance*) nativeMaterialInstance; + MaterialInstance* instance = (MaterialInstance*)nativeMaterialInstance; return instance->isDepthCullingEnabled(); } @@ -548,13 +541,6 @@ extern "C" JNIEXPORT jint JNICALL Java_com_google_android_filament_MaterialInstance_nGetDepthFunc(JNIEnv* env, jclass clazz, jlong nativeMaterialInstance) { - MaterialInstance* instance = (MaterialInstance*) nativeMaterialInstance; + MaterialInstance* instance = (MaterialInstance*)nativeMaterialInstance; return (jint)instance->getDepthFunc(); } - -extern "C" JNIEXPORT jboolean JNICALL -Java_com_google_android_filament_MaterialInstance_nIsFrontFaceWindingInverted(JNIEnv*, - jclass, jlong nativeMaterialInstance) { - MaterialInstance* instance = (MaterialInstance*) nativeMaterialInstance; - return static_cast(instance->isFrontFaceWindingInverted()); -} diff --git a/android/filament-android/src/main/cpp/View.cpp b/android/filament-android/src/main/cpp/View.cpp index 047b71105fe..fc4de145e90 100644 --- a/android/filament-android/src/main/cpp/View.cpp +++ b/android/filament-android/src/main/cpp/View.cpp @@ -202,6 +202,20 @@ Java_com_google_android_filament_View_nIsPostProcessingEnabled(JNIEnv*, return static_cast(view->isPostProcessingEnabled()); } +extern "C" JNIEXPORT void JNICALL +Java_com_google_android_filament_View_nSetFrontFaceWindingInverted(JNIEnv*, + jclass, jlong nativeView, jboolean inverted) { + View* view = (View*) nativeView; + view->setFrontFaceWindingInverted(inverted); +} + +extern "C" JNIEXPORT jboolean JNICALL +Java_com_google_android_filament_View_nIsFrontFaceWindingInverted(JNIEnv*, + jclass, jlong nativeView) { + View* view = (View*) nativeView; + return static_cast(view->isFrontFaceWindingInverted()); +} + extern "C" JNIEXPORT void JNICALL Java_com_google_android_filament_View_nSetAmbientOcclusion(JNIEnv*, jclass, jlong nativeView, jint ordinal) { View* view = (View*) nativeView; diff --git a/android/filament-android/src/main/java/com/google/android/filament/MaterialInstance.java b/android/filament-android/src/main/java/com/google/android/filament/MaterialInstance.java index 44152f9bd7a..389999b7d8e 100644 --- a/android/filament-android/src/main/java/com/google/android/filament/MaterialInstance.java +++ b/android/filament-android/src/main/java/com/google/android/filament/MaterialInstance.java @@ -850,32 +850,6 @@ public void setStencilWriteMask(int writeMask) { setStencilWriteMask(writeMask, StencilFace.FRONT_AND_BACK); } - /** - * Returns true if front face winding order is inverted. - * - * @see #setFrontFaceWindingInverted - */ - public boolean isFrontFaceWindingInverted() { - return nIsFrontFaceWindingInverted(getNativeObject()); - } - - /** - * Inverts the winding order of front faces. By default front faces use a counter-clockwise - * winding order. When the winding order is inverted, front faces are faces with a clockwise - * winding order. - * - * Changing the winding order will directly affect the culling mode in materials - * (see Material#getCullingMode). - * - * Inverting the winding order of front faces is useful when rendering mirrored reflections - * (water, mirror surfaces, front camera in AR, etc.). - * - * @param inverted True to invert front faces, false otherwise. - */ - public void setFrontFaceWindingInverted(boolean inverted) { - nSetFrontFaceWindingInverted(getNativeObject(), inverted); - } - public long getNativeObject() { if (mNativeObject == 0) { throw new IllegalStateException("Calling method on destroyed MaterialInstance"); @@ -967,9 +941,6 @@ private static native void nSetStencilReadMask(long nativeMaterialInstance, int private static native void nSetStencilWriteMask(long nativeMaterialInstance, int writeMask, long face); - private static native void nSetFrontFaceWindingInverted(long nativeView, boolean inverted); - private static native boolean nIsFrontFaceWindingInverted(long nativeView); - private static native String nGetName(long nativeMaterialInstance); private static native long nGetMaterial(long nativeMaterialInstance); diff --git a/android/filament-android/src/main/java/com/google/android/filament/View.java b/android/filament-android/src/main/java/com/google/android/filament/View.java index b5f958369f3..91622d26c41 100644 --- a/android/filament-android/src/main/java/com/google/android/filament/View.java +++ b/android/filament-android/src/main/java/com/google/android/filament/View.java @@ -709,6 +709,32 @@ public void setPostProcessingEnabled(boolean enabled) { nSetPostProcessingEnabled(getNativeObject(), enabled); } + /** + * Returns true if post-processing is enabled. + * + * @see #setPostProcessingEnabled + */ + public boolean isFrontFaceWindingInverted() { + return nIsFrontFaceWindingInverted(getNativeObject()); + } + + /** + * Inverts the winding order of front faces. By default front faces use a counter-clockwise + * winding order. When the winding order is inverted, front faces are faces with a clockwise + * winding order. + * + * Changing the winding order will directly affect the culling mode in materials + * (see Material#getCullingMode). + * + * Inverting the winding order of front faces is useful when rendering mirrored reflections + * (water, mirror surfaces, front camera in AR, etc.). + * + * @param inverted True to invert front faces, false otherwise. + */ + public void setFrontFaceWindingInverted(boolean inverted) { + nSetFrontFaceWindingInverted(getNativeObject(), inverted); + } + /** * Sets options relative to dynamic lighting for this view. * @@ -1185,6 +1211,8 @@ void clearNativeObject() { private static native void nSetColorGrading(long nativeView, long nativeColorGrading); private static native void nSetPostProcessingEnabled(long nativeView, boolean enabled); private static native boolean nIsPostProcessingEnabled(long nativeView); + private static native void nSetFrontFaceWindingInverted(long nativeView, boolean inverted); + private static native boolean nIsFrontFaceWindingInverted(long nativeView); private static native void nSetAmbientOcclusion(long nativeView, int ordinal); private static native int nGetAmbientOcclusion(long nativeView); private static native void nSetAmbientOcclusionOptions(long nativeView, float radius, float bias, float power, float resolution, float intensity, float bilateralThreshold, int quality, int lowPassFilter, int upsampling, boolean enabled, boolean bentNormals, float minHorizonAngleRad); diff --git a/filament/include/filament/MaterialInstance.h b/filament/include/filament/MaterialInstance.h index cbfe230e6b2..0c6ef205730 100644 --- a/filament/include/filament/MaterialInstance.h +++ b/filament/include/filament/MaterialInstance.h @@ -480,27 +480,6 @@ class UTILS_PUBLIC MaterialInstance : public FilamentAPI { void setStencilWriteMask(uint8_t writeMask, StencilFace face = StencilFace::FRONT_AND_BACK) noexcept; - /** - * Inverts the winding order of front faces. By default front faces use a counter-clockwise - * winding order. When the winding order is inverted, front faces are faces with a clockwise - * winding order. - * - * Changing the winding order will directly affect the culling mode in materials - * (see Material::getCullingMode()). - * - * Inverting the winding order of front faces is useful when rendering mirrored reflections - * (water, mirror surfaces, front camera in AR, etc.). - * - * @param inverted True to invert front faces, false otherwise. - */ - void setFrontFaceWindingInverted(bool inverted) noexcept; - - /** - * Returns true if the winding order of front faces is inverted. - * See setFrontFaceWindingInverted() for more information. - */ - bool isFrontFaceWindingInverted() const noexcept; - protected: // prevent heap allocation ~MaterialInstance() = default; diff --git a/filament/include/filament/View.h b/filament/include/filament/View.h index ffa15367b7f..a58587e9ede 100644 --- a/filament/include/filament/View.h +++ b/filament/include/filament/View.h @@ -629,6 +629,27 @@ class UTILS_PUBLIC View : public FilamentAPI { //! Returns true if post-processing is enabled. See setPostProcessingEnabled() for more info. bool isPostProcessingEnabled() const noexcept; + /** + * Inverts the winding order of front faces. By default front faces use a counter-clockwise + * winding order. When the winding order is inverted, front faces are faces with a clockwise + * winding order. + * + * Changing the winding order will directly affect the culling mode in materials + * (see Material::getCullingMode()). + * + * Inverting the winding order of front faces is useful when rendering mirrored reflections + * (water, mirror surfaces, front camera in AR, etc.). + * + * @param inverted True to invert front faces, false otherwise. + */ + void setFrontFaceWindingInverted(bool inverted) noexcept; + + /** + * Returns true if the winding order of front faces is inverted. + * See setFrontFaceWindingInverted() for more information. + */ + bool isFrontFaceWindingInverted() const noexcept; + /** * Enables use of the stencil buffer. * diff --git a/filament/src/MaterialInstance.cpp b/filament/src/MaterialInstance.cpp index e11e6e8b60c..33f3ab764b1 100644 --- a/filament/src/MaterialInstance.cpp +++ b/filament/src/MaterialInstance.cpp @@ -340,12 +340,4 @@ bool MaterialInstance::isDepthCullingEnabled() const noexcept { return downcast(this)->isDepthCullingEnabled(); } -void MaterialInstance::setFrontFaceWindingInverted(bool inverted) noexcept { - downcast(this)->setFrontFaceWindingInverted(inverted); -} - -bool MaterialInstance::isFrontFaceWindingInverted() const noexcept { - return downcast(this)->isFrontFaceWindingInverted(); -} - } // namespace filament diff --git a/filament/src/RenderPass.cpp b/filament/src/RenderPass.cpp index 30aff5ac0ff..2932fcf481b 100644 --- a/filament/src/RenderPass.cpp +++ b/filament/src/RenderPass.cpp @@ -326,7 +326,7 @@ void RenderPass::instanceify(FEngine& engine) noexcept { UTILS_ALWAYS_INLINE // this function exists only to make the code more readable. we want it inlined. inline // and we don't need it in the compilation unit void RenderPass::setupColorCommand(Command& cmdDraw, Variant variant, - FMaterialInstance const* const UTILS_RESTRICT mi, bool invertedFrontFaces) noexcept { + FMaterialInstance const* const UTILS_RESTRICT mi, bool inverseFrontFaces) noexcept { FMaterial const * const UTILS_RESTRICT ma = mi->getMaterial(); variant = Variant::filterVariant(variant, ma->isVariantLit()); @@ -362,7 +362,7 @@ void RenderPass::setupColorCommand(Command& cmdDraw, Variant variant, cmdDraw.primitive.rasterState.blendFunctionDstAlpha = blendingMustBeOff ? BlendFunction::ZERO : cmdDraw.primitive.rasterState.blendFunctionDstAlpha; - cmdDraw.primitive.rasterState.inverseFrontFaces = invertedFrontFaces; + cmdDraw.primitive.rasterState.inverseFrontFaces = inverseFrontFaces; cmdDraw.primitive.rasterState.culling = mi->getCullingMode(); cmdDraw.primitive.rasterState.colorWrite = mi->isColorWriteEnabled(); cmdDraw.primitive.rasterState.depthWrite = mi->isDepthWriteEnabled(); @@ -463,6 +463,7 @@ RenderPass::Command* RenderPass::generateCommandsImpl(uint32_t extraFlags, auto const* const UTILS_RESTRICT soaInstanceInfo = soa.data(); const bool hasShadowing = renderFlags & HAS_SHADOWING; + const bool viewInverseFrontFaces = renderFlags & HAS_INVERSE_FRONT_FACES; const bool hasInstancedStereo = renderFlags & IS_STEREOSCOPIC; Command cmdColor; @@ -517,6 +518,7 @@ RenderPass::Command* RenderPass::generateCommandsImpl(uint32_t extraFlags, const uint32_t distanceBits = reinterpret_cast(distance); // calculate the per-primitive face winding order inversion + const bool inverseFrontFaces = viewInverseFrontFaces ^ soaVisibility[i].reversedWindingOrder; const bool hasMorphing = soaVisibility[i].morphing; const bool hasSkinningOrMorphing = soaVisibility[i].skinning || hasMorphing; @@ -554,6 +556,7 @@ RenderPass::Command* RenderPass::generateCommandsImpl(uint32_t extraFlags, soaInstanceInfo[i].count | PrimitiveInfo::USER_INSTANCE_MASK; cmdDepth.primitive.instanceBufferHandle = soaInstanceInfo[i].handle; cmdDepth.primitive.materialVariant.setSkinning(hasSkinningOrMorphing); + cmdDepth.primitive.rasterState.inverseFrontFaces = inverseFrontFaces; if (UTILS_UNLIKELY(hasInstancedStereo)) { cmdColor.primitive.instanceCount = @@ -565,9 +568,8 @@ RenderPass::Command* RenderPass::generateCommandsImpl(uint32_t extraFlags, renderableVariant.setFog(soaVisibility[i].fog && Variant::isFogVariant(variant)); } - bool const shadowCaster = soaVisibility[i].castShadows & hasShadowing; - bool const writeDepthForShadowCasters = depthContainsShadowCasters & shadowCaster; - bool const reverseWindingOrder = soaVisibility[i].reversedWindingOrder; + const bool shadowCaster = soaVisibility[i].castShadows & hasShadowing; + const bool writeDepthForShadowCasters = depthContainsShadowCasters & shadowCaster; const Slice& primitives = soaPrimitives[i]; const FRenderableManager::SkinningBindingInfo& skinning = soaSkinning[i]; @@ -582,11 +584,10 @@ RenderPass::Command* RenderPass::generateCommandsImpl(uint32_t extraFlags, auto const& morphTargets = morphing.targets[pi]; FMaterialInstance const* const mi = primitive.getMaterialInstance(); FMaterial const* const ma = mi->getMaterial(); - bool const invertedFrontFaces = mi->isFrontFaceWindingInverted() ^ reverseWindingOrder; if constexpr (isColorPass) { cmdColor.primitive.primitiveHandle = primitive.getHwHandle(); - RenderPass::setupColorCommand(cmdColor, renderableVariant, mi, invertedFrontFaces); + RenderPass::setupColorCommand(cmdColor, renderableVariant, mi, inverseFrontFaces); cmdColor.primitive.skinningHandle = skinning.handle; cmdColor.primitive.skinningOffset = skinning.offset; @@ -615,6 +616,7 @@ RenderPass::Command* RenderPass::generateCommandsImpl(uint32_t extraFlags, cmdColor.key |= makeField(primitive.getBlendOrder(), BLEND_ORDER_MASK, BLEND_ORDER_SHIFT); + const TransparencyMode mode = mi->getTransparencyMode(); // handle transparent objects, two techniques: @@ -692,7 +694,6 @@ RenderPass::Command* RenderPass::generateCommandsImpl(uint32_t extraFlags, cmdDepth.primitive.primitiveHandle = primitive.getHwHandle(); cmdDepth.primitive.mi = mi; cmdDepth.primitive.rasterState.culling = mi->getCullingMode(); - cmdDepth.primitive.rasterState.inverseFrontFaces = invertedFrontFaces; cmdDepth.primitive.skinningHandle = skinning.handle; cmdDepth.primitive.skinningOffset = skinning.offset; diff --git a/filament/src/RenderPass.h b/filament/src/RenderPass.h index 3ef806365ab..4474079594f 100644 --- a/filament/src/RenderPass.h +++ b/filament/src/RenderPass.h @@ -264,7 +264,8 @@ class RenderPass { using RenderFlags = uint8_t; static constexpr RenderFlags HAS_SHADOWING = 0x01; - static constexpr RenderFlags IS_STEREOSCOPIC = 0x02; + static constexpr RenderFlags HAS_INVERSE_FRONT_FACES = 0x02; + static constexpr RenderFlags IS_STEREOSCOPIC = 0x04; // Arena used for commands using Arena = utils::Arena< @@ -417,11 +418,11 @@ class RenderPass { math::float3 cameraPosition, math::float3 cameraForward, uint8_t instancedStereoEyeCount) noexcept; - static void setupColorCommand(Command& cmdDraw, Variant variant, FMaterialInstance const* mi, - bool invertedFrontFaces) noexcept; + static void setupColorCommand(Command& cmdDraw, Variant variant, + FMaterialInstance const* mi, bool inverseFrontFaces) noexcept; - static void updateSummedPrimitiveCounts(FScene::RenderableSoa& renderableData, - utils::Range vr) noexcept; + static void updateSummedPrimitiveCounts( + FScene::RenderableSoa& renderableData, utils::Range vr) noexcept; // a reference to the Engine, mostly to get to things like JobSystem diff --git a/filament/src/View.cpp b/filament/src/View.cpp index fd915fbc23b..dd8e9380a75 100644 --- a/filament/src/View.cpp +++ b/filament/src/View.cpp @@ -171,6 +171,14 @@ bool View::isPostProcessingEnabled() const noexcept { return downcast(this)->hasPostProcessPass(); } +void View::setFrontFaceWindingInverted(bool inverted) noexcept { + downcast(this)->setFrontFaceWindingInverted(inverted); +} + +bool View::isFrontFaceWindingInverted() const noexcept { + return downcast(this)->isFrontFaceWindingInverted(); +} + void View::setDynamicLightingOptions(float zLightNear, float zLightFar) noexcept { downcast(this)->setDynamicLightingOptions(zLightNear, zLightFar); } diff --git a/filament/src/details/MaterialInstance.cpp b/filament/src/details/MaterialInstance.cpp index fafd6c71aff..ad3ea314ce0 100644 --- a/filament/src/details/MaterialInstance.cpp +++ b/filament/src/details/MaterialInstance.cpp @@ -42,8 +42,7 @@ FMaterialInstance::FMaterialInstance() noexcept mDepthWrite(false), mHasScissor(false), mIsDoubleSided(false), - mTransparencyMode(TransparencyMode::DEFAULT), - mFrontFaceWindingInverted(false) { + mTransparencyMode(TransparencyMode::DEFAULT) { } FMaterialInstance::FMaterialInstance(FEngine& engine, @@ -61,7 +60,6 @@ FMaterialInstance::FMaterialInstance(FEngine& engine, mHasScissor(false), mIsDoubleSided(other->mIsDoubleSided), mScissorRect(other->mScissorRect), - mFrontFaceWindingInverted(other->mFrontFaceWindingInverted), mName(name ? CString(name) : other->mName) { FEngine::DriverApi& driver = engine.getDriverApi(); diff --git a/filament/src/details/MaterialInstance.h b/filament/src/details/MaterialInstance.h index aa01d1b7799..6be23b7e06c 100644 --- a/filament/src/details/MaterialInstance.h +++ b/filament/src/details/MaterialInstance.h @@ -205,14 +205,6 @@ class FMaterialInstance : public MaterialInstance { } } - void setFrontFaceWindingInverted(bool inverted) noexcept { - mFrontFaceWindingInverted = inverted; - } - - bool isFrontFaceWindingInverted() const noexcept { - return mFrontFaceWindingInverted; - } - const char* getName() const noexcept; void setParameter(std::string_view name, @@ -275,8 +267,6 @@ class FMaterialInstance : public MaterialInstance { (uint32_t)std::numeric_limits::max() }; - bool mFrontFaceWindingInverted : 1; - utils::CString mName; }; diff --git a/filament/src/details/Renderer.cpp b/filament/src/details/Renderer.cpp index ea30a0a4ea3..1304ccb3406 100644 --- a/filament/src/details/Renderer.cpp +++ b/filament/src/details/Renderer.cpp @@ -626,6 +626,7 @@ void FRenderer::renderJob(ArenaScope& arena, FView& view) { RenderPass::RenderFlags renderFlags = 0; if (view.hasShadowing()) renderFlags |= RenderPass::HAS_SHADOWING; + if (view.isFrontFaceWindingInverted()) renderFlags |= RenderPass::HAS_INVERSE_FRONT_FACES; if (view.hasInstancedStereo()) renderFlags |= RenderPass::IS_STEREOSCOPIC; RenderPass pass(engine, commandArena); diff --git a/filament/src/details/View.h b/filament/src/details/View.h index 504beabdf8d..c2152a52f55 100644 --- a/filament/src/details/View.h +++ b/filament/src/details/View.h @@ -115,6 +115,10 @@ class FView : public View { void setFrustumCullingEnabled(bool culling) noexcept { mCulling = culling; } bool isFrustumCullingEnabled() const noexcept { return mCulling; } + void setFrontFaceWindingInverted(bool inverted) noexcept { mFrontFaceWindingInverted = inverted; } + bool isFrontFaceWindingInverted() const noexcept { return mFrontFaceWindingInverted; } + + void setVisibleLayers(uint8_t select, uint8_t values) noexcept; uint8_t getVisibleLayers() const noexcept { return mVisibleLayers; @@ -491,6 +495,7 @@ class FView : public View { Viewport mViewport; bool mCulling = true; + bool mFrontFaceWindingInverted = false; FRenderTarget* mRenderTarget = nullptr; diff --git a/samples/rendertarget.cpp b/samples/rendertarget.cpp index 30025013447..9ff53ccc37d 100644 --- a/samples/rendertarget.cpp +++ b/samples/rendertarget.cpp @@ -65,14 +65,13 @@ struct App { View* offscreenView = nullptr; Scene* offscreenScene = nullptr; Camera* offscreenCamera = nullptr; - MaterialInstance* invertedMeshMatInstance = nullptr; enum class ReflectionMode { - NEGATIVE_SCALE_TRANSFORM, - MATERIAL, + RENDERABLES, + CAMERA, }; - ReflectionMode mode = ReflectionMode::MATERIAL; + ReflectionMode mode = ReflectionMode::CAMERA; Config config; utils::Entity quadEntity; @@ -106,6 +105,22 @@ static mat4f reflectionMatrix(float4 plane) { return transpose(m); } +static void setReflectionMode(App& app, App::ReflectionMode mode) { + switch (mode) { + case App::ReflectionMode::RENDERABLES: + app.offscreenScene->addEntity(app.reflectedMonkey); + app.offscreenScene->remove(app.monkeyMesh.renderable); + app.offscreenView->setFrontFaceWindingInverted(false); + break; + case App::ReflectionMode::CAMERA: + app.offscreenScene->addEntity(app.monkeyMesh.renderable); + app.offscreenScene->remove(app.reflectedMonkey); + app.offscreenView->setFrontFaceWindingInverted(true); + break; + } + app.mode = mode; +} + static void printUsage(char* name) { std::string exec_name(utils::Path(name).getName()); std::string usage( @@ -118,7 +133,7 @@ static void printUsage(char* name) { " --api, -a\n" " Specify the backend API: opengl (default), vulkan, or metal\n" " --mode, -m\n" - " Specify the reflection mode: material (default), or ntransform\n\n" + " Specify the reflection mode: camera (default), or renderables\n\n" ); const std::string from("SHOWCASE"); for (size_t pos = usage.find(from); pos != std::string::npos; pos = usage.find(from, pos)) { @@ -157,12 +172,12 @@ static int handleCommandLineArguments(int argc, char* argv[], App* app) { } break; case 'm': - if (arg == "material") { - app->mode = App::ReflectionMode::MATERIAL; - } else if (arg == "ntransform") { - app->mode = App::ReflectionMode::NEGATIVE_SCALE_TRANSFORM; + if (arg == "camera") { + app->mode = App::ReflectionMode::CAMERA; + } else if (arg == "renderables") { + app->mode = App::ReflectionMode::RENDERABLES; } else { - std::cerr << "Unrecognized mode. Must be 'materail'|'ntransform'.\n"; + std::cerr << "Unrecognized mode. Must be 'camera'|'renderables'.\n"; exit(1); } break; @@ -182,11 +197,6 @@ int main(int argc, char** argv) { auto& em = utils::EntityManager::get(); auto vp = view->getViewport(); - // For Vulkan, DEPTH32F is more readily available. - auto const depthFormat = app.config.backend == Engine::Backend::VULKAN - ? Texture::InternalFormat::DEPTH32F - : Texture::InternalFormat::DEPTH24; - // Instantiate offscreen render target. app.offscreenView = engine->createView(); app.offscreenScene = engine->createScene(); @@ -199,7 +209,7 @@ int main(int argc, char** argv) { app.offscreenDepthTexture = Texture::Builder() .width(vp.width).height(vp.height).levels(1) .usage(Texture::Usage::DEPTH_ATTACHMENT) - .format(depthFormat).build(*engine); + .format(Texture::InternalFormat::DEPTH24).build(*engine); app.offscreenRenderTarget = RenderTarget::Builder() .texture(RenderTarget::AttachmentPoint::COLOR, app.offscreenColorTexture) .texture(RenderTarget::AttachmentPoint::DEPTH, app.offscreenDepthTexture) @@ -276,22 +286,19 @@ int main(int argc, char** argv) { rcm.setCastShadows(rcm.getInstance(app.monkeyMesh.renderable), false); scene->addEntity(app.monkeyMesh.renderable); - auto invertedMi = app.invertedMeshMatInstance = MaterialInstance::duplicate(mi); - invertedMi->setFrontFaceWindingInverted(true); - - // Create a reflected monkey. + // Create a reflected monkey, which is used only for App::ReflectionMode::RENDERABLES. app.reflectedMonkey = em.create(); RenderableManager::Builder(1) .boundingBox({{ -2, -2, -2 }, { 2, 2, 2 }}) - .material(0, app.mode == App::ReflectionMode::MATERIAL ? invertedMi : mi) + .material(0, mi) .geometry(0, RenderableManager::PrimitiveType::TRIANGLES, app.monkeyMesh.vertexBuffer, app.monkeyMesh.indexBuffer) .receiveShadows(true) .castShadows(false) .build(*engine, app.reflectedMonkey); - app.offscreenScene->addEntity(app.reflectedMonkey); + setReflectionMode(app, app.mode); // Add light source to both scenes. - // NOTE: this is slightly wrong when the reflection mode is NEGATIVE_SCALE_TRANSFORM. + // NOTE: this is slightly wrong when the reflection mode is RENDERABLES. app.lightEntity = em.create(); LightManager::Builder(LightManager::Type::SUN) .color(Color::toLinear(sRGBColor(0.98f, 0.92f, 0.89f))) @@ -314,7 +321,6 @@ int main(int argc, char** argv) { engine->destroy(app.reflectedMonkey); engine->destroy(app.lightEntity); engine->destroy(app.quadEntity); - engine->destroy(app.invertedMeshMatInstance); engine->destroy(app.meshMatInstance); engine->destroy(app.meshMaterial); engine->destroy(app.monkeyMesh.renderable); @@ -357,12 +363,11 @@ int main(int argc, char** argv) { app.offscreenCamera->setCustomProjection(renderingProjection, cullingProjection, camera.getNear(), camera.getCullingFar()); switch (app.mode) { - case App::ReflectionMode::NEGATIVE_SCALE_TRANSFORM: + case App::ReflectionMode::RENDERABLES: tcm.setTransform(tcm.getInstance(app.reflectedMonkey), reflection * xform); app.offscreenCamera->setModelMatrix(model); break; - case App::ReflectionMode::MATERIAL: - tcm.setTransform(tcm.getInstance(app.reflectedMonkey), xform); + case App::ReflectionMode::CAMERA: app.offscreenCamera->setModelMatrix(reflection * model); break; } diff --git a/web/filament-js/jsbindings.cpp b/web/filament-js/jsbindings.cpp index b1e36096030..90e4171b7a5 100644 --- a/web/filament-js/jsbindings.cpp +++ b/web/filament-js/jsbindings.cpp @@ -1411,10 +1411,7 @@ class_("MaterialInstance") .function("setStencilWriteMask", EMBIND_LAMBDA(void, (MaterialInstance* self, uint8_t writeMask), { self->setStencilWriteMask(writeMask, backend::StencilFace::FRONT_AND_BACK); - }), allow_raw_pointers()) - .function("setFrontFaceWindingInverted", &MaterialInstance::setFrontFaceWindingInverted) - .function("isFrontFaceWindingInverted", &MaterialInstance::isFrontFaceWindingInverted); - + }), allow_raw_pointers()); class_("TextureSampler") .constructor() From b0a584c9156d3646284d668af7b3de5f8e3f81e2 Mon Sep 17 00:00:00 2001 From: Powei Feng Date: Tue, 14 Nov 2023 13:26:48 -0800 Subject: [PATCH 10/23] Small compilation fixes (#7363) - gltfio: Enable -Wall -Werror for gltfio_core - gltfio: Fix various errors that were missed warnings - matdbg: switch from std::atomic_uint64_t to std::atomic for older clang --- libs/gltfio/CMakeLists.txt | 11 ++++++----- libs/gltfio/include/gltfio/ResourceLoader.h | 1 - libs/gltfio/src/AssetLoader.cpp | 4 ++-- libs/gltfio/src/DracoCache.cpp | 3 ++- libs/gltfio/src/FFilamentAsset.h | 2 +- libs/gltfio/src/Ktx2Provider.cpp | 2 +- libs/gltfio/src/ResourceLoader.cpp | 3 ++- libs/gltfio/src/StbProvider.cpp | 2 +- libs/gltfio/test/gltfio_test.cpp | 4 ---- libs/matdbg/src/ApiHandler.h | 2 +- 10 files changed, 16 insertions(+), 18 deletions(-) diff --git a/libs/gltfio/CMakeLists.txt b/libs/gltfio/CMakeLists.txt index d570f61e8e7..d12f24d2597 100644 --- a/libs/gltfio/CMakeLists.txt +++ b/libs/gltfio/CMakeLists.txt @@ -176,6 +176,11 @@ if (WEBGL_PTHREADS) target_compile_definitions(gltfio_core PUBLIC -DFILAMENT_WASM_THREADS) endif() +set(GLTFIO_WARNINGS -Wall -Werror) +if (NOT MSVC) + target_compile_options(gltfio_core PRIVATE ${GLTFIO_WARNINGS}) +endif() + if (NOT WEBGL AND NOT ANDROID AND NOT IOS) # ================================================================================================== @@ -185,10 +190,6 @@ if (NOT WEBGL AND NOT ANDROID AND NOT IOS) target_link_libraries(${TARGET} PUBLIC filamat gltfio_core) target_include_directories(${TARGET} PUBLIC ${PUBLIC_HDR_DIR}) set_target_properties(${TARGET} PROPERTIES FOLDER Libs) - - # ================================================================================================== - # Compiler flags - # ================================================================================================== if (NOT MSVC) target_compile_options(${TARGET} PRIVATE -Wno-deprecated-register) endif() @@ -229,7 +230,7 @@ if (TNT_DEV AND NOT WEBGL AND NOT ANDROID AND NOT IOS) target_link_libraries(${TEST_TARGET} PRIVATE ${TARGET} gtest uberarchive) if (NOT MSVC) - target_compile_options(${TEST_TARGET} PRIVATE -Wno-deprecated-register) + target_compile_options(${TEST_TARGET} PRIVATE ${GLTFIO_WARNINGS}) endif() set_target_properties(${TEST_TARGET} PROPERTIES FOLDER Tests) endif() diff --git a/libs/gltfio/include/gltfio/ResourceLoader.h b/libs/gltfio/include/gltfio/ResourceLoader.h index 2a13e546f27..05c8a56d358 100644 --- a/libs/gltfio/include/gltfio/ResourceLoader.h +++ b/libs/gltfio/include/gltfio/ResourceLoader.h @@ -157,7 +157,6 @@ class UTILS_PUBLIC ResourceLoader { private: bool loadResources(FFilamentAsset* asset, bool async); void normalizeSkinningWeights(FFilamentAsset* asset) const; - AssetPool* mPool; struct Impl; Impl* pImpl; }; diff --git a/libs/gltfio/src/AssetLoader.cpp b/libs/gltfio/src/AssetLoader.cpp index 3cb8840656a..6e3df0af442 100644 --- a/libs/gltfio/src/AssetLoader.cpp +++ b/libs/gltfio/src/AssetLoader.cpp @@ -42,6 +42,7 @@ #include #include +#include #include #include #include @@ -768,7 +769,6 @@ void FAssetLoader::createRenderable(const cgltf_node* node, Entity entity, const void FAssetLoader::createMaterialVariants(const cgltf_mesh* mesh, Entity entity, FFilamentAsset* fAsset, FFilamentInstance* instance) { - const cgltf_data* srcAsset = fAsset->mSourceAsset->hierarchy; UvMap uvmap {}; for (cgltf_size prim = 0, n = mesh->primitives_count; prim < n; ++prim) { const cgltf_primitive& srcPrim = mesh->primitives[prim]; @@ -1076,7 +1076,7 @@ bool FAssetLoader::createPrimitive(const cgltf_primitive& inPrim, const char* na .build(mEngine); outPrim->targets = targets; fAsset->mMorphTargetBuffers.push_back(targets); - const cgltf_accessor* previous = nullptr; + UTILS_UNUSED_IN_RELEASE cgltf_accessor const* previous = nullptr; for (int tindex = 0; tindex < targetsCount; ++tindex) { const cgltf_morph_target& inTarget = inPrim.targets[tindex]; for (cgltf_size aindex = 0; aindex < inTarget.attributes_count; ++aindex) { diff --git a/libs/gltfio/src/DracoCache.cpp b/libs/gltfio/src/DracoCache.cpp index b0f061a1a1f..871e0054491 100644 --- a/libs/gltfio/src/DracoCache.cpp +++ b/libs/gltfio/src/DracoCache.cpp @@ -20,6 +20,7 @@ #include #endif +#include #include #if GLTFIO_DRACO_SUPPORTED @@ -65,7 +66,7 @@ DracoMesh::~DracoMesh() { } // Gets the number of components in the given cgltf vector type, or -1 for matrices. -static int getNumComponents(cgltf_type ctype) { +UTILS_UNUSED_IN_RELEASE static int getNumComponents(cgltf_type ctype) { return ((int) ctype) <= 4 ? ((int) ctype) : -1; } diff --git a/libs/gltfio/src/FFilamentAsset.h b/libs/gltfio/src/FFilamentAsset.h index dc516f90d13..ba101bc6e1f 100644 --- a/libs/gltfio/src/FFilamentAsset.h +++ b/libs/gltfio/src/FFilamentAsset.h @@ -71,7 +71,7 @@ namespace utils { namespace filament::gltfio { -class Wireframe; +struct Wireframe; // Encapsulates VertexBuffer::setBufferAt() or IndexBuffer::setBuffer(). struct BufferSlot { diff --git a/libs/gltfio/src/Ktx2Provider.cpp b/libs/gltfio/src/Ktx2Provider.cpp index 992db8d7bd8..5ceb3902aa9 100644 --- a/libs/gltfio/src/Ktx2Provider.cpp +++ b/libs/gltfio/src/Ktx2Provider.cpp @@ -116,7 +116,7 @@ Texture* Ktx2Provider::pushTexture(const uint8_t* data, size_t byteCount, } JobSystem* js = &mEngine->getJobSystem(); - item->job = jobs::createJob(*js, mDecoderRootJob, [this, item] { + item->job = jobs::createJob(*js, mDecoderRootJob, [item] { using Result = ktxreader::Ktx2Reader::Result; const bool success = Result::SUCCESS == item->async->doTranscoding(); item->transcoderState.store(success ? TranscoderState::SUCCESS : TranscoderState::ERROR); diff --git a/libs/gltfio/src/ResourceLoader.cpp b/libs/gltfio/src/ResourceLoader.cpp index 25d0a60d32d..9e292ae9c00 100644 --- a/libs/gltfio/src/ResourceLoader.cpp +++ b/libs/gltfio/src/ResourceLoader.cpp @@ -32,6 +32,7 @@ #include +#include #include #include #include @@ -243,7 +244,7 @@ static void decodeMeshoptCompression(cgltf_data* data) { void* destination = malloc(compression->count * compression->stride); assert_invariant(destination); - int error = 0; + UTILS_UNUSED_IN_RELEASE int error = 0; switch (compression->mode) { case cgltf_meshopt_compression_mode_invalid: break; diff --git a/libs/gltfio/src/StbProvider.cpp b/libs/gltfio/src/StbProvider.cpp index 17f84b76e16..c5900fd426f 100644 --- a/libs/gltfio/src/StbProvider.cpp +++ b/libs/gltfio/src/StbProvider.cpp @@ -126,7 +126,7 @@ Texture* StbProvider::pushTexture(const uint8_t* data, size_t byteCount, } JobSystem* js = &mEngine->getJobSystem(); - info->decoderJob = jobs::createJob(*js, mDecoderRootJob, [this, info] { + info->decoderJob = jobs::createJob(*js, mDecoderRootJob, [info] { auto& source = info->sourceBuffer; int width, height, comp; diff --git a/libs/gltfio/test/gltfio_test.cpp b/libs/gltfio/test/gltfio_test.cpp index e678c62688d..44b7d99e223 100644 --- a/libs/gltfio/test/gltfio_test.cpp +++ b/libs/gltfio/test/gltfio_test.cpp @@ -43,9 +43,6 @@ using namespace backend; using namespace gltfio; using namespace utils; -constexpr uint32_t WIDTH = 64; -constexpr uint32_t HEIGHT = 64; - char const* ANIMATED_MORPH_CUBE_GLB = "AnimatedMorphCube.glb"; static std::ifstream::pos_type getFileSize(const char* filename) { @@ -178,7 +175,6 @@ do { \ TEST_F(glTFIOTest, AnimatedMorphCubeTransforms) { FilamentAsset const& morphCubeAsset = *mData[ANIMATED_MORPH_CUBE_GLB]->getAsset(); auto const& transformManager = mEngine->getTransformManager(); - auto const& renderableManager = mEngine->getRenderableManager(); Entity const* renderables = morphCubeAsset.getRenderableEntities(); EXPECT_EQ(morphCubeAsset.getRenderableEntityCount(), 1u); diff --git a/libs/matdbg/src/ApiHandler.h b/libs/matdbg/src/ApiHandler.h index 27464d56267..2a3d06f01f6 100644 --- a/libs/matdbg/src/ApiHandler.h +++ b/libs/matdbg/src/ApiHandler.h @@ -60,7 +60,7 @@ class ApiHandler : public CivetHandler { // This variable is to implement a *hanging* effect for /api/status. The call to /api/status // will always block until statusMaterialId is updated again. The client is expected to keep // calling /api/status (a constant "pull" to simulate a push). - std::atomic_uint64_t mCurrentStatus = 0; + std::atomic mCurrentStatus = 0; }; } // filament::matdbg From 766e4f2ec99a186e45d85d1a4f277923d7ed44a1 Mon Sep 17 00:00:00 2001 From: Eliza Velasquez Date: Tue, 7 Nov 2023 15:02:22 -0800 Subject: [PATCH 11/23] Fix some ES2 issues Fix specification of mipmaps when generating textures. Fix oversight where emulated UBOs would not replace uniforms when swapped out for another. --- NEW_RELEASE_NOTES.md | 1 + filament/backend/src/opengl/OpenGLDriver.cpp | 18 ++++++++++++------ filament/backend/src/opengl/OpenGLDriver.h | 9 ++++----- filament/backend/src/opengl/OpenGLProgram.cpp | 5 +++-- filament/backend/src/opengl/OpenGLProgram.h | 3 ++- filament/src/PostProcessManager.cpp | 3 ++- shaders/src/depth_main.fs | 6 +++--- 7 files changed, 27 insertions(+), 18 deletions(-) diff --git a/NEW_RELEASE_NOTES.md b/NEW_RELEASE_NOTES.md index ec967554ed3..feef80e08ba 100644 --- a/NEW_RELEASE_NOTES.md +++ b/NEW_RELEASE_NOTES.md @@ -10,3 +10,4 @@ appropriate header in [RELEASE_NOTES.md](./RELEASE_NOTES.md). - engine: Support up to 4 side-by-side stereoscopic eyes, configurable at Engine creation time. See `Engine::Config::stereoscopicEyeCount`. [⚠️ **Recompile Materials**] +- engine: Fix critical GLES 2.0 bugs diff --git a/filament/backend/src/opengl/OpenGLDriver.cpp b/filament/backend/src/opengl/OpenGLDriver.cpp index 9e9f1c8ace9..cfe4cacde61 100644 --- a/filament/backend/src/opengl/OpenGLDriver.cpp +++ b/filament/backend/src/opengl/OpenGLDriver.cpp @@ -172,7 +172,7 @@ OpenGLDriver::OpenGLDriver(OpenGLPlatform* platform, const Platform::DriverConfi mShaderCompilerService(*this), mHandleAllocator("Handles", driverConfig.handleArenaSize), mSamplerMap(32) { - + std::fill(mSamplerBindings.begin(), mSamplerBindings.end(), nullptr); // set a reasonable default value for our stream array @@ -268,9 +268,9 @@ void OpenGLDriver::useProgram(OpenGLProgram* p) noexcept { if (UTILS_UNLIKELY(mContext.isES2())) { for (uint32_t i = 0; i < Program::UNIFORM_BINDING_COUNT; i++) { - auto [buffer, age] = mUniformBindings[i]; + auto [id, buffer, age] = mUniformBindings[i]; if (buffer) { - p->updateUniforms(i, buffer, age); + p->updateUniforms(i, id, buffer, age); } } // Set the output colorspace for this program (linear or rec709). This in only relevant @@ -479,6 +479,7 @@ void OpenGLDriver::createBufferObjectR(Handle boh, GLBufferObject* bo = construct(boh, byteCount, bindingType, usage); if (UTILS_UNLIKELY(bindingType == BufferObjectBinding::UNIFORM && gl.isES2())) { + bo->gl.id = ++mLastAssignedEmulatedUboId; bo->gl.buffer = malloc(byteCount); memset(bo->gl.buffer, 0, byteCount); } else { @@ -595,8 +596,9 @@ void OpenGLDriver::textureStorage(OpenGLDriver::GLTexture* t, } } else { glTexImage2D(t->gl.target, level, GLint(t->gl.internalFormat), - GLsizei(width), GLsizei(height), 0, - format, type, nullptr); + std::max(GLsizei(1), GLsizei(width >> level)), + std::max(GLsizei(1), GLsizei(height >> level)), + 0, format, type, nullptr); } } } @@ -2901,7 +2903,11 @@ void OpenGLDriver::bindBufferRange(BufferObjectBinding bindingType, uint32_t ind assert_invariant(offset + size <= ub->byteCount); if (UTILS_UNLIKELY(ub->bindingType == BufferObjectBinding::UNIFORM && gl.isES2())) { - mUniformBindings[index] = { static_cast(ub->gl.buffer) + offset, ub->age }; + mUniformBindings[index] = { + ub->gl.id, + static_cast(ub->gl.buffer) + offset, + ub->age, + }; } else { GLenum const target = GLUtils::getBufferBindingType(bindingType); diff --git a/filament/backend/src/opengl/OpenGLDriver.h b/filament/backend/src/opengl/OpenGLDriver.h index 1cf1be4d36f..f3c87c61768 100644 --- a/filament/backend/src/opengl/OpenGLDriver.h +++ b/filament/backend/src/opengl/OpenGLDriver.h @@ -83,11 +83,9 @@ class OpenGLDriver final : public DriverBase { } struct { + GLuint id; union { - struct { - GLuint id; - GLenum binding; - }; + GLenum binding; void* buffer; }; } gl; @@ -363,7 +361,8 @@ class OpenGLDriver final : public DriverBase { void setScissor(Viewport const& scissor) noexcept; // ES2 only. Uniform buffer emulation binding points - std::array, Program::UNIFORM_BINDING_COUNT> mUniformBindings = {}; + GLuint mLastAssignedEmulatedUboId = 0; + std::array, Program::UNIFORM_BINDING_COUNT> mUniformBindings = {}; // sampler buffer binding points (nullptr if not used) std::array mSamplerBindings = {}; // 4 pointers diff --git a/filament/backend/src/opengl/OpenGLProgram.cpp b/filament/backend/src/opengl/OpenGLProgram.cpp index 1c2593751f7..69412e1aea1 100644 --- a/filament/backend/src/opengl/OpenGLProgram.cpp +++ b/filament/backend/src/opengl/OpenGLProgram.cpp @@ -229,15 +229,16 @@ void OpenGLProgram::updateSamplers(OpenGLDriver* const gld) const noexcept { CHECK_GL_ERROR(utils::slog.e) } -void OpenGLProgram::updateUniforms(uint32_t index, void const* buffer, uint16_t age) noexcept { +void OpenGLProgram::updateUniforms(uint32_t index, GLuint id, void const* buffer, uint16_t age) noexcept { assert_invariant(mUniformsRecords); assert_invariant(buffer); // only update the uniforms if the UBO has changed since last time we updated UniformsRecord const& records = mUniformsRecords[index]; - if (records.age == age) { + if (records.id == id && records.age == age) { return; } + records.id = id; records.age = age; assert_invariant(records.uniforms.size() == records.locations.size()); diff --git a/filament/backend/src/opengl/OpenGLProgram.h b/filament/backend/src/opengl/OpenGLProgram.h index 5d183f6da1c..ddee9496690 100644 --- a/filament/backend/src/opengl/OpenGLProgram.h +++ b/filament/backend/src/opengl/OpenGLProgram.h @@ -72,7 +72,7 @@ class OpenGLProgram : public HwProgram { } gl; // 12 bytes // For ES2 only - void updateUniforms(uint32_t index, void const* buffer, uint16_t age) noexcept; + void updateUniforms(uint32_t index, GLuint id, void const* buffer, uint16_t age) noexcept; void setRec709ColorSpace(bool rec709) const noexcept; private: @@ -98,6 +98,7 @@ class OpenGLProgram : public HwProgram { struct UniformsRecord { Program::UniformInfo uniforms; LocationInfo locations; + mutable GLuint id = 0; mutable uint16_t age = std::numeric_limits::max(); }; UniformsRecord const* mUniformsRecords = nullptr; diff --git a/filament/src/PostProcessManager.cpp b/filament/src/PostProcessManager.cpp index 0930b630ae4..db252e29700 100644 --- a/filament/src/PostProcessManager.cpp +++ b/filament/src/PostProcessManager.cpp @@ -2714,7 +2714,8 @@ FrameGraphId PostProcessManager::upscale(FrameGraph& fg, bool filament::Viewport const& vp, FrameGraphTexture::Descriptor const& outDesc, backend::SamplerMagFilter filter) noexcept { - if (UTILS_LIKELY(!translucent && dsrOptions.quality == QualityLevel::LOW)) { + if (UTILS_LIKELY(!translucent && dsrOptions.quality == QualityLevel::LOW && + mEngine.getDriverApi().getFeatureLevel() >= FeatureLevel::FEATURE_LEVEL_1)) { return opaqueBlit(fg, input, vp, outDesc, filter); } diff --git a/shaders/src/depth_main.fs b/shaders/src/depth_main.fs index fa28309a88c..77608f3e15c 100644 --- a/shaders/src/depth_main.fs +++ b/shaders/src/depth_main.fs @@ -58,9 +58,9 @@ void main() { fragColor.zw = computeDepthMomentsVSM(-1.0 / depth); // requires at least RGBA16F #elif defined(VARIANT_HAS_PICKING) #if MATERIAL_FEATURE_LEVEL == 0 - outPicking.a = float((object_uniforms_objectId / 65536) % 256) / 255.0; - outPicking.b = float((object_uniforms_objectId / 256) % 256) / 255.0; - outPicking.g = float( object_uniforms_objectId % 256) / 255.0; + outPicking.a = mod(float(object_uniforms_objectId / 65536), 256.0) / 255.0; + outPicking.b = mod(float(object_uniforms_objectId / 256), 256.0) / 255.0; + outPicking.g = mod(float(object_uniforms_objectId) , 256.0) / 255.0; outPicking.r = vertex_position.z / vertex_position.w; #else outPicking.x = intBitsToFloat(object_uniforms_objectId); From eebb40d30d77ad5c1293f9165a847526adeb4a71 Mon Sep 17 00:00:00 2001 From: Antonio Maiorano Date: Tue, 14 Nov 2023 18:23:39 -0500 Subject: [PATCH 12/23] Add missing include for latest glslang (#7365) This change in glslang removes the include of "intermediate.h" from GlslangToSpv.h: https://github.com/KhronosGroup/glslang/commit/62de186c3383feef067feb6ea578f79977910722 As a result, the definition of "class TIntermediate" is removed, and will fail compilation of MaterialCompiler.cpp when glslang is updated to a version including the aforementioned change. We fix this by adding an explicit include to this header in MaterialCompiler.cpp. Co-authored-by: Powei Feng --- tools/matc/src/matc/MaterialCompiler.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/tools/matc/src/matc/MaterialCompiler.cpp b/tools/matc/src/matc/MaterialCompiler.cpp index 2785a733439..d9920d4c059 100644 --- a/tools/matc/src/matc/MaterialCompiler.cpp +++ b/tools/matc/src/matc/MaterialCompiler.cpp @@ -34,6 +34,7 @@ #include "ParametersProcessor.h" #include +#include "glslang/Include/intermediate.h" #include "sca/builtinResource.h" From 2306b56ea7a806f3f610eee669ccb54304a75e6c Mon Sep 17 00:00:00 2001 From: Eliza Velasquez Date: Tue, 14 Nov 2023 16:03:21 -0800 Subject: [PATCH 13/23] Add FILAMENT_ENABLE_FEATURE_LEVEL_0 option This allows clients to selectively exclude feature level 0 support. --- CMakeLists.txt | 2 + NEW_RELEASE_NOTES.md | 2 + filament/CMakeLists.txt | 61 ++++++++++++++++++++++++-- filament/src/details/Engine.cpp | 9 +++- filament/src/details/Skybox.cpp | 5 ++- filament/src/materials/blitLow1.mat | 39 ++++++++++++++++ libs/filagui/CMakeLists.txt | 9 ++++ libs/filagui/src/materials/uiBlit1.mat | 28 ++++++++++++ 8 files changed, 150 insertions(+), 5 deletions(-) create mode 100644 filament/src/materials/blitLow1.mat create mode 100644 libs/filagui/src/materials/uiBlit1.mat diff --git a/CMakeLists.txt b/CMakeLists.txt index 24adbb8268d..dd5bfaaf8d6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -41,6 +41,8 @@ option(FILAMENT_LINUX_IS_MOBILE "Treat Linux as Mobile" OFF) option(FILAMENT_ENABLE_ASAN_UBSAN "Enable Address and Undefined Behavior Sanitizers" OFF) +option(FILAMENT_ENABLE_FEATURE_LEVEL_0 "Enable Feature Level 0" ON) + set(FILAMENT_NDK_VERSION "" CACHE STRING "Android NDK version or version prefix to be used when building for Android." ) diff --git a/NEW_RELEASE_NOTES.md b/NEW_RELEASE_NOTES.md index feef80e08ba..45e84e3312e 100644 --- a/NEW_RELEASE_NOTES.md +++ b/NEW_RELEASE_NOTES.md @@ -11,3 +11,5 @@ appropriate header in [RELEASE_NOTES.md](./RELEASE_NOTES.md). - engine: Support up to 4 side-by-side stereoscopic eyes, configurable at Engine creation time. See `Engine::Config::stereoscopicEyeCount`. [⚠️ **Recompile Materials**] - engine: Fix critical GLES 2.0 bugs +- engine: Add `FILAMENT_ENABLE_FEATURE_LEVEL_0` build-time option optionally allow building Filament + without FL0 support. diff --git a/filament/CMakeLists.txt b/filament/CMakeLists.txt index 8eb45b40b57..e6685c7d5ef 100644 --- a/filament/CMakeLists.txt +++ b/filament/CMakeLists.txt @@ -218,7 +218,6 @@ set(MATERIAL_SRCS src/materials/colorGrading/customResolveAsSubpass.mat src/materials/debugShadowCascades.mat src/materials/defaultMaterial.mat - src/materials/defaultMaterial0.mat src/materials/dof/dof.mat src/materials/dof/dofCoc.mat src/materials/dof/dofDownsample.mat @@ -229,7 +228,6 @@ set(MATERIAL_SRCS src/materials/dof/dofMipmap.mat src/materials/dof/dofMedian.mat src/materials/flare/flare.mat - src/materials/blitLow.mat src/materials/bloom/bloomDownsample.mat src/materials/bloom/bloomDownsample2x.mat src/materials/bloom/bloomDownsample9.mat @@ -238,7 +236,6 @@ set(MATERIAL_SRCS src/materials/ssao/bilateralBlurBentNormals.mat src/materials/ssao/mipmapDepth.mat src/materials/skybox.mat - src/materials/skybox0.mat src/materials/ssao/sao.mat src/materials/ssao/saoBentNormals.mat src/materials/separableGaussianBlur.mat @@ -247,6 +244,18 @@ set(MATERIAL_SRCS src/materials/vsmMipmap.mat ) +set(MATERIAL_FL0_SRCS + src/materials/defaultMaterial0.mat + src/materials/skybox0.mat +) + +# TODO(exv): Replace the below duplicated materials with a command-line option +# to matc to force disable including ESSL 1.0 code in FL0 materials. + +set(MATERIAL_SWITCH_FL0_SRCS_HACK + src/materials/blitLow.mat +) + # Embed the binary resource blob for materials. get_resgen_vars(${RESOURCE_DIR} materials) list(APPEND PRIVATE_HDRS ${RESGEN_HEADER}) @@ -271,6 +280,11 @@ if (NOT DFG_LUT_SIZE) endif() message(STATUS "DFG LUT size set to ${DFG_LUT_SIZE}x${DFG_LUT_SIZE}") +# Whether to include FL0 materials. +if (FILAMENT_ENABLE_FEATURE_LEVEL_0) + add_definitions(-DFILAMENT_ENABLE_FEATURE_LEVEL_0) +endif() + # ================================================================================================== # Definitions # ================================================================================================== @@ -312,6 +326,47 @@ foreach (mat_src ${MATERIAL_SRCS}) list(APPEND MATERIAL_BINS ${output_path}) endforeach() + +# HACK: Pick between the normal and FL0-exclusionary variants of the materials +# based on the value of FILAMENT_ENABLE_FEATURE_LEVEL_0. +foreach (mat_src ${MATERIAL_SWITCH_FL0_SRCS_HACK}) + get_filename_component(localname "${mat_src}" NAME_WE) + get_filename_component(fullname "${mat_src}" ABSOLUTE) + set(output_path "${MATERIAL_DIR}/${localname}.filamat") + + if (NOT FILAMENT_ENABLE_FATURE_LEVEL_0) + # Pick the non-FL0 variant instead. + string(REGEX REPLACE "\\.mat$" "1.mat" fullname "${fullname}") + string(REGEX REPLACE "\\.mat$" "1.mat" mat_src "${mat_src}") + endif() + + add_custom_command( + OUTPUT ${output_path} + COMMAND matc ${MATC_BASE_FLAGS} -o ${output_path} ${fullname} + MAIN_DEPENDENCY ${fullname} + DEPENDS matc + COMMENT "Compiling material ${mat_src} to ${output_path}" + ) + list(APPEND MATERIAL_BINS ${output_path}) +endforeach() + +if (FILAMENT_ENABLE_FEATURE_LEVEL_0 AND FILAMENT_SUPPORTS_OPENGL) + foreach (mat_src ${MATERIAL_FL0_SRCS}) + get_filename_component(localname "${mat_src}" NAME_WE) + get_filename_component(fullname "${mat_src}" ABSOLUTE) + set(output_path "${MATERIAL_DIR}/${localname}.filamat") + + add_custom_command( + OUTPUT ${output_path} + COMMAND matc -a opengl -p ${MATC_TARGET} ${MATC_OPT_FLAGS} -o ${output_path} ${fullname} + MAIN_DEPENDENCY ${fullname} + DEPENDS matc + COMMENT "Compiling material ${mat_src} to ${output_path}" + ) + list(APPEND MATERIAL_BINS ${output_path}) + endforeach () +endif () + # Additional dependencies on included files for materials add_custom_command( diff --git a/filament/src/details/Engine.cpp b/filament/src/details/Engine.cpp index 13c9d851b63..5bb51b6902c 100644 --- a/filament/src/details/Engine.cpp +++ b/filament/src/details/Engine.cpp @@ -244,6 +244,10 @@ void FEngine::init() { mActiveFeatureLevel = std::min(mActiveFeatureLevel, driverApi.getFeatureLevel()); +#ifndef FILAMENT_ENABLE_FEATURE_LEVEL_0 + assert_invariant(mActiveFeatureLevel > FeatureLevel::FEATURE_LEVEL_0); +#endif + slog.i << "Backend feature level: " << int(driverApi.getFeatureLevel()) << io::endl; slog.i << "FEngine feature level: " << int(mActiveFeatureLevel) << io::endl; @@ -328,12 +332,15 @@ void FEngine::init() { driverApi.update3DImage(mDummyZeroTexture, 0, 0, 0, 0, 1, 1, 1, { zeroes, 4, Texture::Format::RGBA, Texture::Type::UBYTE }); +#ifdef FILAMENT_ENABLE_FEATURE_LEVEL_0 if (UTILS_UNLIKELY(mActiveFeatureLevel == FeatureLevel::FEATURE_LEVEL_0)) { FMaterial::DefaultMaterialBuilder defaultMaterialBuilder; defaultMaterialBuilder.package( MATERIALS_DEFAULTMATERIAL0_DATA, MATERIALS_DEFAULTMATERIAL0_SIZE); mDefaultMaterial = downcast(defaultMaterialBuilder.build(*const_cast(this))); - } else { + } else +#endif + { mDefaultColorGrading = downcast(ColorGrading::Builder().build(*this)); FMaterial::DefaultMaterialBuilder defaultMaterialBuilder; diff --git a/filament/src/details/Skybox.cpp b/filament/src/details/Skybox.cpp index 7c0dac1512d..bfdb94b3005 100644 --- a/filament/src/details/Skybox.cpp +++ b/filament/src/details/Skybox.cpp @@ -118,9 +118,12 @@ FSkybox::FSkybox(FEngine& engine, const Builder& builder) noexcept FMaterial const* FSkybox::createMaterial(FEngine& engine) { Material::Builder builder; +#ifdef FILAMENT_ENABLE_FEATURE_LEVEL_0 if (UTILS_UNLIKELY(engine.getActiveFeatureLevel() == Engine::FeatureLevel::FEATURE_LEVEL_0)) { builder.package(MATERIALS_SKYBOX0_DATA, MATERIALS_SKYBOX0_SIZE); - } else { + } else +#endif + { builder.package(MATERIALS_SKYBOX_DATA, MATERIALS_SKYBOX_SIZE); } auto material = builder.build(engine); diff --git a/filament/src/materials/blitLow1.mat b/filament/src/materials/blitLow1.mat new file mode 100644 index 00000000000..efe17433c53 --- /dev/null +++ b/filament/src/materials/blitLow1.mat @@ -0,0 +1,39 @@ +material { + name : blitLow, + parameters : [ + { + type : sampler2d, + name : color, + precision: medium + }, + { + type : float4, + name : resolution, + precision: high + }, + { + type : float4, + name : viewport, + precision: high + } + ], + variables : [ + vertex + ], + depthWrite : false, + depthCulling : false, + domain: postprocess, +} + +vertex { + void postProcessVertex(inout PostProcessVertexInputs postProcess) { + postProcess.vertex.xy = materialParams.viewport.xy + postProcess.normalizedUV * materialParams.viewport.zw; + postProcess.vertex.xy = uvToRenderTargetUV(postProcess.vertex.xy); + } +} + +fragment { + void postProcess(inout PostProcessInputs postProcess) { + postProcess.color = textureLod(materialParams_color, variable_vertex.xy, 0.0); + } +} diff --git a/libs/filagui/CMakeLists.txt b/libs/filagui/CMakeLists.txt index e68a511ec2a..cb1024005e9 100644 --- a/libs/filagui/CMakeLists.txt +++ b/libs/filagui/CMakeLists.txt @@ -35,10 +35,19 @@ set(MATERIAL_SRCS file(MAKE_DIRECTORY ${MATERIAL_DIR}) +# HACK: Pick between the normal and FL0-exclusionary variants of the materials +# based on the value of FILAMENT_ENABLE_FEATURE_LEVEL_0. foreach (mat_src ${MATERIAL_SRCS}) get_filename_component(localname "${mat_src}" NAME_WE) get_filename_component(fullname "${mat_src}" ABSOLUTE) set(output_path "${MATERIAL_DIR}/${localname}.filamat") + + if (NOT FILAMENT_ENABLE_FATURE_LEVEL_0) + # Pick the non-FL0 variant instead. + string(REGEX REPLACE "\\.mat$" "1.mat" fullname "${fullname}") + string(REGEX REPLACE "\\.mat$" "1.mat" mat_src "${mat_src}") + endif() + add_custom_command( OUTPUT ${output_path} COMMAND matc ${MATC_BASE_FLAGS} -o ${output_path} ${fullname} diff --git a/libs/filagui/src/materials/uiBlit1.mat b/libs/filagui/src/materials/uiBlit1.mat new file mode 100644 index 00000000000..d0edc109d23 --- /dev/null +++ b/libs/filagui/src/materials/uiBlit1.mat @@ -0,0 +1,28 @@ +material { + name : uiBlit, + parameters : [ + { + type : sampler2d, + name : albedo + } + ], + requires : [ + uv0, + color + ], + shadingModel : unlit, + culling : none, + depthCulling: false, + blending : transparent, +} + +fragment { + void material(inout MaterialInputs material) { + prepareMaterial(material); + vec2 uv = getUV0(); + uv.y = 1.0 - uv.y; + vec4 albedo = texture(materialParams_albedo, uv); + material.baseColor = getColor() * albedo; + material.baseColor.rgb *= material.baseColor.a; + } +} From 4f4a439f8b22217471e086297bb923ea8c3f997e Mon Sep 17 00:00:00 2001 From: Powei Feng Date: Tue, 14 Nov 2023 21:32:27 -0800 Subject: [PATCH 14/23] Release Filament 1.46.0 --- NEW_RELEASE_NOTES.md | 5 ----- README.md | 4 ++-- RELEASE_NOTES.md | 7 +++++++ android/gradle.properties | 2 +- ios/CocoaPods/Filament.podspec | 4 ++-- web/filament-js/package.json | 2 +- 6 files changed, 13 insertions(+), 11 deletions(-) diff --git a/NEW_RELEASE_NOTES.md b/NEW_RELEASE_NOTES.md index 45e84e3312e..c3161a7700b 100644 --- a/NEW_RELEASE_NOTES.md +++ b/NEW_RELEASE_NOTES.md @@ -8,8 +8,3 @@ appropriate header in [RELEASE_NOTES.md](./RELEASE_NOTES.md). ## Release notes for next branch cut -- engine: Support up to 4 side-by-side stereoscopic eyes, configurable at Engine creation time. See - `Engine::Config::stereoscopicEyeCount`. [⚠️ **Recompile Materials**] -- engine: Fix critical GLES 2.0 bugs -- engine: Add `FILAMENT_ENABLE_FEATURE_LEVEL_0` build-time option optionally allow building Filament - without FL0 support. diff --git a/README.md b/README.md index 1c9b9c01057..3090b1fd8a1 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,7 @@ repositories { } dependencies { - implementation 'com.google.android.filament:filament-android:1.45.1' + implementation 'com.google.android.filament:filament-android:1.46.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.45.1' +pod 'Filament', '~> 1.46.0' ``` ### Snapshots diff --git a/RELEASE_NOTES.md b/RELEASE_NOTES.md index 20a3c0fab4c..a87c44268c7 100644 --- a/RELEASE_NOTES.md +++ b/RELEASE_NOTES.md @@ -7,6 +7,10 @@ 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.47.0 +- engine: Support up to 4 side-by-side stereoscopic eyes, configurable at Engine creation time. See + `Engine::Config::stereoscopicEyeCount`. [⚠️ **Recompile Materials**] + ## v1.46.0 - engine: Allow instantiating Engine at a given feature level via `Engine::Builder::featureLevel` @@ -17,6 +21,9 @@ Instead, if you are authoring a PR for the main branch, add your release note to - engine: Add `Material::getFeatureLevel()` - engine: Add missing `Material::getReflectionMode()` method in Java - engine: Support basic usage of post-processing materials on feature level 0 +- engine: Fix critical GLES 2.0 bugs +- engine: Add `FILAMENT_ENABLE_FEATURE_LEVEL_0` build-time option optionally allow building Filament + without FL0 support. ## v1.45.1 diff --git a/android/gradle.properties b/android/gradle.properties index 0fdca3754ec..d4b8dc0c94f 100644 --- a/android/gradle.properties +++ b/android/gradle.properties @@ -1,5 +1,5 @@ GROUP=com.google.android.filament -VERSION_NAME=1.45.1 +VERSION_NAME=1.46.0 POM_DESCRIPTION=Real-time physically based rendering engine for Android. diff --git a/ios/CocoaPods/Filament.podspec b/ios/CocoaPods/Filament.podspec index e6ca3b3dbed..19007b27293 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.1" + spec.version = "1.46.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.45.1/filament-v1.45.1-ios.tgz" } + spec.source = { :http => "https://github.com/google/filament/releases/download/v1.46.0/filament-v1.46.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 3f5afa8743d..d91ac748924 100644 --- a/web/filament-js/package.json +++ b/web/filament-js/package.json @@ -1,6 +1,6 @@ { "name": "filament", - "version": "1.45.1", + "version": "1.46.0", "description": "Real-time physically based rendering engine", "main": "filament.js", "module": "filament.js", From 0191e1fe46bdecf5c422fe8f43d9ffa8ca810cd1 Mon Sep 17 00:00:00 2001 From: Powei Feng Date: Tue, 14 Nov 2023 21:38:45 -0800 Subject: [PATCH 15/23] Bump version to 1.47.0 --- 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 3090b1fd8a1..b0278db43f3 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,7 @@ repositories { } dependencies { - implementation 'com.google.android.filament:filament-android:1.46.0' + implementation 'com.google.android.filament:filament-android:1.47.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.46.0' +pod 'Filament', '~> 1.47.0' ``` ### Snapshots diff --git a/android/gradle.properties b/android/gradle.properties index d4b8dc0c94f..6dda5b45e97 100644 --- a/android/gradle.properties +++ b/android/gradle.properties @@ -1,5 +1,5 @@ GROUP=com.google.android.filament -VERSION_NAME=1.46.0 +VERSION_NAME=1.47.0 POM_DESCRIPTION=Real-time physically based rendering engine for Android. diff --git a/ios/CocoaPods/Filament.podspec b/ios/CocoaPods/Filament.podspec index 19007b27293..24db7659fb0 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.46.0" + spec.version = "1.47.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.46.0/filament-v1.46.0-ios.tgz" } + spec.source = { :http => "https://github.com/google/filament/releases/download/v1.47.0/filament-v1.47.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 d91ac748924..ef4cd7e79d9 100644 --- a/web/filament-js/package.json +++ b/web/filament-js/package.json @@ -1,6 +1,6 @@ { "name": "filament", - "version": "1.46.0", + "version": "1.47.0", "description": "Real-time physically based rendering engine", "main": "filament.js", "module": "filament.js", From 64f4c097ac6d4adc51aa49a70832165c63fdea1e Mon Sep 17 00:00:00 2001 From: Ben Doherty Date: Wed, 15 Nov 2023 13:50:59 -0800 Subject: [PATCH 16/23] Remove debug code in MetalShaderCompiler (#7368) --- filament/backend/src/metal/MetalShaderCompiler.mm | 3 --- 1 file changed, 3 deletions(-) diff --git a/filament/backend/src/metal/MetalShaderCompiler.mm b/filament/backend/src/metal/MetalShaderCompiler.mm index e798d1b5ae3..7f4280979ab 100644 --- a/filament/backend/src/metal/MetalShaderCompiler.mm +++ b/filament/backend/src/metal/MetalShaderCompiler.mm @@ -170,9 +170,6 @@ bool isReady() const noexcept { CompilerPriorityQueue const priorityQueue = program.getPriorityQueue(); mCompilerThreadPool.queue(priorityQueue, token, [this, name, device = mDevice, program = std::move(program), token]() { - int sleepTime = atoi(name.c_str()); - sleep(sleepTime); - MetalFunctionBundle compiledProgram = compileProgram(program, device); token->set(compiledProgram); From 687b6da800fdca6b9da89e2f05cbe62b6d5d483f Mon Sep 17 00:00:00 2001 From: Eliza Velasquez Date: Wed, 15 Nov 2023 15:00:58 -0800 Subject: [PATCH 17/23] Enable preprocessor optimization of ESSL 1.0 Since #7358 is blocked by an upstream spirv-cross issue, we can at least do a bit of preprocessor optimization for ESSL 1.0 code in the meantime and introduce the FILAMENT_EFFECTIVE_VERSION preprocessor definitions. --- filament/src/materials/blitLow.mat | 2 +- libs/filamat/src/GLSLPostProcessor.cpp | 7 ++++- libs/filamat/src/shaders/CodeGenerator.cpp | 30 ++++++++++++++++++++++ shaders/src/depth_main.fs | 2 +- 4 files changed, 38 insertions(+), 3 deletions(-) diff --git a/filament/src/materials/blitLow.mat b/filament/src/materials/blitLow.mat index 4bebc8612e6..09e6c79920c 100644 --- a/filament/src/materials/blitLow.mat +++ b/filament/src/materials/blitLow.mat @@ -35,7 +35,7 @@ vertex { fragment { void postProcess(inout PostProcessInputs postProcess) { -#if __VERSION__ == 100 +#if FILAMENT_EFFECTIVE_VERSION == 100 postProcess.color = texture2D(materialParams_color, variable_vertex.xy); #else postProcess.color = textureLod(materialParams_color, variable_vertex.xy, 0.0); diff --git a/libs/filamat/src/GLSLPostProcessor.cpp b/libs/filamat/src/GLSLPostProcessor.cpp index 871bc298002..817429bb665 100644 --- a/libs/filamat/src/GLSLPostProcessor.cpp +++ b/libs/filamat/src/GLSLPostProcessor.cpp @@ -23,6 +23,7 @@ #include #include +#include "backend/DriverEnums.h" #include "sca/builtinResource.h" #include "sca/GLSLTools.h" @@ -395,7 +396,11 @@ bool GLSLPostProcessor::process(const std::string& inputShader, Config const& co break; case MaterialBuilder::Optimization::SIZE: case MaterialBuilder::Optimization::PERFORMANCE: - fullOptimization(tShader, config, internalConfig); + if (config.featureLevel == filament::backend::FeatureLevel::FEATURE_LEVEL_0) { + preprocessOptimization(tShader, config, internalConfig); + } else { + fullOptimization(tShader, config, internalConfig); + } break; } diff --git a/libs/filamat/src/shaders/CodeGenerator.cpp b/libs/filamat/src/shaders/CodeGenerator.cpp index b4818a50fde..a5b48ff7c8b 100644 --- a/libs/filamat/src/shaders/CodeGenerator.cpp +++ b/libs/filamat/src/shaders/CodeGenerator.cpp @@ -151,6 +151,36 @@ utils::io::sstream& CodeGenerator::generateProlog(utils::io::sstream& out, Shade out << "#define FILAMENT_HAS_FEATURE_INSTANCING\n"; } + // During compilation and optimization, __VERSION__ reflects the shader language version of the + // intermediate code, not the version of the final code. spirv-cross automatically adapts + // certain language features (e.g. fragment output) but leaves others untouched (e.g. sampler + // functions, bit shift operations). Client code may have to make decisions based on this + // information, so define a FILAMENT_EFFECTIVE_VERSION constant. + const char *effective_version; + if (mTargetLanguage == TargetLanguage::GLSL) { + effective_version = "__VERSION__"; + } else { + switch (mShaderModel) { + case ShaderModel::MOBILE: + if (mFeatureLevel >= FeatureLevel::FEATURE_LEVEL_1) { + effective_version = "300"; + } else { + effective_version = "100"; + } + break; + case ShaderModel::DESKTOP: + if (mFeatureLevel >= FeatureLevel::FEATURE_LEVEL_2) { + effective_version = "450"; + } else { + effective_version = "410"; + } + break; + default: + assert(false); + } + } + generateDefine(out, "FILAMENT_EFFECTIVE_VERSION", effective_version); + if (stage == ShaderStage::VERTEX) { CodeGenerator::generateDefine(out, "FLIP_UV_ATTRIBUTE", material.flipUV); CodeGenerator::generateDefine(out, "LEGACY_MORPHING", material.useLegacyMorphing); diff --git a/shaders/src/depth_main.fs b/shaders/src/depth_main.fs index 77608f3e15c..1632509f4aa 100644 --- a/shaders/src/depth_main.fs +++ b/shaders/src/depth_main.fs @@ -57,7 +57,7 @@ void main() { fragColor.xy = computeDepthMomentsVSM(depth); fragColor.zw = computeDepthMomentsVSM(-1.0 / depth); // requires at least RGBA16F #elif defined(VARIANT_HAS_PICKING) -#if MATERIAL_FEATURE_LEVEL == 0 +#if FILAMENT_EFFECTIVE_VERSION == 0 outPicking.a = mod(float(object_uniforms_objectId / 65536), 256.0) / 255.0; outPicking.b = mod(float(object_uniforms_objectId / 256), 256.0) / 255.0; outPicking.g = mod(float(object_uniforms_objectId) , 256.0) / 255.0; From 731e52a3c114681dcdffb779b85009816f9248fa Mon Sep 17 00:00:00 2001 From: Eliza Velasquez Date: Wed, 15 Nov 2023 15:56:38 -0800 Subject: [PATCH 18/23] Add option to matc to disable ESSL 1.0 codegen --- CMakeLists.txt | 5 +++ filament/CMakeLists.txt | 32 +-------------- filament/src/materials/blitLow1.mat | 39 ------------------- libs/filagui/CMakeLists.txt | 8 ---- libs/filagui/src/materials/uiBlit1.mat | 28 ------------- .../filamat/include/filamat/MaterialBuilder.h | 4 ++ libs/filamat/src/MaterialBuilder.cpp | 10 ++++- tools/matc/src/matc/CommandlineConfig.cpp | 9 ++++- tools/matc/src/matc/Config.h | 5 +++ tools/matc/src/matc/MaterialCompiler.cpp | 1 + 10 files changed, 32 insertions(+), 109 deletions(-) delete mode 100644 filament/src/materials/blitLow1.mat delete mode 100644 libs/filagui/src/materials/uiBlit1.mat diff --git a/CMakeLists.txt b/CMakeLists.txt index dd5bfaaf8d6..4bb1422da64 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -525,6 +525,11 @@ if (FILAMENT_SUPPORTS_METAL) set(MATC_API_FLAGS ${MATC_API_FLAGS} -a metal) endif() +# Disable ESSL 1.0 code generation. +if (NOT FILAMENT_ENABLE_FEATURE_LEVEL_0) + set(MATC_API_FLAGS ${MATC_API_FLAGS} -1) +endif() + # Enable debug info (preserves names in SPIR-V) if (FILAMENT_ENABLE_MATDBG) set(MATC_OPT_FLAGS ${MATC_OPT_FLAGS} -d) diff --git a/filament/CMakeLists.txt b/filament/CMakeLists.txt index e6685c7d5ef..f9bba52968f 100644 --- a/filament/CMakeLists.txt +++ b/filament/CMakeLists.txt @@ -242,6 +242,7 @@ set(MATERIAL_SRCS src/materials/antiAliasing/fxaa.mat src/materials/antiAliasing/taa.mat src/materials/vsmMipmap.mat + src/materials/blitLow.mat ) set(MATERIAL_FL0_SRCS @@ -249,13 +250,6 @@ set(MATERIAL_FL0_SRCS src/materials/skybox0.mat ) -# TODO(exv): Replace the below duplicated materials with a command-line option -# to matc to force disable including ESSL 1.0 code in FL0 materials. - -set(MATERIAL_SWITCH_FL0_SRCS_HACK - src/materials/blitLow.mat -) - # Embed the binary resource blob for materials. get_resgen_vars(${RESOURCE_DIR} materials) list(APPEND PRIVATE_HDRS ${RESGEN_HEADER}) @@ -326,30 +320,6 @@ foreach (mat_src ${MATERIAL_SRCS}) list(APPEND MATERIAL_BINS ${output_path}) endforeach() - -# HACK: Pick between the normal and FL0-exclusionary variants of the materials -# based on the value of FILAMENT_ENABLE_FEATURE_LEVEL_0. -foreach (mat_src ${MATERIAL_SWITCH_FL0_SRCS_HACK}) - get_filename_component(localname "${mat_src}" NAME_WE) - get_filename_component(fullname "${mat_src}" ABSOLUTE) - set(output_path "${MATERIAL_DIR}/${localname}.filamat") - - if (NOT FILAMENT_ENABLE_FATURE_LEVEL_0) - # Pick the non-FL0 variant instead. - string(REGEX REPLACE "\\.mat$" "1.mat" fullname "${fullname}") - string(REGEX REPLACE "\\.mat$" "1.mat" mat_src "${mat_src}") - endif() - - add_custom_command( - OUTPUT ${output_path} - COMMAND matc ${MATC_BASE_FLAGS} -o ${output_path} ${fullname} - MAIN_DEPENDENCY ${fullname} - DEPENDS matc - COMMENT "Compiling material ${mat_src} to ${output_path}" - ) - list(APPEND MATERIAL_BINS ${output_path}) -endforeach() - if (FILAMENT_ENABLE_FEATURE_LEVEL_0 AND FILAMENT_SUPPORTS_OPENGL) foreach (mat_src ${MATERIAL_FL0_SRCS}) get_filename_component(localname "${mat_src}" NAME_WE) diff --git a/filament/src/materials/blitLow1.mat b/filament/src/materials/blitLow1.mat deleted file mode 100644 index efe17433c53..00000000000 --- a/filament/src/materials/blitLow1.mat +++ /dev/null @@ -1,39 +0,0 @@ -material { - name : blitLow, - parameters : [ - { - type : sampler2d, - name : color, - precision: medium - }, - { - type : float4, - name : resolution, - precision: high - }, - { - type : float4, - name : viewport, - precision: high - } - ], - variables : [ - vertex - ], - depthWrite : false, - depthCulling : false, - domain: postprocess, -} - -vertex { - void postProcessVertex(inout PostProcessVertexInputs postProcess) { - postProcess.vertex.xy = materialParams.viewport.xy + postProcess.normalizedUV * materialParams.viewport.zw; - postProcess.vertex.xy = uvToRenderTargetUV(postProcess.vertex.xy); - } -} - -fragment { - void postProcess(inout PostProcessInputs postProcess) { - postProcess.color = textureLod(materialParams_color, variable_vertex.xy, 0.0); - } -} diff --git a/libs/filagui/CMakeLists.txt b/libs/filagui/CMakeLists.txt index cb1024005e9..66058c4103c 100644 --- a/libs/filagui/CMakeLists.txt +++ b/libs/filagui/CMakeLists.txt @@ -35,19 +35,11 @@ set(MATERIAL_SRCS file(MAKE_DIRECTORY ${MATERIAL_DIR}) -# HACK: Pick between the normal and FL0-exclusionary variants of the materials -# based on the value of FILAMENT_ENABLE_FEATURE_LEVEL_0. foreach (mat_src ${MATERIAL_SRCS}) get_filename_component(localname "${mat_src}" NAME_WE) get_filename_component(fullname "${mat_src}" ABSOLUTE) set(output_path "${MATERIAL_DIR}/${localname}.filamat") - if (NOT FILAMENT_ENABLE_FATURE_LEVEL_0) - # Pick the non-FL0 variant instead. - string(REGEX REPLACE "\\.mat$" "1.mat" fullname "${fullname}") - string(REGEX REPLACE "\\.mat$" "1.mat" mat_src "${mat_src}") - endif() - add_custom_command( OUTPUT ${output_path} COMMAND matc ${MATC_BASE_FLAGS} -o ${output_path} ${fullname} diff --git a/libs/filagui/src/materials/uiBlit1.mat b/libs/filagui/src/materials/uiBlit1.mat deleted file mode 100644 index d0edc109d23..00000000000 --- a/libs/filagui/src/materials/uiBlit1.mat +++ /dev/null @@ -1,28 +0,0 @@ -material { - name : uiBlit, - parameters : [ - { - type : sampler2d, - name : albedo - } - ], - requires : [ - uv0, - color - ], - shadingModel : unlit, - culling : none, - depthCulling: false, - blending : transparent, -} - -fragment { - void material(inout MaterialInputs material) { - prepareMaterial(material); - vec2 uv = getUV0(); - uv.y = 1.0 - uv.y; - vec4 albedo = texture(materialParams_albedo, uv); - material.baseColor = getColor() * albedo; - material.baseColor.rgb *= material.baseColor.a; - } -} diff --git a/libs/filamat/include/filamat/MaterialBuilder.h b/libs/filamat/include/filamat/MaterialBuilder.h index 36c24c4161c..6e7c70a0e21 100644 --- a/libs/filamat/include/filamat/MaterialBuilder.h +++ b/libs/filamat/include/filamat/MaterialBuilder.h @@ -135,6 +135,7 @@ class UTILS_PUBLIC MaterialBuilderBase { Optimization mOptimization = Optimization::PERFORMANCE; bool mPrintShaders = false; bool mGenerateDebugInfo = false; + bool mIncludeEssl1 = true; utils::bitset32 mShaderModels; struct CodeGenParams { ShaderModel shaderModel; @@ -271,6 +272,9 @@ class UTILS_PUBLIC MaterialBuilder : public MaterialBuilderBase { MaterialBuilder& noSamplerValidation(bool enabled) noexcept; + //! Enable generation of ESSL 1.0 code in FL0 materials. + MaterialBuilder& includeEssl1(bool enabled) noexcept; + //! Set the name of this material. MaterialBuilder& name(const char* name) noexcept; diff --git a/libs/filamat/src/MaterialBuilder.cpp b/libs/filamat/src/MaterialBuilder.cpp index d175c4e1c63..52cba77d6df 100644 --- a/libs/filamat/src/MaterialBuilder.cpp +++ b/libs/filamat/src/MaterialBuilder.cpp @@ -139,8 +139,9 @@ void MaterialBuilderBase::prepare(bool vulkanSemantics, glTargetLanguage, effectiveFeatureLevel, }); - if (featureLevel == filament::backend::FeatureLevel::FEATURE_LEVEL_0 - && shaderModel == ShaderModel::MOBILE) { + if (mIncludeEssl1 + && featureLevel == filament::backend::FeatureLevel::FEATURE_LEVEL_0 + && shaderModel == ShaderModel::MOBILE) { // ESSL1 code may never be compiled to SPIR-V. mCodeGenPermutations.push_back({ shaderModel, @@ -1565,4 +1566,9 @@ MaterialBuilder& MaterialBuilder::noSamplerValidation(bool enabled) noexcept { return *this; } +MaterialBuilder& MaterialBuilder::includeEssl1(bool enabled) noexcept { + mIncludeEssl1 = enabled; + return *this; +} + } // namespace filamat diff --git a/tools/matc/src/matc/CommandlineConfig.cpp b/tools/matc/src/matc/CommandlineConfig.cpp index 5323fb87419..30d6114e99d 100644 --- a/tools/matc/src/matc/CommandlineConfig.cpp +++ b/tools/matc/src/matc/CommandlineConfig.cpp @@ -62,6 +62,9 @@ static void usage(char* name) { " MATC --api opengl --api metal ...\n\n" " --feature-level, -l\n" " Specify the maximum feature level allowed (default is 3).\n\n" + " --no-essl1, -1\n" + " Don't generate ESSL 1.0 code even for Feature Level 0 mobile shaders.\n" + " Shaders are still validated against ESSL 1.0.\n\n" " --define, -D\n" " Add a preprocessor define macro via =. defaults to 1 if omitted.\n" " Can be repeated to specify multiple definitions:\n" @@ -171,7 +174,7 @@ static void parseDefine(std::string defineString, Config::StringReplacementMap& } bool CommandlineConfig::parse() { - static constexpr const char* OPTSTR = "hLxo:f:dm:a:l:p:D:T:OSEr:vV:gtwF"; + static constexpr const char* OPTSTR = "hLxo:f:dm:a:l:p:D:T:OSEr:vV:gtwF1"; static const struct option OPTIONS[] = { { "help", no_argument, nullptr, 'h' }, { "license", no_argument, nullptr, 'L' }, @@ -187,6 +190,7 @@ bool CommandlineConfig::parse() { { "preprocessor-only", no_argument, nullptr, 'E' }, { "api", required_argument, nullptr, 'a' }, { "feature-level", required_argument, nullptr, 'l' }, + { "no-essl1", no_argument, nullptr, '1' }, { "define", required_argument, nullptr, 'D' }, { "template", required_argument, nullptr, 'T' }, { "reflect", required_argument, nullptr, 'r' }, @@ -270,6 +274,9 @@ bool CommandlineConfig::parse() { } break; } + case '1': + mIncludeEssl1 = false; + break; case 'D': parseDefine(arg, mDefines); break; diff --git a/tools/matc/src/matc/Config.h b/tools/matc/src/matc/Config.h index c5403d693d3..79c6786541a 100644 --- a/tools/matc/src/matc/Config.h +++ b/tools/matc/src/matc/Config.h @@ -119,6 +119,10 @@ class Config { return mNoSamplerValidation; } + bool includeEssl1() const noexcept { + return mIncludeEssl1; + } + filament::UserVariantFilterMask getVariantFilter() const noexcept { return mVariantFilter; } @@ -150,6 +154,7 @@ class Config { StringReplacementMap mDefines; StringReplacementMap mTemplateMap; filament::UserVariantFilterMask mVariantFilter = 0; + bool mIncludeEssl1 = true; }; } diff --git a/tools/matc/src/matc/MaterialCompiler.cpp b/tools/matc/src/matc/MaterialCompiler.cpp index d9920d4c059..b21f75f84c5 100644 --- a/tools/matc/src/matc/MaterialCompiler.cpp +++ b/tools/matc/src/matc/MaterialCompiler.cpp @@ -399,6 +399,7 @@ bool MaterialCompiler::run(const Config& config) { builder .noSamplerValidation(config.noSamplerValidation()) + .includeEssl1(config.includeEssl1()) .includeCallback(includer) .fileName(materialFilePath.getName().c_str()) .platform(config.getPlatform()) From 8ed9678cbeeb9a7675a2ee8222454d87094956a4 Mon Sep 17 00:00:00 2001 From: Eliza Velasquez Date: Wed, 15 Nov 2023 16:02:41 -0800 Subject: [PATCH 19/23] Update NEW_RELEASE_NOTES.md --- NEW_RELEASE_NOTES.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/NEW_RELEASE_NOTES.md b/NEW_RELEASE_NOTES.md index c3161a7700b..19887bf7a8b 100644 --- a/NEW_RELEASE_NOTES.md +++ b/NEW_RELEASE_NOTES.md @@ -8,3 +8,5 @@ appropriate header in [RELEASE_NOTES.md](./RELEASE_NOTES.md). ## Release notes for next branch cut +- matc: New option `-1` to disable generation of ESSL 1.0 code in Feature Level 0 materials +- matc: Enable preprocessor optimization of ESSL 1.0 shader code [⚠️ **Recompile materials**] From b92c5cab07ea42901f742be7a25b340e1eb084f6 Mon Sep 17 00:00:00 2001 From: Eliza Velasquez Date: Thu, 16 Nov 2023 12:47:07 -0800 Subject: [PATCH 20/23] Incorporate feedback --- CMakeLists.txt | 2 +- libs/filamat/src/GLSLPostProcessor.cpp | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 4bb1422da64..00e03a598fe 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -527,7 +527,7 @@ endif() # Disable ESSL 1.0 code generation. if (NOT FILAMENT_ENABLE_FEATURE_LEVEL_0) - set(MATC_API_FLAGS ${MATC_API_FLAGS} -1) + set(MATC_API_FLAGS ${MATC_API_FLAGS} -1) endif() # Enable debug info (preserves names in SPIR-V) diff --git a/libs/filamat/src/GLSLPostProcessor.cpp b/libs/filamat/src/GLSLPostProcessor.cpp index 817429bb665..b4aaf54e4a2 100644 --- a/libs/filamat/src/GLSLPostProcessor.cpp +++ b/libs/filamat/src/GLSLPostProcessor.cpp @@ -397,6 +397,8 @@ bool GLSLPostProcessor::process(const std::string& inputShader, Config const& co case MaterialBuilder::Optimization::SIZE: case MaterialBuilder::Optimization::PERFORMANCE: if (config.featureLevel == filament::backend::FeatureLevel::FEATURE_LEVEL_0) { + // Full optimization blocked by upstream issue: + // https://github.com/KhronosGroup/SPIRV-Cross/issues/2223 preprocessOptimization(tShader, config, internalConfig); } else { fullOptimization(tShader, config, internalConfig); From 2fab93faff6e39c089b2c72d97a2ddf04e92872f Mon Sep 17 00:00:00 2001 From: Eliza Velasquez Date: Thu, 16 Nov 2023 12:51:53 -0800 Subject: [PATCH 21/23] Fix typo in depth_main.fs --- shaders/src/depth_main.fs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/shaders/src/depth_main.fs b/shaders/src/depth_main.fs index 1632509f4aa..e50d7620f17 100644 --- a/shaders/src/depth_main.fs +++ b/shaders/src/depth_main.fs @@ -57,7 +57,7 @@ void main() { fragColor.xy = computeDepthMomentsVSM(depth); fragColor.zw = computeDepthMomentsVSM(-1.0 / depth); // requires at least RGBA16F #elif defined(VARIANT_HAS_PICKING) -#if FILAMENT_EFFECTIVE_VERSION == 0 +#if FILAMENT_EFFECTIVE_VERSION == 100 outPicking.a = mod(float(object_uniforms_objectId / 65536), 256.0) / 255.0; outPicking.b = mod(float(object_uniforms_objectId / 256), 256.0) / 255.0; outPicking.g = mod(float(object_uniforms_objectId) , 256.0) / 255.0; From 4116af79713c480d14a2bcbc1326c2e38040c251 Mon Sep 17 00:00:00 2001 From: Eliza Velasquez Date: Tue, 7 Nov 2023 13:03:24 -0800 Subject: [PATCH 22/23] Enable optimizations for ESSL 1.0 code The CL introducing the ESSL 1.0 chunk in materials inadvertently disabled optimizations for said code. This commit reintroduces those optimizations and fixes associated bugs which manifested. In particular, spirv-cross was generating uints for bools; this has been fixed with a hack. Additionally, spirv-cross is now compiled with exceptions enabled so that matc can gracefully fail and show the code which failed to compile rather than abruptly aborting. --- CMakeLists.txt | 6 ++++++ NEW_RELEASE_NOTES.md | 2 +- libs/filamat/src/GLSLPostProcessor.cpp | 21 ++++++++++++++------- libs/filamat/src/GLSLPostProcessor.h | 2 +- libs/filamat/src/MaterialBuilder.cpp | 9 ++++----- libs/filamat/src/shaders/CodeGenerator.cpp | 16 ++++++++++------ third_party/spirv-cross/spirv_glsl.cpp | 13 +++++++++++-- 7 files changed, 47 insertions(+), 22 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 00e03a598fe..33c2bb29685 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -67,6 +67,9 @@ set(FILAMENT_METAL_HANDLE_ARENA_SIZE_IN_MB "8" CACHE STRING "Size of the Metal handle arena, default 8." ) +# Enable exceptions by default in spirv-cross. +set(SPIRV_CROSS_EXCEPTIONS_TO_ASSERTIONS OFF) + # ================================================================================================== # CMake policies # ================================================================================================== @@ -339,6 +342,7 @@ endif() if (CYGWIN) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fno-exceptions -fno-rtti") + set(SPIRV_CROSS_EXCEPTIONS_TO_ASSERTIONS ON) endif() if (MSVC) @@ -375,6 +379,7 @@ endif() # saved by -fno-exception and 10 KiB saved by -fno-rtti). if (ANDROID OR IOS OR WEBGL) set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -fno-exceptions -fno-rtti") + set(SPIRV_CROSS_EXCEPTIONS_TO_ASSERTIONS ON) if (ANDROID OR WEBGL) # Omitting unwind info prevents the generation of readable stack traces in crash reports on iOS @@ -386,6 +391,7 @@ endif() # std::visit, which is not supported on iOS 11.0 when exceptions are enabled. if (IOS) set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -fno-exceptions") + set(SPIRV_CROSS_EXCEPTIONS_TO_ASSERTIONS ON) endif() # With WebGL, we disable RTTI even for debug builds because we pass emscripten::val back and forth diff --git a/NEW_RELEASE_NOTES.md b/NEW_RELEASE_NOTES.md index 19887bf7a8b..d000a526635 100644 --- a/NEW_RELEASE_NOTES.md +++ b/NEW_RELEASE_NOTES.md @@ -9,4 +9,4 @@ appropriate header in [RELEASE_NOTES.md](./RELEASE_NOTES.md). ## Release notes for next branch cut - matc: New option `-1` to disable generation of ESSL 1.0 code in Feature Level 0 materials -- matc: Enable preprocessor optimization of ESSL 1.0 shader code [⚠️ **Recompile materials**] +- matc: Support optimizations for ESSL 1.0 code [⚠️ **Recompile materials**] diff --git a/libs/filamat/src/GLSLPostProcessor.cpp b/libs/filamat/src/GLSLPostProcessor.cpp index b4aaf54e4a2..7c1c4138fd7 100644 --- a/libs/filamat/src/GLSLPostProcessor.cpp +++ b/libs/filamat/src/GLSLPostProcessor.cpp @@ -33,6 +33,7 @@ #include "MetalArgumentBuffer.h" #include "SpirvFixup.h" +#include "utils/ostream.h" #include @@ -396,12 +397,8 @@ bool GLSLPostProcessor::process(const std::string& inputShader, Config const& co break; case MaterialBuilder::Optimization::SIZE: case MaterialBuilder::Optimization::PERFORMANCE: - if (config.featureLevel == filament::backend::FeatureLevel::FEATURE_LEVEL_0) { - // Full optimization blocked by upstream issue: - // https://github.com/KhronosGroup/SPIRV-Cross/issues/2223 - preprocessOptimization(tShader, config, internalConfig); - } else { - fullOptimization(tShader, config, internalConfig); + if (!fullOptimization(tShader, config, internalConfig)) { + return false; } break; } @@ -485,7 +482,7 @@ void GLSLPostProcessor::preprocessOptimization(glslang::TShader& tShader, } } -void GLSLPostProcessor::fullOptimization(const TShader& tShader, +bool GLSLPostProcessor::fullOptimization(const TShader& tShader, GLSLPostProcessor::Config const& config, InternalConfig& internalConfig) const { SpirvBlob spirv; @@ -553,7 +550,16 @@ void GLSLPostProcessor::fullOptimization(const TShader& tShader, } } +#ifdef SPIRV_CROSS_EXCEPTIONS_TO_ASSERTIONS *internalConfig.glslOutput = glslCompiler.compile(); +#else + try { + *internalConfig.glslOutput = glslCompiler.compile(); + } catch (spirv_cross::CompilerError e) { + slog.e << "ERROR: " << e.what() << io::endl; + return false; + } +#endif // spirv-cross automatically redeclares gl_ClipDistance if it's used. Some drivers don't // like this, so we simply remove it. @@ -566,6 +572,7 @@ void GLSLPostProcessor::fullOptimization(const TShader& tShader, str.replace(found, clipDistanceDefinition.length(), ""); } } + return true; } std::shared_ptr GLSLPostProcessor::createOptimizer( diff --git a/libs/filamat/src/GLSLPostProcessor.h b/libs/filamat/src/GLSLPostProcessor.h index 8e3c2e1e4ac..c13dece6369 100644 --- a/libs/filamat/src/GLSLPostProcessor.h +++ b/libs/filamat/src/GLSLPostProcessor.h @@ -93,7 +93,7 @@ class GLSLPostProcessor { ShaderMinifier minifier; }; - void fullOptimization(const glslang::TShader& tShader, + bool fullOptimization(const glslang::TShader& tShader, GLSLPostProcessor::Config const& config, InternalConfig& internalConfig) const; void preprocessOptimization(glslang::TShader& tShader, diff --git a/libs/filamat/src/MaterialBuilder.cpp b/libs/filamat/src/MaterialBuilder.cpp index 52cba77d6df..a95bb077819 100644 --- a/libs/filamat/src/MaterialBuilder.cpp +++ b/libs/filamat/src/MaterialBuilder.cpp @@ -142,11 +142,10 @@ void MaterialBuilderBase::prepare(bool vulkanSemantics, if (mIncludeEssl1 && featureLevel == filament::backend::FeatureLevel::FEATURE_LEVEL_0 && shaderModel == ShaderModel::MOBILE) { - // ESSL1 code may never be compiled to SPIR-V. mCodeGenPermutations.push_back({ shaderModel, TargetApi::OPENGL, - TargetLanguage::GLSL, + glTargetLanguage, filament::backend::FeatureLevel::FEATURE_LEVEL_0 }); } @@ -1422,15 +1421,15 @@ void MaterialBuilder::writeCommonChunks(ChunkContainer& container, MaterialInfo& uniforms.push_back({ "objectUniforms.data[0].morphTargetCount", offsetof(PerRenderableUib, data[0].morphTargetCount), 1, - UniformType::UINT }); + UniformType::INT }); uniforms.push_back({ "objectUniforms.data[0].flagsChannels", offsetof(PerRenderableUib, data[0].flagsChannels), 1, - UniformType::UINT }); + UniformType::INT }); uniforms.push_back({ "objectUniforms.data[0].objectId", offsetof(PerRenderableUib, data[0].objectId), 1, - UniformType::UINT }); + UniformType::INT }); uniforms.push_back({ "objectUniforms.data[0].userData", offsetof(PerRenderableUib, data[0].userData), 1, diff --git a/libs/filamat/src/shaders/CodeGenerator.cpp b/libs/filamat/src/shaders/CodeGenerator.cpp index a5b48ff7c8b..2c20307b7f4 100644 --- a/libs/filamat/src/shaders/CodeGenerator.cpp +++ b/libs/filamat/src/shaders/CodeGenerator.cpp @@ -301,8 +301,9 @@ utils::io::sstream& CodeGenerator::generateProlog(utils::io::sstream& out, Shade out << '\n'; out << SHADERS_COMMON_DEFINES_GLSL_DATA; - if (mFeatureLevel > FeatureLevel::FEATURE_LEVEL_0 && - material.featureLevel == FeatureLevel::FEATURE_LEVEL_0) { + if (material.featureLevel == FeatureLevel::FEATURE_LEVEL_0 && + (mFeatureLevel > FeatureLevel::FEATURE_LEVEL_0 + || mTargetLanguage == TargetLanguage::SPIRV)) { // Insert compatibility definitions for ESSL 1.0 functions which were removed in ESSL 3.0. // This is the minimum required value according to the OpenGL ES Shading Language Version @@ -490,11 +491,14 @@ io::sstream& CodeGenerator::generateOutput(io::sstream& out, ShaderStage type, const char* materialTypeString = getOutputTypeName(materialOutputType); const char* typeString = getOutputTypeName(outputType); + bool generate_essl3_code = mTargetLanguage == TargetLanguage::SPIRV + || mFeatureLevel >= FeatureLevel::FEATURE_LEVEL_1; + out << "\n#define FRAG_OUTPUT" << index << " " << name.c_str(); - if (mFeatureLevel == FeatureLevel::FEATURE_LEVEL_0) { - out << "\n#define FRAG_OUTPUT_AT" << index << " gl_FragColor"; - } else { + if (generate_essl3_code) { out << "\n#define FRAG_OUTPUT_AT" << index << " output_" << name.c_str(); + } else { + out << "\n#define FRAG_OUTPUT_AT" << index << " gl_FragColor"; } out << "\n#define FRAG_OUTPUT_MATERIAL_TYPE" << index << " " << materialTypeString; out << "\n#define FRAG_OUTPUT_PRECISION" << index << " " << precisionString; @@ -502,7 +506,7 @@ io::sstream& CodeGenerator::generateOutput(io::sstream& out, ShaderStage type, out << "\n#define FRAG_OUTPUT_SWIZZLE" << index << " " << swizzleString; out << "\n"; - if (mFeatureLevel >= FeatureLevel::FEATURE_LEVEL_1) { + if (generate_essl3_code) { out << "\nlayout(location=" << index << ") out " << precisionString << " " << typeString << " output_" << name.c_str() << ";\n"; } diff --git a/third_party/spirv-cross/spirv_glsl.cpp b/third_party/spirv-cross/spirv_glsl.cpp index 0d63d35f8f2..e0b39f95bb7 100644 --- a/third_party/spirv-cross/spirv_glsl.cpp +++ b/third_party/spirv-cross/spirv_glsl.cpp @@ -14977,7 +14977,12 @@ string CompilerGLSL::flags_to_qualifiers_glsl(const SPIRType &type, const Bitset { auto &execution = get_entry_point(); - if (flags.get(DecorationRelaxedPrecision)) + if (type.basetype == SPIRType::UInt && is_legacy_es()) + { + // HACK: This is a bool. See comment in type_to_glsl(). + qual += "lowp "; + } + else if (flags.get(DecorationRelaxedPrecision)) { bool implied_fmediump = type.basetype == SPIRType::Float && options.fragment.default_float_precision == Options::Mediump && @@ -15503,7 +15508,11 @@ string CompilerGLSL::type_to_glsl(const SPIRType &type, uint32_t id) if (type.basetype == SPIRType::UInt && is_legacy()) { if (options.es) - SPIRV_CROSS_THROW("Unsigned integers are not supported on legacy ESSL."); + // HACK: spirv-cross changes bools into uints and generates code which compares them to + // zero. Input code will have already been validated as not to have contained any uints, + // so any remaining uints must in fact be bools. However, simply returning "bool" here + // will result in invalid code. Instead, return an int. + return backend.basic_int_type; else require_extension_internal("GL_EXT_gpu_shader4"); } From 99ba40e965865fdd297fcff043480ce9b205eb7c Mon Sep 17 00:00:00 2001 From: Powei Feng Date: Mon, 27 Nov 2023 11:40:00 -0800 Subject: [PATCH 23/23] Update MATERIAL_VERSION to 47 --- libs/filabridge/include/filament/MaterialEnums.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libs/filabridge/include/filament/MaterialEnums.h b/libs/filabridge/include/filament/MaterialEnums.h index b6d677a4c94..67cdd8620f9 100644 --- a/libs/filabridge/include/filament/MaterialEnums.h +++ b/libs/filabridge/include/filament/MaterialEnums.h @@ -28,7 +28,7 @@ namespace filament { // update this when a new version of filament wouldn't work with older materials -static constexpr size_t MATERIAL_VERSION = 46; +static constexpr size_t MATERIAL_VERSION = 47; /** * Supported shading models