function copyBufferToTextureViaRender()

in src/webgpu/util/texture.ts [416:620]


function copyBufferToTextureViaRender(
  t: GPUTestBase,
  encoder: GPUCommandEncoder,
  source: GPUTexelCopyBufferInfo,
  sourceFormat: GPUTextureFormat,
  dest: GPUTexelCopyTextureInfo,
  size: GPUExtent3D
) {
  const { format: textureFormat, sampleCount } = dest.texture;
  const origin = reifyOrigin3D(dest.origin ?? [0]);
  const copySize = reifyExtent3D(size);
  const { useFragDepth, discardWithStencil } = getDepthStencilOptionsForFormat(dest.texture.format);
  const resourcesToDestroy: (GPUTexture | GPUBuffer)[] = [];

  const { device } = t;
  const numBlits = discardWithStencil ? 8 : 1;
  for (let blitCount = 0; blitCount < numBlits; ++blitCount) {
    const { code, dataFormat } = getCopyBufferToTextureViaRenderCode(
      sourceFormat,
      dest.texture.format
    );
    const stencilWriteMask = 1 << blitCount;
    const id = JSON.stringify({
      textureFormat,
      sourceFormat,
      useFragDepth,
      stencilWriteMask,
      discardWithStencil,
      sampleCount,
      code,
    });
    const pipelines =
      s_copyBufferToTextureViaRenderPipelines.get(device) ?? new Map<string, GPURenderPipeline>();
    s_copyBufferToTextureViaRenderPipelines.set(device, pipelines);
    let pipeline = pipelines.get(id);
    if (!pipeline) {
      const module = device.createShaderModule({ code });
      pipeline = device.createRenderPipeline({
        label: `blitCopyFor-${textureFormat}`,
        layout: 'auto',
        vertex: { module },
        ...(discardWithStencil
          ? {
              fragment: {
                module,
                targets: [],
              },
              depthStencil: {
                depthWriteEnabled: false,
                depthCompare: 'always',
                format: textureFormat,
                stencilWriteMask,
                stencilFront: {
                  passOp: 'replace',
                },
              },
            }
          : useFragDepth
          ? {
              fragment: {
                module,
                targets: [],
              },
              depthStencil: {
                depthWriteEnabled: true,
                depthCompare: 'always',
                format: textureFormat,
              },
            }
          : {
              fragment: {
                module,
                targets: [{ format: textureFormat }],
              },
            }),
        primitive: {
          topology: 'triangle-strip',
        },
        ...(sampleCount > 1 && { multisample: { count: sampleCount } }),
      });
      pipelines.set(id, pipeline);
    }

    const width = 1024;
    const bytesPerRow = width * 4;
    const fullRows = Math.floor(source.buffer.size / bytesPerRow);
    const rows = Math.ceil(source.buffer.size / bytesPerRow);
    const srcTexture = t.createTextureTracked({
      format: dataFormat,
      size: [width, rows],
      usage: GPUTextureUsage.COPY_DST | GPUTextureUsage.TEXTURE_BINDING,
    });
    resourcesToDestroy.push(srcTexture);

    if (fullRows > 0) {
      encoder.copyBufferToTexture({ buffer: source.buffer, bytesPerRow }, { texture: srcTexture }, [
        width,
        fullRows,
      ]);
    }
    if (rows > fullRows) {
      const totalPixels = source.buffer.size / 4;
      const pixelsCopied = fullRows * width;
      const pixelsInLastRow = totalPixels - pixelsCopied;
      encoder.copyBufferToTexture(
        {
          buffer: source.buffer,
          offset: pixelsCopied * 4,
          bytesPerRow,
        },
        {
          texture: srcTexture,
          origin: [0, fullRows],
        },
        [pixelsInLastRow, 1]
      );
    }
    const baseMipLevel = dest.mipLevel;
    for (let l = 0; l < copySize.depthOrArrayLayers; ++l) {
      const baseArrayLayer = origin.z + l;
      const mipLevelCount = 1;
      const arrayLayerCount = 1;
      const pass = encoder.beginRenderPass(
        discardWithStencil
          ? {
              colorAttachments: [],
              depthStencilAttachment: {
                view: dest.texture.createView({
                  baseMipLevel,
                  baseArrayLayer,
                  mipLevelCount,
                  arrayLayerCount,
                }),
                stencilClearValue: 0,
                stencilLoadOp: 'load',
                stencilStoreOp: 'store',
              },
            }
          : useFragDepth
          ? {
              colorAttachments: [],
              depthStencilAttachment: {
                view: dest.texture.createView({
                  baseMipLevel,
                  baseArrayLayer,
                  mipLevelCount,
                  arrayLayerCount,
                }),
                depthClearValue: 0,
                depthLoadOp: 'clear',
                depthStoreOp: 'store',
                stencilReadOnly: true,
              },
            }
          : {
              colorAttachments: [
                {
                  view: dest.texture.createView({
                    baseMipLevel,
                    baseArrayLayer,
                    mipLevelCount,
                    arrayLayerCount,
                  }),
                  loadOp: 'clear',
                  storeOp: 'store',
                },
              ],
            }
      );
      pass.setViewport(origin.x, origin.y, copySize.width, copySize.height, 0, 1);
      pass.setPipeline(pipeline);

      const info = getBlockInfoForTextureFormat(sourceFormat);
      const offset =
        (source.offset ?? 0) + (source.bytesPerRow ?? 0) * (source.rowsPerImage ?? 0) * l;
      const uniforms = new Uint32Array([
        copySize.height, //  numTexelRows: u32,
        source.bytesPerRow!, //  bytesPerRow: u32,
        info.bytesPerBlock!, //  bytesPerSample: u32,
        dest.texture.sampleCount, //  sampleCount: u32,
        offset, //  offset: u32,
      ]);

      const uniformBuffer = t.makeBufferWithContents(
        uniforms,
        GPUBufferUsage.COPY_DST | GPUBufferUsage.UNIFORM
      );
      resourcesToDestroy.push(uniformBuffer);
      const bindGroup = device.createBindGroup({
        layout: pipeline.getBindGroupLayout(0),
        entries: [
          { binding: 0, resource: { buffer: uniformBuffer } },
          { binding: 1, resource: srcTexture.createView() },
        ],
      });

      pass.setBindGroup(0, bindGroup);
      pass.setStencilReference(0xff);
      pass.draw(4 * copySize.height * dest.texture.sampleCount, 1, 0, blitCount);
      pass.end();
    }
  }

  return resourcesToDestroy;
}