/* Copyright (c) 2024-2026 The Khronos Group Inc.
 * Copyright (c) 2024-2026 Valve Corporation
 * Copyright (c) 2024-2026 LunarG, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#include "layer_validation_tests.h"
#include "pipeline_helper.h"
#include "descriptor_helper.h"

class PositiveGpuAVSpirv : public GpuAVTest {};

TEST_F(PositiveGpuAVSpirv, LoopPhi) {
    TEST_DESCRIPTION("Loop that has the Phi parent pointed to itself");
    RETURN_IF_SKIP(InitGpuAvFramework());
    RETURN_IF_SKIP(InitState());
    InitRenderTarget();

    vkt::Buffer buffer_uniform(*m_device, 1024, VK_BUFFER_USAGE_UNIFORM_BUFFER_BIT, kHostVisibleMemProps);
    vkt::Buffer buffer_storage(*m_device, 1024, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT, kHostVisibleMemProps);

    uint32_t* data = (uint32_t*)buffer_uniform.Memory().Map();
    data[0] = 4;  // Scene.lightCount

    OneOffDescriptorSet descriptor_set(m_device, {
                                                     {0, VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER, 1, VK_SHADER_STAGE_ALL, nullptr},
                                                     {1, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_ALL, nullptr},
                                                 });
    const vkt::PipelineLayout pipeline_layout(*m_device, {&descriptor_set.layout_});

    descriptor_set.WriteDescriptorBufferInfo(0, buffer_uniform, 0, VK_WHOLE_SIZE);
    descriptor_set.WriteDescriptorBufferInfo(1, buffer_storage, 0, VK_WHOLE_SIZE, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER);
    descriptor_set.UpdateDescriptorSets();

    // compiled with
    //  dxc -spirv -T ps_6_0 -E psmain -fspv-target-env=vulkan1.1
    //
    //  struct SceneData {
    //      uint lightCount;
    //  };
    //  struct Light {
    //      float3 position;
    //  };
    //  ConstantBuffer<SceneData> Scene  : register(b0, space0);
    //  StructuredBuffer<Light>   Lights : register(t1, space0);
    //
    //  float4 main(float4 Position : SV_POSITION, float2 TexCoord : TEXCOORD) : SV_TARGET {
    //      float3 color = (float3)0;
    //      for (uint i = 0; i < Scene.lightCount; ++i) {
    //          color += normalize(Lights[i].position);
    //      }
    //      return float4(color, 1.0);
    //  }
    //
    // Produces this nasty loop pattern where it seems at first glance the Phi 2nd Parent is actually after it
    //
    // %1 = OpLabel
    // OpBranch %2
    //
    // %2 = OpLabel
    // %phi = OpPhi %int %A %1 %B %3
    // OpLoopMerge %4 %3 None
    // OpBranchConditional %bool %3 %4
    //
    // %3 = OpLabel
    // %B = OpIAdd %int %_ %_
    // OpBranch %2
    //
    // %4 = OpLabel
    const char* fs_source = R"(
               OpCapability Shader
          %1 = OpExtInstImport "GLSL.std.450"
               OpMemoryModel Logical GLSL450
               OpEntryPoint Fragment %psmain "main" %out_var_SV_TARGET
               OpExecutionMode %psmain OriginUpperLeft
               OpDecorate %out_var_SV_TARGET Location 0
               OpDecorate %Scene DescriptorSet 0
               OpDecorate %Scene Binding 0
               OpDecorate %Lights DescriptorSet 0
               OpDecorate %Lights Binding 1
               OpMemberDecorate %type_ConstantBuffer_SceneData 0 Offset 0
               OpDecorate %type_ConstantBuffer_SceneData Block
               OpMemberDecorate %Light 0 Offset 0
               OpDecorate %_runtimearr_Light ArrayStride 16
               OpMemberDecorate %type_StructuredBuffer_Light 0 Offset 0
               OpMemberDecorate %type_StructuredBuffer_Light 0 NonWritable
               OpDecorate %type_StructuredBuffer_Light BufferBlock
      %float = OpTypeFloat 32
    %float_0 = OpConstant %float 0
    %v3float = OpTypeVector %float 3
         %13 = OpConstantComposite %v3float %float_0 %float_0 %float_0
       %uint = OpTypeInt 32 0
     %uint_0 = OpConstant %uint 0
        %int = OpTypeInt 32 1
      %int_0 = OpConstant %int 0
     %uint_1 = OpConstant %uint 1
    %float_1 = OpConstant %float 1
%type_ConstantBuffer_SceneData = OpTypeStruct %uint
%_ptr_Uniform_type_ConstantBuffer_SceneData = OpTypePointer Uniform %type_ConstantBuffer_SceneData
      %Light = OpTypeStruct %v3float
%_runtimearr_Light = OpTypeRuntimeArray %Light
%type_StructuredBuffer_Light = OpTypeStruct %_runtimearr_Light
%_ptr_Uniform_type_StructuredBuffer_Light = OpTypePointer Uniform %type_StructuredBuffer_Light
    %v4float = OpTypeVector %float 4
%_ptr_Output_v4float = OpTypePointer Output %v4float
       %void = OpTypeVoid
         %25 = OpTypeFunction %void
%_ptr_Uniform_uint = OpTypePointer Uniform %uint
       %bool = OpTypeBool
%_ptr_Uniform_v3float = OpTypePointer Uniform %v3float
      %Scene = OpVariable %_ptr_Uniform_type_ConstantBuffer_SceneData Uniform
     %Lights = OpVariable %_ptr_Uniform_type_StructuredBuffer_Light Uniform
%out_var_SV_TARGET = OpVariable %_ptr_Output_v4float Output
     %psmain = OpFunction %void None %25
         %29 = OpLabel
               OpBranch %30
         %30 = OpLabel
         %31 = OpPhi %v3float %13 %29 %32 %33
         %34 = OpPhi %uint %uint_0 %29 %35 %33
         %36 = OpAccessChain %_ptr_Uniform_uint %Scene %int_0
         %37 = OpLoad %uint %36
         %38 = OpULessThan %bool %34 %37
               OpLoopMerge %39 %33 None
               OpBranchConditional %38 %33 %39
         %33 = OpLabel
         %40 = OpAccessChain %_ptr_Uniform_v3float %Lights %int_0 %34 %int_0
         %41 = OpLoad %v3float %40
         %42 = OpExtInst %v3float %1 Normalize %41
         %32 = OpFAdd %v3float %31 %42
         %35 = OpIAdd %uint %34 %uint_1
               OpBranch %30
         %39 = OpLabel
         %43 = OpCompositeExtract %float %31 0
         %44 = OpCompositeExtract %float %31 1
         %45 = OpCompositeExtract %float %31 2
         %46 = OpCompositeConstruct %v4float %43 %44 %45 %float_1
               OpStore %out_var_SV_TARGET %46
               OpReturn
               OpFunctionEnd
        )";
    VkShaderObj fs(*m_device, fs_source, VK_SHADER_STAGE_FRAGMENT_BIT, SPV_ENV_VULKAN_1_0, SPV_SOURCE_ASM);

    CreatePipelineHelper pipe(*this);
    pipe.shader_stages_ = {pipe.vs_->GetStageCreateInfo(), fs.GetStageCreateInfo()};
    pipe.gp_ci_.layout = pipeline_layout;
    pipe.CreateGraphicsPipeline();

    m_command_buffer.Begin();
    m_command_buffer.BeginRenderPass(m_renderPassBeginInfo);
    vk::CmdBindPipeline(m_command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipe);
    vk::CmdBindDescriptorSets(m_command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pipeline_layout, 0, 1, &descriptor_set.set_, 0,
                              nullptr);
    vk::CmdDraw(m_command_buffer, 3, 1, 0, 0);
    vk::CmdEndRenderPass(m_command_buffer);
    m_command_buffer.End();

    m_default_queue->SubmitAndWait(m_command_buffer);
}

TEST_F(PositiveGpuAVSpirv, LoopHeaderPhi) {
    TEST_DESCRIPTION("Require injection in the Loop Header block that contains a Phi");
    SetTargetApiVersion(VK_API_VERSION_1_2);
    RETURN_IF_SKIP(InitGpuAvFramework());
    RETURN_IF_SKIP(InitState());

    // The folling HLSL:
    //
    // RWStructuredBuffer<int> data : register(u0);
    // [numthreads(1, 1, 1)]
    // void main() {
    //     int i = 0;
    //     for (i = 0; i < data[i]; i++) {
    //         data[0] += i;
    //     }
    // }
    const char* cs_source = R"(
               OpCapability Shader
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main" %data
               OpExecutionMode %main LocalSize 1 1 1
               OpDecorate %data DescriptorSet 0
               OpDecorate %data Binding 0
               OpDecorate %runtimearr ArrayStride 4
               OpMemberDecorate %type_RWStructuredBuffer 0 Offset 0
               OpDecorate %type_RWStructuredBuffer Block
        %int = OpTypeInt 32 1
      %int_0 = OpConstant %int 0
       %uint = OpTypeInt 32 0
     %uint_0 = OpConstant %uint 0
      %int_1 = OpConstant %int 1
%runtimearr = OpTypeRuntimeArray %int
%type_RWStructuredBuffer = OpTypeStruct %runtimearr
%ptr_RWStructuredBuffer = OpTypePointer StorageBuffer %type_RWStructuredBuffer
       %void = OpTypeVoid
         %12 = OpTypeFunction %void
%ptr_StorageBuffer = OpTypePointer StorageBuffer %int
       %bool = OpTypeBool
       %data = OpVariable %ptr_RWStructuredBuffer StorageBuffer
       %main = OpFunction %void None %12
         %15 = OpLabel
               OpBranch %16
         %16 = OpLabel
         %17 = OpPhi %int %int_0 %15 %18 %19
         %20 = OpBitcast %uint %17
         %21 = OpAccessChain %ptr_StorageBuffer %data %int_0 %20
         %22 = OpLoad %int %21
         %23 = OpSLessThan %bool %17 %22
               OpLoopMerge %24 %19 None
               OpBranchConditional %23 %19 %24
         %19 = OpLabel
         %25 = OpAccessChain %ptr_StorageBuffer %data %int_0 %uint_0
         %26 = OpLoad %int %25
         %27 = OpIAdd %int %26 %17
               OpStore %25 %27
         %18 = OpIAdd %int %17 %int_1
               OpBranch %16
         %24 = OpLabel
               OpReturn
               OpFunctionEnd
        )";

    CreateComputePipelineHelper pipe(*this);
    pipe.dsl_bindings_[0] = {0, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_ALL, nullptr};
    pipe.cs_ = VkShaderObj(*m_device, cs_source, VK_SHADER_STAGE_COMPUTE_BIT, SPV_ENV_VULKAN_1_2, SPV_SOURCE_ASM);
    pipe.CreateComputePipeline();

    vkt::Buffer buffer(*m_device, 16, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT, kHostVisibleMemProps);
    uint32_t* data = (uint32_t*)buffer.Memory().Map();
    data[0] = 1;  // data[0]
    data[1] = 2;  // data[1]
    data[2] = 3;  // data[2]
    data[3] = 0;  // data[3]

    pipe.descriptor_set_.WriteDescriptorBufferInfo(0, buffer, 0, VK_WHOLE_SIZE, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER);
    pipe.descriptor_set_.UpdateDescriptorSets();

    m_command_buffer.Begin();
    vk::CmdBindPipeline(m_command_buffer, VK_PIPELINE_BIND_POINT_COMPUTE, pipe);
    vk::CmdBindDescriptorSets(m_command_buffer, VK_PIPELINE_BIND_POINT_COMPUTE, pipe.pipeline_layout_, 0, 1,
                              &pipe.descriptor_set_.set_, 0, nullptr);
    vk::CmdDispatch(m_command_buffer, 1, 1, 1);
    m_command_buffer.End();

    m_default_queue->SubmitAndWait(m_command_buffer);
    ASSERT_EQ(4u, data[0]);
}

TEST_F(PositiveGpuAVSpirv, VulkanMemoryModelDeviceScope) {
    TEST_DESCRIPTION("Test adding VulkanMemoryModelDeviceScope support");
    SetTargetApiVersion(VK_API_VERSION_1_2);
    AddRequiredFeature(vkt::Feature::bufferDeviceAddress);
    AddRequiredFeature(vkt::Feature::vulkanMemoryModel);
    AddRequiredFeature(vkt::Feature::vulkanMemoryModelDeviceScope);
    RETURN_IF_SKIP(InitGpuAvFramework());
    RETURN_IF_SKIP(InitState());
    InitRenderTarget();

    vkt::Buffer buffer(*m_device, 256, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT, kHostVisibleMemProps);

    uint32_t* data = (uint32_t*)buffer.Memory().Map();
    data[0] = 1;

    OneOffDescriptorSet descriptor_set(m_device, {
                                                     {0, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_ALL, nullptr},
                                                 });
    const vkt::PipelineLayout pipeline_layout(*m_device, {&descriptor_set.layout_});
    descriptor_set.WriteDescriptorBufferInfo(0, buffer, 0, VK_WHOLE_SIZE, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER);
    descriptor_set.UpdateDescriptorSets();

    // Simple shader with added MemoryModel capability
    //
    // layout(set=0, binding=0) buffer InOut {
    //   uint x;
    //   uint bar[8];
    // } foo;
    // void main() {
    //     foo.bar[0] = foo.bar[foo.x];
    // }
    const char* cs_source = R"(
               OpCapability Shader
               OpCapability VulkanMemoryModel
               OpCapability PhysicalStorageBufferAddresses
          %1 = OpExtInstImport "GLSL.std.450"
               OpMemoryModel PhysicalStorageBuffer64 Vulkan
               OpEntryPoint GLCompute %main "main" %foo
               OpExecutionMode %main LocalSize 1 1 1
               OpDecorate %_arr_uint_uint_8 ArrayStride 4
               OpMemberDecorate %InOut 0 Offset 0
               OpMemberDecorate %InOut 1 Offset 4
               OpDecorate %InOut Block
               OpDecorate %foo DescriptorSet 0
               OpDecorate %foo Binding 0
       %void = OpTypeVoid
          %3 = OpTypeFunction %void
       %uint = OpTypeInt 32 0
     %uint_8 = OpConstant %uint 8
%_arr_uint_uint_8 = OpTypeArray %uint %uint_8
      %InOut = OpTypeStruct %uint %_arr_uint_uint_8
%_ptr_StorageBuffer_InOut = OpTypePointer StorageBuffer %InOut
        %foo = OpVariable %_ptr_StorageBuffer_InOut StorageBuffer
        %int = OpTypeInt 32 1
      %int_1 = OpConstant %int 1
      %int_0 = OpConstant %int 0
%_ptr_StorageBuffer_uint = OpTypePointer StorageBuffer %uint
       %main = OpFunction %void None %3
          %5 = OpLabel
         %16 = OpAccessChain %_ptr_StorageBuffer_uint %foo %int_0
         %17 = OpLoad %uint %16
         %18 = OpAccessChain %_ptr_StorageBuffer_uint %foo %int_1 %17
         %19 = OpLoad %uint %18
         %20 = OpAccessChain %_ptr_StorageBuffer_uint %foo %int_1 %int_0
               OpStore %20 %19
               OpReturn
               OpFunctionEnd
        )";

    CreateComputePipelineHelper pipe(*this);
    pipe.cs_ = VkShaderObj(*m_device, cs_source, VK_SHADER_STAGE_COMPUTE_BIT, SPV_ENV_VULKAN_1_2, SPV_SOURCE_ASM);
    pipe.cp_ci_.layout = pipeline_layout;
    pipe.CreateComputePipeline();

    m_command_buffer.Begin();
    vk::CmdBindPipeline(m_command_buffer, VK_PIPELINE_BIND_POINT_COMPUTE, pipe);
    vk::CmdBindDescriptorSets(m_command_buffer, VK_PIPELINE_BIND_POINT_COMPUTE, pipeline_layout, 0, 1, &descriptor_set.set_, 0,
                              nullptr);
    vk::CmdDispatch(m_command_buffer, 1, 1, 1);
    m_command_buffer.End();

    m_default_queue->SubmitAndWait(m_command_buffer);
}

TEST_F(PositiveGpuAVSpirv, FindMultipleStores) {
    TEST_DESCRIPTION("Catches bug when various OpStore are in top of a function");
    RETURN_IF_SKIP(InitGpuAvFramework());
    RETURN_IF_SKIP(InitState());

    const char shader_source[] = R"glsl(
      #version 450
      layout(set = 0, binding = 0) buffer StorageBuffer { uint data[]; } Data;  // data[4]

      layout(local_size_x = 1) in;
      int foo() {
            return (gl_WorkGroupSize.x > 1) ? 1 : 0;
      }

      void main() {
            int index = foo(); // first OpStore is not instrumented
            Data.data[index] = 0xdeadca71;
            Data.data[index + 1] = 0xdeadca71;
      }
    )glsl";

    CreateComputePipelineHelper pipe(*this);
    pipe.dsl_bindings_[0] = {0, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_ALL, nullptr};
    pipe.cs_ = VkShaderObj(*m_device, shader_source, VK_SHADER_STAGE_COMPUTE_BIT);
    pipe.CreateComputePipeline();

    vkt::Buffer write_buffer(*m_device, 64, VK_BUFFER_USAGE_STORAGE_BUFFER_BIT, kHostVisibleMemProps);
    pipe.descriptor_set_.WriteDescriptorBufferInfo(0, write_buffer, 0, VK_WHOLE_SIZE, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER);
    pipe.descriptor_set_.UpdateDescriptorSets();

    m_command_buffer.Begin();
    vk::CmdBindPipeline(m_command_buffer, VK_PIPELINE_BIND_POINT_COMPUTE, pipe);
    vk::CmdBindDescriptorSets(m_command_buffer, VK_PIPELINE_BIND_POINT_COMPUTE, pipe.pipeline_layout_, 0, 1,
                              &pipe.descriptor_set_.set_, 0, nullptr);
    vk::CmdDispatch(m_command_buffer, 1, 1, 1);
    m_command_buffer.End();

    m_default_queue->SubmitAndWait(m_command_buffer);
}

TEST_F(PositiveGpuAVSpirv, UniformWithoutAccessChain) {
    TEST_DESCRIPTION("dEQP-VK.spirv_assembly.instruction.compute.composite_insert.nested_struct");
    RETURN_IF_SKIP(InitGpuAvFramework());
    RETURN_IF_SKIP(InitState());

    const char* spv_source = R"(
               OpCapability Shader
          %1 = OpExtInstImport "GLSL.std.450"
               OpMemoryModel Logical GLSL450
               OpEntryPoint GLCompute %main "main"
               OpExecutionMode %main LocalSize 1 1 1
               OpSource GLSL 430
               OpDecorate %_arr_mat4v4f32_uint_8 ArrayStride 64
               OpMemberDecorate %S 0 ColMajor
               OpMemberDecorate %S 0 Offset 0
               OpMemberDecorate %S 0 MatrixStride 16
               OpMemberDecorate %Output 0 Offset 0
               OpDecorate %Output BufferBlock
               OpDecorate %dataOutput DescriptorSet 0
               OpDecorate %dataOutput Binding 0
        %f32 = OpTypeFloat 32
      %v4f32 = OpTypeVector %f32 4
  %mat4v4f32 = OpTypeMatrix %v4f32 4
       %uint = OpTypeInt 32 0
     %uint_8 = OpConstant %uint 8
%_arr_mat4v4f32_uint_8 = OpTypeArray %mat4v4f32 %uint_8
          %S = OpTypeStruct %_arr_mat4v4f32_uint_8
     %Output = OpTypeStruct %S
%_ptr_Uniform_Output = OpTypePointer Uniform %Output
%_ptr_Function_Output = OpTypePointer Function %Output
 %dataOutput = OpVariable %_ptr_Uniform_Output Uniform
    %c_f32_0 = OpConstant %f32 0
    %c_f32_1 = OpConstant %f32 1
 %identity0 = OpConstantComposite %v4f32 %c_f32_1 %c_f32_0 %c_f32_0 %c_f32_0
 %identity1 = OpConstantComposite %v4f32 %c_f32_0 %c_f32_1 %c_f32_0 %c_f32_0
 %identity2 = OpConstantComposite %v4f32 %c_f32_0 %c_f32_0 %c_f32_1 %c_f32_0
 %identity3 = OpConstantComposite %v4f32 %c_f32_0 %c_f32_0 %c_f32_0 %c_f32_1
       %void = OpTypeVoid
          %3 = OpTypeFunction %void
       %main = OpFunction %void None %3
      %entry = OpLabel
%nestedstruct = OpVariable %_ptr_Function_Output Function
       %tmp0 = OpLoad %Output %nestedstruct
       %tmp1 = OpCompositeInsert %Output %identity0 %tmp0 0 0 0 0
       %tmp2 = OpCompositeInsert %Output %identity1 %tmp1 0 0 0 1
       %tmp3 = OpCompositeInsert %Output %identity2 %tmp2 0 0 0 2
       %tmp4 = OpCompositeInsert %Output %identity3 %tmp3 0 0 0 3
       %tmp5 = OpCompositeInsert %Output %identity0 %tmp4 0 0 1 0
       %tmp6 = OpCompositeInsert %Output %identity1 %tmp5 0 0 1 1
       %tmp7 = OpCompositeInsert %Output %identity2 %tmp6 0 0 1 2
       %tmp8 = OpCompositeInsert %Output %identity3 %tmp7 0 0 1 3
       %tmp9 = OpCompositeInsert %Output %identity0 %tmp8 0 0 2 0
      %tmp10 = OpCompositeInsert %Output %identity1 %tmp9 0 0 2 1
      %tmp11 = OpCompositeInsert %Output %identity2 %tmp10 0 0 2 2
      %tmp12 = OpCompositeInsert %Output %identity3 %tmp11 0 0 2 3
      %tmp13 = OpCompositeInsert %Output %identity0 %tmp12 0 0 3 0
      %tmp14 = OpCompositeInsert %Output %identity1 %tmp13 0 0 3 1
      %tmp15 = OpCompositeInsert %Output %identity2 %tmp14 0 0 3 2
      %tmp16 = OpCompositeInsert %Output %identity3 %tmp15 0 0 3 3
      %tmp17 = OpCompositeInsert %Output %identity0 %tmp16 0 0 4 0
      %tmp18 = OpCompositeInsert %Output %identity1 %tmp17 0 0 4 1
      %tmp19 = OpCompositeInsert %Output %identity2 %tmp18 0 0 4 2
      %tmp20 = OpCompositeInsert %Output %identity3 %tmp19 0 0 4 3
      %tmp21 = OpCompositeInsert %Output %identity0 %tmp20 0 0 5 0
      %tmp22 = OpCompositeInsert %Output %identity1 %tmp21 0 0 5 1
      %tmp23 = OpCompositeInsert %Output %identity2 %tmp22 0 0 5 2
      %tmp24 = OpCompositeInsert %Output %identity3 %tmp23 0 0 5 3
      %tmp25 = OpCompositeInsert %Output %identity0 %tmp24 0 0 6 0
      %tmp26 = OpCompositeInsert %Output %identity1 %tmp25 0 0 6 1
      %tmp27 = OpCompositeInsert %Output %identity2 %tmp26 0 0 6 2
      %tmp28 = OpCompositeInsert %Output %identity3 %tmp27 0 0 6 3
      %tmp29 = OpCompositeInsert %Output %identity0 %tmp28 0 0 7 0
      %tmp30 = OpCompositeInsert %Output %identity1 %tmp29 0 0 7 1
      %tmp31 = OpCompositeInsert %Output %identity2 %tmp30 0 0 7 2
      %tmp32 = OpCompositeInsert %Output %identity3 %tmp31 0 0 7 3
               OpStore %dataOutput %tmp32
               OpReturn
               OpFunctionEnd
    )";

    CreateComputePipelineHelper pipe(*this);
    pipe.dsl_bindings_[0] = {0, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_COMPUTE_BIT, nullptr};
    pipe.cs_ = VkShaderObj(*m_device, spv_source, VK_SHADER_STAGE_COMPUTE_BIT, SPV_ENV_VULKAN_1_0, SPV_SOURCE_ASM);
    pipe.CreateComputePipeline();
}

TEST_F(PositiveGpuAVSpirv, TypeSamplerAsParameter) {
    TEST_DESCRIPTION("https://github.com/KhronosGroup/Vulkan-ValidationLayers/issues/12647");
    RETURN_IF_SKIP(InitGpuAvFramework());
    RETURN_IF_SKIP(InitState());
    InitRenderTarget();

    const char* fs_source = R"(
                        OpCapability Shader
                        OpMemoryModel Logical GLSL450
                        OpEntryPoint Fragment %main "main" %frag_color
                        OpExecutionMode %main OriginUpperLeft
                        OpDecorate %frag_color Location 0
                        OpDecorate %image_var DescriptorSet 0
                        OpDecorate %image_var Binding 0
                        OpDecorate %sampler_var DescriptorSet 0
                        OpDecorate %sampler_var Binding 1
                  %void = OpTypeVoid
      %void_func_t = OpTypeFunction %void
            %float = OpTypeFloat 32
            %v2float = OpTypeVector %float 2
            %v4float = OpTypeVector %float 4
            %image_2d = OpTypeImage %float 2D 0 0 0 1 Unknown
            %sampler_t = OpTypeSampler
      %sampled_img_t = OpTypeSampledImage %image_2d
      %sampling_func_t = OpTypeFunction %v4float %sampler_t %v2float
      %image_ptr_t = OpTypePointer UniformConstant %image_2d
      %sampler_ptr_t = OpTypePointer UniformConstant %sampler_t
      %v4float_out_ptr = OpTypePointer Output %v4float
            %float_0 = OpConstant %float 0
            %coords = OpConstantComposite %v2float %float_0 %float_0
      %frag_color = OpVariable %v4float_out_ptr Output
      %image_var = OpVariable %image_ptr_t UniformConstant
      %sampler_var = OpVariable %sampler_ptr_t UniformConstant
                  %main = OpFunction %void None %void_func_t
            %main_label = OpLabel
            %smpl_load = OpLoad %sampler_t %sampler_var
            %color = OpFunctionCall %v4float %sampling_func %smpl_load %coords
                        OpStore %frag_color %color
                        OpReturn
                        OpFunctionEnd
      %sampling_func = OpFunction %v4float None %sampling_func_t
            %smpl_param = OpFunctionParameter %sampler_t
      %tex_coords = OpFunctionParameter %v2float
%sampling_func_label = OpLabel
            %img_load = OpLoad %image_2d %image_var
      %combined_img = OpSampledImage %sampled_img_t %img_load %smpl_param
            %retval = OpImageSampleImplicitLod %v4float %combined_img %tex_coords
                        OpReturnValue %retval
                        OpFunctionEnd
    )";

    OneOffDescriptorSet descriptor_set(m_device, {
                                                     {0, VK_DESCRIPTOR_TYPE_SAMPLED_IMAGE, 1, VK_SHADER_STAGE_ALL, nullptr},
                                                     {1, VK_DESCRIPTOR_TYPE_SAMPLER, 1, VK_SHADER_STAGE_ALL, nullptr},
                                                 });
    const vkt::PipelineLayout pipeline_layout(*m_device, {&descriptor_set.layout_});

    VkShaderObj fs(*m_device, fs_source, VK_SHADER_STAGE_FRAGMENT_BIT, SPV_ENV_VULKAN_1_0, SPV_SOURCE_ASM);
    CreatePipelineHelper pipe(*this);
    pipe.shader_stages_ = {pipe.vs_->GetStageCreateInfo(), fs.GetStageCreateInfo()};
    pipe.gp_ci_.layout = pipeline_layout;
    pipe.CreateGraphicsPipeline();
}

TEST_F(PositiveGpuAVSpirv, TypeSampledImageAsParameter) {
    TEST_DESCRIPTION("dEQP-VK.spirv_assembly.instruction.function_params.sampler_param");
    RETURN_IF_SKIP(InitGpuAvFramework());
    RETURN_IF_SKIP(InitState());
    InitRenderTarget();

    const char* fs_source = R"(
                           OpCapability Shader
                           OpMemoryModel Logical GLSL450
                           OpEntryPoint Fragment %main "main" %frag_color
                           OpExecutionMode %main OriginUpperLeft
                           OpDecorate %frag_color Location 0
                           OpDecorate %sampled_img_var DescriptorSet 0
                           OpDecorate %sampled_img_var Binding 0
                   %void = OpTypeVoid
            %void_func_t = OpTypeFunction %void
                  %float = OpTypeFloat 32
                %v2float = OpTypeVector %float 2
                %v4float = OpTypeVector %float 4
               %image_2d = OpTypeImage %float 2D 0 0 0 1 Unknown
             %sampled_img = OpTypeSampledImage %image_2d
        %sampling_func_t = OpTypeFunction %v4float %sampled_img %v2float
%sampled_img_uniform_ptr = OpTypePointer UniformConstant %sampled_img
        %v4float_out_ptr = OpTypePointer Output %v4float
                %float_0 = OpConstant %float 0
                 %coords = OpConstantComposite %v2float %float_0 %float_0
             %frag_color = OpVariable %v4float_out_ptr Output
        %sampled_img_var = OpVariable %sampled_img_uniform_ptr UniformConstant

                   %main = OpFunction %void None %void_func_t
             %main_label = OpLabel
             %sample_arg = OpLoad %sampled_img %sampled_img_var
                  %color = OpFunctionCall %v4float %sampling_func %sample_arg %coords
                           OpStore %frag_color %color
                           OpReturn
                           OpFunctionEnd

          %sampling_func = OpFunction %v4float None %sampling_func_t
                %sampler = OpFunctionParameter %sampled_img
            %tex_coords = OpFunctionParameter %v2float
    %sampling_func_label = OpLabel
                 %retval = OpImageSampleImplicitLod %v4float %sampler %tex_coords
                           OpReturnValue %retval
                           OpFunctionEnd
    )";

    VkShaderObj fs(*m_device, fs_source, VK_SHADER_STAGE_FRAGMENT_BIT, SPV_ENV_VULKAN_1_0, SPV_SOURCE_ASM);
    CreatePipelineHelper pipe(*this);
    pipe.shader_stages_ = {pipe.vs_->GetStageCreateInfo(), fs.GetStageCreateInfo()};
    pipe.dsl_bindings_[0] = {0, VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 1, VK_SHADER_STAGE_FRAGMENT_BIT, nullptr};
    pipe.CreateGraphicsPipeline();
}

TEST_F(PositiveGpuAVSpirv, ConstantFoldFConvert) {
    SetTargetApiVersion(VK_API_VERSION_1_1);
    AddRequiredExtensions(VK_KHR_SHADER_FLOAT_CONTROLS_EXTENSION_NAME);
    AddRequiredExtensions(VK_KHR_SHADER_FLOAT16_INT8_EXTENSION_NAME);
    AddRequiredFeature(vkt::Feature::shaderFloat16);
    AddRequiredFeature(vkt::Feature::fragmentStoresAndAtomics);
    RETURN_IF_SKIP(InitGpuAvFramework());
    RETURN_IF_SKIP(InitState());
    InitRenderTarget();

    const char* fs_source = R"(
               OpCapability Shader
               OpCapability Float16
               OpCapability RoundingModeRTE
               OpExtension "SPV_KHR_float_controls"
               OpMemoryModel Logical GLSL450
               OpEntryPoint Fragment %main "main" %frag_color
               OpExecutionMode %main OriginUpperLeft
               OpExecutionMode %main RoundingModeRTE 16
               OpDecorate %frag_color Location 0
               OpDecorate %type_u32_arr_1 ArrayStride 4
               OpMemberDecorate %SSBO_out 0 Offset 0
               OpDecorate %SSBO_out BufferBlock
               OpDecorate %ssbo_out DescriptorSet 0
               OpDecorate %ssbo_out Binding 1
       %void = OpTypeVoid
  %void_func = OpTypeFunction %void
   %type_i32 = OpTypeInt 32 1
   %type_u32 = OpTypeInt 32 0
   %type_f16 = OpTypeFloat 16
   %type_f32 = OpTypeFloat 32
%type_f16_vec2 = OpTypeVector %type_f16 2
%type_f32_vec4 = OpTypeVector %type_f32 4
%type_f32_vec4_optr = OpTypePointer Output %type_f32_vec4
   %c_i32_0 = OpConstant %type_i32 0
   %c_i32_1 = OpConstant %type_i32 1
   %c_f16_0 = OpConstant %type_f16 0.0
   %c_f32_1 = OpConstant %type_f32 1.0
     %c_arg = OpConstant %type_f32 !1198739455
    %result = OpSpecConstantOp %type_f16 FConvert %c_arg
%type_u32_arr_1 = OpTypeArray %type_u32 %c_i32_1
%type_u32_uptr = OpTypePointer Uniform %type_u32
  %SSBO_out = OpTypeStruct %type_u32_arr_1
%up_SSBO_out = OpTypePointer Uniform %SSBO_out
 %frag_color = OpVariable %type_f32_vec4_optr Output
  %ssbo_out = OpVariable %up_SSBO_out Uniform
      %main = OpFunction %void None %void_func
     %label = OpLabel
     %color = OpCompositeConstruct %type_f32_vec4 %c_f32_1 %c_f32_1 %c_f32_1 %c_f32_1
              OpStore %frag_color %color
%result_f16_vec2 = OpCompositeConstruct %type_f16_vec2 %result %c_f16_0
%result_u32 = OpBitcast %type_u32 %result_f16_vec2
    %outloc = OpAccessChain %type_u32_uptr %ssbo_out %c_i32_0 %c_i32_0
              OpStore %outloc %result_u32
              OpReturn
              OpFunctionEnd
    )";

    VkShaderObj fs(*m_device, fs_source, VK_SHADER_STAGE_FRAGMENT_BIT, SPV_ENV_VULKAN_1_1, SPV_SOURCE_ASM);
    CreatePipelineHelper pipe(*this);
    pipe.shader_stages_ = {pipe.vs_->GetStageCreateInfo(), fs.GetStageCreateInfo()};
    pipe.dsl_bindings_ = {{1, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER, 1, VK_SHADER_STAGE_FRAGMENT_BIT, nullptr}};
    pipe.CreateGraphicsPipeline();
}