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;
}