export const description = `
Execution tests for the 'textureGatherCompare' builtin function

A texture gather compare operation performs a depth comparison on four texels in a depth texture and collects the results into a single vector, as follows:
 * Find the four texels that would be used in a depth sampling operation with linear filtering, from mip level 0:
   - Use the specified coordinate, array index (when present), and offset (when present).
   - The texels are adjacent, forming a square, when considering their texture space coordinates (u,v).
   - Selected texels at the texture edge, cube face edge, or cube corners are handled as in ordinary texture sampling.
 * For each texel, perform a comparison against the depth reference value, yielding a 0.0 or 1.0 value, as controlled by the comparison sampler parameters.
 * Yield the four-component vector where the components are the comparison results with the texels with relative texel coordinates as follows:

   Result component  Relative texel coordinate
    x                (umin,vmax)
    y                (umax,vmax)
    z                (umax,vmin)
    w                (umin,vmin)
`;

import { makeTestGroup } from '../../../../../../common/framework/test_group.js';
import { kCompareFunctions } from '../../../../../capability_info.js';
import { isDepthTextureFormat, kDepthStencilFormats } from '../../../../../format_info.js';
import { AllFeaturesMaxLimitsGPUTest } from '../../../../../gpu_test.js';

import {
  checkCallResults,
  chooseTextureSize,
  createTextureWithRandomDataAndGetTexels,
  doTextureCalls,
  generateSamplePointsCube,
  generateTextureBuiltinInputs2D,
  kCubeSamplePointMethods,
  kSamplePointMethods,
  kShortAddressModes,
  kShortAddressModeToAddressMode,
  kShortShaderStages,
  makeRandomDepthComparisonTexelGenerator,
  TextureCall,
  vec2,
  vec3,
} from './texture_utils.js';

export const g = makeTestGroup(AllFeaturesMaxLimitsGPUTest);

g.test('array_2d_coords')
  .specURL('https://www.w3.org/TR/WGSL/#texturegathercompare')
  .desc(
    `
A: i32, u32

fn textureGatherCompare(t: texture_depth_2d_array, s: sampler_comparison, coords: vec2<f32>, array_index: A, depth_ref: f32) -> vec4<f32>
fn textureGatherCompare(t: texture_depth_2d_array, s: sampler_comparison, coords: vec2<f32>, array_index: A, depth_ref: f32, offset: vec2<i32>) -> vec4<f32>

Parameters:
 * t: The depth texture to read from
 * s: The sampler_comparison
 * coords: The texture coordinates
 * array_index: The 0-based array index.
 * depth_ref: The reference value to compare the sampled depth value against
 * offset:
    - The optional texel offset applied to the unnormalized texture coordinate before sampling the texture.
      This offset is applied before applying any texture wrapping modes.
    - The offset expression must be a creation-time expression (e.g. vec2<i32>(1, 2)).
    - Each offset component must be at least -8 and at most 7.
      Values outside of this range will result in a shader-creation error.
`
  )
  .params(u =>
    u
      .combine('stage', kShortShaderStages)
      .combine('format', kDepthStencilFormats)
      // filter out stencil only formats
      .filter(t => isDepthTextureFormat(t.format))
      .combine('filt', ['nearest', 'linear'] as const)
      .combine('modeU', kShortAddressModes)
      .combine('modeV', kShortAddressModes)
      .combine('offset', [false, true] as const)
      .beginSubcases()
      .combine('samplePoints', kSamplePointMethods)
      .combine('A', ['i32', 'u32'] as const)
      .combine('compare', kCompareFunctions)
      .combine('depthOrArrayLayers', [1, 8] as const)
  )
  .fn(async t => {
    const {
      format,
      stage,
      samplePoints,
      A,
      modeU,
      modeV,
      filt: minFilter,
      compare,
      offset,
      depthOrArrayLayers,
    } = t.params;
    t.skipIfTextureFormatNotSupported(format);

    const [width, height] = chooseTextureSize({ minSize: 8, minBlocks: 4, format });
    const size = { width, height, depthOrArrayLayers };

    const descriptor: GPUTextureDescriptor = {
      format,
      size,
      usage: GPUTextureUsage.COPY_DST | GPUTextureUsage.TEXTURE_BINDING,
      ...(t.isCompatibility && { textureBindingViewDimension: '2d-array' }),
    };
    const { texels, texture } = await createTextureWithRandomDataAndGetTexels(t, descriptor, {
      generator: makeRandomDepthComparisonTexelGenerator(descriptor, compare),
    });
    const sampler: GPUSamplerDescriptor = {
      addressModeU: kShortAddressModeToAddressMode[modeU],
      addressModeV: kShortAddressModeToAddressMode[modeV],
      compare,
      minFilter,
      magFilter: minFilter,
      mipmapFilter: minFilter,
    };

    const calls: TextureCall<vec2>[] = generateTextureBuiltinInputs2D(50, {
      method: samplePoints,
      textureBuiltin: 'textureGatherCompare',
      sampler,
      descriptor,
      arrayIndex: { num: texture.depthOrArrayLayers, type: A },
      depthRef: true,
      offset,
      hashInputs: [stage, format, samplePoints, A, modeU, modeV, minFilter, offset],
    }).map(({ coords, arrayIndex, depthRef, offset }) => {
      return {
        builtin: 'textureGatherCompare',
        coordType: 'f',
        coords,
        arrayIndex,
        arrayIndexType: A === 'i32' ? 'i' : 'u',
        depthRef,
        offset,
      };
    });
    const textureType = 'texture_depth_2d_array';
    const viewDescriptor: GPUTextureViewDescriptor = { dimension: '2d-array' };
    const results = await doTextureCalls(
      t,
      texture,
      viewDescriptor,
      textureType,
      sampler,
      calls,
      stage
    );
    const res = await checkCallResults(
      t,
      { texels, descriptor, viewDescriptor },
      textureType,
      sampler,
      calls,
      results,
      stage,
      texture
    );
    t.expectOK(res);
  });

g.test('array_3d_coords')
  .specURL('https://www.w3.org/TR/WGSL/#texturegathercompare')
  .desc(
    `
A: i32, u32

fn textureGatherCompare(t: texture_depth_cube_array, s: sampler_comparison, coords: vec3<f32>, array_index: A, depth_ref: f32) -> vec4<f32>

Parameters:
 * t: The depth texture to read from
 * s: The sampler_comparison
 * coords: The texture coordinates
 * array_index: The 0-based array index.
 * depth_ref: The reference value to compare the sampled depth value against
`
  )
  .params(u =>
    u
      .combine('stage', kShortShaderStages)
      .combine('format', kDepthStencilFormats)
      // filter out stencil only formats
      .filter(t => isDepthTextureFormat(t.format))
      .combine('filt', ['nearest', 'linear'] as const)
      .combine('mode', kShortAddressModes)
      .beginSubcases()
      .combine('samplePoints', kCubeSamplePointMethods)
      .combine('A', ['i32', 'u32'] as const)
      .combine('compare', kCompareFunctions)
  )
  .fn(async t => {
    const { format, A, stage, samplePoints, mode, filt: minFilter, compare } = t.params;
    t.skipIfTextureViewDimensionNotSupported('cube-array');
    t.skipIfTextureFormatNotSupported(format);

    const viewDimension: GPUTextureViewDimension = 'cube-array';
    const size = chooseTextureSize({ minSize: 8, minBlocks: 2, format, viewDimension });

    const descriptor: GPUTextureDescriptor = {
      format,
      ...(t.isCompatibility && { textureBindingViewDimension: viewDimension }),
      size,
      usage: GPUTextureUsage.COPY_DST | GPUTextureUsage.TEXTURE_BINDING,
    };
    const { texels, texture } = await createTextureWithRandomDataAndGetTexels(t, descriptor, {
      generator: makeRandomDepthComparisonTexelGenerator(descriptor, compare),
    });
    const sampler: GPUSamplerDescriptor = {
      addressModeU: kShortAddressModeToAddressMode[mode],
      addressModeV: kShortAddressModeToAddressMode[mode],
      addressModeW: kShortAddressModeToAddressMode[mode],
      compare,
      minFilter,
      magFilter: minFilter,
      mipmapFilter: minFilter,
    };

    const calls: TextureCall<vec3>[] = generateSamplePointsCube(50, {
      method: samplePoints,
      sampler,
      descriptor,
      textureBuiltin: 'textureGatherCompare',
      arrayIndex: { num: texture.depthOrArrayLayers / 6, type: A },
      depthRef: true,
      hashInputs: [stage, format, samplePoints, mode, minFilter],
    }).map(({ coords, depthRef, arrayIndex }) => {
      return {
        builtin: 'textureGatherCompare',
        arrayIndex,
        arrayIndexType: A === 'i32' ? 'i' : 'u',
        coordType: 'f',
        coords,
        depthRef,
      };
    });
    const viewDescriptor = {
      dimension: viewDimension,
    };
    const textureType = 'texture_depth_cube_array';
    const results = await doTextureCalls(
      t,
      texture,
      viewDescriptor,
      textureType,
      sampler,
      calls,
      stage
    );
    const res = await checkCallResults(
      t,
      { texels, descriptor, viewDescriptor },
      textureType,
      sampler,
      calls,
      results,
      stage,
      texture
    );
    t.expectOK(res);
  });

g.test('sampled_2d_coords')
  .specURL('https://www.w3.org/TR/WGSL/#texturegathercompare')
  .desc(
    `
fn textureGatherCompare(t: texture_depth_2d, s: sampler_comparison, coords: vec2<f32>, depth_ref: f32) -> vec4<f32>
fn textureGatherCompare(t: texture_depth_2d, s: sampler_comparison, coords: vec2<f32>, depth_ref: f32, offset: vec2<i32>) -> vec4<f32>

Parameters:
 * t: The depth texture to read from
 * s: The sampler_comparison
 * coords: The texture coordinates
 * depth_ref: The reference value to compare the sampled depth value against
 * offset:
    - The optional texel offset applied to the unnormalized texture coordinate before sampling the texture.
      This offset is applied before applying any texture wrapping modes.
    - The offset expression must be a creation-time expression (e.g. vec2<i32>(1, 2)).
    - Each offset component must be at least -8 and at most 7.
      Values outside of this range will result in a shader-creation error.
`
  )
  .params(u =>
    u
      .combine('stage', kShortShaderStages)
      .combine('format', kDepthStencilFormats)
      // filter out stencil only formats
      .filter(t => isDepthTextureFormat(t.format))
      .combine('filt', ['nearest', 'linear'] as const)
      .combine('mode', kShortAddressModes)
      .combine('offset', [false, true] as const)
      .beginSubcases()
      .combine('C', ['i32', 'u32'] as const)
      .combine('samplePoints', kSamplePointMethods)
      .combine('compare', kCompareFunctions)
  )
  .fn(async t => {
    const { format, C, stage, samplePoints, mode, compare, filt: minFilter, offset } = t.params;
    t.skipIfTextureFormatNotSupported(format);

    const size = chooseTextureSize({ minSize: 8, minBlocks: 4, format });
    const descriptor: GPUTextureDescriptor = {
      format,
      size,
      usage: GPUTextureUsage.COPY_DST | GPUTextureUsage.TEXTURE_BINDING,
    };
    const { texels, texture } = await createTextureWithRandomDataAndGetTexels(t, descriptor, {
      generator: makeRandomDepthComparisonTexelGenerator(descriptor, compare),
    });
    const sampler: GPUSamplerDescriptor = {
      addressModeU: kShortAddressModeToAddressMode[mode],
      addressModeV: kShortAddressModeToAddressMode[mode],
      compare,
      minFilter,
      magFilter: minFilter,
      mipmapFilter: minFilter,
    };

    const calls: TextureCall<vec2>[] = generateTextureBuiltinInputs2D(50, {
      method: samplePoints,
      textureBuiltin: 'textureGatherCompare',
      sampler,
      descriptor,
      offset,
      depthRef: true,
      hashInputs: [stage, format, C, samplePoints, mode, minFilter, compare, offset],
    }).map(({ coords, depthRef, offset }) => {
      return {
        builtin: 'textureGatherCompare',
        coordType: 'f',
        coords,
        depthRef,
        offset,
      };
    });
    const textureType = 'texture_depth_2d';
    const viewDescriptor = {};
    const results = await doTextureCalls(
      t,
      texture,
      viewDescriptor,
      textureType,
      sampler,
      calls,
      stage
    );
    const res = await checkCallResults(
      t,
      { texels, descriptor, viewDescriptor },
      textureType,
      sampler,
      calls,
      results,
      stage,
      texture
    );
    t.expectOK(res);
  });

g.test('sampled_3d_coords')
  .specURL('https://www.w3.org/TR/WGSL/#texturegathercompare')
  .desc(
    `
fn textureGatherCompare(t: texture_depth_cube, s: sampler_comparison, coords: vec3<f32>, depth_ref: f32) -> vec4<f32>

Parameters:
 * t: The depth texture to read from
 * s: The sampler_comparison
 * coords: The texture coordinates
 * depth_ref: The reference value to compare the sampled depth value against
`
  )
  .params(u =>
    u
      .combine('stage', kShortShaderStages)
      .combine('format', kDepthStencilFormats)
      // filter out stencil only formats
      .filter(t => isDepthTextureFormat(t.format))
      .combine('filt', ['nearest', 'linear'] as const)
      .combine('mode', kShortAddressModes)
      .beginSubcases()
      .combine('samplePoints', kCubeSamplePointMethods)
      .combine('compare', kCompareFunctions)
  )
  .fn(async t => {
    const { format, stage, samplePoints, mode, filt: minFilter, compare } = t.params;
    t.skipIfTextureFormatNotSupported(format);

    const viewDimension: GPUTextureViewDimension = 'cube';
    const size = chooseTextureSize({ minSize: 8, minBlocks: 2, format, viewDimension });

    const descriptor: GPUTextureDescriptor = {
      format,
      ...(t.isCompatibility && { textureBindingViewDimension: viewDimension }),
      size,
      usage: GPUTextureUsage.COPY_DST | GPUTextureUsage.TEXTURE_BINDING,
    };
    const { texels, texture } = await createTextureWithRandomDataAndGetTexels(t, descriptor, {
      generator: makeRandomDepthComparisonTexelGenerator(descriptor, compare),
    });
    const sampler: GPUSamplerDescriptor = {
      addressModeU: kShortAddressModeToAddressMode[mode],
      addressModeV: kShortAddressModeToAddressMode[mode],
      addressModeW: kShortAddressModeToAddressMode[mode],
      compare,
      minFilter,
      magFilter: minFilter,
      mipmapFilter: minFilter,
    };

    const calls: TextureCall<vec3>[] = generateSamplePointsCube(50, {
      method: samplePoints,
      sampler,
      descriptor,
      depthRef: true,
      textureBuiltin: 'textureGatherCompare',
      hashInputs: [stage, format, samplePoints, mode, minFilter, compare],
    }).map(({ coords, depthRef }) => {
      return {
        builtin: 'textureGatherCompare',
        coordType: 'f',
        coords,
        depthRef,
      };
    });
    const viewDescriptor = {
      dimension: viewDimension,
    };
    const textureType = 'texture_depth_cube';
    const results = await doTextureCalls(
      t,
      texture,
      viewDescriptor,
      textureType,
      sampler,
      calls,
      stage
    );
    const res = await checkCallResults(
      t,
      { texels, descriptor, viewDescriptor },
      textureType,
      sampler,
      calls,
      results,
      stage,
      texture
    );
    t.expectOK(res);
  });
