All files / webgpu / row_padding.ts

100.00% Branches 1/1
100.00% Lines 27/27
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
 
 
 
 
 
 
 
 
 
 
 
 
x4
 
 
x4
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
x4
 
 
 
 
 
x12
x12
x12
x12
x12
 
x12
x12
x12
x12
x12
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
x4
x4
x4
x4
 
x6
x6
 
x6
x9
x9
x9
 
x9
x9
 
x6
x6
























































































// Copyright 2018-2025 the Deno authors. MIT license.
// This module is browser compatible.

/** Return value for {@linkcode getRowPadding}. */
export interface Padding {
  /** The number of bytes per row without padding calculated. */
  unpadded: number;
  /** The number of bytes per row with padding calculated. */
  padded: number;
}

/** Buffer-Texture copies must have [`bytes_per_row`] aligned to this number. */
export const COPY_BYTES_PER_ROW_ALIGNMENT = 256;

/** Number of bytes per pixel. */
export const BYTES_PER_PIXEL = 4;

/**
 * Calculates the number of bytes including necessary padding when passing a
 * {@linkcode GPUImageCopyBuffer}.
 *
 * Ref: https://en.wikipedia.org/wiki/Data_structure_alignment#Computing_padding
 *
 * @example Usage
 * ```ts
 * import { getRowPadding } from "@std/webgpu/row-padding";
 * import { assertEquals } from "@std/assert";
 *
 * assertEquals(getRowPadding(1), { unpadded: 4, padded: 256 });
 * ```
 *
 * @param width The width to get the padding for
 * @returns The padded and unpadded values
 */
export function getRowPadding(width: number): Padding {
  // It is a WebGPU requirement that
  // GPUImageCopyBuffer.layout.bytesPerRow % COPY_BYTES_PER_ROW_ALIGNMENT == 0
  // So we calculate paddedBytesPerRow by rounding unpaddedBytesPerRow
  // up to the next multiple of COPY_BYTES_PER_ROW_ALIGNMENT.

  const unpaddedBytesPerRow = width * BYTES_PER_PIXEL;
  const paddedBytesPerRowPadding = (COPY_BYTES_PER_ROW_ALIGNMENT -
    (unpaddedBytesPerRow % COPY_BYTES_PER_ROW_ALIGNMENT)) %
    COPY_BYTES_PER_ROW_ALIGNMENT;
  const paddedBytesPerRow = unpaddedBytesPerRow + paddedBytesPerRowPadding;

  return {
    unpadded: unpaddedBytesPerRow,
    padded: paddedBytesPerRow,
  };
}

/**
 * Creates a new buffer while removing any unnecessary empty bytes.
 * Useful for when wanting to save an image as a specific format.
 *
 * @example Usage
 * ```ts
 * import { resliceBufferWithPadding } from "@std/webgpu/row-padding";
 * import { assertEquals } from "@std/assert";
 *
 * const input = new Uint8Array([0, 255, 0, 255, 120, 120, 120]);
 * const result = resliceBufferWithPadding(input, 1, 1);
 *
 * assertEquals(result, new Uint8Array([0, 255, 0, 255]));
 * ```
 *
 * @param buffer The buffer to reslice.
 * @param width The width of the output buffer.
 * @param height The height of the output buffer.
 * @returns The resliced buffer.
 */
export function resliceBufferWithPadding(
  buffer: Uint8Array,
  width: number,
  height: number,
): Uint8Array {
  const { padded, unpadded } = getRowPadding(width);
  const outputBuffer = new Uint8Array(unpadded * height);

  for (let i = 0; i < height; i++) {
    const slice = buffer
      .slice(i * padded, (i + 1) * padded)
      .slice(0, unpadded);

    outputBuffer.set(slice, i * unpadded);
  }

  return outputBuffer;
}