export const description = `
Tests for capabilities added by bgra8unorm-storage flag.
`;

import { makeTestGroup } from '../../../../common/framework/test_group.js';
import { assert } from '../../../../common/util/util.js';
import {
  kTextureUsages,
  kValidCombinationsOfOneOrTwoTextureUsages,
  usageIsTypeErrorForConfigure,
} from '../../../capability_info.js';
import { GPUConst } from '../../../constants.js';
import { UniqueFeaturesOrLimitsGPUTest } from '../../../gpu_test.js';
import { kAllCanvasTypes, createCanvas } from '../../../util/create_elements.js';

class BGRA8UnormStorageValidationTests extends UniqueFeaturesOrLimitsGPUTest {
  testCreateShaderModuleWithBGRA8UnormStorage(
    shaderType: 'fragment' | 'compute',
    success: boolean
  ): void {
    let shaderDeclaration = '';
    switch (shaderType) {
      case 'fragment':
        shaderDeclaration = '@fragment';
        break;
      case 'compute':
        shaderDeclaration = '@compute @workgroup_size(1)';
        break;
    }
    this.expectValidationError(() => {
      this.device.createShaderModule({
        code: `
      @group(0) @binding(1) var outputTex: texture_storage_2d<bgra8unorm, write>;
      ${shaderDeclaration} fn main() {
        textureStore(outputTex, vec2<i32>(), vec4<f32>());
      }
      `,
      });
    }, !success);
  }
}

export const g = makeTestGroup(BGRA8UnormStorageValidationTests);

g.test('create_texture')
  .desc(
    `
Test that it is valid to create bgra8unorm texture with STORAGE usage iff the feature
bgra8unorm-storage is enabled. Note, the createTexture test suite covers the validation cases where
this feature is not enabled, which are skipped here.
`
  )
  .beforeAllSubcases(t => {
    t.selectDeviceOrSkipTestCase('bgra8unorm-storage');
  })
  .fn(t => {
    const descriptor = {
      size: [1, 1, 1],
      format: 'bgra8unorm' as const,
      usage: GPUConst.TextureUsage.STORAGE_BINDING,
    };
    t.createTextureTracked(descriptor);
  });

g.test('create_bind_group_layout')
  .desc(
    `
Test that it is valid to create GPUBindGroupLayout that uses bgra8unorm as storage texture format
iff the feature bgra8unorm-storage is enabled. Note, the createBindGroupLayout test suite covers the
validation cases where this feature is not enabled, which are skipped here.
`
  )
  .beforeAllSubcases(t => {
    t.selectDeviceOrSkipTestCase('bgra8unorm-storage');
  })
  .fn(t => {
    t.device.createBindGroupLayout({
      entries: [
        {
          binding: 0,
          visibility: GPUShaderStage.COMPUTE,
          storageTexture: { format: 'bgra8unorm' },
        },
      ],
    });
  });

g.test('configure_storage_usage_on_canvas_context_without_bgra8unorm_storage')
  .desc(
    `
Test that it is invalid to configure a GPUCanvasContext to 'GPUStorageBinding' usage with
'bgra8unorm' format on a GPUDevice with 'bgra8unorm-storage' disabled.
`
  )
  .params(u =>
    u
      .combine('canvasType', kAllCanvasTypes)
      .beginSubcases()
      .combine(
        'usage',
        kValidCombinationsOfOneOrTwoTextureUsages
          // Don't test disallowed usages here, that's covered by configure() tests.
          .filter(usage => !usageIsTypeErrorForConfigure(usage))
      )
  )
  .fn(t => {
    const { canvasType, usage } = t.params;

    // MAINTENANCE_TODO(#4509): Remove this after all implementations have TRANSIENT_ATTACHMENT.
    if ((usage & GPUConst.TextureUsage.TRANSIENT_ATTACHMENT) !== 0) {
      t.skipIfTransientAttachmentNotSupported();
    }

    const canvas = createCanvas(t, canvasType, 1, 1);
    const ctx = canvas.getContext('webgpu');
    assert(ctx instanceof GPUCanvasContext, 'Failed to get WebGPU context from canvas');

    const requiredStorageBinding = !!(usage & GPUTextureUsage.STORAGE_BINDING);
    t.expectValidationError(() => {
      ctx.configure({
        device: t.device,
        format: 'bgra8unorm',
        usage,
      });
    }, requiredStorageBinding);
  });

g.test('configure_storage_usage_on_canvas_context_with_bgra8unorm_storage')
  .desc(
    `
Test that it is valid to configure a GPUCanvasContext with GPUStorageBinding usage and a GPUDevice
with 'bgra8unorm-storage' enabled.
`
  )
  .beforeAllSubcases(t => {
    t.selectDeviceOrSkipTestCase({
      requiredFeatures: ['bgra8unorm-storage'],
      requiredLimits: {
        maxStorageTexturesInFragmentStage: 1,
      },
    });
  })
  .params(u =>
    u
      .combine('canvasType', kAllCanvasTypes)
      .beginSubcases()
      .combine(
        'usage',
        kTextureUsages
          // Don't test disallowed usages here, that's covered by configure() tests.
          .filter(usage => !usageIsTypeErrorForConfigure(usage))
          .map(usage => usage | GPUConst.TextureUsage.STORAGE_BINDING)
      )
  )
  .fn(t => {
    const { canvasType, usage } = t.params;

    // MAINTENANCE_TODO(#4509): Remove this after all implementations have TRANSIENT_ATTACHMENT.
    if ((usage & GPUConst.TextureUsage.TRANSIENT_ATTACHMENT) !== 0) {
      t.skipIfTransientAttachmentNotSupported();
    }

    const canvas = createCanvas(t, canvasType, 1, 1);
    const ctx = canvas.getContext('webgpu');
    assert(ctx instanceof GPUCanvasContext, 'Failed to get WebGPU context from canvas');

    ctx.configure({
      device: t.device,
      format: 'bgra8unorm',
      usage,
    });

    const currentTexture = ctx.getCurrentTexture();
    const bindGroupLayout = t.device.createBindGroupLayout({
      entries: [
        {
          binding: 0,
          visibility: GPUShaderStage.FRAGMENT,
          storageTexture: { access: 'write-only', format: currentTexture.format },
        },
      ],
    });
    t.device.createBindGroup({
      layout: bindGroupLayout,
      entries: [
        {
          binding: 0,
          resource: currentTexture.createView(),
        },
      ],
    });
  });
