export const description = `
Tests for resource compatibility between pipeline layout and shader modules
  `;

import { makeTestGroup } from '../../../../common/framework/test_group.js';
import { keysOf } from '../../../../common/util/data_tables.js';
import {
  kAPIResources,
  getWGSLShaderForResource,
  getAPIBindGroupLayoutForResource,
  doResourcesMatch,
} from '../utils.js';
import * as vtu from '../validation_test_utils.js';

import { CreateRenderPipelineValidationTest } from './common.js';

export const g = makeTestGroup(CreateRenderPipelineValidationTest);

g.test('resource_compatibility')
  .desc(
    'Tests validation of resource (bind group) compatibility between pipeline layout and WGSL shader'
  )
  .params(u =>
    u //
      .combine('stage', ['vertex', 'fragment'] as const)
      .combine('apiResource', keysOf(kAPIResources))
      .filter(t => {
        const res = kAPIResources[t.apiResource];
        if (t.stage === 'vertex') {
          if (res.buffer && res.buffer.type === 'storage') {
            return false;
          }
          if (res.storageTexture && res.storageTexture.access !== 'read-only') {
            return false;
          }
        }
        return true;
      })
      .beginSubcases()
      .combine('isAsync', [true, false] as const)
      .combine('wgslResource', keysOf(kAPIResources))
  )
  .fn(t => {
    const apiResource = kAPIResources[t.params.apiResource];
    const wgslResource = kAPIResources[t.params.wgslResource];
    t.skipIf(
      wgslResource.storageTexture !== undefined &&
        wgslResource.storageTexture.access !== 'write-only' &&
        !t.hasLanguageFeature('readonly_and_readwrite_storage_textures'),
      'Storage textures require language feature'
    );
    t.skipIf(
      t.params.stage === 'vertex' &&
        ((wgslResource.buffer !== undefined && wgslResource.buffer.type === 'storage') ||
          (wgslResource.storageTexture !== undefined &&
            wgslResource.storageTexture.access !== 'read-only')),
      'Read-Write Storage buffers and textures cannot be used in vertex shaders'
    );
    if (t.isCompatibility) {
      t.skipIf(
        t.params.stage === 'vertex' &&
          (apiResource.buffer?.type === 'storage' ||
            apiResource.buffer?.type === 'read-only-storage') &&
          t.device.limits.maxStorageBuffersInVertexStage === 0,
        'Storage buffers can not be used in vertex shaders because maxStorageBuffersInVertexStage === 0'
      );
      t.skipIf(
        t.params.stage === 'vertex' &&
          apiResource.storageTexture !== undefined &&
          t.device.limits.maxStorageTexturesInVertexStage === 0,
        'Storage textures can not be used in vertex shaders because maxStorageTexturesInVertexStage === 0'
      );
      t.skipIf(
        t.params.stage === 'fragment' &&
          (apiResource.buffer?.type === 'storage' ||
            apiResource.buffer?.type === 'read-only-storage') &&
          t.device.limits.maxStorageBuffersInFragmentStage === 0,
        'Storage buffers can not be used in fragment shaders because maxStorageBuffersInFragmentStage === 0'
      );
      t.skipIf(
        t.params.stage === 'fragment' &&
          apiResource.storageTexture !== undefined &&
          t.device.limits.maxStorageTexturesInFragmentStage === 0,
        'Storage textures can not be used in fragment shaders because maxStorageTexturesInFragmentStage === 0'
      );
    }
    t.skipIfTextureViewDimensionNotSupported(wgslResource.texture?.viewDimension);
    const emptyVS = `
@vertex
fn main() -> @builtin(position) vec4f {
  return vec4f();
}
`;
    const emptyFS = `
@fragment
fn main() -> @location(0) vec4f {
  return vec4f();
}
`;

    const code = getWGSLShaderForResource(t.params.stage, wgslResource);
    const vsCode = t.params.stage === 'vertex' ? code : emptyVS;
    const fsCode = t.params.stage === 'fragment' ? code : emptyFS;
    const gpuStage: GPUShaderStageFlags =
      t.params.stage === 'vertex' ? GPUShaderStage.VERTEX : GPUShaderStage.FRAGMENT;
    const layout = t.device.createPipelineLayout({
      bindGroupLayouts: [getAPIBindGroupLayoutForResource(t.device, gpuStage, apiResource)],
    });

    const descriptor = {
      layout,
      vertex: {
        module: t.device.createShaderModule({
          code: vsCode,
        }),
        entryPoint: 'main',
      },
      fragment: {
        module: t.device.createShaderModule({
          code: fsCode,
        }),
        entryPoint: 'main',
        targets: [{ format: 'rgba8unorm' }] as const,
      },
    };

    vtu.doCreateRenderPipelineTest(
      t,
      t.params.isAsync,
      doResourcesMatch(apiResource, wgslResource),
      descriptor
    );
  });
