1

我正在尝试使用 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枚举(我试过但在值中使用的破折号有问题)

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

4

3 回答 3

3

首先 - 如果您想要一个现成的 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 已被证明是一种救命稻草,尤其是当它正在迅速发展时,当规范发生变化时,编译错误可以帮助您找到问题;)祝您好运!

于 2021-11-16T06:59:40.747 回答
1

几个月前,我根据我手写的 externs 做了 elemental2 之类的 webgpu 包装器。</p>

你可以看看这里:

https://github.com/treblereel/elemental2-experimental/tree/main/java/org/treblereel/gwt/elemental2 </a>

简单演示: https ://github.com/treblereel/j2cl-tests/blob/webgpu/src/main/java/org/treblereel/App.java


它是基于 j2cl 的,但与 gwt2 的实现方式非常接近。它的主要缺点是 webgpu api 不稳定,因此很难使其保持最新状态。最好从 idl 生成外部:

@peter-donald 正在致力于更好地实现 Web API,但它不是基于 elemental2。 https://github.com/akasha/akasha

于 2021-11-15T23:25:40.700 回答
1

添加到其他答案中,因为它们缺少我认为至少在某些特定情况下可能很重要的一部分。

  1. 如何映射无符号长长?

首先,请注意 Javalong等价的,因为虽然 Java long 是 64 位宽,但它们是有符号的。

根据我对规范的阅读,它很复杂,但很灵活。https://webidl.spec.whatwg.org/#es-unsigned-long-long

通过运行以下算法,将 ECMAScript 值 V 转换为 IDL unsigned long long 值:

  1. 让 x 是 ? ConvertToInt(V, 64, "无符号")。
  2. 返回表示与 x 相同的数值的 IDL unsigned long long 值。

将 IDL unsigned long long 值转换为 ECMAScript 值的结果是一个表示与 unsigned long long 最接近的数值的数值,如果有两个相等接近的值,则选择具有偶数有效位的数值。如果 unsigned long long 小于或等于 253 - 1,则 Number 将能够表示与 unsigned long long 完全相同的值。

好的,那么接下来是什么ConvertToInt?请参阅https://webidl.spec.whatwg.org/#abstract-opdef-converttoint,但它建立了一些界限(基于64and "unsigned"),然后使用ecmascript 规范https://tc39ToNumber的更标准部分。 es/ecma262/#sec-tonumber。在其他输入中,这可以通过StringToNumber https://tc39.es/ecma262/#sec-stringtonumber处理字符串。

这样做的最终效果是,您可以将带有无符号 64 位整数值的字符串传递给这些 API,它将在内部进行解释。因此,只要您的值适合 Java long,您就可以使用它Long.toString(value)来生成字符串值。我建议提供 native doubleintString重载,以及一个long用 注释的方法@JsOverlay,然后将其委托给 String 重载。

于 2021-11-17T14:58:15.453 回答