async function identifySamplePoints()

in src/webgpu/shader/execution/expression/call/builtin/texture_utils.ts [3340:3678]


async function identifySamplePoints<T extends Dimensionality>(
  softwareTexture: SoftwareTexture,
  sampler: GPUSamplerDescriptor,
  callForSamples: TextureCall<T>,
  originalCall: TextureCall<T>,
  texels: TexelView[] | undefined,
  run: (texels: TexelView[]) => Promise<PerTexelComponent<number>>
) {
  const info = softwareTexture.descriptor;
  const isCube = isCubeViewDimension(softwareTexture.viewDescriptor);
  const mipLevelCount = softwareTexture.descriptor.mipLevelCount ?? 1;
  const mipLevelSizes = range(mipLevelCount, mipLevel =>
    virtualMipSize(
      softwareTexture.descriptor.dimension ?? '2d',
      softwareTexture.descriptor.size,
      mipLevel
    )
  );
  const numTexelsPerLevel = mipLevelSizes.map(size => size.reduce((s, v) => s * v));
  const numTexelsOfPrecedingLevels = (() => {
    let total = 0;
    return numTexelsPerLevel.map(v => {
      const num = total;
      total += v;
      return num;
    });
  })();
  const numTexels = numTexelsPerLevel.reduce((sum, v) => sum + v);

  const getMipLevelFromTexelId = (texelId: number) => {
    for (let mipLevel = mipLevelCount - 1; mipLevel > 0; --mipLevel) {
      if (texelId - numTexelsOfPrecedingLevels[mipLevel] >= 0) {
        return mipLevel;
      }
    }
    return 0;
  };

  const getTexelCoordFromTexelId = (texelId: number) => {
    const mipLevel = getMipLevelFromTexelId(texelId);
    const size = mipLevelSizes[mipLevel];
    const texelsPerSlice = size[0] * size[1];
    const id = texelId - numTexelsOfPrecedingLevels[mipLevel];
    const layer = Math.floor(id / texelsPerSlice);
    const xyId = id - layer * texelsPerSlice;
    const y = (xyId / size[0]) | 0;
    const x = xyId % size[0];
    return { x, y, z: layer, mipLevel, xyId };
  };

  // This isn't perfect. We already know there was an error. We're just
  // generating info so it seems okay it's not perfect. This format will
  // be used to generate weights by drawing with a texture of this format
  // with a specific pixel set to [1, 1, 1, 1]. As such, if the result
  // is > 0 then that pixel was sampled and the results are the weights.
  //
  // Ideally, this texture with a single pixel set to [1, 1, 1, 1] would
  // be the same format we were originally testing, the one we already
  // detected an error for. This way, whatever subtle issues there are
  // from that format will affect the weight values we're computing. But,
  // if that format is not encodable, for example if it's a compressed
  // texture format, then we have no way to build a texture so we use
  // rgba8unorm instead.
  const format = (
    kEncodableTextureFormats.includes(info.format as EncodableTextureFormat)
      ? info.format
      : isDepthTextureFormat(info.format)
      ? 'depth16unorm'
      : 'rgba8unorm'
  ) as EncodableTextureFormat;
  const rep = kTexelRepresentationInfo[format];

  const components = isBuiltinGather(callForSamples.builtin) ? kRGBAComponents : rep.componentOrder;
  const convertResultAsAppropriate = isBuiltinGather(callForSamples.builtin)
    ? <T>(v: T) => v
    : convertResultFormatToTexelViewFormat;

  // Identify all the texels that are sampled, and their weights.
  const sampledTexelWeights = new Map<number, PerTexelComponent<number>>();
  const unclassifiedStack = [new Set<number>(range(numTexels, v => v))];
  while (unclassifiedStack.length > 0) {
    // Pop the an unclassified texels stack
    const unclassified = unclassifiedStack.pop()!;

    // Split unclassified texels evenly into two new sets
    const setA = new Set<number>();
    const setB = new Set<number>();
    [...unclassified.keys()].forEach((t, i) => ((i & 1) === 0 ? setA : setB).add(t));

    // Push setB to the unclassified texels stack
    if (setB.size > 0) {
      unclassifiedStack.push(setB);
    }

    // See if any of the texels in setA were sampled.0
    const results = convertResultAsAppropriate(
      await run(
        range(mipLevelCount, mipLevel =>
          TexelView.fromTexelsAsColors(
            format,
            (coords: Required<GPUOrigin3DDict>): Readonly<PerTexelComponent<number>> => {
              const size = mipLevelSizes[mipLevel];
              const texelsPerSlice = size[0] * size[1];
              const texelsPerRow = size[0];
              const texelId =
                numTexelsOfPrecedingLevels[mipLevel] +
                coords.x +
                coords.y * texelsPerRow +
                coords.z * texelsPerSlice;
              const isCandidate = setA.has(texelId);
              const texel: PerTexelComponent<number> = {};
              for (const component of rep.componentOrder) {
                texel[component] = isCandidate ? 1 : 0;
              }
              return texel;
            }
          )
        )
      ),
      format
    );
    if (components.some(c => results[c] !== 0)) {
      // One or more texels of setA were sampled.
      if (setA.size === 1) {
        // We identified a specific texel was sampled.
        // As there was only one texel in the set, results holds the sampling weights.
        setA.forEach(texel => sampledTexelWeights.set(texel, results));
      } else {
        // More than one texel in the set. Needs splitting.
        unclassifiedStack.push(setA);
      }
    }
  }

  // separate the sampledTexelWeights by mipLevel, then by layer, within a layer the texelId only includes x and y
  const levels: Map<number, PerTexelComponent<number>>[][] = [];
  for (const [texelId, weight] of sampledTexelWeights.entries()) {
    const { xyId, z, mipLevel } = getTexelCoordFromTexelId(texelId);
    const level = levels[mipLevel] ?? [];
    levels[mipLevel] = level;
    const layerEntries = level[z] ?? new Map();
    level[z] = layerEntries;
    layerEntries.set(xyId, weight);
  }

  // example when blockWidth = 2, blockHeight = 2
  //
  //     0   1   2   3
  //   ╔═══╤═══╦═══╤═══╗
  // 0 ║ a │   ║   │   ║
  //   ╟───┼───╫───┼───╢
  // 1 ║   │   ║   │   ║
  //   ╠═══╪═══╬═══╪═══╣
  // 2 ║   │   ║   │   ║
  //   ╟───┼───╫───┼───╢
  // 3 ║   │   ║   │ b ║
  //   ╚═══╧═══╩═══╧═══╝

  /* prettier-ignore */
  const blockParts = {
    top:      { left: '╔', fill: '═══', right: '╗', block: '╦', texel: '╤' },
    mid:      { left: '╠', fill: '═══', right: '╣', block: '╬', texel: '╪' },
    bot:      { left: '╚', fill: '═══', right: '╝', block: '╩', texel: '╧' },
    texelMid: { left: '╟', fill: '───', right: '╢', block: '╫', texel: '┼' },
    value:    { left: '║', fill: '   ', right: '║', block: '║', texel: '│' },
  } as const;
  /* prettier-ignore */
  const nonBlockParts = {
    top:      { left: '┌', fill: '───', right: '┐', block: '┬', texel: '┬' },
    mid:      { left: '├', fill: '───', right: '┤', block: '┼', texel: '┼' },
    bot:      { left: '└', fill: '───', right: '┘', block: '┴', texel: '┴' },
    texelMid: { left: '├', fill: '───', right: '┤', block: '┼', texel: '┼' },
    value:    { left: '│', fill: '   ', right: '│', block: '│', texel: '│' },
  } as const;

  const lines: string[] = [];
  const letter = (idx: number) => String.fromCodePoint(idx < 30 ? 97 + idx : idx + 9600 - 30); // 97: 'a'
  let idCount = 0;

  const { blockWidth, blockHeight } = getBlockInfoForTextureFormat(
    softwareTexture.descriptor.format
  );
  // range + concatenate results.
  const rangeCat = <T>(num: number, fn: (i: number) => T) => range(num, fn).join('');
  const joinFn = (arr: string[], fn: (i: number) => string) => {
    const joins = range(arr.length - 1, fn);
    return arr.map((s, i) => `${s}${joins[i] ?? ''}`).join('');
  };
  const parts = Math.max(blockWidth, blockHeight) > 1 ? blockParts : nonBlockParts;
  /**
   * Makes a row that's [left, fill, texel, fill, block, fill, texel, fill, right]
   * except if `contents` is supplied then it would be
   * [left, contents[0], texel, contents[1], block, contents[2], texel, contents[3], right]
   */
  const makeRow = (
    blockPaddedWidth: number,
    width: number,
    {
      left,
      fill,
      right,
      block,
      texel,
    }: {
      left: string;
      fill: string;
      right: string;
      block: string;
      texel: string;
    },
    contents?: string[]
  ) => {
    return `${left}${joinFn(contents ?? range(blockPaddedWidth, x => fill), x => {
      return (x + 1) % blockWidth === 0 ? block : texel;
    })}${right}`;
  };

  for (let mipLevel = 0; mipLevel < mipLevelCount; ++mipLevel) {
    const level = levels[mipLevel];
    if (!level) {
      continue;
    }

    const [width, height, depthOrArrayLayers] = mipLevelSizes[mipLevel];
    const texelsPerRow = width;

    for (let layer = 0; layer < depthOrArrayLayers; ++layer) {
      const layerEntries = level[layer];

      const orderedTexelIndices: number[] = [];
      lines.push('');
      const unSampled = layerEntries ? '' : 'un-sampled';
      if (isCube) {
        const face = kFaceNames[layer % 6];
        lines.push(
          `layer: ${layer} mip(${mipLevel}), cube-layer: ${(layer / 6) | 0} (${face}) ${unSampled}`
        );
      } else {
        lines.push(`layer: ${layer} mip(${mipLevel}) ${unSampled}`);
      }

      if (!layerEntries) {
        continue;
      }

      const blockPaddedHeight = align(height, blockHeight);
      const blockPaddedWidth = align(width, blockWidth);
      lines.push(`   ${rangeCat(width, x => `  ${x.toString().padEnd(2)}`)}`);
      lines.push(`   ${makeRow(blockPaddedWidth, width, parts.top)}`);
      for (let y = 0; y < blockPaddedHeight; y++) {
        lines.push(
          `${y.toString().padStart(2)} ${makeRow(
            blockPaddedWidth,
            width,
            parts.value,
            range(blockPaddedWidth, x => {
              const texelIdx = x + y * texelsPerRow;
              const weight = layerEntries.get(texelIdx);
              const outside = y >= height || x >= width;
              if (outside || weight === undefined) {
                return outside ? '░░░' : '   ';
              } else {
                const id = letter(idCount + orderedTexelIndices.length);
                orderedTexelIndices.push(texelIdx);
                return ` ${id} `;
              }
            })
          )}`
        );
        // It's either a block row, a texel row, or the last row.
        const end = y < blockPaddedHeight - 1;
        const lineParts = end
          ? (y + 1) % blockHeight === 0
            ? parts.mid
            : parts.texelMid
          : parts.bot;
        lines.push(`   ${makeRow(blockPaddedWidth, width, lineParts)}`);
      }

      const pad2 = (n: number) => n.toString().padStart(2);
      const pad3 = (n: number) => n.toString().padStart(3);
      const fix5 = (n: number) => {
        const s = n.toFixed(5);
        return s === '0.00000' && n !== 0 ? n.toString() : s;
      };
      const formatValue = isSintOrUintFormat(format) ? pad3 : fix5;
      const formatTexel = (texel: PerTexelComponent<number> | undefined) =>
        texel
          ? Object.entries(texel)
              .map(([k, v]) => `${k}: ${formatValue(v)}`)
              .join(', ')
          : '*texel values unavailable*';

      const colorLines: string[] = [];
      const compareLines: string[] = [];
      let levelWeight = 0;
      orderedTexelIndices.forEach((texelIdx, i) => {
        const weights = layerEntries.get(texelIdx)!;
        const y = Math.floor(texelIdx / texelsPerRow);
        const x = texelIdx % texelsPerRow;
        const singleWeight = valueIfAllComponentsAreEqual(weights, components)!;
        levelWeight += singleWeight;
        const w =
          singleWeight !== undefined
            ? `weight: ${fix5(singleWeight)}`
            : `weights: [${components.map(c => `${c}: ${fix5(weights[c]!)}`).join(', ')}]`;
        const coord = `${pad2(x)}, ${pad2(y)}, ${pad2(layer)}`;
        const texel =
          texels &&
          convertToTexelViewFormat(
            texels[mipLevel].color({ x, y, z: layer }),
            softwareTexture.descriptor.format
          );

        const texelStr = formatTexel(texel);
        const id = letter(idCount + i);
        lines.push(`${id}: mip(${mipLevel}) at: [${coord}], ${w}`);
        colorLines.push(`${id}: value: ${texelStr}`);
        if (isBuiltinComparison(originalCall.builtin)) {
          assert(!!texel);
          const compareTexel = applyCompare(originalCall, sampler, [TexelComponent.Depth], texel);
          compareLines.push(
            `${id}: compare(${sampler.compare}) result with depthRef(${fix5(
              originalCall.depthRef!
            )}): ${fix5(compareTexel.Depth!)}`
          );
        }
      });
      lines.push(...colorLines);
      lines.push(...compareLines);
      if (!isNaN(levelWeight)) {
        lines.push(`mip level (${mipLevel}) weight: ${fix5(levelWeight)}`);
      }
      idCount += orderedTexelIndices.length;
    }
  }

  return lines;
}