@@ -14,6 +14,7 @@ import { UniqueFeaturesOrLimitsGPUTest } from '../../../../gpu_test.js';
1414import {
1515 isIdentitySwizzle ,
1616 kSwizzleTests ,
17+ swizzlesAreTheSame ,
1718 swizzleSpecToGPUTextureComponentSwizzle ,
1819} from './texture_component_swizzle_utils.js' ;
1920
@@ -80,3 +81,120 @@ g.test('no_render_nor_storage')
8081 texture . createView ( { swizzle } ) ;
8182 } , shouldError ) ;
8283 } ) ;
84+
85+ g . test ( 'compatibility_mode' )
86+ . desc (
87+ `
88+ Test that in compatibility mode, swizzles must be equivalent.
89+ `
90+ )
91+ . beforeAllSubcases ( t => {
92+ // MAINTENANCE_TODO: Remove this cast once texture-component-swizzle is added to @webgpu/types
93+ t . selectDeviceOrSkipTestCase ( 'texture-component-swizzle' as GPUFeatureName ) ;
94+ } )
95+ . params ( u =>
96+ u
97+ . beginSubcases ( )
98+ . combine ( 'swizzleSpec' , kSwizzleTests )
99+ . combine ( 'otherSwizzleSpec' , kSwizzleTests )
100+ . combine ( 'pipelineType' , [ 'render' , 'compute' ] as const )
101+ )
102+ . fn ( t => {
103+ const { swizzleSpec, otherSwizzleSpec, pipelineType } = t . params ;
104+ const swizzle = swizzleSpecToGPUTextureComponentSwizzle ( swizzleSpec ) ;
105+ const otherSwizzle = swizzleSpecToGPUTextureComponentSwizzle ( otherSwizzleSpec ) ;
106+
107+ const module = t . device . createShaderModule ( {
108+ code : `
109+ @group(0) @binding(0) var tex0: texture_2d<f32>;
110+ @group(1) @binding(0) var tex1: texture_2d<f32>;
111+
112+ @compute @workgroup_size(1) fn cs() {
113+ _ = tex0;
114+ _ = tex1;
115+ }
116+
117+ @vertex fn vs() -> @builtin(position) vec4f {
118+ return vec4f(0);
119+ }
120+
121+ @fragment fn fs() -> @location(0) vec4f {
122+ _ = tex0;
123+ _ = tex1;
124+ return vec4f(0);
125+ }
126+ ` ,
127+ } ) ;
128+
129+ const pipeline =
130+ pipelineType === 'compute'
131+ ? t . device . createComputePipeline ( {
132+ layout : 'auto' ,
133+ compute : { module } ,
134+ } )
135+ : t . device . createRenderPipeline ( {
136+ layout : 'auto' ,
137+ vertex : { module } ,
138+ fragment : { module, targets : [ { format : 'rgba8unorm' } ] } ,
139+ } ) ;
140+
141+ const texture = t . createTextureTracked ( {
142+ size : [ 1 ] ,
143+ format : 'rgba8unorm' ,
144+ usage : GPUTextureUsage . TEXTURE_BINDING ,
145+ } ) ;
146+
147+ const bindGroup0 = t . device . createBindGroup ( {
148+ layout : pipeline . getBindGroupLayout ( 0 ) ,
149+ entries : [
150+ {
151+ binding : 0 ,
152+ resource : texture . createView ( { swizzle } ) ,
153+ } ,
154+ ] ,
155+ } ) ;
156+
157+ const bindGroup1 = t . device . createBindGroup ( {
158+ layout : pipeline . getBindGroupLayout ( 0 ) ,
159+ entries : [
160+ {
161+ binding : 0 ,
162+ resource : texture . createView ( { swizzle : otherSwizzle } ) ,
163+ } ,
164+ ] ,
165+ } ) ;
166+
167+ const encoder = t . device . createCommandEncoder ( ) ;
168+ switch ( pipelineType ) {
169+ case 'compute' : {
170+ const pass = encoder . beginComputePass ( ) ;
171+ pass . setPipeline ( pipeline as GPUComputePipeline ) ;
172+ pass . setBindGroup ( 0 , bindGroup0 ) ;
173+ pass . setBindGroup ( 1 , bindGroup1 ) ;
174+ pass . dispatchWorkgroups ( 1 ) ;
175+ pass . end ( ) ;
176+ break ;
177+ }
178+ case 'render' : {
179+ const view = t . createTextureTracked ( {
180+ size : [ 1 ] ,
181+ format : 'rgba8unorm' ,
182+ usage : GPUTextureUsage . RENDER_ATTACHMENT ,
183+ } ) ;
184+ const pass = encoder . beginRenderPass ( {
185+ colorAttachments : [ { view, loadOp : 'clear' , storeOp : 'store' } ] ,
186+ } ) ;
187+ pass . setPipeline ( pipeline as GPURenderPipeline ) ;
188+ pass . setBindGroup ( 0 , bindGroup0 ) ;
189+ pass . setBindGroup ( 1 , bindGroup1 ) ;
190+ pass . draw ( 3 ) ;
191+ pass . end ( ) ;
192+ }
193+ }
194+
195+ const shouldError = t . isCompatibility && ! swizzlesAreTheSame ( swizzle , otherSwizzle ) ;
196+
197+ t . expectValidationError ( ( ) => {
198+ encoder . finish ( ) ;
199+ } , shouldError ) ;
200+ } ) ;
0 commit comments