调用 javafx.scene.effect.DropShadow 时 Javafx .exe 崩溃

Javafx .exe crashes while invoking javafx.scene.effect.DropShadow

我的原生 javafx 应用程序在调用 javafx.scene.effect.DropShadow 时在运行时崩溃。 该应用程序仅在我注释掉相关代码时才有效。如何使用 DropShadow 效果来实现?

错误日志:

[Di. Feb. 02 18:58:50 MEZ 2021][INFO] ==================== RUN TASK ====================
[Di. Feb. 02 18:58:50 MEZ 2021][FINE] PB Command for run until end: c:\Users\...\target\client\x86_64-windows\Cleptomania.exe
[Di. Feb. 02 18:58:50 MEZ 2021][FINE] Start process run until end...
[Di. Feb. 02 18:58:59 MEZ 2021][INFO] [SUB] java.lang.RuntimeException: Could not create peer  Merge for renderer com.sun.scenario.effect.impl.prism.ps.PPSRenderer@7464688f
[Di. Feb. 02 18:58:59 MEZ 2021][INFO] [SUB]     at com.sun.scenario.effect.impl.Renderer.getPeerInstance(Renderer.java:254)
[Di. Feb. 02 18:58:59 MEZ 2021][INFO] [SUB]     at com.sun.scenario.effect.CoreEffect.getPeer(CoreEffect.java:66)
[Di. Feb. 02 18:58:59 MEZ 2021][INFO] [SUB]     at com.sun.scenario.effect.CoreEffect.getPeer(CoreEffect.java:92)
[Di. Feb. 02 18:58:59 MEZ 2021][INFO] [SUB]     at com.sun.scenario.effect.CoreEffect.filterImageDatas(CoreEffect.java:106)
[Di. Feb. 02 18:58:59 MEZ 2021][INFO] [SUB]     at com.sun.scenario.effect.Merge.filterImageDatas(Merge.java:39)
[Di. Feb. 02 18:58:59 MEZ 2021][INFO] [SUB]     at com.sun.scenario.effect.Merge.filter(Merge.java:172)
[Di. Feb. 02 18:58:59 MEZ 2021][INFO] [SUB]     at com.sun.scenario.effect.DelegateEffect.filter(DelegateEffect.java:70)
[Di. Feb. 02 18:58:59 MEZ 2021][INFO] [SUB]     at com.sun.scenario.effect.impl.prism.PrEffectHelper.render(PrEffectHelper.java:166)
[Di. Feb. 02 18:58:59 MEZ 2021][INFO] [SUB]     at com.sun.javafx.sg.prism.EffectFilter.render(EffectFilter.java:61)
[Di. Feb. 02 18:58:59 MEZ 2021][INFO] [SUB]     at com.sun.javafx.sg.prism.NGNode.renderEffect(NGNode.java:2384)
[Di. Feb. 02 18:58:59 MEZ 2021][INFO] [SUB]     at com.sun.javafx.sg.prism.NGNode.doRender(NGNode.java:2069)
[Di. Feb. 02 18:58:59 MEZ 2021][INFO] [SUB]     at com.sun.javafx.sg.prism.NGNode.render(NGNode.java:1964)
[Di. Feb. 02 18:58:59 MEZ 2021][INFO] [SUB]     at com.sun.javafx.sg.prism.NGGroup.renderContent(NGGroup.java:270)
[Di. Feb. 02 18:58:59 MEZ 2021][INFO] [SUB]     at com.sun.javafx.sg.prism.NGRegion.renderContent(NGRegion.java:579)
[Di. Feb. 02 18:58:59 MEZ 2021][INFO] [SUB]     at com.sun.javafx.sg.prism.NGNode.doRender(NGNode.java:2072)
[Di. Feb. 02 18:58:59 MEZ 2021][INFO] [SUB]     at com.sun.javafx.sg.prism.NGNode.render(NGNode.java:1964)
[Di. Feb. 02 18:58:59 MEZ 2021][INFO] [SUB]     at com.sun.javafx.tk.quantum.ViewPainter.doPaint(ViewPainter.java:479)
[Di. Feb. 02 18:58:59 MEZ 2021][INFO] [SUB]     at com.sun.javafx.tk.quantum.ViewPainter.paintImpl(ViewPainter.java:328)
[Di. Feb. 02 18:58:59 MEZ 2021][INFO] [SUB]     at com.sun.javafx.tk.quantum.PresentingPainter.run(PresentingPainter.java:91)
[Di. Feb. 02 18:58:59 MEZ 2021][INFO] [SUB]     at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:515)
[Di. Feb. 02 18:58:59 MEZ 2021][INFO] [SUB]     at java.util.concurrent.FutureTask.runAndReset(FutureTask.java:305)
[Di. Feb. 02 18:58:59 MEZ 2021][INFO] [SUB]     at com.sun.javafx.tk.RenderJob.run(RenderJob.java:58)
[Di. Feb. 02 18:58:59 MEZ 2021][INFO] [SUB]     at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
[Di. Feb. 02 18:58:59 MEZ 2021][INFO] [SUB]     at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
[Di. Feb. 02 18:58:59 MEZ 2021][INFO] [SUB]     at com.sun.javafx.tk.quantum.QuantumRenderer$PipelineRunnable.run(QuantumRenderer.java:125)
[Di. Feb. 02 18:58:59 MEZ 2021][INFO] [SUB]     at java.lang.Thread.run(Thread.java:834)
[Di. Feb. 02 18:58:59 MEZ 2021][INFO] [SUB]     at com.oracle.svm.core.thread.JavaThreads.threadStartRoutine(JavaThreads.java:519)
[Di. Feb. 02 18:58:59 MEZ 2021][INFO] [SUB]     at com.oracle.svm.core.windows.WindowsJavaThreads.osThreadStartRoutine(WindowsJavaThreads.java:138)
[Di. Feb. 02 18:58:59 MEZ 2021][INFO] [SUB] java.lang.IllegalStateException: Operation requires resource lock
[Di. Feb. 02 18:58:59 MEZ 2021][INFO] [SUB]     at com.sun.prism.impl.ManagedResource.assertLocked(ManagedResource.java:96)
[Di. Feb. 02 18:58:59 MEZ 2021][INFO] [SUB]     at com.sun.prism.impl.BaseTexture.assertLocked(BaseTexture.java:267)
[Di. Feb. 02 18:58:59 MEZ 2021][INFO] [SUB]     at com.sun.prism.impl.ps.BaseShaderContext.setTexture(BaseShaderContext.java:695)
[Di. Feb. 02 18:58:59 MEZ 2021][INFO] [SUB]     at com.sun.prism.impl.ps.BaseShaderContext.validateTextureOp(BaseShaderContext.java:591)
[Di. Feb. 02 18:58:59 MEZ 2021][INFO] [SUB]     at com.sun.prism.impl.ps.BaseShaderContext.validateTextureOp(BaseShaderContext.java:507)
[Di. Feb. 02 18:58:59 MEZ 2021][INFO] [SUB]     at com.sun.prism.impl.BaseGraphics.drawTextureRaw(BaseGraphics.java:727)
[Di. Feb. 02 18:58:59 MEZ 2021][INFO] [SUB]     at com.sun.scenario.effect.impl.prism.ps.PPSOneSamplerPeer.filterImpl(PPSOneSamplerPeer.java:117)
[Di. Feb. 02 18:58:59 MEZ 2021][INFO] [SUB]     at com.sun.scenario.effect.impl.prism.ps.PPSEffectPeer.filter(PPSEffectPeer.java:54)
[Di. Feb. 02 18:58:59 MEZ 2021][INFO] [SUB]     at com.sun.scenario.effect.LinearConvolveCoreEffect.filterImageDatas(LinearConvolveCoreEffect.java:85)
[Di. Feb. 02 18:58:59 MEZ 2021][INFO] [SUB]     at com.sun.scenario.effect.LinearConvolveCoreEffect.filterImageDatas(LinearConvolveCoreEffect.java:41)
[Di. Feb. 02 18:58:59 MEZ 2021][INFO] [SUB]     at com.sun.scenario.effect.FilterEffect.filter(FilterEffect.java:195)
[Di. Feb. 02 18:58:59 MEZ 2021][INFO] [SUB]     at com.sun.scenario.effect.Offset.filter(Offset.java:160)
[Di. Feb. 02 18:58:59 MEZ 2021][INFO] [SUB]     at com.sun.scenario.effect.Merge.filter(Merge.java:148)
[Di. Feb. 02 18:58:59 MEZ 2021][INFO] [SUB]     at com.sun.scenario.effect.DelegateEffect.filter(DelegateEffect.java:70)
[Di. Feb. 02 18:58:59 MEZ 2021][INFO] [SUB]     at com.sun.scenario.effect.impl.prism.PrEffectHelper.render(PrEffectHelper.java:166)
[Di. Feb. 02 18:58:59 MEZ 2021][INFO] [SUB]     at com.sun.javafx.sg.prism.EffectFilter.render(EffectFilter.java:61)
[Di. Feb. 02 18:58:59 MEZ 2021][INFO] [SUB]     at com.sun.javafx.sg.prism.NGNode.renderEffect(NGNode.java:2384)
[Di. Feb. 02 18:58:59 MEZ 2021][INFO] [SUB]     at com.sun.javafx.sg.prism.NGNode.doRender(NGNode.java:2069)
[Di. Feb. 02 18:58:59 MEZ 2021][INFO] [SUB]     at com.sun.javafx.sg.prism.NGNode.render(NGNode.java:1964)
[Di. Feb. 02 18:58:59 MEZ 2021][INFO] [SUB]     at com.sun.javafx.sg.prism.NGGroup.renderContent(NGGroup.java:270)
[Di. Feb. 02 18:58:59 MEZ 2021][INFO] [SUB]     at com.sun.javafx.sg.prism.NGRegion.renderContent(NGRegion.java:579)
[Di. Feb. 02 18:58:59 MEZ 2021][INFO] [SUB]     at com.sun.javafx.sg.prism.NGNode.doRender(NGNode.java:2072)
[Di. Feb. 02 18:58:59 MEZ 2021][INFO] [SUB]     at com.sun.javafx.sg.prism.NGNode.render(NGNode.java:1964)
[Di. Feb. 02 18:58:59 MEZ 2021][INFO] [SUB]     at com.sun.javafx.tk.quantum.ViewPainter.doPaint(ViewPainter.java:479)
[Di. Feb. 02 18:58:59 MEZ 2021][INFO] [SUB]     at com.sun.javafx.tk.quantum.ViewPainter.paintImpl(ViewPainter.java:328)
[Di. Feb. 02 18:58:59 MEZ 2021][INFO] [SUB]     at com.sun.javafx.tk.quantum.PresentingPainter.run(PresentingPainter.java:91)
[Di. Feb. 02 18:58:59 MEZ 2021][INFO] [SUB]     at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:515)
[Di. Feb. 02 18:58:59 MEZ 2021][INFO] [SUB]     at java.util.concurrent.FutureTask.runAndReset(FutureTask.java:305)
[Di. Feb. 02 18:58:59 MEZ 2021][INFO] [SUB]     at com.sun.javafx.tk.RenderJob.run(RenderJob.java:58)
[Di. Feb. 02 18:58:59 MEZ 2021][INFO] [SUB]     at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
[Di. Feb. 02 18:58:59 MEZ 2021][INFO] [SUB]     at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
[Di. Feb. 02 18:58:59 MEZ 2021][INFO] [SUB]     at com.sun.javafx.tk.quantum.QuantumRenderer$PipelineRunnable.run(QuantumRenderer.java:125)
[Di. Feb. 02 18:58:59 MEZ 2021][INFO] [SUB]     at java.lang.Thread.run(Thread.java:834)
[Di. Feb. 02 18:58:59 MEZ 2021][INFO] [SUB]     at com.oracle.svm.core.thread.JavaThreads.threadStartRoutine(JavaThreads.java:519)
[Di. Feb. 02 18:58:59 MEZ 2021][INFO] [SUB]     at com.oracle.svm.core.windows.WindowsJavaThreads.osThreadStartRoutine(WindowsJavaThreads.java:138)

更新 - 示例代码

import javafx.application.Application;
import javafx.application.Platform;
import javafx.event.EventHandler;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.effect.DropShadow;
import javafx.scene.layout.Background;
import javafx.scene.layout.BackgroundFill;
import javafx.scene.layout.CornerRadii;
import javafx.scene.layout.Pane;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.scene.text.Font;
import javafx.scene.text.FontWeight;
import javafx.scene.text.Text;
import javafx.stage.Stage;
import javafx.stage.WindowEvent;

public class SampleApp extends Application {

    @Override
    public void start(Stage stage) throws Exception 
    {
        VBox root = new VBox();     
        root.getChildren().add(getButtonBox(root)); 

        Scene scene = new Scene(root, 600, 600);  
        stage.setScene(scene);
        stage.setOnCloseRequest(new EventHandler<WindowEvent>() 
        {
            @Override
            public void handle(WindowEvent t) 
            {
                Platform.exit();
                System.exit(0);
            }
        });
        stage.show();
    }
    
    private VBox getButtonBox(Pane pane)
    {
        VBox box = new VBox();
        
        Button btn = new Button("No Effect");
        btn.setOnAction(action -> setChild(pane, false));
        
        Button btnEff = new Button("With Effect");
        btnEff.setOnAction(action -> setChild(pane, true));
        
        box.getChildren().addAll(btn, btnEff);
        return box;
    }
    
    private void setChild(Pane pane, boolean withEffect)
    {   
        VBox box = new VBox();
        Text text = new Text("Hello");
        text.setFill(Color.WHITE);
        text.setFont(Font.font("Courier New", FontWeight.BOLD, 40));
        box.getChildren().add(text);
        box.setAlignment(Pos.CENTER);
        box.setBackground(new Background(new BackgroundFill(Color.BLUE, new CornerRadii(5,5,5,5,false) ,Insets.EMPTY)));
        box.setTranslateZ(-4);
        if(withEffect)      
        {
            DropShadow dropShadow = new DropShadow();
            dropShadow.setRadius(5.0);
            dropShadow.setSpread(1.0); 
            box.setEffect(dropShadow);
        }
        if(pane.getChildren().size() > 1)   
            pane.getChildren().set(1, box);
        else
            pane.getChildren().add(box);
    }
    
    public static void main(String args[]) 
    {
        launch(args);
    }
}

TL;DR

反射列表中缺少 class com.sun.scenario.effect.impl.prism.PrMergePeer。添加它,重新构建原生镜像,就可以了。

说明

如果您的应用程序 运行 在 JVM/Hotspot 上正常(即通过 mvn javafx:run),并且在 运行 时间通过本机图像失败(即通过 mvn client:run),通常是配置问题,可能很容易修复。

通常,它与反射列表中缺少 classes 有关,这会导致 class 未找到异常。

但是,有时,无法反射调用的缺失 class 不会引发异常,并且应用程序保持 运行ning,但会出现一些意外行为。 DropShadow 效果似乎就是这种情况。

这让事情更难追踪。 class not found 异常已经给出了 class 缺失的提示,但在当前情况下,运行time 异常本身并没有。

如这里所述,https://docs.gluonhq.com/#_jni_and_reflection,客户端插件在target/client/$arch-$os/gvm/reflectionconfig-$arch-$os.json下创建了一个反射配置文件,其中有一组JavaFX classes用于反射.

然而,这一套并不完整,有时会遗漏一些 classes。一旦确定了这些,就有两个选择:

  • 将它们添加到 pom 中的 reflectionList,例如:
<plugin>
    <groupId>com.gluonhq</groupId>
    <artifactId>client-maven-plugin</artifactId>
    <version>0.1.36</version>
    <configuration>
         <target>${client.target}</target>
         <mainClass>${main.class}</mainClass>
        <reflectionList>
            <list>com.sun.scenario.effect.impl.prism.PrMergePeer</list>
        </reflectionList>
    </configuration>
</plugin>
  • src/main/resources/META-INF/substrate/config 下创建文件 reflectionconfig.json,并添加 class(以及所需的 fields/methods):
{
  "name":"com.sun.scenario.effect.impl.prism.PrMergePeer",
  "methods":[{"name":"<init>", "parameterTypes":["com.sun.scenario.effect.FilterContext", "com.sun.scenario.effect.impl.Renderer",  "java.lang.String"] }]
}

后者是首选选项,因为它仅包含给定 class 所需的方法和字段,而前者包含所有方法和字段。

无论如何,我们还是要找到失踪的class。我们可以从堆栈跟踪中检查涉及的 JavaFX classes,或者我们可以使用 GraalVM 图像代理,幸运的是,它会为我们做这件事。

GraalVM 图像代理

GraalVM 有一个 Java 代理创建反射配置文件(以及其他),但需要先 运行 在 Hotspot 上安装应用程序。

这可以组合如下:

  • 创建文件夹 src/main/resources/META-INF/native-image

  • 将代理添加到 javafx-maven-plugin:

<plugin>
        <groupId>org.openjfx</groupId>
        <artifactId>javafx-maven-plugin</artifactId>
        <version>0.0.5</version>
        <configuration>
            <mainClass>${main.class}</mainClass>
            <options>
                <option>-agentlib:native-image-agent=config-merge-dir=src/main/resources/META-INF/native-image</option>
            </options>
        </configuration>
    </plugin>
  • 运行 mvn javafx:run,并探索所有可能的场景(因此代理可以在 运行 时间内发现 class 使用的资源和资源) .

  • 浏览添加到 src/main/resources/META-INF/native-image 的文件。

在 DropShadow 效果失败的情况下,客户端插件生成的配置文件中唯一缺少的相关 class 是 com.sun.scenario.effect.impl.prism.PrMergePeer

  • 最后,运行 再次 mvn client:build client:run,以创建和 运行 本机图像。

如果您保留 src/main/resources/META-INF/native-image,其中的配置文件将与客户端插件已经创建的配置文件结合使用。或者,您可以删除它们,只需使用上面解释的两个选项之一:将 class 添加到 reflectionList 或创建一个 json 文件。