diff --git a/src/webgpu/api/validation/encoding/cmds/render/draw.spec.ts b/src/webgpu/api/validation/encoding/cmds/render/draw.spec.ts index 570d36207fbf..751816e8e079 100644 --- a/src/webgpu/api/validation/encoding/cmds/render/draw.spec.ts +++ b/src/webgpu/api/validation/encoding/cmds/render/draw.spec.ts @@ -5,7 +5,11 @@ and parameters as expect. `; import { makeTestGroup } from '../../../../../../common/framework/test_group.js'; -import { kVertexFormatInfo } from '../../../../../capability_info.js'; +import { + kPrimitiveTopology, + kIndexFormat, + kVertexFormatInfo, +} from '../../../../../capability_info.js'; import { GPUTest, AllFeaturesMaxLimitsGPUTest } from '../../../../../gpu_test.js'; import * as vtu from '../../../validation_test_utils.js'; @@ -287,6 +291,147 @@ In this test we test that a small buffer bound to unused buffer slot won't cause } }); +g.test(`index_buffer_format`) + .desc( + ` +Check that pipelines with a strip topology require their stripIndexFormat to match the setIndexBuffer calls' indexFormat. + - Issues an indexed draw call after a setPipeline and setIndexBuffer call. + - For all valid (stripIndexFormat, topology) combinations. + - For all setIndexBuffer indexFormats. + - For all render encoders. + - For both orderings of setIndexBuffer and setPipeline. +` + ) + .paramsSubcasesOnly(u => + u + .combine('topology', kPrimitiveTopology) + .combine('stripIndexFormat', [undefined, ...kIndexFormat] as const) + .filter( + p => + p.topology === 'line-strip' || + p.topology === 'triangle-strip' || + p.stripIndexFormat === undefined + ) + .combine('indexFormat', kIndexFormat) + .combine('drawType', ['drawIndexed', 'drawIndexedIndirect'] as const) + ) + .fn(t => { + const { indexFormat, topology, stripIndexFormat, drawType } = t.params; + + const pipeline = t.device.createRenderPipeline({ + layout: 'auto', + vertex: { + module: t.device.createShaderModule({ code: vtu.getNoOpShaderCode('VERTEX') }), + }, + fragment: { + module: t.device.createShaderModule({ code: vtu.getNoOpShaderCode('FRAGMENT') }), + targets: [{ format: 'rgba8unorm', writeMask: 0 }], + }, + primitive: { + topology, + stripIndexFormat, + }, + }); + const indexBuffer = vtu.createBufferWithState(t, 'valid', { + size: 16, + usage: GPUBufferUsage.INDEX | GPUBufferUsage.COPY_DST, + }); + + // Make the encoders that test the validation. + const isStrip = topology === 'line-strip' || topology === 'triangle-strip'; + const success = !isStrip || stripIndexFormat === indexFormat; + + for (const encoderType of ['render bundle', 'render pass'] as const) { + for (const setPipelineBeforeBuffer of [false, true]) { + const commandBufferMaker = t.createEncoder(encoderType); + const renderEncoder = commandBufferMaker.encoder; + + if (setPipelineBeforeBuffer) { + renderEncoder.setPipeline(pipeline); + } + renderEncoder.setIndexBuffer(indexBuffer, indexFormat); + if (!setPipelineBeforeBuffer) { + renderEncoder.setPipeline(pipeline); + } + + callDrawIndexed(t, renderEncoder, drawType, { indexCount: 3 }); + commandBufferMaker.validateFinishAndSubmit(success, true); + } + } + }); + +g.test(`index_buffer_format_dirtying`) + .desc( + ` + Check that the validation for indexFormat matching stripIndexFormat is dirtied if either the pipeline or the index buffer is changed. +` + ) + .paramsSubcasesOnly(p => + p + .combine('dirty', ['pipeline', 'indexBuffer', 'neither']) + .combine('drawType', ['drawIndexed', 'drawIndexedIndirect'] as const) + ) + .fn(t => { + const { dirty, drawType } = t.params; + + // Create render pipelines with both stripIndexFormats. + const makeStripIndexPipeline = ( + topology: GPUPrimitiveTopology, + stripIndexFormat: GPUIndexFormat + ) => { + return t.device.createRenderPipeline({ + layout: 'auto', + vertex: { + module: t.device.createShaderModule({ code: vtu.getNoOpShaderCode('VERTEX') }), + }, + fragment: { + module: t.device.createShaderModule({ code: vtu.getNoOpShaderCode('FRAGMENT') }), + targets: [{ format: 'rgba8unorm', writeMask: 0 }], + }, + primitive: { + topology, + stripIndexFormat, + }, + }); + }; + + const pipelineUint32 = makeStripIndexPipeline('triangle-strip', 'uint32'); + const pipelineUint16 = makeStripIndexPipeline('triangle-strip', 'uint16'); + + const indexBuffer = vtu.createBufferWithState(t, 'valid', { + size: 16, + usage: GPUBufferUsage.INDEX | GPUBufferUsage.COPY_DST, + }); + + // Make the encoders that test the validation. + const success = dirty === 'neither'; + + for (const encoderType of ['render bundle', 'render pass'] as const) { + const commandBufferMaker = t.createEncoder(encoderType); + const renderEncoder = commandBufferMaker.encoder; + + // First draw that's valid (checked with 'dirty': 'neither'). + renderEncoder.setPipeline(pipelineUint32); + renderEncoder.setIndexBuffer(indexBuffer, 'uint32'); + callDrawIndexed(t, renderEncoder, drawType, { indexCount: 3 }); + + // Dirty the pipeline or the buffer such that the validation should fail. + switch (dirty) { + case 'pipeline': + renderEncoder.setPipeline(pipelineUint16); + break; + case 'indexBuffer': + renderEncoder.setIndexBuffer(indexBuffer, 'uint16'); + break; + case 'neither': + break; + } + + callDrawIndexed(t, renderEncoder, drawType, { indexCount: 3 }); + commandBufferMaker.validateFinishAndSubmit(success, true); + } + }); + g.test(`index_buffer_OOB`) .desc( ` diff --git a/src/webgpu/listing_meta.json b/src/webgpu/listing_meta.json index 76b1c3974bb1..f3ba974ce0a2 100644 --- a/src/webgpu/listing_meta.json +++ b/src/webgpu/listing_meta.json @@ -250,6 +250,7 @@ "webgpu:api,operation,vertex_state,index_format:primitive_restart:*": { "subcaseMS": 12.080 }, "webgpu:api,validation,buffer,create:createBuffer_invalid_and_oom:*": { "subcaseMS": 1.500 }, "webgpu:api,validation,buffer,create:limit:*": { "subcaseMS": 31.433 }, + "webgpu:api,validation,buffer,create:new_usages:*": { "subcaseMS": 1.159 }, "webgpu:api,validation,buffer,create:size:*": { "subcaseMS": 5.570 }, "webgpu:api,validation,buffer,create:usage:*": { "subcaseMS": 3.971 }, "webgpu:api,validation,buffer,destroy:all_usages:*": { "subcaseMS": 3.250 }, @@ -485,6 +486,7 @@ "webgpu:api,validation,createTexture:mipLevelCount,bound_check,bigger_than_integer_bit_width:*": { "subcaseMS": 2.301 }, "webgpu:api,validation,createTexture:mipLevelCount,bound_check:*": { "subcaseMS": 0.801 }, "webgpu:api,validation,createTexture:mipLevelCount,format:*": { "subcaseMS": 1.258 }, + "webgpu:api,validation,createTexture:new_usages:*": { "subcaseMS": 1.268 }, "webgpu:api,validation,createTexture:sampleCount,valid_sampleCount_with_other_parameter_varies:*": { "subcaseMS": 0.525 }, "webgpu:api,validation,createTexture:sampleCount,various_sampleCount_with_all_formats:*": { "subcaseMS": 2.336 }, "webgpu:api,validation,createTexture:sample_count,1d_2d_array_3d:*": { "subcaseMS": 2.480 }, @@ -496,6 +498,7 @@ "webgpu:api,validation,createTexture:texture_size,default_value_and_smallest_size,compressed_format:*": { "subcaseMS": 1.863 }, "webgpu:api,validation,createTexture:texture_size,default_value_and_smallest_size,uncompressed_format:*": { "subcaseMS": 1.694 }, "webgpu:api,validation,createTexture:texture_usage:*": { "subcaseMS": 0.870 }, + "webgpu:api,validation,createTexture:usage:*": { "subcaseMS": 7.583 }, "webgpu:api,validation,createTexture:viewFormats:*": { "subcaseMS": 0.632 }, "webgpu:api,validation,createTexture:zero_size_and_usage:*": { "subcaseMS": 3.250 }, "webgpu:api,validation,createView:array_layers:*": { "subcaseMS": 0.491 }, @@ -506,6 +509,7 @@ "webgpu:api,validation,createView:mip_levels:*": { "subcaseMS": 0.436 }, "webgpu:api,validation,createView:texture_state:*": { "subcaseMS": 0.400 }, "webgpu:api,validation,createView:texture_view_usage:*": { "subcaseMS": 3106.634 }, + "webgpu:api,validation,createView:texture_view_usage_of_multiple_usages:*": { "subcaseMS": 5.349 }, "webgpu:api,validation,createView:texture_view_usage_with_view_format:*": { "subcaseMS": 2406.440 }, "webgpu:api,validation,debugMarker:push_pop_call_count_unbalance,command_encoder:*": { "subcaseMS": 1.522 }, "webgpu:api,validation,debugMarker:push_pop_call_count_unbalance,render_compute_pass:*": { "subcaseMS": 0.601 }, @@ -560,6 +564,8 @@ "webgpu:api,validation,encoding,cmds,index_access:out_of_bounds_zero_sized_index_buffer:*": { "subcaseMS": 12.400 }, "webgpu:api,validation,encoding,cmds,render,draw:buffer_binding_overlap:*": { "subcaseMS": 0.446 }, "webgpu:api,validation,encoding,cmds,render,draw:index_buffer_OOB:*": { "subcaseMS": 5.825 }, + "webgpu:api,validation,encoding,cmds,render,draw:index_buffer_format:*": { "subcaseMS": 8.864 }, + "webgpu:api,validation,encoding,cmds,render,draw:index_buffer_format_dirtying:*": { "subcaseMS": 0.389 }, "webgpu:api,validation,encoding,cmds,render,draw:last_buffer_setting_take_account:*": { "subcaseMS": 30.801 }, "webgpu:api,validation,encoding,cmds,render,draw:max_draw_count:*": { "subcaseMS": 3.521 }, "webgpu:api,validation,encoding,cmds,render,draw:unused_buffer_bound:*": { "subcaseMS": 1.413 }, @@ -703,6 +709,7 @@ "webgpu:api,validation,image_copy,texture_related:valid:*": { "subcaseMS": 3.678 }, "webgpu:api,validation,layout_shader_compat:pipeline_layout_shader_exact_match:*": { "subcaseMS": 2.000 }, "webgpu:api,validation,non_filterable_texture:non_filterable_texture_with_filtering_sampler:*": { "subcaseMS": 170.470 }, + "webgpu:api,validation,pipeline,immediates:pipeline_creation_immediate_size_mismatch:*": { "subcaseMS": 108.993 }, "webgpu:api,validation,query_set,create:count:*": { "subcaseMS": 0.967 }, "webgpu:api,validation,query_set,destroy:invalid_queryset:*": { "subcaseMS": 0.801 }, "webgpu:api,validation,query_set,destroy:twice:*": { "subcaseMS": 0.700 },