export const description = `
Test texture views can reinterpret the format of the original texture.
- TODO: test compressed texture reinterpretation
`;
import { makeTestGroup } from '../../../../common/framework/test_group.js';
import {
EncodableTextureFormat,
getBaseFormatForTextureFormat,
kDifferentBaseFormatRegularTextureFormats,
RegularTextureFormat,
} from '../../../format_info.js';
import { AllFeaturesMaxLimitsGPUTest } from '../../../gpu_test.js';
import * as ttu from '../../../texture_test_utils.js';
import { TexelView } from '../../../util/texture/texel_view.js';
export const g = makeTestGroup(AllFeaturesMaxLimitsGPUTest);
const kColors = [
{ R: 1.0, G: 0.0, B: 0.0, A: 0.8 },
{ R: 0.0, G: 1.0, B: 0.0, A: 0.7 },
{ R: 0.0, G: 0.0, B: 0.0, A: 0.6 },
{ R: 0.0, G: 0.0, B: 0.0, A: 0.5 },
{ R: 1.0, G: 1.0, B: 1.0, A: 0.4 },
{ R: 0.7, G: 0.0, B: 0.0, A: 0.3 },
{ R: 0.0, G: 0.8, B: 0.0, A: 0.2 },
{ R: 0.0, G: 0.0, B: 0.9, A: 0.1 },
{ R: 0.1, G: 0.2, B: 0.0, A: 0.3 },
{ R: 0.4, G: 0.3, B: 0.6, A: 0.8 },
];
const kTextureSize = 16;
function makeInputTexelView(format: EncodableTextureFormat) {
return TexelView.fromTexelsAsColors(
format,
coords => {
const pixelPos = coords.y * kTextureSize + coords.x;
return kColors[pixelPos % kColors.length];
},
{ clampToFormatRange: true }
);
}
function makeBlitPipeline(
device: GPUDevice,
format: GPUTextureFormat,
multisample: { sample: number; render: number }
) {
return device.createRenderPipeline({
layout: 'auto',
vertex: {
module: device.createShaderModule({
code: `
@vertex fn main(@builtin(vertex_index) VertexIndex : u32) -> @builtin(position) vec4<f32> {
var pos = array<vec2<f32>, 6>(
vec2<f32>(-1.0, -1.0),
vec2<f32>(-1.0, 1.0),
vec2<f32>( 1.0, -1.0),
vec2<f32>(-1.0, 1.0),
vec2<f32>( 1.0, -1.0),
vec2<f32>( 1.0, 1.0));
return vec4<f32>(pos[VertexIndex], 0.0, 1.0);
}`,
}),
entryPoint: 'main',
},
fragment: {
module:
multisample.sample > 1
? device.createShaderModule({
code: `
@group(0) @binding(0) var src: texture_multisampled_2d<f32>;
@fragment fn main(@builtin(position) coord: vec4<f32>) -> @location(0) vec4<f32> {
var result : vec4<f32>;
for (var i = 0; i < ${multisample.sample}; i = i + 1) {
result = result + textureLoad(src, vec2<i32>(coord.xy), i);
}
return result * ${1 / multisample.sample};
}`,
})
: device.createShaderModule({
code: `
@group(0) @binding(0) var src: texture_2d<f32>;
@fragment fn main(@builtin(position) coord: vec4<f32>) -> @location(0) vec4<f32> {
return textureLoad(src, vec2<i32>(coord.xy), 0);
}`,
}),
entryPoint: 'main',
targets: [{ format }],
},
multisample: {
count: multisample.render,
},
});
}
g.test('texture_binding')
.desc(`Test that a regular texture allocated as 'format' is correctly sampled as 'viewFormat'.`)
.params(u =>
u
.combine('format', kDifferentBaseFormatRegularTextureFormats)
)
.beforeAllSubcases(t => t.skipIf(t.isCompatibility))
.fn(t => {
const { format } = t.params;
const viewFormat = getBaseFormatForTextureFormat(format) as RegularTextureFormat;
t.skipIfTextureFormatNotSupported(format, viewFormat);
const cases = [
{ format, viewFormat },
{ format: viewFormat, viewFormat: format },
];
for (const { format, viewFormat } of cases) {
const inputTexelView = makeInputTexelView(format);
const texture = ttu.createTextureFromTexelView(t, inputTexelView, {
size: [kTextureSize, kTextureSize],
usage: GPUTextureUsage.TEXTURE_BINDING,
viewFormats: [viewFormat],
});
const reinterpretedView = texture.createView({ format: viewFormat });
const reinterpretedTexelView = TexelView.fromTexelsAsBytes(viewFormat, inputTexelView.bytes);
const pipeline = t.device.createComputePipeline({
layout: 'auto',
compute: {
module: t.device.createShaderModule({
code: `
@group(0) @binding(0) var src: texture_2d<f32>;
@group(0) @binding(1) var dst: texture_storage_2d<rgba8unorm, write>;
@compute @workgroup_size(1, 1) fn main(
@builtin(global_invocation_id) global_id: vec3<u32>,
) {
var coord = vec2<i32>(global_id.xy);
textureStore(dst, coord, textureLoad(src, coord, 0));
}`,
}),
entryPoint: 'main',
},
});
const outputTexture = t.createTextureTracked({
format: 'rgba8unorm',
size: [kTextureSize, kTextureSize],
usage: GPUTextureUsage.STORAGE_BINDING | GPUTextureUsage.COPY_SRC,
});
const commandEncoder = t.device.createCommandEncoder();
const pass = commandEncoder.beginComputePass();
pass.setPipeline(pipeline);
pass.setBindGroup(
0,
t.device.createBindGroup({
layout: pipeline.getBindGroupLayout(0),
entries: [
{
binding: 0,
resource: reinterpretedView,
},
{
binding: 1,
resource: outputTexture.createView(),
},
],
})
);
pass.dispatchWorkgroups(kTextureSize, kTextureSize);
pass.end();
t.device.queue.submit([commandEncoder.finish()]);
ttu.expectTexelViewComparisonIsOkInTexture(
t,
{ texture: outputTexture },
TexelView.fromTexelsAsColors('rgba8unorm', reinterpretedTexelView.color, {
clampToFormatRange: true,
}),
[kTextureSize, kTextureSize]
);
}
});
g.test('render_and_resolve_attachment')
.desc(
`Test that a color render attachment allocated as 'format' is correctly rendered to as 'viewFormat',
and resolved to an attachment allocated as 'format' viewed as 'viewFormat'.
Other combinations aren't possible because the render and resolve targets must both match
in view format and match in base format.`
)
.params(u =>
u
.combine('format', kDifferentBaseFormatRegularTextureFormats)
.combine('sampleCount', [1, 4])
)
.beforeAllSubcases(t => t.skipIf(t.isCompatibility))
.fn(t => {
const { format, sampleCount } = t.params;
const viewFormat = getBaseFormatForTextureFormat(format) as RegularTextureFormat;
t.skipIfTextureFormatNotSupported(format, viewFormat);
const cases = [
{ format, viewFormat },
{ format: viewFormat, viewFormat: format },
];
for (const { format, viewFormat } of cases) {
const inputTexelView = makeInputTexelView(format);
const renderTexture = t.createTextureTracked({
format,
size: [kTextureSize, kTextureSize],
usage:
GPUTextureUsage.RENDER_ATTACHMENT |
(sampleCount > 1 ? GPUTextureUsage.TEXTURE_BINDING : GPUTextureUsage.COPY_SRC),
viewFormats: [viewFormat],
sampleCount,
});
const resolveTexture =
sampleCount === 1
? undefined
: t.createTextureTracked({
format,
size: [kTextureSize, kTextureSize],
usage: GPUTextureUsage.COPY_SRC | GPUTextureUsage.RENDER_ATTACHMENT,
viewFormats: [viewFormat],
});
const sampleSource = ttu.createTextureFromTexelView(t, inputTexelView, {
size: [kTextureSize, kTextureSize],
usage: GPUTextureUsage.TEXTURE_BINDING,
});
const reinterpretedRenderView = renderTexture.createView({ format: viewFormat });
const reinterpretedResolveView =
resolveTexture && resolveTexture.createView({ format: viewFormat });
const pipeline = makeBlitPipeline(t.device, viewFormat, {
sample: 1,
render: sampleCount,
});
const commandEncoder = t.device.createCommandEncoder();
const pass = commandEncoder.beginRenderPass({
colorAttachments: [
{
view: reinterpretedRenderView,
resolveTarget: reinterpretedResolveView,
loadOp: 'load',
storeOp: 'store',
},
],
});
pass.setPipeline(pipeline);
pass.setBindGroup(
0,
t.device.createBindGroup({
layout: pipeline.getBindGroupLayout(0),
entries: [
{
binding: 0,
resource: sampleSource.createView(),
},
],
})
);
pass.draw(6);
pass.end();
const singleSampleRenderTexture = resolveTexture
? t.createTextureTracked({
format,
size: [kTextureSize, kTextureSize],
usage: GPUTextureUsage.COPY_SRC | GPUTextureUsage.RENDER_ATTACHMENT,
})
: renderTexture;
if (resolveTexture) {
const pipeline = makeBlitPipeline(t.device, format, { sample: sampleCount, render: 1 });
const pass = commandEncoder.beginRenderPass({
colorAttachments: [
{
view: singleSampleRenderTexture.createView(),
loadOp: 'load',
storeOp: 'store',
},
],
});
pass.setPipeline(pipeline);
pass.setBindGroup(
0,
t.device.createBindGroup({
layout: pipeline.getBindGroupLayout(0),
entries: [
{
binding: 0,
resource: renderTexture.createView(),
},
],
})
);
pass.draw(6);
pass.end();
}
t.device.queue.submit([commandEncoder.finish()]);
const renderViewTexels = TexelView.fromTexelsAsColors(viewFormat, inputTexelView.color, {
clampToFormatRange: true,
});
ttu.expectTexelViewComparisonIsOkInTexture(
t,
{ texture: singleSampleRenderTexture },
renderViewTexels,
[kTextureSize, kTextureSize],
{ maxDiffULPsForNormFormat: 2 }
);
if (resolveTexture) {
const resolveView = TexelView.fromTexelsAsColors(viewFormat, renderViewTexels.color, {
clampToFormatRange: true,
});
ttu.expectTexelViewComparisonIsOkInTexture(
t,
{ texture: resolveTexture },
resolveView,
[kTextureSize, kTextureSize],
{ maxDiffULPsForNormFormat: 2 }
);
}
}
});