jfx icon indicating copy to clipboard operation
jfx copied to clipboard

8271024: Implement macOS Metal Rendering Pipeline

Open arapte opened this issue 5 months ago • 15 comments
trafficstars

Description

This is the implementation of new graphics rendering pipeline for JavaFX using Metal APIs on MacOS. We released two Early Access (EA) builds and have reached a stage where it is ready to be integrated. Default rendering pipeline on macOS has not been changed by this PR. OpenGL still stays as the default rendering pipeline and Metal rendering pipeline is optional to choose (by providing -Dprism.order=mtl) The -Dprism.verbose=true option can be used to verify the rendering pipeline in use.

Details about the changes

Shader changes

  • MSLBackend class: This is the primary class that parses (Prism and Decora) jsl shaders into Metal shaders(msl)
  • There are a few additional Metal shader files added under directory : modules/javafx.graphics/src/main/native-prism-mtl/msl

Build changes - There are new tasks added to build.gradle for

  • Generation/ Compilation/ linking of Metal shaders
  • Compilation of Prism Java and Objective C files

Prism - Prism is the rendering engine of JavaFX.

  • Added Metal specific Java classes and respective native implementation which use Metal APIs
  • Java side changes:
    • New Metal specific classes: Classes prefixed with MTL, are added here : modules/javafx.graphics/src/main/java/com/sun/prism/mtl
    • Modification to Prism common classes: A few limited changes were required in Prism common classes to support Metal classes.
  • Native side changes:
    • New Metal specific Objective C implementation is added here: modules/javafx.graphics/src/main/native-prism-mtl

Glass - Glass is the windowing toolkit of JavaFX

  • Existing Glass classes are refactored to support both OpenGL and Metal pipelines
  • Added Metal specific Glass implementation.

Testing

  • Testing performed on different hardware and macOS versions.
    • HW - macBooks with Intel, Intel with discrete graphics card, Apple Silicon (M1, M2 and M4)
    • macOS versions - macOS 13, macOS 14 and macOS 15
  • Functional Testing:
    • All headful tests pass with Metal pipeline.
    • Verified samples applications: Ensemble and toys
  • Performance Testing:
    • Tested with RenderPerfTest: Almost all tests match/exceed es2 performance except a few that fall a little short on different hardwares. These will be addressed separately (there are open bugs already filed)

Note to reviewers :

  • Providing -Dprism.order=mtl option is a must for testing the Metal pipeline
  • It would be a good idea to test both OpenGL and Metal pipelines
  • Known issues and tasks can be found here: https://bugs.openjdk.org/issues/?filter=46763

/skara contributor add @kevinrushforth /skara contributor add @aghaisas /skara contributor add @jayathirthrao


Progress

  • [x] Change must not contain extraneous whitespace
  • [x] Commit message must refer to an issue
  • [ ] Change must be properly reviewed (3 reviews required, with at least 1 Reviewer, 2 Authors)

Issue

  • JDK-8271024: Implement macOS Metal Rendering Pipeline (Enhancement - P3)

Contributors

Reviewing

Using git

Checkout this PR locally:
$ git fetch https://git.openjdk.org/jfx.git pull/1824/head:pull/1824
$ git checkout pull/1824

Update a local copy of the PR:
$ git checkout pull/1824
$ git pull https://git.openjdk.org/jfx.git pull/1824/head

Using Skara CLI tools

Checkout this PR locally:
$ git pr checkout 1824

View PR using the GUI difftool:
$ git pr show -t 1824

Using diff file

Download this PR as a diff file:
https://git.openjdk.org/jfx/pull/1824.diff

Using Webrev

Link to Webrev Comment

arapte avatar Jun 12 '25 05:06 arapte

:wave: Welcome back arapte! A progress list of the required criteria for merging this PR into master will be added to the body of your pull request. There are additional pull request commands available for use with this pull request.

bridgekeeper[bot] avatar Jun 12 '25 05:06 bridgekeeper[bot]

@arapte This change now passes all automated pre-integration checks.

ℹ️ This project also has non-automated pre-integration requirements. Please see the file CONTRIBUTING.md for details.

After integration, the commit message for the final commit will be:

8271024: Implement macOS Metal Rendering Pipeline

Co-authored-by: Kevin Rushforth <[email protected]>
Co-authored-by: Ajit Ghaisas <[email protected]>
Co-authored-by: Jayathirth D V <[email protected]>
Co-authored-by: Ambarish Rapte <[email protected]>
Reviewed-by: angorya, nlisker, kcr, lkostyra

You can use pull request commands such as /summary, /contributor and /issue to adjust it as needed.

At the time when this comment was updated there had been no new commits pushed to the master branch. If another commit should be pushed before you perform the /integrate command, your PR will be automatically rebased. If you prefer to avoid any potential automatic rebasing, please check the documentation for the /integrate command for further details.

➡️ To integrate this PR with the above commit message to the master branch, type /integrate in a new comment.

openjdk[bot] avatar Jun 12 '25 05:06 openjdk[bot]

@arapte Contributor Kevin Rushforth <[email protected]> successfully added.

openjdk[bot] avatar Jun 12 '25 05:06 openjdk[bot]

@arapte Contributor Ajit Ghaisas <[email protected]> successfully added.

openjdk[bot] avatar Jun 12 '25 05:06 openjdk[bot]

@arapte Contributor Jayathirth D V <[email protected]> successfully added.

openjdk[bot] avatar Jun 12 '25 05:06 openjdk[bot]

/skara contributor add @arapte

arapte avatar Jun 12 '25 09:06 arapte

@arapte Contributor Ambarish Rapte <[email protected]> successfully added.

openjdk[bot] avatar Jun 12 '25 09:06 openjdk[bot]

/reviewers 3

kevinrushforth avatar Jun 12 '25 13:06 kevinrushforth

@kevinrushforth The total number of required reviews for this PR (including the jcheck configuration and the last /reviewers command) is now set to 3 (with at least 1 Reviewer, 2 Authors).

openjdk[bot] avatar Jun 12 '25 13:06 openjdk[bot]

As this is a large PR I don't expect any one reviewer to review the whole thing, but I do want to make sure that each piece is reviewed by someone. So we'd like as many eyes on this as possible.

The parts we are most concerned with getting reviewed are changes in existing classes that could impact other pipelines, particularly the default ES2 pipeline on macOS

For example, there are some minor changes in the Prism base classes, which necessitated changes in the pipeline-specific classes.

The largest change in shared code is the glass refactoring to move the rendering-pipeline-specific code (OpenGL or Metal) into pipeline-specific classes. For some classes, the existing code is split between the base class and the CGL subclass (with the MTL class being new implementation for Metal).

Here are the main classes to look at:

  • GlassCGLFrameBufferObject -- renamed from GlassFrameBufferObject with minor changes

  • GlassMTLFrameBufferObject -- sibling class

  • GlassOffscreen -- pipeline-specific pieces moved to subclasses

    • GlassCGLOffscreen
    • GlassMTLOffscreen
  • GlassLayer3D -- pipeline-specific pieces moved to sub-layers (not using inheritance)

    • GlassLayerCGL3D
    • GlassLayerMTL3D
  • GlassView -- minor changes to remove OpenGL-isms and fix a few other interface issues

  • GlassView3D -- pipeline-specific pieces moved to subclasses

    • GlassViewCGL3D
    • GlassViewMTL3D

@jayathirthrao can give more details on the above glass refacatoring changes if you are interested

@beldenfox If you are willing to look at it, we would be quite appreciative of your comments or suggestions on the glass refactoring.

kevinrushforth avatar Jun 12 '25 23:06 kevinrushforth

@tiainen Would you be able to look at the build changes and also do a test build?

kevinrushforth avatar Jun 12 '25 23:06 kevinrushforth

@beldenfox Thanks for your inputs.

  1. Regarding adding comments in GlassView/Layer classes about how they are not subclasses but subViews/subLayers : Sure i will go ahead and add comments in the class. Also as you mentioned we can name them better:
  • GlassView3D -> GlassViewEventHandler(Since it is common class which handles events for both OpenGL and Metal pipeline) Or GlassSubView(Since it is added as subView in GlassHostView in GlassView.m). Please suggest if you have better names that we can use here.
  • GlassViewCGL3D -> GlassViewCGL(With comment mentioning how it is not subclass but a subView of GlassView3D)
  • GlassViewMTL3D -> GlassViewMTL(With comment mentioning how it is not subclass but a subView of GlassView3D)
  • At other places also i will remove 3D in names and appropriate comment in CGL/MTL layer class.
  1. Regarding NSOpenGLView : Since both CGL and MTL views are not subclass of same NSView we have current structure with Event handling in common class GlassView3D and CGL & MTL Views as subViews of GlassView3D. I also tried removing NSOpenGLView reference and then running Ensemble8 in ES2 pipeline and things work fine. But we want to minimize changes related to already present default ES2 pipeline in this PR as part of refactoring effort. We can work on it in as a follow-up issue.

  2. Regarding change in MacOSXWindowSystemInterface.m : Same here we want to minimize changes related to ES2 in this PR. We can work on it in as a follow-up issue.

  3. Regarding setOpaque() in GlassWindow.m : While refactoring Glass for Metal pipeline under JDK-8325379 this was identified as follow up issue : JDK-8356758. We were aware of how this code related to JDK-8095004 but thanks for pointing to JDK-8095040 which was not verified. There are no regression seen with this refactoring, PickTest3D works properly in both ES2 and MTL pipeline of this PR and matches output seen with mainline code. I verified that test mentioned in JDK-8095040 on latest mainline code and i still see that black filling is drawn. Same behaviour is seen with ES2 and MTL pipeline with this PR. But it works properly in 8u431, looks like the issue has resurfaced so i have created new bug : JDK-8359904

  4. Regarding removal of JNI method getNativeLayer() from GlassView.m : Yes this is not used anywhere and we can remove it. I removed this code, built and tested Ensemble8, started CI headful test run. If everything is green will go ahead and remove it and its corresponding code in MacView.java

jayathirthrao avatar Jun 18 '25 10:06 jayathirthrao

@jayathirthrao Thanks for the thorough response. And, again, it's great to see Metal happening.

  • GlassView3D -> GlassViewEventHandler(Since it is common class which handles events for both OpenGL and Metal pipeline) Or GlassSubView(Since it is added as subView in GlassHostView in GlassView.m). Please suggest if you have better names that we can use here.

I want to keep the GlassView prefix since these classes implement the platform side of the View class in the toolkit. If we were to rename anything it would be GlassViewCGL and GlassViewMTL since these are more closely related to Prism than the toolkit View class. But I'm overthinking things. For now I think we should keep GlassView and GlassView3D along with GlassViewCGL and GlassViewMTL and just clarify what's going on in the comments. At some point I want to combine GlassView and GlassView3D into one class but that can wait for a follow-up issue.

  1. Regarding setOpaque() in GlassWindow.m

In my testing the setOpaque call isn't happening in this PR since the layer hasn't been created yet. But it doesn't seem to matter (?)

I'm having a hard time reproducing the original PickTest3D bug in the master branch. The screenshots in the bug report show translucency but the original bug report doesn't mention it and the PickTest3D code doesn't call for it. I tweaked PickTest3D to try a few combinations of setOpacity and DECORATED/TRANSPARENT and that setOpaque call didn't seem to make any difference even in the master branch. I must be missing something.

As for the UNIFIED issue I don't think there's a bug. The implementation relied on the OS to draw a brushed metal background but Apple removed that years ago and now just draws black. I will take a look but only because I want to understand how this works; we might need similar logic if we want to support desktop translucency effects. UNIFIED is deprecated and doesn't work on Windows either so we should probably close the new bug as Never Fix.

beldenfox avatar Jun 18 '25 20:06 beldenfox

I'm having a hard time reproducing the original PickTest3D bug in the master branch.

Spoke too soon. PickTest3D has been tweaked since the original Mac bug was posted and resolved.

beldenfox avatar Jun 18 '25 21:06 beldenfox

@beldenfox Found out the root cause for why we are seeing layer as nil GlassWindow.m.

We have additional level of abstraction for views and we set layer for GlassViewCGL/MTL and not for GlassView3D. We already have change in GlassView.m->getGlassView() to get GlassView3D object and then call getLayer() on it. We need to make similar change in GlassWindow.m->getMacView(), with this change we are able to get appropriate layer by calling getLayer(). This change is made and testing is in progress.

Also what i am also noticing is setOpaque() in GlassWindow.m is never called on OpenGL pipeline when i run Ensemble8 demos, PickTest3D or test present in JDK-8095040. I will keep this OpenGL specific setOpaque call since we don't want to make changes related to OpenGL in this PR. But i will add comment about refactoring and moving it to CGL specific class in future.

Regarding classname change, i will keep GlassView3D name as it is but remove 3D references in other classes.

jayathirthrao avatar Jun 19 '25 14:06 jayathirthrao

Glass changes are updated based on review inputs.

Also @arapte noticed commented out GLASS_POOL_PUSH/POP logic in GlassView.m which is updated to match mainline code. Since this enables common autoreleasepool, explicit autorelease calls for Vertex & Index buffer in MetalMesh.m are removed. Latest code is tested again and headful CI runs are green with both OpenGL and Metal.

jayathirthrao avatar Jun 23 '25 12:06 jayathirthrao

@jayathirthrao Thanks for renaming the classes. Much appreciated.

  1. Regarding setOpaque() in GlassWindow.m :

That setOpaque: call still isn't being executed. The code is looking at the GlassLayer, not the GlassLayerCGL, so the isKindOfClass: is failing and setOpaque: is never called.

But there's no point in changing that code until we can reproduce the original PickTest3D bug and I haven't been able to do that. Have you? The bug (JDK-8095004) was entered in 2013 but the version of PickTest3D in apps/toys was added in early 2015. I don't know how closely it matches the code that caused the original bug report.

The original bug report doesn't add up. The comments state that the problem stems from using a Phong material that has a diffuse color with an alpha of 0.5. That should cause the individual objects to become translucent which is what happens when I tweak the PickTest3D code. But the screen shot in the bug report shows solid objects; the entire scene is washed out but the individual objects are opaque.

I can't test this on my Windows box probably because I'm running in a VM. Prism reports that the D3D renderer is running without error but I still get blank screens except for the ColorCube toy.

beldenfox avatar Jun 23 '25 18:06 beldenfox

I did a build on our infrastructure and everything worked fine. We successfully tested the resulting runtime with both Metal and OpenGL and on Intel and Apple Silicon hardwares.

tiainen avatar Jun 25 '25 06:06 tiainen

@beldenfox Thanks for pointing to improper isKindOfClass: usage. That is updated now and setOpaque is getting called properly as in mainline.

Regarding reproducing the issue originally seen in PickTest3D : PickTest3D is updated in 2015 under JDK-8130532 to use alpha value as 1.0 instead of 0.5 for diffuse color. I reverted this change to use 0.5 and disabled calling setOpaque in GlassWindow.m. Unfortunately, i see proper output with translucent objects and there is no difference in output of PickTest3D with/without setOpaque call even with 0.5 alpha value. As you have also observed in current code setOpaque call has no effect in output even when we use 0.5 alpha value. Currently i am trying get information related to PickTest3D code pre 2015.

Regarding running demos in VM : Is Prism falling back to SW pipeline? (You can check for this info using -Dprism.verbose=true). If it is falling back to SW pipeline in VM, we can try to force it to use hardware pipeline using -Dprism.forceGPU=true.

jayathirthrao avatar Jun 25 '25 13:06 jayathirthrao

Regarding reproducing the issue originally seen in PickTest3D : PickTest3D is updated in 2015 under JDK-8130532 to use alpha value as 1.0 instead of 0.5 for diffuse color.

Thanks for tracking that down. The description in JDK-8130532 references JDK-8095058. From what I can tell that was the original bug that was causing incorrect results on macOS and covering up the incorrect alpha value in the diffuse color. I think using setOpaque: to rip out the alpha channel was a work-around for JDK-8095058 and is probably no longer necessary.

But removing the alpha channel made macOS match the output of Windows and Linux. Is JavaFX designed to work without an alpha channel when the window is not TRANSPARENT?

Regarding running demos in VM : Is Prism falling back to SW pipeline? (You can check for this info using -Dprism.verbose=true).

In my Windows VM Prism reports that it's using the D3D pipeline. And Prism isn't throwing any errors or issuing any warnings, it just doesn't draw except for the ColorCube toy.

beldenfox avatar Jun 25 '25 16:06 beldenfox

@beldenfox JDK-8095004 talks about default opacity of CALayer being NO. But current documentation mentions that by default CALayer is opaque. And there are no pointers whether Apple has changed this behaviour in-between. Also as you mentioned there is update in shader also to ignore alpha channel when its value is 0.0 in JDK-8095058. So there are lot of factors at different levels which will make PickTest3D to behave differently now compared to pre 2015.

So on current code i have verified that PickTest3D works properly with different combinations of alpha value in diffusecolor and opacity for the stage. Regarding whether we need to remove setOpaque/not : As part for glass refactoring in this PR its better to keep the default ES2 pipeline code as it is.

jayathirthrao avatar Jun 26 '25 14:06 jayathirthrao

see a few warnings:

Description	Resource	Type	Path	Location
The method createStockShader(String) of type DummyResourceFactory should be tagged with @Override since it actually overrides a superinterface method	DummyResourceFactory.java	Java Problem	/graphics/src/main/java/com/sun/prism/null3d	line 132
The method getResourceFactory() of type MTLContext should be tagged with @Override since it actually overrides a superclass method	MTLContext.java	Java Problem	/graphics/src/main/java/com/sun/prism/mtl	line 127
The method getNativeHandle() of type MTLRTTexture should be tagged with @Override since it actually overrides a superclass method	MTLRTTexture.java	Java Problem	/graphics/src/main/java/com/sun/prism/mtl	line 110
The method createRTTexture(int, int, Texture.WrapMode, boolean) of type MTLResourceFactory should be tagged with @Override since it actually overrides a superinterface method	MTLResourceFactory.java	Java Problem	/graphics/src/main/java/com/sun/prism/mtl	line 342
The method getAccelType() of type MTLShaderSource should be tagged with @Override since it actually overrides a superinterface method	MTLShaderSource.java	Java Problem	/graphics/src/main/java/com/sun/scenario/effect/impl/hw/mtl	line 41
The method loadSource(String) of type MTLShaderSource should be tagged with @Override since it actually overrides a superinterface method	MTLShaderSource.java	Java Problem	/graphics/src/main/java/com/sun/scenario/effect/impl/hw/mtl	line 34

andy-goryachev-oracle avatar Jun 30 '25 15:06 andy-goryachev-oracle

Hmm, I must be doing something wrong. Launching the monkey tester with the following args:

-Dprism.order=mtl
-Dprism.verbose=true

and getting this exception (macOS 15.5 on M1 silicon):

Prism pipeline init order: mtl 
Using Double Precision Marlin Rasterizer
Using dirty region optimizations
Not using texture mask for primitives
Not forcing power of 2 sizes for textures
Using hardware CLAMP_TO_ZERO mode
Opting in for HiDPI pixel scaling
Not forcing UploadingPainter
Prism pipeline name = com.sun.prism.mtl.MTLPipeline
Loading native metal library, named: prism_mtl
Succeeded: Loading native metal library.
(X) Got class = class com.sun.prism.mtl.MTLPipeline
Initialized prism pipeline: com.sun.prism.mtl.MTLPipeline
RenderJob.run: internal exception
java.lang.ExceptionInInitializerError
	at javafx.graphics/com.sun.prism.mtl.MTLResourceFactory.<init>(MTLResourceFactory.java:60)
	at javafx.graphics/com.sun.prism.mtl.MTLPipeline.getResourceFactory(MTLPipeline.java:87)
	at javafx.graphics/com.sun.prism.mtl.MTLPipeline.getDefaultResourceFactory(MTLPipeline.java:78)
	at javafx.graphics/com.sun.prism.GraphicsPipeline.getDefaultResourceFactory(GraphicsPipeline.java:155)
	at javafx.graphics/com.sun.javafx.tk.quantum.QuantumRenderer.lambda$1(QuantumRenderer.java:155)
	at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:545)
	at java.base/java.util.concurrent.FutureTask.runAndReset(FutureTask.java:369)
	at javafx.graphics/com.sun.javafx.tk.RenderJob.run(RenderJob.java:58)
	at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1095)
	at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:619)
	at javafx.graphics/com.sun.javafx.tk.quantum.QuantumRenderer$PipelineRunnable.run(QuantumRenderer.java:122)
	at java.base/java.lang.Thread.run(Thread.java:1447)
Caused by: java.lang.RuntimeException: java.io.IOException: Stream closed
	at javafx.graphics/com.sun.prism.mtl.MTLContext.<clinit>(MTLContext.java:109)
	... 12 more
Caused by: java.io.IOException: Stream closed
	at java.base/java.io.BufferedInputStream.getInIfOpen(BufferedInputStream.java:170)
	at java.base/java.io.BufferedInputStream.read1(BufferedInputStream.java:328)
	at java.base/java.io.BufferedInputStream.read(BufferedInputStream.java:388)
	at java.base/java.io.InputStream.readNBytes(InputStream.java:411)
	at java.base/java.io.InputStream.readAllBytes(InputStream.java:348)
	at javafx.graphics/com.sun.prism.mtl.MTLContext.<clinit>(MTLContext.java:104)
	... 12 more

andy-goryachev-oracle avatar Jun 30 '25 15:06 andy-goryachev-oracle

The method createStockShader(String) of type DummyResourceFactory should be tagged with @Override since it actually overrides a superinterface method DummyResourceFactory.java Java Problem /graphics/src/main/java/com/sun/prism/null3d line 132

Thank you, added @Override annotation to mentioned methods.

arapte avatar Jul 02 '25 08:07 arapte

Still getting the exception loading "msl/jfxshaders.metallib". I am running in eclipse, so perhaps the eclipse project files need to include some added paths? I've got a few of them in the workspace: Screenshot 2025-07-02 at 07 50 49

andy-goryachev-oracle avatar Jul 02 '25 14:07 andy-goryachev-oracle

Is mtl a good name for the prism.order property? Should it be metal?

andy-goryachev-oracle avatar Jul 02 '25 14:07 andy-goryachev-oracle

Is mtl a good name for the prism.order property? Should it be metal?

"mtl" is sometimes used a short for "material", which has overlapping context here. I'd prefer the explicit "metal".

nlisker avatar Jul 02 '25 14:07 nlisker

Is mtl a good name for the prism.order property? Should it be metal?

"mtl" is sometimes used a short for "material", which has overlapping context here. I'd prefer the explicit "metal".

mtl was chose because

  • It aligns with our existing abbreviated names for pipelines like d3d, es2, sw
  • The metal library also uses 'MTL' as prefix all metal library types like MTLDevice, MTLCommandQueue, MTLCommandBuffer and more..

I think It would feel relevant in the context of -Dprism.order

arapte avatar Jul 03 '25 12:07 arapte

Still getting the exception loading "msl/jfxshaders.metallib". I am running in eclipse, so perhaps the eclipse project files need to include some added paths? I've got a few of them in the workspace: Screenshot 2025-07-02 at 07 50 49

It should use the file from build directory: build/modular-sdk/modules/javafx.graphics/com/sun/prism/mtl/msl/jfxshaders.metallib

arapte avatar Jul 05 '25 01:07 arapte