Skip to content

[macOS] Fix Metal RHI 3D viewer crashes and enable qtAliceVision plugins#3030

Open
NeverGET wants to merge 6 commits into
alicevision:developfrom
NeverGET:feature/macos-metal-3d-viewer
Open

[macOS] Fix Metal RHI 3D viewer crashes and enable qtAliceVision plugins#3030
NeverGET wants to merge 6 commits into
alicevision:developfrom
NeverGET:feature/macos-metal-3d-viewer

Conversation

@NeverGET
Copy link
Copy Markdown

@NeverGET NeverGET commented Mar 7, 2026

Summary

  • Fix Metal RHI 3D viewer crashes on macOS caused by missing vertexNormal attributes in custom Qt3D geometries
  • Compute flat normals via dFdx/dFdy derivatives instead of requiring normals as vertex attributes in SphericalHarmonics shaders
  • Add ensureNormals() to dynamically patch loaded meshes (OBJ/PLY) that lack normals
  • Add PySide6 bundled QML module path discovery (Qt3D, QtQuick.Scene3D)
  • Add macOS support to setupEnvironment() (Metal RHI backend default, Qt plugin path existence guards)
  • Add groupDesc backward compatibility for AliceVision node descriptors

Acknowledgments

Huge thanks to @music-dsp-collection for the incredible work on MTL-AliceVision (alicevision/AliceVision#2019) — bringing Metal GPU support and the complete macOS build pipeline to AliceVision is a massive achievement. And to the entire @alicevision team for building such an amazing open-source photogrammetry ecosystem. This PR is a small contribution building on top of their foundational work to help get the Meshroom 3D viewer running smoothly on macOS.

Context

While testing Meshroom on macOS 26.3 (Apple Silicon M4 Max, Mac15,6) with the MTL-AliceVision bundle from PR alicevision/AliceVision#2019, we found that the 3D viewer crashed due to Metal's strict vertex descriptor validation when geometries lacked normal attributes that built-in Qt3D materials expect.

Root cause: Metal RHI validates that all vertex shader inputs (layout(location = N) in ...) are satisfied by the geometry's vertex descriptor. When a shader declares vertexNormal but the geometry only provides vertexPosition, Metal's pipeline state creation fails, crashing the application.

Fixes applied:

  1. SphericalHarmonics shaders: Remove vertexNormal input entirely; compute flat normals from dFdx/dFdy derivatives of world position in the fragment shader
  2. Custom geometries (Grid3D, BoundingBox, Locator3D): Add default (0, 1, 0) normal attributes
  3. Loaded meshes: New Scene3DHelper.ensureNormals() dynamically adds normals to geometries missing them
  4. Platform support: Metal RHI default, Qt plugin path guards

Test Plan

  • Launch Meshroom on macOS with Metal RHI (no QT3D_RENDERER override needed)
  • Enable 3D viewer — no crash
  • Load mesh from Texturing node — renders correctly
  • Resize window — no crash
  • Switch render modes (Solid, Wireframe, Textured, SH)
  • Verify qtAliceVision plugin loads (no "Missing plugin" warning)
  • Verify EXR textures load via QtAliceVisionImageIO
  • Grid, BoundingBox, Locator render without crash
  • All pipeline nodes execute correctly (camera init, feature extraction, etc.)

Tested on

🤖 Generated with Claude Code

@NeverGET NeverGET marked this pull request as ready for review March 7, 2026 17:46
@philippremy
Copy link
Copy Markdown

This is AWESOME! I ran into all these issues and did some very dirty hacking to get a bare minimum of it working 😅.
That would have been a follow-up project for me eventually but honestly: I am really not into Qt and QML... you are a lifesaver! I'll make sure to do some testing the following days ^^.
So a big thank you for looking into this!!!

@NeverGET
Copy link
Copy Markdown
Author

NeverGET commented Mar 9, 2026

You're welcome, BTW I bundled the app and using daily 😄.
I didnt test all the features in the app, i am still investigating the app, if you have a test list for it, i can try on and keep fixing the issues for MacOS

Comment on lines +98 to +100
ambient: root.ambient
shininess: root.shininess
specular: root.specular
ambient: "#111"
shininess: 1.0
specular: "#000"
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's the reason to change that?

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actually the mesh preview is looking a bit phased off, like whiteish. I tried to make it look like full saturated colour but it didn't worked either and I was feeling very happy about the app is working, I directly go into PR.

Tldr; it just a mess I forgot to revert

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you fix that?

@codecov
Copy link
Copy Markdown

codecov Bot commented Mar 20, 2026

Codecov Report

❌ Patch coverage is 15.00000% with 17 lines in your changes missing coverage. Please review.
✅ Project coverage is 83.21%. Comparing base (b913d50) to head (4097642).
⚠️ Report is 12 commits behind head on develop.
✅ All tests successful. No failed tests found.

Files with missing lines Patch % Lines
meshroom/__init__.py 13.33% 13 Missing ⚠️
meshroom/core/cgroup.py 0.00% 3 Missing ⚠️
meshroom/core/desc/attribute.py 50.00% 1 Missing ⚠️
Additional details and impacted files
@@             Coverage Diff             @@
##           develop    #3030      +/-   ##
===========================================
- Coverage    83.35%   83.21%   -0.14%     
===========================================
  Files           73       73              
  Lines         9882     9901      +19     
===========================================
+ Hits          8237     8239       +2     
- Misses        1645     1662      +17     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Comment thread meshroom/core/desc/attribute.py Outdated
@@ -196,12 +196,15 @@ def matchDescription(self, value, strict=True):
class GroupAttribute(Attribute):
""" A macro Attribute composed of several Attributes """
@deprecated.depreciateParam("group", "Param 'group' on {name} should not be used anymore. Please use 'commandLineGroup' instead")
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
@deprecated.depreciateParam("group", "Param 'group' on {name} should not be used anymore. Please use 'commandLineGroup' instead")
@deprecated.depreciateParam("group", "Param argument 'group' on {name} should not be used anymore. Please use 'commandLineGroup' instead")
@deprecated.depreciateParam("groupDesc", "GroupAttribute argument 'groupDesc' on {name} should not be used anymore. Please use 'items' instead")

@fabiencastan fabiencastan added this to the Meshroom 2026.1.0 milestone Mar 20, 2026
@fabiencastan
Copy link
Copy Markdown
Member

@NeverGET Could you rebase this PR onto the develop branch (instead of a merge)?
@philippremy Could you review and test this PR on macos?

@philippremy
Copy link
Copy Markdown

@philippremy Could you review and test this PR on macos?

Will do. I think I have a valid AdaptiveCpp build now and will report any issues asap. I'll be on a train ride tonight, so maybe I can take it for a spin then.

@philippremy
Copy link
Copy Markdown

I just went ahead and did the testing:

(+): It does not crash anymore, models and images are loaded correctly and (afaict) the 3D Viewer now works correctly on macOS (🚀)1

(-): I don't know what to think about the DYLD_FALLBACK_LIBRARY_PATH and AV_BUNDLE parts.

  1. Regarding DYLD_FALLBACK_LIBRARY_PATH, using environment variables which directly affect the dynamic linker is really not a good idea and an inherent safety hazard (and I assume it already is/soon will be nonfunctional because of macOS sandbox restrictions and System Integrity Protection). I would rather prefer to modify the relevant libraries (namely AliceVision and QtAliceVision) and set the correct rpaths when building them. For AliceVision, I guess we are already fine since my PR got merged. The resulting bundle folder can just be renamed to aliceVision and works out of the box. Remaining are the libraries for QtAliceVision, which would be a very simple PR I could submit.

  2. The AV_BUNDLE environment variable assumes the bundle structure from my heavily-modified MTL-AliceVision fork (i.e., a macOS .bundle folder) afaik. Since the relevant macOS PR got merged over at AliceVision, macOS now also uses a standard Unix root bundle (i.e., lib, bin, share). So I guess this is not needed anymore, since macOS now essentially behaves like Linux.

Everything else LGTM. But I would really prefer to not use/introduce more environment variables but instead try to reduce them in general.

@NeverGET NeverGET force-pushed the feature/macos-metal-3d-viewer branch from 4097642 to a6d4304 Compare May 22, 2026 20:22
@NeverGET
Copy link
Copy Markdown
Author

Hi @fabiencastan @philippremy — apologies for the long silence on this one. The past couple of months turned out to be unexpectedly busy and I only now got the time to come back and properly address the review. Thanks a lot for the detailed feedback and for taking the time to test it on macOS.

I've pushed an updated branch that addresses every point:

  • Rebased onto the latest develop — the merge commit is gone, it's a clean linear history now.
  • MaterialSwitcher.qml: reverted the ambient/shininess/specular override. It was a leftover experiment that didn't actually change anything visually, so the textured material is back to using the mesh's own material properties.
  • attribute.py: applied the suggested @deprecated.depreciateParam("groupDesc", ...) decorator.
  • DYLD_FALLBACK_LIBRARY_PATH: removed. Agreed it shouldn't be set from Python — library resolution should rely on the rpaths baked into the AliceVision bundle, which works now that the macOS PR is merged. Happy to follow up once the QtAliceVision rpath PR lands.
  • AV_BUNDLE: removed. Since the macOS support PR was merged, macOS uses the standard Unix bundle layout, so the special-case path discovery is no longer needed.

What remains is the macOS RHI/shader work — Metal normal fixes, OpenGL fallback techniques, the cgroup fix, and the Qt plugin-path existence guards. I re-tested locally on macOS (Apple Silicon, Metal RHI): the full pipeline recomputes end to end and the 3D viewer works.

Let me know if anything else needs adjusting.

@servantftransperfect
Copy link
Copy Markdown
Contributor

servantftransperfect commented May 25, 2026

Hello,

Thanks for your contribution.

  • I don't think you understood the cgroup usage. This is passed to the command line as a user defined cap on the number of cpu/ max memory to use. The command line are already using the necessary logic to detect the real number of cpus (available memory). This chunk of code is necessary for large shared computers which have per user limitations. It should return -1 if cgroup does not exists. See this link : https://github.com/alicevision/AliceVision/blob/develop/src/aliceVision/system/hardwareContext.cpp .

  • Why are you creating alternatives to RHI shaders ? can you describe a real use case ?

Thanks !

NeverGET and others added 6 commits May 25, 2026 15:45
Metal RHI strictly validates that vertex shader inputs match the vertex
descriptor provided by the geometry. The SphericalHarmonics shaders
declared a vertexNormal input, but custom Qt3D geometries (Grid3D,
BoundingBox, Locator3D) and some loaded meshes don't provide normal
attributes, causing Metal pipeline creation to fail with a crash.

Instead of requiring normals as a vertex attribute, compute flat face
normals in the fragment shader using dFdx/dFdy screen-space derivatives
of the world position. This produces correct lighting for flat-shaded
geometry and works regardless of whether the mesh provides normals.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Built-in Qt3D materials (PhongMaterial, DiffuseSpecularMaterial) expect
a vertexNormal attribute in the vertex descriptor. On Metal RHI, if a
geometry lacks this attribute, the render pipeline state creation fails
with a crash because Metal strictly validates that all shader inputs
are satisfied by the vertex descriptor.

Add default upward-facing (0, 1, 0) normal attributes to:
- Grid3D.qml (dynamic count matching grid vertices)
- BoundingBox.qml (24 vertices)
- Locator3D.qml (6 vertices)

These normals ensure Metal pipeline compatibility while maintaining
correct visual appearance for these helper geometries.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Meshes loaded via SceneLoader (OBJ, PLY, etc.) may lack normal
attributes. When these meshes are rendered with built-in Qt3D materials
that require vertexNormal, Metal RHI crashes due to missing vertex
descriptor entries.

Add Scene3DHelper.ensureNormals(entity) that traverses all QGeometry
children of a loaded entity and adds default (0, 1, 0) normal
attributes to any geometry missing them. Call this method in
MediaLoader's sceneLoaderPostProcess before material setup.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add QML import paths for PySide6's bundled QML modules and the
AliceVision bundle's PlugIns directory. This enables the QML engine
to discover and load qtAliceVision, SfmDataEntity, and DepthMapEntity
QML plugins that provide camera visualization, SfM data overlay,
and depth map rendering in the 3D viewer.

The PySide6 QML path ensures Qt3D and QtQuick.Scene3D modules are
found, while the AV_BUNDLE/PlugIns path provides the AliceVision-
specific visualization plugins.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Guard QT_PLUGIN_PATH and QML2_IMPORT_PATH setup behind directory
  existence checks. On macOS with PySide6, these directories may not
  exist in the standard locations and setting invalid paths causes
  plugin loading failures.

- Add macOS-specific DYLD_FALLBACK_LIBRARY_PATH handling (analogous
  to LD_LIBRARY_PATH on Linux) so AliceVision framework libraries
  can be found at runtime.

- Use Metal as the default RHI backend on macOS (QSG_RHI_BACKEND=metal)
  via setdefault so it can be overridden, while keeping OpenGL as
  the default on other platforms.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Some AliceVision node descriptors from the MTL-AliceVision bundle
use the older 'groupDesc' parameter name instead of 'items' in
GroupAttribute constructors. Add backward compatibility by accepting
'groupDesc' as a deprecated alias and defaulting positional params
to allow keyword-only usage patterns.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@NeverGET NeverGET force-pushed the feature/macos-metal-3d-viewer branch from a6d4304 to d62e860 Compare May 25, 2026 12:46
@NeverGET
Copy link
Copy Markdown
Author

Hi @servantftransperfect — thanks, both points are fair.

cgroup: you're right, I had the semantics backwards. Looking at the hardwareContext.cpp you linked, --maxCores is meant to express a user-imposed cap, not the detected core count — and the existing OSError-catching path in getCgroupCpuCount() already returns -1 correctly on non-Linux. So the early-return I added was both unnecessary and semantically wrong.

For context on why I touched that file in the first place: while debugging the macOS crashes I also noticed Meshroom wasn't using all CPU threads on my machine, and I tried a few things to nudge it — that change was one of those experiments and ended up being a leftover. With it dropped, AliceVision's own detection takes over, which is what should happen.

OpenGL fallback shaders: no real-world use case. The actual macOS Metal fix is in the modified SphericalHarmonics.vert/frag (RHI path); the parallel _gl technique I added was speculative — it would only kick in if someone explicitly set QT3D_RENDERER=opengl to force Qt3D onto its legacy non-RHI OpenGL backend, which isn't something I had a concrete reason for. Dropped.

Branch is force-pushed; the PR now has 6 focused commits.

@servantftransperfect servantftransperfect self-requested a review May 26, 2026 10:32
@servantftransperfect
Copy link
Copy Markdown
Contributor

I am not sure to know why, but without your PR, the bounding box and the widgets were correctly lighted using the directional light (in the 3D viewer).

Now using your PR, as you explicitely added wrong normals, the lighting is weird.

I am not sure we wanted (in the first place) those widgets to be affected by the lighting.

Would you have time finding a solution to make them unlit ? I can see in the doc there is a PerVertexColorMaterial which requires to replace normals with color.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants