export const description = ` createRenderBundleEncoder validation tests. TODO(#3363): Make this into a MaxLimitTest and increase kMaxColorAttachments. `; import { makeTestGroup } from '../../../../common/framework/test_group.js'; import { range } from '../../../../common/util/util.js'; import { getDefaultLimits } from '../../../capability_info.js'; import { computeBytesPerSampleFromFormats, getColorRenderByteCost, isDepthOrStencilTextureFormat, isTextureFormatColorRenderable, kAllTextureFormats, kDepthStencilFormats, kPossibleColorRenderableTextureFormats, } from '../../../format_info.js'; import { AllFeaturesMaxLimitsGPUTest } from '../../../gpu_test.js'; // MAINTENANCE_TODO: This should be changed to kMaxColorAttachmentsToTest // when this is made a MaxLimitTest (see above). const kMaxColorAttachments = getDefaultLimits('core').maxColorAttachments.default; export const g = makeTestGroup(AllFeaturesMaxLimitsGPUTest); g.test('attachment_state,limits,maxColorAttachments') .desc(`Tests that attachment state must have <= device.limits.maxColorAttachments.`) .params(u => u.beginSubcases().combine( 'colorFormatCount', range(kMaxColorAttachments, i => i + 1) ) ) .fn(t => { const { colorFormatCount } = t.params; const maxColorAttachments = t.device.limits.maxColorAttachments; t.skipIf( colorFormatCount > maxColorAttachments, `${colorFormatCount} > maxColorAttachments: ${maxColorAttachments}` ); t.expectValidationError(() => { t.device.createRenderBundleEncoder({ colorFormats: Array(colorFormatCount).fill('r8unorm'), }); }, colorFormatCount > t.device.limits.maxColorAttachments); }); g.test('attachment_state,limits,maxColorAttachmentBytesPerSample,aligned') .desc( ` Tests that the total color attachment bytes per sample <= device.limits.maxColorAttachmentBytesPerSample when using the same format (aligned) for multiple attachments. ` ) .params(u => u .combine('format', kPossibleColorRenderableTextureFormats) .beginSubcases() .combine( 'colorFormatCount', range(kMaxColorAttachments, i => i + 1) ) ) .fn(t => { const { format, colorFormatCount } = t.params; t.skipIfTextureFormatNotSupported(format); const maxColorAttachments = t.device.limits.maxColorAttachments; t.skipIf( colorFormatCount > maxColorAttachments, `${colorFormatCount} > maxColorAttachments: ${maxColorAttachments}` ); const shouldError = !isTextureFormatColorRenderable(t.device.features, format) || getColorRenderByteCost(format) * colorFormatCount > t.device.limits.maxColorAttachmentBytesPerSample; t.expectValidationError(() => { t.device.createRenderBundleEncoder({ colorFormats: Array(colorFormatCount).fill(format), }); }, shouldError); }); g.test('attachment_state,limits,maxColorAttachmentBytesPerSample,unaligned') .desc( ` Tests that the total color attachment bytes per sample <= device.limits.maxColorAttachmentBytesPerSample when using various sets of (potentially) unaligned formats. ` ) .params(u => u.combineWithParams([ // Alignment causes the first 1 byte R8Unorm to become 4 bytes. So even though // 1+4+8+16+1 < 32, the 4 byte alignment requirement of R32Float makes the first R8Unorm // become 4 and 4+4+8+16+1 > 32. Re-ordering this so the R8Unorm's are at the end, however // is allowed: 4+8+16+1+1 < 32. { formats: [ 'r8unorm', 'r32float', 'rgba8unorm', 'rgba32float', 'r8unorm', ] as GPUTextureFormat[], }, { formats: [ 'r32float', 'rgba8unorm', 'rgba32float', 'r8unorm', 'r8unorm', ] as GPUTextureFormat[], }, ]) ) .fn(t => { const { formats } = t.params; t.skipIf( formats.length > t.device.limits.maxColorAttachments, `numColorAttachments: ${formats.length} > maxColorAttachments: ${t.device.limits.maxColorAttachments}` ); const shouldError = computeBytesPerSampleFromFormats(formats) > t.device.limits.maxColorAttachmentBytesPerSample; t.expectValidationError(() => { t.device.createRenderBundleEncoder({ colorFormats: formats, }); }, shouldError); }); g.test('attachment_state,empty_color_formats') .desc(`Tests that if no colorFormats are given, a depthStencilFormat must be specified.`) .params(u => u.beginSubcases().combine('depthStencilFormat', [undefined, 'depth24plus-stencil8'] as const) ) .fn(t => { const { depthStencilFormat } = t.params; t.expectValidationError(() => { t.device.createRenderBundleEncoder({ colorFormats: [], depthStencilFormat, }); }, depthStencilFormat === undefined); }); g.test('valid_texture_formats') .desc( ` Tests that createRenderBundleEncoder only accepts valid formats for its attachments. - colorFormats - depthStencilFormat ` ) .params(u => u // .combine('format', kAllTextureFormats) .beginSubcases() .combine('attachment', ['color', 'depthStencil']) ) .fn(t => { const { format, attachment } = t.params; t.skipIfTextureFormatNotSupported(format); const colorRenderable = isTextureFormatColorRenderable(t.device.features, format); const depthStencil = isDepthOrStencilTextureFormat(format); switch (attachment) { case 'color': { t.expectValidationError(() => { t.device.createRenderBundleEncoder({ colorFormats: [format], }); }, !colorRenderable); break; } case 'depthStencil': { t.expectValidationError(() => { t.device.createRenderBundleEncoder({ colorFormats: [], depthStencilFormat: format, }); }, !depthStencil); break; } } }); g.test('depth_stencil_readonly') .desc( ` Test that allow combinations of depth-stencil format, depthReadOnly and stencilReadOnly are allowed. ` ) .params(u => u // .combine('depthStencilFormat', kDepthStencilFormats) .beginSubcases() .combine('depthReadOnly', [false, true]) .combine('stencilReadOnly', [false, true]) ) .fn(t => { const { depthStencilFormat, depthReadOnly, stencilReadOnly } = t.params; t.skipIfTextureFormatNotSupported(depthStencilFormat); t.device.createRenderBundleEncoder({ colorFormats: [], depthStencilFormat, depthReadOnly, stencilReadOnly, }); });