Skip to main content
Go to Latest
File
// Copyright 2018-2024 the Deno authors. All rights reserved. MIT license.
import { assertExists } from "../assert/assert_exists.ts";import { describeTextureFormat } from "./describe_texture_format.ts";
function textureDimensionArrayLayerCount( texture: GPUTextureDescriptor,): number { switch (texture.dimension) { case "1d": case "3d": return 1; case undefined: case "2d": return normalizeExtent3D(texture.size).depthOrArrayLayers ?? 1; }}
function normalizeExtent3D(size: GPUExtent3D): GPUExtent3DDict { if (Array.isArray(size)) { assertExists(size[0]); return { width: size[0], height: size[1], depthOrArrayLayers: size[2], }; } else { return size; }}
function extent3DPhysicalSize( size: GPUExtent3D, format: GPUTextureFormat,): GPUExtent3DDict { const [blockWidth, blockHeight] = describeTextureFormat(format).blockDimensions; const nSize = normalizeExtent3D(size);
const width = Math.floor((nSize.width + blockWidth - 1) / blockWidth) * blockWidth; const height = Math.floor(((nSize.height ?? 1) + blockHeight - 1) / blockHeight) * blockHeight;
return { width, height, depthOrArrayLayers: nSize.depthOrArrayLayers, };}
function extent3DMipLevelSize( size: GPUExtent3D, level: number, is3D: boolean,): GPUExtent3DDict { const nSize = normalizeExtent3D(size); return { height: Math.max(1, nSize.width >> level), width: Math.max(1, (nSize.height ?? 1) >> level), depthOrArrayLayers: is3D ? Math.max(1, (nSize.depthOrArrayLayers ?? 1) >> level) : (nSize.depthOrArrayLayers ?? 1), };}
function textureMipLevelSize( descriptor: GPUTextureDescriptor, level: number,): GPUExtent3DDict | undefined { if (level >= (descriptor.mipLevelCount ?? 1)) { return undefined; }
return extent3DMipLevelSize( descriptor.size, level, descriptor.dimension === "3d", );}
/** * Create a {@linkcode GPUTexture} with data. * * @example * ```ts * import { createTextureWithData } from "https://deno.land/std@$STD_VERSION/webgpu/texture_with_data.ts"; * * const adapter = await navigator.gpu.requestAdapter(); * const device = await adapter?.requestDevice()!; * * createTextureWithData(device, { * format: "bgra8unorm-srgb", * size: { * width: 3, * height: 2, * }, * usage: GPUTextureUsage.COPY_SRC, * }, new Uint8Array([1, 1, 1, 1, 1, 1, 1])); * ``` */export function createTextureWithData( device: GPUDevice, descriptor: GPUTextureDescriptor, data: Uint8Array,): GPUTexture { descriptor.usage |= GPUTextureUsage.COPY_DST;
const texture = device.createTexture(descriptor); const layerIterations = textureDimensionArrayLayerCount(descriptor); const formatInfo = describeTextureFormat(descriptor.format);
let binaryOffset = 0; for (let layer = 0; layer < layerIterations; layer++) { for (let mip = 0; mip < (descriptor.mipLevelCount ?? 1); mip++) { const mipSize = textureMipLevelSize(descriptor, mip)!; if (descriptor.dimension !== "3d") { mipSize.depthOrArrayLayers = 1; }
const mipPhysical = extent3DPhysicalSize(mipSize, descriptor.format); const widthBlocks = Math.floor( mipPhysical.width / formatInfo.blockDimensions[0], ); const heightBlocks = Math.floor( mipPhysical.height! / formatInfo.blockDimensions[1], );
const bytesPerRow = widthBlocks * formatInfo.blockSize; const dataSize = bytesPerRow * heightBlocks * mipSize.depthOrArrayLayers!;
const endOffset = binaryOffset + dataSize;
device.queue.writeTexture( { texture, mipLevel: mip, origin: { x: 0, y: 0, z: layer, }, }, data.subarray(binaryOffset, endOffset), { bytesPerRow, rowsPerImage: heightBlocks, }, mipPhysical, );
binaryOffset = endOffset; } }
return texture;}