webgpuCreateTexture function

TextureHandle webgpuCreateTexture(
  1. GPUDevice gpu,
  2. List<WebGpuTexture> tracked,
  3. RenderTargetSpec spec, {
  4. int levels = 1,
})

A texture matching spec, with a chain levels deep. See GraphicsDevice.createTexture.

deviceTransient is Impeller's tile memory, which WebGPU does not have. The honest translation is an attachment allocated without TEXTURE_BINDING: not sampleable, attachment only, which is exactly what the storage mode already promises. Multisampled targets go the same way — this engine resolves them rather than sampling them, and a texture that cannot be sampled is one fewer usage flag for the implementation to plan around.

Implementation

TextureHandle webgpuCreateTexture(
  GPUDevice gpu,
  List<WebGpuTexture> tracked,
  RenderTargetSpec spec, {
  int levels = 1,
}) {
  final format = gpuTextureFormat(spec.format);
  if (format == null) {
    throw ArgumentError.value(
      spec.format,
      'format',
      'has no WebGPU spelling; ask supportsTextureFormat first',
    );
  }
  // **A block-compressed target is refused here rather than by the browser.**
  // Every one of these has a spelling now that the compression features are
  // asked for, so a spelling is no longer the thing that stops one becoming a
  // render target — and `RENDER_ATTACHMENT` on a compressed format is a
  // validation error whose message names a usage flag rather than the call that
  // wanted it. The contract already says a compressed format is sample-only
  // everywhere (`TextureFormatCompression.isCompressed`), which is the same
  // refusal the WebGL2 backend gives by having no non-compressed table entry to
  // hand back.
  if (spec.format.isCompressed) {
    throw ArgumentError.value(
      spec.format,
      'format',
      'is block-compressed, and a compressed texture cannot be a render '
          'target on any backend. Upload one through createTextureFromPixels',
    );
  }
  final attachmentOnly =
      spec.sampleCount > 1 || spec.storageMode == StorageMode.deviceTransient;
  final texture = gpu.createTexture(
    GPUTextureDescriptor(
      size: GPUExtent3DDict(
        width: spec.width,
        height: spec.height,
        depthOrArrayLayers: 1,
      ),
      format: format,
      usage: attachmentOnly
          ? GpuTextureUsage.renderAttachment
          : GpuTextureUsage.renderAttachment |
                GpuTextureUsage.textureBinding |
                GpuTextureUsage.copyDst |
                (webgpuIsDepthStencil(spec.format)
                    ? 0
                    : GpuTextureUsage.copySrc),
      sampleCount: spec.sampleCount,
      mipLevelCount: levels,
      dimension: '2d',
      label: 'target ${spec.width}x${spec.height} $format',
    ),
  );
  final backend = WebGpuTexture(
    texture: texture,
    dimension: WebGpuTextureDimension.twoDimensional,
    sampleable: !attachmentOnly,
  );
  tracked.add(backend);
  return TextureHandle(
    backend: backend,
    width: spec.width,
    height: spec.height,
    format: spec.format,
    sampleCount: spec.sampleCount,
    storageMode: spec.storageMode,
  );
}