首页 > 解决方案 > WebGPU JsInterop 包装器

问题描述

我正在尝试使用 JsInterop 在 GWT 2.9.0 中使用 WebGPU,但在尝试将所有 WebGPU 接口映射到 Java 时遇到了一些问题。我所指的定义位于https://www.w3.org/TR/webgpu/#idl-index

1)我如何映射一个无符号长长的?

有一个 typedef:typedef [EnforceRange] unsigned long long GPUSize64; 例如在这里使用:

interface mixin GPURenderEncoderBase { 
    //...other declarations left out...
    undefined drawIndirect(GPUBuffer indirectBuffer, GPUSize64 indirectOffset);
};

如果我把它包起来

@JsType(isNative = true, namespace = JsPackage.GLOBAL)
public class GPURenderEncoderBase {
    //...other declarations left out...
    @JsMethod
    public final native void drawIndirect(GPUBuffer indirectBuffer, long indirectOffset);
}

我收到一条错误消息:

Parameter 'sourceOffset': type 'long' is not safe to access in JSNI code

鉴于我的更高级别的 API 仅在此处公开一个 int 以与其他 API 兼容,我可能只使用一个 int,但映射 GPUSize64 的正确解决方案是什么?

2)我如何包装字典?

当我尝试翻译以下定义时

dictionary GPUExtent3DDict {
    required GPUIntegerCoordinate width;
    GPUIntegerCoordinate height = 1;
    GPUIntegerCoordinate depthOrArrayLayers = 1;
};
typedef (sequence<GPUIntegerCoordinate> or GPUExtent3DDict) GPUExtent3D;

像这样:

@JsType(isNative = false, namespace = JsPackage.GLOBAL)
public class GPUExtent3D {
    public int width;
    public int height = 1;
    public int depthOrArrayLayers = 1;
}

然后按以下方式使用它:

    ...
    GPUExtent3D size = new GPUExtent3D();
    size.width = canvasWidth;
    size.height = canvasHeight;
    GPUCanvasConfiguration config = new GPUCanvasConfiguration();
    config.size = size;
    gpuCanvasContext.configure(config);

我可以编译得很好,但在运行时出现错误说

Uncaught (in promise) TypeError: Failed to execute 'configure' on 'GPUCanvasContext': Failed to read the 'size' property from 'GPUCanvasConfiguration': Failed to read the 'width' property from 'GPUExtent3DDict': Failed to read the 'width' property from 'GPUExtent3DDict': Required member is undefined.

令我困惑的是,它两次说“无法从'GPUExtent3DDict'读取'width'属性”,这暗示它需要嵌套的东西,并且可能与typedef中关于“sequence or GPUExtent3DDict”的最后一行有关,我不明白。当我以这种方式定义 GPUExtent3D 时:

public final class GPUExtent3D extends JavaScriptObject {
    public static final native GPUExtent3D createNew() /*-{
        return {height: 1, depthOrArrayLayers: 1};
    }-*/;

    protected GPUExtent3D() {}

    public final native void width(int width) /*-{
        this["width"] = width;
    }-*/;

    //...same for height and depthOrArrayLayers
}

然后像这样使用它:

    ...
    GPUExtent3D size = GPUExtent3D.createNew();
    size.width(canvasWidth);
    size.height(canvasHeight);
    size.depthOrArrayLayers(1);
    GPUCanvasConfiguration config = new GPUCanvasConfiguration();
    config.size = size;
    gpuCanvasContext.configure(config);

它工作得很好,但我想用 JsInterop 方式而不是 JavaScriptObject 范围。我该怎么做呢?

3)如何映射一个枚举?

我还在这里找到了一个可行的解决方案,我想知道这是推荐的还是不推荐使用的/旧的方法给定一个枚举声明:

enum GPUPowerPreference {
    "low-power",
    "high-performance"
};

我可以像这样映射它吗

public final class GPUPowerPreference {
    public static final String LOW_POWER = "low-power";
    public static final String HIGH_POWER = "high-power";
    private GPUPowerPreference() {}
}

或者有没有办法为此使用带有@JsEnum的java枚举(我试过但在值中使用的破折号有问题)

非常感谢,祝您有愉快的一天!

标签: javagwtwebapigwt-jsinteropwebgpu

解决方案


首先 - 如果您想要一个现成的 WebGPU(和所有其他浏览器 API)的 jsinterop 绑定,它是通过处理规范中的 webidl 并从 webidl 构建绑定而构建的,那么Akasha在 GWT2.9 和 J2CL 兼容中提供了它变体(Java API 是相同的,但两个变体之间的注解和内部函数略有不同)。最新版本的 GWT akasha 变体的坐标是org.realityforge.akasha:akasha-gwt:jar:0.29,一个简单的纹理旋转立方体的示例可在Main.java获得

具体问题:

  • 您如何表示“无符号长长”?如果值是可选的,则为adoubleint... 或 a 。Double这些表示中没有一个与 API 所期望的完全匹配,但只要这些值被一致地映射并来自外部源(即 gltf 或其他格式),那么就没有明显的惩罚。我将它映射到int类型不是可选的和Double可选的时候。有点难看,但在 GWT 领域中效果最好(尽管 J2CL 很快就会有一种方法来代表原生多头 IIRC)
  • 如何包装字典?由于底层表示只是一个 javascript 对象,因此可能有很多很多不同的表示。无论您是针对 j2cl 还是 GWT,这都会有所不同,但对于 GWT,您通常需要如下所示的内容。在我的生成器中,我更进一步,像在GPUExtent3DDict中一样定义了一个构建器(但请注意,此源是 J2CL 变体的一个示例,与下面描述的代码有一些细微差别),这使得字典数据的构建更加容易。IEGPUExtent3DDict.width( 10 ).height( 10 ).depthOrArrayLayers( 1 )
@JsType(isNative = true, namespace = JsPackage.GLOBAL, name = "?")
public interface GPUExtent3DDict {
 @JsProperty(name = "width")
  int width();

  @JsProperty
  void setWidth(int width);

  @JsProperty(name = "depthOrArrayLayers")
  int depthOrArrayLayers();

  @JsProperty
  void setDepthOrArrayLayers(int depthOrArrayLayers);

  @JsProperty(name = "height")
  int height();

  @JsProperty
  void setHeight(int height);
}
  • 如何映射枚举?由于枚举实际上只是字符串值的包,因此具有字符串常量的类就足够了。但是,如果您想要更好的可用性(例如 IntelliJ IDEA 等 IDE 中的选项卡完成),那么我通常将它们建模如下。然后注释作为枚举类型的参数/返回值@GPUPowerPreference
@MagicConstant(valuesFromClass = GPUPowerPreference.class)
public @interface GPUPowerPreference {
  @Nonnull
  String high_performance = "high-performance";

  @Nonnull
  String low_power = "low-power";

  final class Util {
    private Util() {
    }

    @GPUPowerPreference
    public static String requireValid(final String value) {
      assertValid( value );
      return value;
    }

    public static void assertValid(@Nonnull final String value) {
      assert isValid( value );
    }

    public static boolean isValid(@Nonnull final String value) {
      return GPUPowerPreference.high_performance.equals( value ) || GPUPowerPreference.low_power.equals( value );
    }
  }
}

为了它的价值。在 Java 中使用 WebGPU 已被证明是一种救命稻草,尤其是当它正在迅速发展时,当规范发生变化时,编译错误可以帮助您找到问题;)祝您好运!


推荐阅读