Merge vk-gl-cts/master into vk-gl-cts/vulkan-cts-next-dev
authorAlexander Galazin <alexander.galazin@arm.com>
Tue, 25 Sep 2018 16:18:49 +0000 (18:18 +0200)
committerAlexander Galazin <alexander.galazin@arm.com>
Tue, 25 Sep 2018 17:49:42 +0000 (19:49 +0200)
Change-Id: Icea9eb43ccaca29c7f381078684800723ac9376c

12 files changed:
1  2 
external/fetch_sources.py
external/vulkancts/modules/vulkan/shaderexecutor/vktShaderExecutor.cpp
external/vulkancts/modules/vulkan/spirv_assembly/vktSpvAsm16bitStorageTests.cpp
external/vulkancts/modules/vulkan/spirv_assembly/vktSpvAsmComputeShaderCase.cpp
external/vulkancts/modules/vulkan/spirv_assembly/vktSpvAsmComputeShaderTestUtil.hpp
external/vulkancts/modules/vulkan/spirv_assembly/vktSpvAsmFloatControlsTests.cpp
external/vulkancts/modules/vulkan/spirv_assembly/vktSpvAsmGraphicsShaderTestUtil.cpp
external/vulkancts/modules/vulkan/spirv_assembly/vktSpvAsmLoopDepInfTests.cpp
external/vulkancts/modules/vulkan/spirv_assembly/vktSpvAsmLoopDepLenTests.cpp
external/vulkancts/modules/vulkan/spirv_assembly/vktSpvAsmSpirvVersionTests.cpp
external/vulkancts/modules/vulkan/spirv_assembly/vktSpvAsmUtils.hpp
external/vulkancts/modules/vulkan/vktTestCase.cpp

@@@ -250,19 -250,19 +250,19 @@@ PACKAGES = 
                "libpng",
                postExtract = postExtractLibpng),
        GitRepo(
--              "https://github.com/KhronosGroup/SPIRV-Tools.git",
--              None,
-               "a9d32682334f15efe4a95929134db35d23b138b4",
 -              "90a12b3d4d1d589b3d61c194f983eb9c22459226",
++              "https://gitlab.khronos.org/spirv/spirv-tools.git",
++              "git@gitlab.khronos.org:spirv/spirv-tools.git",
++              "d48d559aec2195c9381c01f9b50a49977687dea4",
                "spirv-tools"),
        GitRepo(
                "https://github.com/KhronosGroup/glslang.git",
                None,
 -              "d122a72852900eed187fa95bdebf97a70bf6af2",
 +              "92fab62362fb0ed5d7a64ac15c2a0c5a249731df",
                "glslang"),
        GitRepo(
 -              "https://github.com/KhronosGroup/SPIRV-Headers.git",
 -              None,
 -              "d5b2e1255f706ce1f88812217e9a554f299848af",
 +              "https://gitlab.khronos.org/spirv/SPIRV-Headers.git",
 +              "git@gitlab.khronos.org:spirv/SPIRV-Headers.git",
-               "b3cc134a48f4f9563f333267c89e3a939e61d5e4",
++              "bb81abe5bc55c93055a1b2041b86515650456483",
                "spirv-headers"),
  ]
  
@@@ -2020,315 -1717,9 +2020,315 @@@ std::string sclarComparison(const std::
        return src.str();
  }
  
 +std::string generateSpirv(const ShaderSpec& spec, const bool are16Bit, const bool isMediump)
 +{
 +      const int                       operationAmount = 10;
 +      int                                     moveBitNdx              = 0;
 +      const std::string       inputType1              = getTypeSpirv(spec.inputs[0].varType.getBasicType());
 +      const std::string       inputType2              = getTypeSpirv(spec.inputs[1].varType.getBasicType());
 +      const std::string       outputType              = getTypeSpirv(spec.outputs[0].varType.getBasicType());
 +      const std::string       packType                = spec.packFloat16Bit ? getTypeSpirv(getDataTypeFloat16Scalars(spec.inputs[0].varType.getBasicType())) : "";
 +
 +      std::string     opeartions[operationAmount]     =
 +      {
 +              "OpFOrdEqual",
 +              "OpFOrdGreaterThan",
 +              "OpFOrdLessThan",
 +              "OpFOrdGreaterThanEqual",
 +              "OpFOrdLessThanEqual",
 +              "OpFUnordEqual",
 +              "OpFUnordGreaterThan",
 +              "OpFUnordLessThan",
 +              "OpFUnordGreaterThanEqual",
 +              "OpFUnordLessThanEqual"
 +      };
 +
 +      std::ostringstream      src;
 +      src << "; SPIR-V\n"
 +              "; Version: 1.0\n"
 +              "; Generator: Khronos Glslang Reference Front End; 4\n"
 +              "; Bound: 114\n"
 +              "; Schema: 0\n"
 +              "OpCapability Shader\n";
 +
 +      if (spec.packFloat16Bit || are16Bit)
 +              src << "OpCapability Float16\n";
 +
 +      if (are16Bit)
 +              src << "OpCapability StorageBuffer16BitAccess\n"
 +                      "OpCapability UniformAndStorageBuffer16BitAccess\n";
 +
 +      if (spec.packFloat16Bit || are16Bit)
 +              src << "OpExtension \"SPV_AMD_gpu_shader_half_float\"\n";
 +
 +      if (are16Bit)
 +              src << "OpExtension \"SPV_AMD_gpu_shader_half_float\"\n"
 +                      "OpExtension \"SPV_KHR_16bit_storage\"\n";
 +
 +      src << "%1 = OpExtInstImport \"GLSL.std.450\"\n"
 +              "OpMemoryModel Logical GLSL450\n"
 +              "OpEntryPoint GLCompute %BP_main \"main\" %BP_id3uNum %BP_id3uID\n"
 +              "OpExecutionMode %BP_main LocalSize 1 1 1\n"
 +              "OpDecorate %BP_id3uNum BuiltIn NumWorkgroups\n"
 +              "OpDecorate %BP_id3uID BuiltIn WorkgroupId\n";
 +
 +      //input offset
 +      {
 +              int offset = 0;
 +              int ndx = 0;
 +              for (vector<Symbol>::const_iterator symIter = spec.inputs.begin(); symIter != spec.inputs.end(); ++symIter)
 +              {
 +                      src << "OpMemberDecorate %SSB0_IN "<< ndx <<" Offset " << offset << "\n";
 +                      ++ndx;
 +                      offset += (symIter->varType.getScalarSize() == 3 ? 4 : symIter->varType.getScalarSize()) * (isDataTypeFloat16OrVec(symIter->varType.getBasicType()) ? (int)sizeof(deUint16) : (int)sizeof(deUint32));
 +              }
 +              src << "OpDecorate %up_SSB0_IN ArrayStride "<< offset << "\n";
 +      }
 +
 +      src << "OpMemberDecorate %ssboIN 0 Offset 0\n"
 +              "OpDecorate %ssboIN BufferBlock\n"
 +              "OpDecorate %ssbo_src DescriptorSet 0\n"
 +              "OpDecorate %ssbo_src Binding 0\n"
 +              "\n";
 +
 +      if (isMediump)
 +      {
 +              src << "OpMemberDecorate %SSB0_IN 1 RelaxedPrecision\n"
 +                      "OpDecorate %in0 RelaxedPrecision\n"
 +                      "OpMemberDecorate %SSB0_IN 0 RelaxedPrecision\n"
 +                      "OpDecorate %src_val_0_0 RelaxedPrecision\n"
 +                      "OpDecorate %src_val_0_0 RelaxedPrecision\n"
 +                      "OpDecorate %in1 RelaxedPrecision\n"
 +                      "OpDecorate %src_val_0_1 RelaxedPrecision\n"
 +                      "OpDecorate %src_val_0_1 RelaxedPrecision\n"
 +                      "OpDecorate %in0_val RelaxedPrecision\n"
 +                      "OpDecorate %in1_val RelaxedPrecision\n"
 +                      "OpDecorate %in0_val RelaxedPrecision\n"
 +                      "OpDecorate %in1_val RelaxedPrecision\n"
 +                      "OpMemberDecorate %SSB0_OUT 0 RelaxedPrecision\n";
 +      }
 +
 +      //output offset
 +      {
 +              int offset = 0;
 +              int ndx = 0;
 +              for (vector<Symbol>::const_iterator symIter = spec.outputs.begin(); symIter != spec.outputs.end(); ++symIter)
 +              {
 +                      src << "OpMemberDecorate %SSB0_OUT " << ndx << " Offset " << offset << "\n";
 +                      ++ndx;
 +                      offset += (symIter->varType.getScalarSize() == 3 ? 4 : symIter->varType.getScalarSize()) * (isDataTypeFloat16OrVec(symIter->varType.getBasicType()) ? (int)sizeof(deUint16) : (int)sizeof(deUint32));
 +              }
 +              src << "OpDecorate %up_SSB0_OUT ArrayStride " << offset << "\n";
 +      }
 +
 +      src << "OpMemberDecorate %ssboOUT 0 Offset 0\n"
 +              "OpDecorate %ssboOUT BufferBlock\n"
 +              "OpDecorate %ssbo_dst DescriptorSet 0\n"
 +              "OpDecorate %ssbo_dst Binding 1\n"
 +              "\n"
 +              "%void  = OpTypeVoid\n"
 +              "%bool  = OpTypeBool\n"
 +              "%v2bool = OpTypeVector %bool 2\n"
 +              "%v3bool = OpTypeVector %bool 3\n"
 +              "%v4bool = OpTypeVector %bool 4\n"
 +              "%u32   = OpTypeInt 32 0\n";
 +
 +      if (!are16Bit) //f32 is not needed when shader operates only on f16
 +              src << "%f32   = OpTypeFloat 32\n"
 +                      "%v2f32 = OpTypeVector %f32 2\n"
 +                      "%v3f32 = OpTypeVector %f32 3\n"
 +                      "%v4f32 = OpTypeVector %f32 4\n";
 +
 +      if (spec.packFloat16Bit || are16Bit)
 +              src << "%f16   = OpTypeFloat 16\n"
 +                      "%v2f16 = OpTypeVector %f16 2\n"
 +                      "%v3f16 = OpTypeVector %f16 3\n"
 +                      "%v4f16 = OpTypeVector %f16 4\n";
 +
 +      src << "%i32   = OpTypeInt 32 1\n"
 +              "%v2i32 = OpTypeVector %i32 2\n"
 +              "%v3i32 = OpTypeVector %i32 3\n"
 +              "%v4i32 = OpTypeVector %i32 4\n"
 +              "%v3u32 = OpTypeVector %u32 3\n"
 +              "\n"
 +              "%ip_u32   = OpTypePointer Input %u32\n"
 +              "%ip_v3u32 = OpTypePointer Input %v3u32\n"
 +              "%up_float   = OpTypePointer Uniform " << inputType1 << "\n"
 +              "\n"
 +              "%fun     = OpTypeFunction %void\n"
 +              "%fp_u32  = OpTypePointer Function %u32\n"
 +              "%fp_i32  = OpTypePointer Function " << outputType << "\n"
 +              "%fp_f32  = OpTypePointer Function " << inputType1 << "\n"
 +              "%fp_operation =  OpTypePointer Function %i32\n";
 +
 +      if (spec.packFloat16Bit)
 +              src << "%fp_f16  = OpTypePointer Function " << packType << "\n";
 +
 +      src << "%BP_id3uID = OpVariable %ip_v3u32 Input\n"
 +              "%BP_id3uNum = OpVariable %ip_v3u32 Input\n"
 +              "%up_i32 = OpTypePointer Uniform " << outputType << "\n"
 +              "\n"
 +              "%c_u32_0 = OpConstant %u32 0\n"
 +              "%c_u32_1 = OpConstant %u32 1\n"
 +              "%c_u32_2 = OpConstant %u32 2\n"
 +              "%c_i32_0 = OpConstant %i32 0\n"
 +              "%c_i32_1 = OpConstant %i32 1\n"
 +              "%c_v2i32_0 = OpConstantComposite %v2i32 %c_i32_0 %c_i32_0\n"
 +              "%c_v2i32_1 = OpConstantComposite %v2i32 %c_i32_1 %c_i32_1\n"
 +              "%c_v3i32_0 = OpConstantComposite %v3i32 %c_i32_0 %c_i32_0 %c_i32_0\n"
 +              "%c_v3i32_1 = OpConstantComposite %v3i32 %c_i32_1 %c_i32_1 %c_i32_1\n"
 +              "%c_v4i32_0 = OpConstantComposite %v4i32 %c_i32_0 %c_i32_0 %c_i32_0 %c_i32_0\n"
 +              "%c_v4i32_1 = OpConstantComposite %v4i32 %c_i32_1 %c_i32_1 %c_i32_1 %c_i32_1\n"
 +              "\n"
 +              "%SSB0_IN    = OpTypeStruct " << inputType1 << " " << inputType2 << "\n"
 +              "%up_SSB0_IN = OpTypeRuntimeArray %SSB0_IN\n"
 +              "%ssboIN     = OpTypeStruct %up_SSB0_IN\n"
 +              "%up_ssboIN  = OpTypePointer Uniform %ssboIN\n"
 +              "%ssbo_src   = OpVariable %up_ssboIN Uniform\n"
 +              "\n"
 +              "%SSB0_OUT    = OpTypeStruct " << outputType << "\n"
 +              "%up_SSB0_OUT = OpTypeRuntimeArray %SSB0_OUT\n"
 +              "%ssboOUT     = OpTypeStruct %up_SSB0_OUT\n"
 +              "%up_ssboOUT  = OpTypePointer Uniform %ssboOUT\n"
 +              "%ssbo_dst    = OpVariable %up_ssboOUT Uniform\n"
 +              "\n"
 +              "%BP_main = OpFunction %void None %fun\n"
 +              "%BP_label = OpLabel\n"
 +              "%invocationNdx = OpVariable  %fp_u32 Function\n";
 +
 +      if (spec.packFloat16Bit)
 +              src << "%in0 = OpVariable %fp_f16 Function\n"
 +                      "%in1 = OpVariable %fp_f16 Function\n";
 +      else
 +              src << "%in0 = OpVariable %fp_f32 Function\n"
 +                      "%in1 = OpVariable %fp_f32 Function\n";
 +
 +      src << "%operation = OpVariable %fp_operation Function\n"
 +              "%out = OpVariable %fp_i32 Function\n"
 +              "%BP_id_0_ptr  = OpAccessChain %ip_u32 %BP_id3uID %c_u32_0\n"
 +              "%BP_id_1_ptr  = OpAccessChain %ip_u32 %BP_id3uID %c_u32_1\n"
 +              "%BP_id_2_ptr  = OpAccessChain %ip_u32 %BP_id3uID %c_u32_2\n"
 +              "%BP_num_0_ptr  = OpAccessChain %ip_u32 %BP_id3uNum %c_u32_0\n"
 +              "%BP_num_1_ptr  = OpAccessChain %ip_u32 %BP_id3uNum %c_u32_1\n"
 +              "%BP_id_0_val = OpLoad %u32 %BP_id_0_ptr\n"
 +              "%BP_id_1_val = OpLoad %u32 %BP_id_1_ptr\n"
 +              "%BP_id_2_val = OpLoad %u32 %BP_id_2_ptr\n"
 +              "%BP_num_0_val = OpLoad %u32 %BP_num_0_ptr\n"
 +              "%BP_num_1_val = OpLoad %u32 %BP_num_1_ptr\n"
 +              "\n"
 +              "%mul_1 = OpIMul %u32 %BP_num_0_val %BP_num_1_val\n"
 +              "%mul_2 = OpIMul %u32 %mul_1 %BP_id_2_val\n"
 +              "%mul_3 = OpIMul %u32 %BP_num_0_val %BP_id_1_val\n"
 +              "%add_1 = OpIAdd %u32 %mul_2 %mul_3\n"
 +              "%add_2 = OpIAdd %u32 %add_1 %BP_id_0_val\n"
 +              "OpStore %invocationNdx %add_2\n"
 +              "%invocationNdx_val = OpLoad %u32 %invocationNdx\n"
 +              "\n"
 +              "%src_ptr_0_0 = OpAccessChain %up_float %ssbo_src %c_i32_0 %invocationNdx_val %c_i32_0\n"
 +              "%src_val_0_0 = OpLoad " << inputType1 << " %src_ptr_0_0\n";
 +
 +      if(spec.packFloat16Bit)
 +              src << "%val_f16_0_0 = OpFConvert " << packType <<" %src_val_0_0\n"
 +                      "OpStore %in0 %val_f16_0_0\n";
 +      else
 +              src << "OpStore %in0 %src_val_0_0\n";
 +
 +      src << "\n"
 +              "%src_ptr_0_1 = OpAccessChain %up_float %ssbo_src %c_i32_0 %invocationNdx_val %c_i32_1\n"
 +              "%src_val_0_1 = OpLoad " << inputType2 << " %src_ptr_0_1\n";
 +
 +      if (spec.packFloat16Bit)
 +              src << "%val_f16_0_1 = OpFConvert " << packType << " %src_val_0_1\n"
 +                      "OpStore %in1 %val_f16_0_1\n";
 +      else
 +              src << "OpStore %in1 %src_val_0_1\n";
 +
 +      src << "\n"
 +              "OpStore %operation %c_i32_1\n"
 +              "OpStore %out %c_" << &outputType[1] << "_0\n"
 +              "\n";
 +
 +      if (spec.packFloat16Bit)
 +              src << "%in0_val = OpLoad " << packType << " %in0\n"
 +                      "%in1_val = OpLoad " << packType << " %in1\n";
 +      else
 +              src << "%in0_val = OpLoad " << inputType1 << " %in0\n"
 +                      "%in1_val = OpLoad " << inputType2 << " %in1\n";
 +
 +      src << "\n";
 +      for(int operationNdx = 0; operationNdx < operationAmount; ++operationNdx)
 +      {
 +              src << sclarComparison  (opeartions[operationNdx], operationNdx,
 +                                                              spec.inputs[0].varType.getBasicType(),
 +                                                              outputType,
 +                                                              spec.outputs[0].varType.getScalarSize());
 +              src << moveBitOperation("%operation", moveBitNdx);
 +              ++moveBitNdx;
 +      }
 +
 +      src << "\n"
 +              "%out_val_final = OpLoad " << outputType << " %out\n"
 +              "%ssbo_dst_ptr = OpAccessChain %up_i32 %ssbo_dst %c_i32_0 %invocationNdx_val %c_i32_0\n"
 +              "OpStore %ssbo_dst_ptr %out_val_final\n"
 +              "\n"
 +              "OpReturn\n"
 +              "OpFunctionEnd\n";
 +      return src.str();
 +}
 +
 +
 +std::string ComputeShaderExecutor::generateComputeShader (const ShaderSpec& spec)
 +{
 +      if(spec.spirVShader)
 +      {
 +              bool    are16Bit        = false;
 +              bool    isMediump       = false;
 +              for (vector<Symbol>::const_iterator symIter = spec.inputs.begin(); symIter != spec.inputs.end(); ++symIter)
 +              {
 +                      if (glu::isDataTypeFloat16OrVec(symIter->varType.getBasicType()))
 +                              are16Bit = true;
 +
 +                      if (symIter->varType.getPrecision() == glu::PRECISION_MEDIUMP)
 +                              isMediump = true;
 +
 +                      if(isMediump && are16Bit)
 +                              break;
 +              }
 +
 +              return generateSpirv(spec, are16Bit, isMediump);
 +      }
 +      else
 +      {
 +              std::ostringstream src;
 +              src << glu::getGLSLVersionDeclaration(spec.glslVersion) << "\n";
 +
 +              if (!spec.globalDeclarations.empty())
 +                      src << spec.globalDeclarations << "\n";
 +
 +              src << "layout(local_size_x = 1) in;\n"
 +                      << "\n";
 +
 +              declareBufferBlocks(src, spec);
 +
 +              src << "void main (void)\n"
 +                      << "{\n"
 +                      << "    uint invocationNdx = gl_NumWorkGroups.x*gl_NumWorkGroups.y*gl_WorkGroupID.z\n"
 +                      << "                       + gl_NumWorkGroups.x*gl_WorkGroupID.y + gl_WorkGroupID.x;\n";
 +
 +              generateExecBufferIo(src, spec, "invocationNdx");
 +
 +              src << "}\n";
 +
 +              return src.str();
 +      }
 +}
 +
  void ComputeShaderExecutor::generateSources (const ShaderSpec& shaderSpec, SourceCollections& programCollection)
  {
 -      programCollection.glslSources.add("compute") << glu::ComputeSource(generateComputeShader(shaderSpec)) << shaderSpec.buildOptions;
 +      if(shaderSpec.spirVShader)
-               programCollection.spirvAsmSources.add("compute") << SpirVAsmBuildOptions(SPIRV_VERSION_1_3) << generateComputeShader(shaderSpec);
++              programCollection.spirvAsmSources.add("compute") << SpirVAsmBuildOptions(programCollection.usedVulkanVersion, SPIRV_VERSION_1_3) << generateComputeShader(shaderSpec);
 +      else
 +              programCollection.glslSources.add("compute") << glu::ComputeSource(generateComputeShader(shaderSpec)) << shaderSpec.buildOptions;
  }
  
  void ComputeShaderExecutor::execute (int numValues, const void* const* inputs, void* const* outputs, VkDescriptorSet extraResources)
index be31108,0000000..4e656b8
mode 100644,000000..100644
--- /dev/null
@@@ -1,3141 -1,0 +1,3141 @@@
-       SpirvVersion targetSpirvVersion;
-       targetSpirvVersion = context.resources.spirvVersion;
 +/*-------------------------------------------------------------------------
 + * Vulkan Conformance Tests
 + * ------------------------
 + *
 + * Copyright (c) 2018 The Khronos Group 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.
 + *
 + *//*!
 + * \file
 + * \brief VK_KHR_shader_float_controls tests.
 + *//*--------------------------------------------------------------------*/
 +
 +
 +#include "vktSpvAsmFloatControlsTests.hpp"
 +#include "vktSpvAsmComputeShaderCase.hpp"
 +#include "vktSpvAsmGraphicsShaderTestUtil.hpp"
 +#include "vktTestGroupUtil.hpp"
 +#include "tcuFloat.hpp"
 +#include "tcuFloatFormat.hpp"
 +#include "tcuStringTemplate.hpp"
 +#include "deUniquePtr.hpp"
 +#include "deFloat16.h"
 +#include "vkRefUtil.hpp"
 +#include <vector>
 +#include <limits>
 +#include <fenv.h>
 +
 +namespace vkt
 +{
 +namespace SpirVAssembly
 +{
 +
 +namespace
 +{
 +
 +using namespace std;
 +using namespace tcu;
 +
 +enum FloatType
 +{
 +      FP16 = 0,
 +      FP32,
 +      FP64
 +};
 +
 +// Enum containing float behaviors that its possible to test.
 +enum BehaviorId
 +{
 +      B_DENORM_PERSERVE = 0,  // DenormPreserve
 +      B_DENORM_FLUSH,                 // DenormFlushToZero
 +      B_ZIN_PERSERVE,                 // SignedZeroInfNanPreserve
 +      B_RTE_ROUNDING,                 // RoundingModeRTE
 +      B_RTZ_ROUNDING,                 // RoundingModeRTZ
 +};
 +
 +// Codes for all float values used in tests as arguments and operation results
 +// This approach allows to replace values with different types reducing complexity of the tests implementation
 +enum ValueId
 +{
 +      // common values used as both arguments and results
 +      V_UNUSED = 0,           //  used to mark arguments that are not used in operation
 +      V_MINUS_INF,            //    or results of tests cases that should be skipped
 +      V_MINUS_ONE,            // -1.0
 +      V_MINUS_ZERO,           // -0.0
 +      V_ZERO,                         //  0.0
 +      V_HALF,                         //  0.5
 +      V_ONE,                          //  1.0
 +      V_INF,
 +      V_DENORM,
 +      V_NAN,
 +
 +      // arguments for rounding mode tests - used only when arguments are passed from input
 +      V_ADD_ARG_A,
 +      V_ADD_ARG_B,
 +      V_SUB_ARG_A,
 +      V_SUB_ARG_B,
 +      V_MUL_ARG_A,
 +      V_MUL_ARG_B,
 +      V_DOT_ARG_A,
 +      V_DOT_ARG_B,
 +
 +      // arguments of conversion operations - used only when arguments are passed from input
 +      V_CONV_FROM_FP32_ARG,
 +      V_CONV_FROM_FP64_ARG,
 +
 +      // arguments of rounding operations
 +      V_ADD_RTZ_RESULT,
 +      V_ADD_RTE_RESULT,
 +      V_SUB_RTZ_RESULT,
 +      V_SUB_RTE_RESULT,
 +      V_MUL_RTZ_RESULT,
 +      V_MUL_RTE_RESULT,
 +      V_DOT_RTZ_RESULT,
 +      V_DOT_RTE_RESULT,
 +
 +      // non comon results of some operation
 +      V_MINUS_ONE_OR_CLOSE,                   // value used only fur fp16 subtraction result of preserved denorm and one
 +      V_PI_DIV_2,
 +      V_ZERO_OR_MINUS_ZERO,                   // both +0 and -0 are accepted
 +      V_ZERO_OR_FP16_DENORM_TO_FP32,  // both 0 and fp32 representation of fp16 denorm are accepted
 +      V_ZERO_OR_FP16_DENORM_TO_FP64,
 +      V_ZERO_OR_FP32_DENORM_TO_FP64,
 +      V_DENORM_TIMES_TWO,
 +      V_DEGREES_DENORM,
 +
 +      //results of conversion operations
 +      V_CONV_TO_FP16_RTZ_RESULT,
 +      V_CONV_TO_FP16_RTE_RESULT,
 +      V_CONV_TO_FP32_RTZ_RESULT,
 +      V_CONV_TO_FP32_RTE_RESULT,
 +      V_CONV_DENORM_SMALLER,  // used e.g. when converting fp16 denorm to fp32
 +      V_CONV_DENORM_BIGGER,
 +};
 +
 +// Enum containing all tested operatios. Operations are defined in generic way so that
 +// they can be used to generate tests operating on arguments with different values of
 +// specified float type.
 +enum OperationId
 +{
 +      // spir-v unary operations
 +      O_NEGATE = 0,
 +      O_COMPOSITE,
 +      O_COMPOSITE_INS,
 +      O_COPY,
 +      O_D_EXTRACT,
 +      O_D_INSERT,
 +      O_SHUFFLE,
 +      O_TRANSPOSE,
 +      O_CONV_FROM_FP16,
 +      O_CONV_FROM_FP32,
 +      O_CONV_FROM_FP64,
 +      O_SCONST_CONV_FROM_FP32_TO_FP16,
 +      O_SCONST_CONV_FROM_FP64_TO_FP32,
 +      O_SCONST_CONV_FROM_FP64_TO_FP16,
 +      O_RETURN_VAL,
 +
 +      // spir-v binary operations
 +      O_ADD,
 +      O_SUB,
 +      O_MUL,
 +      O_DIV,
 +      O_REM,
 +      O_MOD,
 +      O_PHI,
 +      O_SELECT,
 +      O_DOT,
 +      O_VEC_MUL_S,
 +      O_VEC_MUL_M,
 +      O_MAT_MUL_S,
 +      O_MAT_MUL_V,
 +      O_MAT_MUL_M,
 +      O_OUT_PROD,
 +      O_ORD_EQ,
 +      O_UORD_EQ,
 +      O_ORD_NEQ,
 +      O_UORD_NEQ,
 +      O_ORD_LS,
 +      O_UORD_LS,
 +      O_ORD_GT,
 +      O_UORD_GT,
 +      O_ORD_LE,
 +      O_UORD_LE,
 +      O_ORD_GE,
 +      O_UORD_GE,
 +
 +      // glsl unary operations
 +      O_ROUND,
 +      O_ROUND_EV,
 +      O_TRUNC,
 +      O_ABS,
 +      O_SIGN,
 +      O_FLOOR,
 +      O_CEIL,
 +      O_FRACT,
 +      O_RADIANS,
 +      O_DEGREES,
 +      O_SIN,
 +      O_COS,
 +      O_TAN,
 +      O_ASIN,
 +      O_ACOS,
 +      O_ATAN,
 +      O_SINH,
 +      O_COSH,
 +      O_TANH,
 +      O_ASINH,
 +      O_ACOSH,
 +      O_ATANH,
 +      O_EXP,
 +      O_LOG,
 +      O_EXP2,
 +      O_LOG2,
 +      O_SQRT,
 +      O_INV_SQRT,
 +      O_MODF,
 +      O_MODF_ST,
 +      O_FREXP,
 +      O_FREXP_ST,
 +      O_LENGHT,
 +      O_NORMALIZE,
 +      O_REFLECT,
 +      O_REFRACT,
 +      O_MAT_DET,
 +      O_MAT_INV,
 +      O_PH_DENORM,    // PackHalf2x16
 +      O_UPH_DENORM,
 +      O_PD_DENORM,    // PackDouble2x32
 +      O_UPD_DENORM_FLUSH,
 +      O_UPD_DENORM_PRESERVE,
 +
 +      // glsl binary operations
 +      O_ATAN2,
 +      O_POW,
 +      O_MIX,
 +      O_FMA,
 +      O_MIN,
 +      O_MAX,
 +      O_CLAMP,
 +      O_STEP,
 +      O_SSTEP,
 +      O_DIST,
 +      O_CROSS,
 +      O_FACE_FWD,
 +      O_NMIN,
 +      O_NMAX,
 +      O_NCLAMP,
 +
 +      O_ORTE_ROUND,
 +      O_ORTZ_ROUND
 +};
 +
 +// Structures storing data required to test DenormPreserve and DenormFlushToZero modes.
 +// Operations are separated into binary and unary lists because binary operations can be tested with
 +// two attributes and thus denorms can be tested in combination with value, denorm, inf and nan.
 +// Unary operations are only tested with denorms.
 +struct BinaryCase
 +{
 +      OperationId     operationId;
 +      ValueId         opVarResult;
 +      ValueId         opDenormResult;
 +      ValueId         opInfResult;
 +      ValueId         opNanResult;
 +};
 +struct UnaryCase
 +{
 +      OperationId     operationId;
 +      ValueId         result;
 +};
 +
 +// Function replacing all occurrences of substring with string passed in last parameter.
 +string replace(string str, const string& from, const string& to)
 +{
 +      // to keep spir-v code clean and easier to read parts of it are processed
 +      // with this method instead of StringTemplate; main usage of this method is the
 +      // replacement of "float_" with "f16_", "f32_" or "f64_" depending on test case
 +
 +      size_t start_pos = 0;
 +      while((start_pos = str.find(from, start_pos)) != std::string::npos)
 +      {
 +              str.replace(start_pos, from.length(), to);
 +              start_pos += to.length();
 +      }
 +      return str;
 +}
 +
 +// Structure used to perform bits conversion int type <-> float type.
 +template<typename FLOAT_TYPE, typename UINT_TYPE>
 +struct RawConvert
 +{
 +      union Value
 +      {
 +              FLOAT_TYPE      fp;
 +              UINT_TYPE       ui;
 +      };
 +};
 +
 +// Traits used to get int type that can store equivalent float type.
 +template<typename FLOAT_TYPE>
 +struct GetCoresponding
 +{
 +      typedef deUint16 uint_type;
 +};
 +template<>
 +struct GetCoresponding<float>
 +{
 +      typedef deUint32 uint_type;
 +};
 +template<>
 +struct GetCoresponding<double>
 +{
 +      typedef deUint64 uint_type;
 +};
 +
 +// All values used for arguments and operation results are stored in single map.
 +// Each float type (fp16, fp32, fp64) has its own map that is used during
 +// test setup and during verification. TypeValuesBase is interface to that map.
 +class TypeValuesBase
 +{
 +public:
 +      TypeValuesBase();
 +      virtual ~TypeValuesBase()       {}
 +
 +      virtual BufferSp        constructInputBuffer(const ValueId* twoArguments) const = 0;
 +      virtual BufferSp        constructOutputBuffer(ValueId result) const = 0;
 +
 +protected:
 +      const double    pi;
 +};
 +
 +TypeValuesBase::TypeValuesBase()
 +      : pi(3.14159265358979323846)
 +{
 +}
 +
 +typedef de::SharedPtr<TypeValuesBase> TypeValuesSP;
 +
 +template <typename FLOAT_TYPE>
 +class TypeValues: public TypeValuesBase
 +{
 +public:
 +      TypeValues();
 +
 +      BufferSp constructInputBuffer(const ValueId* twoArguments) const;
 +      BufferSp constructOutputBuffer(ValueId result) const;
 +
 +      FLOAT_TYPE getValue(ValueId id) const;
 +
 +      template <typename UINT_TYPE>
 +      FLOAT_TYPE exactByteEquivalent(UINT_TYPE byteValue) const;
 +
 +private:
 +      typedef map<ValueId, FLOAT_TYPE> ValueMap;
 +      ValueMap m_valueIdToFloatType;
 +};
 +
 +template <typename FLOAT_TYPE>
 +BufferSp TypeValues<FLOAT_TYPE>::constructInputBuffer(const ValueId* twoArguments) const
 +{
 +      std::vector<FLOAT_TYPE> inputData(2);
 +      inputData[0] = m_valueIdToFloatType.at(twoArguments[0]);
 +      inputData[1] = m_valueIdToFloatType.at(twoArguments[1]);
 +      return BufferSp(new Buffer<FLOAT_TYPE>(inputData));
 +}
 +
 +template <typename FLOAT_TYPE>
 +BufferSp TypeValues<FLOAT_TYPE>::constructOutputBuffer(ValueId result) const
 +{
 +      // note: we are not doing maping here, ValueId is directly saved in
 +      // float type in order to be able to retireve it during verification
 +
 +      typedef typename GetCoresponding<FLOAT_TYPE>::uint_type uint_t;
 +      uint_t value = static_cast<uint_t>(result);
 +
 +      std::vector<FLOAT_TYPE> outputData(1, exactByteEquivalent<uint_t>(value));
 +      return BufferSp(new Buffer<FLOAT_TYPE>(outputData));
 +}
 +
 +template <typename FLOAT_TYPE>
 +FLOAT_TYPE TypeValues<FLOAT_TYPE>::getValue(ValueId id) const
 +{
 +      return m_valueIdToFloatType.at(id);
 +}
 +
 +template <typename FLOAT_TYPE>
 +template <typename UINT_TYPE>
 +FLOAT_TYPE TypeValues<FLOAT_TYPE>::exactByteEquivalent(UINT_TYPE byteValue) const
 +{
 +      typename RawConvert<FLOAT_TYPE, UINT_TYPE>::Value value;
 +      value.ui = byteValue;
 +      return value.fp;
 +}
 +
 +template <>
 +TypeValues<deFloat16>::TypeValues()
 +      : TypeValuesBase()
 +{
 +      // NOTE: when updating entries in m_valueIdToFloatType make sure to
 +      // update also valueIdToSnippetArgMap defined in updateSpirvSnippets()
 +      ValueMap& vm = m_valueIdToFloatType;
 +      vm[V_UNUSED]                    = deFloat32To16(0.0f);
 +      vm[V_MINUS_INF]                 = 0xfc00;
 +      vm[V_MINUS_ONE]                 = deFloat32To16(-1.0f);
 +      vm[V_MINUS_ZERO]                = 0x8000;
 +      vm[V_ZERO]                              = 0x0000;
 +      vm[V_HALF]                              = deFloat32To16(0.5f);
 +      vm[V_ONE]                               = deFloat32To16(1.0f);
 +      vm[V_INF]                               = 0x7c00;
 +      vm[V_DENORM]                    = 0x03f0; // this value should be the same as the result of denormBase - epsilon
 +      vm[V_NAN]                               = 0x7cf0;
 +
 +      vm[V_PI_DIV_2]                  = 0x3e48;
 +      vm[V_DENORM_TIMES_TWO]  = 0x07e0;
 +      vm[V_DEGREES_DENORM]    = 0x1b0c;
 +
 +      vm[V_ADD_ARG_A]                                 = 0x3c03;
 +      vm[V_ADD_ARG_B]                                 = vm[V_ONE];
 +      vm[V_SUB_ARG_A]                                 = vm[V_ADD_ARG_A];
 +      vm[V_SUB_ARG_B]                                 = 0x4203;
 +      vm[V_MUL_ARG_A]                                 = vm[V_ADD_ARG_A];
 +      vm[V_MUL_ARG_B]                                 = 0x1900;
 +      vm[V_DOT_ARG_A]                                 = vm[V_ADD_ARG_A];
 +      vm[V_DOT_ARG_B]                                 = vm[V_MUL_ARG_B];
 +      vm[V_CONV_FROM_FP32_ARG]                = vm[V_UNUSED];
 +      vm[V_CONV_FROM_FP64_ARG]                = vm[V_UNUSED];
 +
 +      vm[V_ADD_RTZ_RESULT]                    = 0x4001;       // deFloat16Add(vm[V_ADD_ARG_A], vm[V_ADD_ARG_B], rtz)
 +      vm[V_SUB_RTZ_RESULT]                    = 0xc001;       // deFloat16Sub(vm[V_SUB_ARG_A], vm[V_SUB_ARG_B], rtz)
 +      vm[V_MUL_RTZ_RESULT]                    = 0x1903;       // deFloat16Mul(vm[V_MUL_ARG_A], vm[V_MUL_ARG_B], rtz)
 +      vm[V_DOT_RTZ_RESULT]                    = 0x1d03;
 +      vm[V_CONV_TO_FP16_RTZ_RESULT]   = deFloat32To16Round(1.22334445f, DE_ROUNDINGMODE_TO_ZERO);
 +      vm[V_CONV_TO_FP32_RTZ_RESULT]   = vm[V_UNUSED];
 +
 +      vm[V_ADD_RTE_RESULT]                    = 0x4002;       // deFloat16Add(vm[V_ADD_ARG_A], vm[V_ADD_ARG_B], rte)
 +      vm[V_SUB_RTE_RESULT]                    = 0xc002;       // deFloat16Sub(vm[V_SUB_ARG_A], vm[V_SUB_ARG_B], rte)
 +      vm[V_MUL_RTE_RESULT]                    = 0x1904;       // deFloat16Mul(vm[V_MUL_ARG_A], vm[V_MUL_ARG_B], rte)
 +      vm[V_DOT_RTE_RESULT]                    = 0x1d04;
 +      vm[V_CONV_TO_FP16_RTE_RESULT]   = deFloat32To16Round(1.22334445f, DE_ROUNDINGMODE_TO_NEAREST_EVEN);
 +      vm[V_CONV_TO_FP32_RTE_RESULT]   = vm[V_UNUSED];
 +
 +      // there is no precision to store fp32 denorm nor fp64 denorm
 +      vm[V_CONV_DENORM_SMALLER]               = vm[V_ZERO];
 +      vm[V_CONV_DENORM_BIGGER]                = vm[V_ZERO];
 +}
 +
 +template <>
 +TypeValues<float>::TypeValues()
 +      : TypeValuesBase()
 +{
 +      // NOTE: when updating entries in m_valueIdToFloatType make sure to
 +      // update also valueIdToSnippetArgMap defined in updateSpirvSnippets()
 +      ValueMap& vm = m_valueIdToFloatType;
 +      vm[V_UNUSED]                    =  0.0f;
 +      vm[V_MINUS_INF]                 = -std::numeric_limits<float>::infinity();
 +      vm[V_MINUS_ONE]                 = -1.0f;
 +      vm[V_MINUS_ZERO]                = -0.0f;
 +      vm[V_ZERO]                              =  0.0f;
 +      vm[V_HALF]                              =  0.5f;
 +      vm[V_ONE]                               =  1.0f;
 +      vm[V_INF]                               =  std::numeric_limits<float>::infinity();
 +      vm[V_DENORM]                    =  static_cast<float>(1.413e-42); // 0x000003f0
 +      vm[V_NAN]                               =  std::numeric_limits<float>::quiet_NaN();
 +
 +      vm[V_PI_DIV_2]                  =  static_cast<float>(pi / 2);
 +      vm[V_DENORM_TIMES_TWO]  =  vm[V_DENORM] + vm[V_DENORM];
 +      vm[V_DEGREES_DENORM]    =  deFloatDegrees(vm[V_DENORM]);
 +
 +      float e = std::numeric_limits<float>::epsilon();
 +      vm[V_ADD_ARG_A]                                 = 1.0f + 3 * e;
 +      vm[V_ADD_ARG_B]                                 = 1.0f;
 +      vm[V_SUB_ARG_A]                                 = vm[V_ADD_ARG_A];
 +      vm[V_SUB_ARG_B]                                 = 3.0f + 6 * e;
 +      vm[V_MUL_ARG_A]                                 = vm[V_ADD_ARG_A];
 +      vm[V_MUL_ARG_B]                                 = 5 * e;
 +      vm[V_DOT_ARG_A]                                 = vm[V_ADD_ARG_A];
 +      vm[V_DOT_ARG_B]                                 = 5 * e;
 +      vm[V_CONV_FROM_FP32_ARG]                = 1.22334445f;
 +      vm[V_CONV_FROM_FP64_ARG]                = vm[V_UNUSED];
 +
 +      int prevRound = fegetround();
 +      fesetround(FE_TOWARDZERO);
 +      vm[V_ADD_RTZ_RESULT]                    = vm[V_ADD_ARG_A] + vm[V_ADD_ARG_B];
 +      vm[V_SUB_RTZ_RESULT]                    = vm[V_SUB_ARG_A] - vm[V_SUB_ARG_B];
 +      vm[V_MUL_RTZ_RESULT]                    = vm[V_MUL_ARG_A] * vm[V_MUL_ARG_B];
 +      vm[V_DOT_RTZ_RESULT]                    = vm[V_MUL_RTZ_RESULT] + vm[V_MUL_RTZ_RESULT];
 +      vm[V_CONV_TO_FP16_RTZ_RESULT]   = vm[V_UNUSED];
 +      vm[V_CONV_TO_FP32_RTZ_RESULT]   = exactByteEquivalent<deUint32>(0x3f9c968d); // result of conversion from double(1.22334455)
 +
 +      fesetround(FE_TONEAREST);
 +      vm[V_ADD_RTE_RESULT]                    = vm[V_ADD_ARG_A] + vm[V_ADD_ARG_B];
 +      vm[V_SUB_RTE_RESULT]                    = vm[V_SUB_ARG_A] - vm[V_SUB_ARG_B];
 +      vm[V_MUL_RTE_RESULT]                    = vm[V_MUL_ARG_A] * vm[V_MUL_ARG_B];
 +      vm[V_DOT_RTE_RESULT]                    = vm[V_MUL_RTE_RESULT] + vm[V_MUL_RTE_RESULT];
 +      vm[V_CONV_TO_FP16_RTE_RESULT]   = vm[V_UNUSED];
 +      vm[V_CONV_TO_FP32_RTE_RESULT]   = exactByteEquivalent<deUint32>(0x3f9c968e); // result of conversion from double(1.22334455)
 +      fesetround(prevRound);
 +
 +      // there is no precision to store fp64 denorm
 +      vm[V_CONV_DENORM_SMALLER]               = exactByteEquivalent<deUint32>(0x387c0000); // fp16 denorm
 +      vm[V_CONV_DENORM_BIGGER]                = vm[V_ZERO];
 +}
 +
 +template <>
 +TypeValues<double>::TypeValues()
 +      : TypeValuesBase()
 +{
 +      // NOTE: when updating entries in m_valueIdToFloatType make sure to
 +      // update also valueIdToSnippetArgMap defined in updateSpirvSnippets()
 +      ValueMap& vm = m_valueIdToFloatType;
 +      vm[V_UNUSED]                    =  0.0;
 +      vm[V_MINUS_INF]                 = -std::numeric_limits<double>::infinity();
 +      vm[V_MINUS_ONE]                 = -1.0;
 +      vm[V_MINUS_ZERO]                = -0.0;
 +      vm[V_ZERO]                              =  0.0;
 +      vm[V_HALF]                              =  0.5;
 +      vm[V_ONE]                               =  1.0;
 +      vm[V_INF]                               =  std::numeric_limits<double>::infinity();
 +      vm[V_DENORM]                    =  4.98e-321; // 0x00000000000003F0
 +      vm[V_NAN]                               =  std::numeric_limits<double>::quiet_NaN();
 +
 +      vm[V_PI_DIV_2]                  =  pi / 2;
 +      vm[V_DENORM_TIMES_TWO]  =  vm[V_DENORM] + vm[V_DENORM];
 +      vm[V_DEGREES_DENORM]    =  vm[V_UNUSED];
 +
 +      double e = std::numeric_limits<double>::epsilon();
 +      vm[V_ADD_ARG_A]                         = 1.0 + 3 * e;
 +      vm[V_ADD_ARG_B]                         = 1.0;
 +      vm[V_SUB_ARG_A]                         = vm[V_ADD_ARG_A];
 +      vm[V_SUB_ARG_B]                         = 3.0 + 6 * e;
 +      vm[V_MUL_ARG_A]                         = vm[V_ADD_ARG_A];
 +      vm[V_MUL_ARG_B]                         = 5 * e;
 +      vm[V_DOT_ARG_A]                         = vm[V_ADD_ARG_A];
 +      vm[V_DOT_ARG_B]                         = 5 * e;
 +      vm[V_CONV_FROM_FP32_ARG]        = vm[V_UNUSED];
 +      vm[V_CONV_FROM_FP64_ARG]        = 1.22334455;
 +
 +      int prevRound = fegetround();
 +      fesetround(FE_TOWARDZERO);
 +      vm[V_ADD_RTZ_RESULT]                    = vm[V_ADD_ARG_A] + vm[V_ADD_ARG_B];
 +      vm[V_SUB_RTZ_RESULT]                    = vm[V_SUB_ARG_A] - vm[V_SUB_ARG_B];
 +      vm[V_MUL_RTZ_RESULT]                    = vm[V_MUL_ARG_A] * vm[V_MUL_ARG_B];
 +      vm[V_DOT_RTZ_RESULT]                    = vm[V_MUL_RTZ_RESULT] + vm[V_MUL_RTZ_RESULT];
 +      vm[V_CONV_TO_FP16_RTZ_RESULT]   = vm[V_UNUSED];
 +      vm[V_CONV_TO_FP32_RTZ_RESULT]   = vm[V_UNUSED];
 +
 +      fesetround(FE_TONEAREST);
 +      vm[V_ADD_RTE_RESULT]                    = vm[V_ADD_ARG_A] + vm[V_ADD_ARG_B];
 +      vm[V_SUB_RTE_RESULT]                    = vm[V_SUB_ARG_A] - vm[V_SUB_ARG_B];
 +      vm[V_MUL_RTE_RESULT]                    = vm[V_MUL_ARG_A] * vm[V_MUL_ARG_B];
 +      vm[V_DOT_RTE_RESULT]                    = vm[V_MUL_RTE_RESULT] + vm[V_MUL_RTE_RESULT];
 +      vm[V_CONV_TO_FP16_RTE_RESULT]   = vm[V_UNUSED];
 +      vm[V_CONV_TO_FP32_RTE_RESULT]   = vm[V_UNUSED];
 +      fesetround(prevRound);
 +
 +      vm[V_CONV_DENORM_SMALLER]               = exactByteEquivalent<deUint64>(0x3f0f800000000000); // 0x03f0 is fp16 denorm
 +      vm[V_CONV_DENORM_BIGGER]                = exactByteEquivalent<deUint64>(0x373f800000000000); // 0x000003f0 is fp32 denorm
 +}
 +
 +// Each float type (fp16, fp32, fp64) has specific set of SPIR-V snippets
 +// that was extracted to separate template specialization. Those snippets
 +// are used to compose final test shaders. With this approach
 +// parameterization can be done just once per type and reused for many tests.
 +class TypeSnippetsBase
 +{
 +public:
 +      virtual ~TypeSnippetsBase() {}
 +
 +protected:
 +      void updateSpirvSnippets();
 +
 +public: // Type specific data:
 +
 +      // Number of bits consumed by float type
 +      string bitWidth;
 +
 +      // Minimum positive normal
 +      string epsilon;
 +
 +      // denormBase is a normal value (found empirically) used to generate denorm value.
 +      // Denorm is generated by substracting epsilon from denormBase.
 +      // denormBase is not a denorm - it is used to create denorm.
 +      // This value is needed when operations are tested with arguments that were
 +      // generated in the code. Generated denorm should be the same as denorm
 +      // used when arguments are passed via input (m_valueIdToFloatType[V_DENORM]).
 +      // This is required as result of some operations depends on actual denorm value
 +      // e.g. OpRadians(0x0001) is 0 but OpRadians(0x03f0) is denorm.
 +      string denormBase;
 +
 +      string capabilities;
 +      string extensions;
 +      string arrayStride;
 +
 +public: // Type specific spir-v snippets:
 +
 +      // Common annotations
 +      string typeAnnotationsSnippet;
 +
 +      // Definitions of all types commonly used by tests
 +      string typeDefinitionsSnippet;
 +
 +      // Definitions of all constants commonly used by tests
 +      string constantsDefinitionsSnippet;
 +
 +      // Map that stores instructions that generate arguments of specified value.
 +      // Every test that uses generated inputod will select up to two items from this map
 +      typedef map<ValueId, string> SnippetMap;
 +      SnippetMap valueIdToSnippetArgMap;
 +
 +      // Spir-v snippet that reads argument from SSBO
 +      string argumentsFromInputSnippet;
 +
 +      // SSBO with stage input/output definitions
 +      string inputAnnotationsSnippet;
 +      string inputDefinitionsSnippet;
 +      string outputAnnotationsSnippet;
 +      string outputDefinitionsSnippet;
 +
 +      // Varying is required to pass result from vertex stage to fragment stage,
 +      // one of requirements was to not use SSBO writes in vertex stage so we
 +      // need to do that in fragment stage; we also cant pass operation result
 +      // directly because of interpolation, to avoid it we do a bitcast to uint
 +      string varyingsTypesSnippet;
 +      string inputVaryingsSnippet;
 +      string outputVaryingsSnippet;
 +      string storeVertexResultSnippet;
 +      string loadVertexResultSnippet;
 +
 +      string storeResultsSnippet;
 +};
 +
 +void TypeSnippetsBase::updateSpirvSnippets()
 +{
 +      // annotations to types that are commonly used by tests
 +      const string typeAnnotationsTemplate =
 +              "OpDecorate %type_float_arr_1 ArrayStride " + arrayStride + "\n"
 +              "OpDecorate %type_float_arr_2 ArrayStride " + arrayStride + "\n";
 +
 +      // definition off all types that are commonly used by tests
 +      const string typeDefinitionsTemplate =
 +              "%type_float             = OpTypeFloat " + bitWidth + "\n"
 +              "%type_float_uptr        = OpTypePointer Uniform %type_float\n"
 +              "%type_float_fptr        = OpTypePointer Function %type_float\n"
 +              "%type_float_vec2        = OpTypeVector %type_float 2\n"
 +              "%type_float_vec3        = OpTypeVector %type_float 3\n"
 +              "%type_float_vec4        = OpTypeVector %type_float 4\n"
 +              "%type_float_vec4_iptr   = OpTypePointer Input %type_float_vec4\n"
 +              "%type_float_vec4_optr   = OpTypePointer Output %type_float_vec4\n"
 +              "%type_float_mat2x2      = OpTypeMatrix %type_float_vec2 2\n"
 +              "%type_float_arr_1       = OpTypeArray %type_float %c_i32_1\n"
 +              "%type_float_arr_2       = OpTypeArray %type_float %c_i32_2\n";
 +
 +      // definition off all constans that are used by tests
 +      const string constantsDefinitionsTemplate =
 +              "%c_float_n1             = OpConstant %type_float -1\n"
 +              "%c_float_0              = OpConstant %type_float 0.0\n"
 +              "%c_float_0_5            = OpConstant %type_float 0.5\n"
 +              "%c_float_1              = OpConstant %type_float 1\n"
 +              "%c_float_2              = OpConstant %type_float 2\n"
 +              "%c_float_3              = OpConstant %type_float 3\n"
 +              "%c_float_4              = OpConstant %type_float 4\n"
 +              "%c_float_5              = OpConstant %type_float 5\n"
 +              "%c_float_6              = OpConstant %type_float 6\n"
 +              "%c_float_eps            = OpConstant %type_float " + epsilon + "\n"
 +              "%c_float_denorm_base    = OpConstant %type_float " + denormBase + "\n";
 +
 +      // when arguments are read from SSBO this snipped is placed in main function
 +      const string argumentsFromInputTemplate =
 +              "%arg1loc                = OpAccessChain %type_float_uptr %ssbo_in %c_i32_0 %c_i32_0\n"
 +              "%arg1                   = OpLoad %type_float %arg1loc\n"
 +              "%arg2loc                = OpAccessChain %type_float_uptr %ssbo_in %c_i32_0 %c_i32_1\n"
 +              "%arg2                   = OpLoad %type_float %arg2loc\n";
 +
 +      // when tested shader stage reads from SSBO it has to have this snippet
 +      inputAnnotationsSnippet =
 +              "OpMemberDecorate %SSBO_in 0 Offset 0\n"
 +              "OpDecorate %SSBO_in BufferBlock\n"
 +              "OpDecorate %ssbo_in DescriptorSet 0\n"
 +              "OpDecorate %ssbo_in Binding 0\n";
 +
 +      const string inputDefinitionsTemplate =
 +              "%SSBO_in              = OpTypeStruct %type_float_arr_2\n"
 +              "%up_SSBO_in           = OpTypePointer Uniform %SSBO_in\n"
 +              "%ssbo_in              = OpVariable %up_SSBO_in Uniform\n";
 +
 +      outputAnnotationsSnippet =
 +              "OpMemberDecorate %SSBO_out 0 Offset 0\n"
 +              "OpDecorate %SSBO_out BufferBlock\n"
 +              "OpDecorate %ssbo_out DescriptorSet 0\n"
 +              "OpDecorate %ssbo_out Binding 1\n";
 +
 +      const string outputDefinitionsTemplate =
 +              "%SSBO_out             = OpTypeStruct %type_float_arr_1\n"
 +              "%up_SSBO_out          = OpTypePointer Uniform %SSBO_out\n"
 +              "%ssbo_out             = OpVariable %up_SSBO_out Uniform\n";
 +
 +      // this snippet is used by compute and fragment stage but not by vertex stage
 +      const string storeResultsTemplate =
 +              "%outloc               = OpAccessChain %type_float_uptr %ssbo_out %c_i32_0 %c_i32_0\n"
 +              "OpStore %outloc %result\n";
 +
 +      const string typeToken  = "_float";
 +      const string typeName   = "_f" + bitWidth;
 +
 +      typeAnnotationsSnippet          = replace(typeAnnotationsTemplate, typeToken, typeName);
 +      typeDefinitionsSnippet          = replace(typeDefinitionsTemplate, typeToken, typeName);
 +      constantsDefinitionsSnippet     = replace(constantsDefinitionsTemplate, typeToken, typeName);
 +      argumentsFromInputSnippet       = replace(argumentsFromInputTemplate, typeToken, typeName);
 +      inputDefinitionsSnippet         = replace(inputDefinitionsTemplate, typeToken, typeName);
 +      outputDefinitionsSnippet        = replace(outputDefinitionsTemplate, typeToken, typeName);
 +      storeResultsSnippet                     = replace(storeResultsTemplate, typeToken, typeName);
 +
 +      // NOTE: only values used as _generated_ arguments in test operations
 +      // need to be in this map, arguments that are only used by tests,
 +      // that grab arguments from input, do need to be in this map
 +      // NOTE: when updating entries in valueIdToSnippetArgMap make
 +      // sure to update also m_valueIdToFloatType for all float width
 +      SnippetMap& sm = valueIdToSnippetArgMap;
 +      sm[V_UNUSED]            = "OpFSub %type_float %c_float_0 %c_float_0\n";
 +      sm[V_MINUS_INF]         = "OpFDiv %type_float %c_float_n1 %c_float_0\n";
 +      sm[V_MINUS_ONE]         = "OpFAdd %type_float %c_float_n1 %c_float_0\n";
 +      sm[V_MINUS_ZERO]        = "OpFMul %type_float %c_float_n1 %c_float_0\n";
 +      sm[V_ZERO]                      = "OpFMul %type_float %c_float_0 %c_float_0\n";
 +      sm[V_HALF]                      = "OpFAdd %type_float %c_float_0_5 %c_float_0\n";
 +      sm[V_ONE]                       = "OpFAdd %type_float %c_float_1 %c_float_0\n";
 +      sm[V_INF]                       = "OpFDiv %type_float %c_float_1 %c_float_0\n";                                 // x / 0                == Inf
 +      sm[V_DENORM]            = "OpFSub %type_float %c_float_denorm_base %c_float_eps\n";
 +      sm[V_NAN]                       = "OpFDiv %type_float %c_float_0 %c_float_0\n";                                 // 0 / 0                == Nan
 +
 +      map<ValueId, string>::iterator it;
 +      for ( it = sm.begin(); it != sm.end(); it++ )
 +              sm[it->first] = replace(it->second, typeToken, typeName);
 +}
 +
 +typedef de::SharedPtr<TypeSnippetsBase> TypeSnippetsSP;
 +
 +template<typename FLOAT_TYPE>
 +class TypeSnippets: public TypeSnippetsBase
 +{
 +public:
 +      TypeSnippets();
 +};
 +
 +template<>
 +TypeSnippets<deFloat16>::TypeSnippets()
 +{
 +      bitWidth                = "16";
 +      epsilon                 = "6.104e-5";   // 2^-14 = 0x0400
 +
 +      // 1.2113e-4 is 0x07f0 which after substracting epsilon will give 0x03f0 (same as vm[V_DENORM])
 +      // NOTE: constants in SPIR-V cant be specified as exact fp16 - there is conversion from double to fp16
 +      denormBase              = "1.2113e-4";
 +
 +      capabilities    = "OpCapability StorageUniform16\n";
 +      extensions              = "OpExtension \"SPV_KHR_16bit_storage\"\n";
 +      arrayStride             = "2";
 +
 +      varyingsTypesSnippet =
 +                                      "%type_u32_iptr        = OpTypePointer Input %type_u32\n"
 +                                      "%type_u32_optr        = OpTypePointer Output %type_u32\n";
 +      inputVaryingsSnippet =
 +                                      "%BP_vertex_result    = OpVariable %type_u32_iptr Input\n";
 +      outputVaryingsSnippet =
 +                                      "%BP_vertex_result    = OpVariable %type_u32_optr Output\n";
 +      storeVertexResultSnippet =
 +                                      "%tmp_vec2            = OpCompositeConstruct %type_f16_vec2 %result %c_f16_0\n"
 +                                      "%packed_result       = OpBitcast %type_u32 %tmp_vec2\n"
 +                                      "OpStore %BP_vertex_result %packed_result\n";
 +      loadVertexResultSnippet =
 +                                      "%packed_result       = OpLoad %type_u32 %BP_vertex_result\n"
 +                                      "%tmp_vec2            = OpBitcast %type_f16_vec2 %packed_result\n"
 +                                      "%result              = OpCompositeExtract %type_f16 %tmp_vec2 0\n";
 +
 +      updateSpirvSnippets();
 +}
 +
 +template<>
 +TypeSnippets<float>::TypeSnippets()
 +{
 +      bitWidth                = "32";
 +      epsilon                 = "1.175494351e-38";
 +      denormBase              = "1.1756356e-38";
 +      capabilities    = "";
 +      extensions              = "";
 +      arrayStride             = "4";
 +
 +      varyingsTypesSnippet =
 +                                      "%type_u32_iptr        = OpTypePointer Input %type_u32\n"
 +                                      "%type_u32_optr        = OpTypePointer Output %type_u32\n";
 +      inputVaryingsSnippet =
 +                                      "%BP_vertex_result    = OpVariable %type_u32_iptr Input\n";
 +      outputVaryingsSnippet =
 +                                      "%BP_vertex_result    = OpVariable %type_u32_optr Output\n";
 +      storeVertexResultSnippet =
 +                                      "%packed_result       = OpBitcast %type_u32 %result\n"
 +                                      "OpStore %BP_vertex_result %packed_result\n";
 +      loadVertexResultSnippet =
 +                                      "%packed_result       = OpLoad %type_u32 %BP_vertex_result\n"
 +                                      "%result              = OpBitcast %type_f32 %packed_result\n";
 +
 +      updateSpirvSnippets();
 +}
 +
 +template<>
 +TypeSnippets<double>::TypeSnippets()
 +{
 +      bitWidth                = "64";
 +      epsilon                 = "2.2250738585072014e-308"; // 0x0010000000000000
 +      denormBase              = "2.2250738585076994e-308"; // 0x00100000000003F0
 +      capabilities    = "OpCapability Float64\n";
 +      extensions              = "";
 +      arrayStride             = "8";
 +
 +      varyingsTypesSnippet =
 +                                      "%type_u32_vec2_iptr   = OpTypePointer Input %type_u32_vec2\n"
 +                                      "%type_u32_vec2_optr   = OpTypePointer Output %type_u32_vec2\n";
 +      inputVaryingsSnippet =
 +                                      "%BP_vertex_result     = OpVariable %type_u32_vec2_iptr Input\n";
 +      outputVaryingsSnippet =
 +                                      "%BP_vertex_result     = OpVariable %type_u32_vec2_optr Output\n";
 +      storeVertexResultSnippet =
 +                                      "%packed_result        = OpBitcast %type_u32_vec2 %result\n"
 +                                      "OpStore %BP_vertex_result %packed_result\n";
 +      loadVertexResultSnippet =
 +                                      "%packed_result        = OpLoad %type_u32_vec2 %BP_vertex_result\n"
 +                                      "%result               = OpBitcast %type_f64 %packed_result\n";
 +
 +      updateSpirvSnippets();
 +}
 +
 +class TypeTestResultsBase
 +{
 +public:
 +      virtual ~TypeTestResultsBase() {}
 +      FloatType floatType() const;
 +
 +protected:
 +      FloatType m_floatType;
 +
 +public:
 +      // Vectors containing test data for float controls
 +      vector<BinaryCase>      binaryOpFTZ;
 +      vector<UnaryCase>       unaryOpFTZ;
 +      vector<BinaryCase>      binaryOpDenormPreserve;
 +      vector<UnaryCase>       unaryOpDenormPreserve;
 +};
 +
 +FloatType TypeTestResultsBase::floatType() const
 +{
 +      return m_floatType;
 +}
 +
 +typedef de::SharedPtr<TypeTestResultsBase> TypeTestResultsSP;
 +
 +template<typename FLOAT_TYPE>
 +class TypeTestResults: public TypeTestResultsBase
 +{
 +public:
 +      TypeTestResults();
 +};
 +
 +template<>
 +TypeTestResults<deFloat16>::TypeTestResults()
 +{
 +      m_floatType = FP16;
 +
 +      // note: there are many FTZ test cases that can produce diferent result depending
 +      // on input denorm being flushed or not; because of that FTZ tests can be limited
 +      // to those that return denorm as those are the ones affected by tested extension
 +      const BinaryCase binaryOpFTZArr[] = {
 +              //operation             den op one              den op den              den op inf              den op nan
 +              { O_ADD,                V_ONE,                  V_ZERO,                 V_INF,                  V_UNUSED },
 +              { O_SUB,                V_MINUS_ONE,    V_ZERO,                 V_MINUS_INF,    V_UNUSED },
 +              { O_MUL,                V_ZERO,                 V_ZERO,                 V_UNUSED,               V_UNUSED },
 +              { O_DIV,                V_ZERO,                 V_UNUSED,               V_ZERO,                 V_UNUSED },
 +              { O_REM,                V_ZERO,                 V_UNUSED,               V_UNUSED,               V_UNUSED },
 +              { O_MOD,                V_ZERO,                 V_UNUSED,               V_UNUSED,               V_UNUSED },
 +              { O_VEC_MUL_S,  V_ZERO,                 V_ZERO,                 V_UNUSED,               V_UNUSED },
 +              { O_VEC_MUL_M,  V_ZERO,                 V_ZERO,                 V_UNUSED,               V_UNUSED },
 +              { O_MAT_MUL_S,  V_ZERO,                 V_ZERO,                 V_UNUSED,               V_UNUSED },
 +              { O_MAT_MUL_V,  V_ZERO,                 V_ZERO,                 V_UNUSED,               V_UNUSED },
 +              { O_MAT_MUL_M,  V_ZERO,                 V_ZERO,                 V_UNUSED,               V_UNUSED },
 +              { O_OUT_PROD,   V_ZERO,                 V_ZERO,                 V_UNUSED,               V_UNUSED },
 +              { O_DOT,                V_ZERO,                 V_ZERO,                 V_UNUSED,               V_UNUSED },
 +              { O_ATAN2,              V_ZERO,                 V_UNUSED,               V_ZERO,                 V_UNUSED },
 +              { O_POW,                V_ZERO,                 V_UNUSED,               V_ZERO,                 V_UNUSED },
 +              { O_MIX,                V_HALF,                 V_ZERO,                 V_INF,                  V_UNUSED },
 +              { O_MIN,                V_ZERO,                 V_ZERO,                 V_ZERO,                 V_UNUSED },
 +              { O_MAX,                V_ONE,                  V_ZERO,                 V_INF,                  V_UNUSED },
 +              { O_CLAMP,              V_ONE,                  V_ZERO,                 V_INF,                  V_UNUSED },
 +              { O_STEP,               V_ONE,                  V_ONE,                  V_ONE,                  V_UNUSED },
 +              { O_SSTEP,              V_HALF,                 V_ONE,                  V_ZERO,                 V_UNUSED },
 +              { O_FMA,                V_HALF,                 V_HALF,                 V_UNUSED,               V_UNUSED },
 +              { O_FACE_FWD,   V_MINUS_ONE,    V_MINUS_ONE,    V_MINUS_ONE,    V_MINUS_ONE },
 +              { O_NMIN,               V_ZERO,                 V_ZERO,                 V_ZERO,                 V_ZERO },
 +              { O_NMAX,               V_ONE,                  V_ZERO,                 V_INF,                  V_ZERO },
 +              { O_NCLAMP,             V_ONE,                  V_ZERO,                 V_INF,                  V_ZERO },
 +              { O_DIST,               V_ONE,                  V_ZERO,                 V_INF,                  V_UNUSED },
 +              { O_CROSS,              V_ZERO,                 V_ZERO,                 V_UNUSED,               V_UNUSED },
 +      };
 +
 +      const UnaryCase unaryOpFTZArr[] = {
 +              //operation                     op den
 +              { O_NEGATE,                     V_MINUS_ZERO },
 +              { O_ROUND,                      V_ZERO },
 +              { O_ROUND_EV,           V_ZERO },
 +              { O_TRUNC,                      V_ZERO },
 +              { O_ABS,                        V_ZERO },
 +              { O_FLOOR,                      V_ZERO },
 +              { O_CEIL,                       V_ZERO },
 +              { O_FRACT,                      V_ZERO },
 +              { O_RADIANS,            V_ZERO },
 +              { O_DEGREES,            V_ZERO },
 +              { O_SIN,                        V_ZERO },
 +              { O_COS,                        V_ONE },
 +              { O_TAN,                        V_ZERO },
 +              { O_ASIN,                       V_ZERO },
 +              { O_ACOS,                       V_PI_DIV_2 },
 +              { O_ATAN,                       V_ZERO },
 +              { O_SINH,                       V_ZERO },
 +              { O_COSH,                       V_ONE },
 +              { O_TANH,                       V_ZERO },
 +              { O_ASINH,                      V_ZERO },
 +              { O_ACOSH,                      V_UNUSED },
 +              { O_ATANH,                      V_ZERO },
 +              { O_EXP,                        V_ONE },
 +              { O_LOG,                        V_MINUS_INF },
 +              { O_EXP2,                       V_ONE },
 +              { O_LOG2,                       V_MINUS_INF },
 +              { O_SQRT,                       V_ZERO },
 +              { O_INV_SQRT,           V_INF },
 +              { O_MAT_DET,            V_ZERO },
 +              { O_MAT_INV,            V_ZERO_OR_MINUS_ZERO },
 +              { O_MODF,                       V_ZERO },
 +              { O_MODF_ST,            V_ZERO },
 +              { O_NORMALIZE,          V_ZERO },
 +              { O_REFLECT,            V_ZERO },
 +              { O_REFRACT,            V_ZERO },
 +              { O_LENGHT,                     V_ZERO },
 +      };
 +
 +      const BinaryCase binaryOpDenormPreserveArr[] = {
 +              //operation                     den op one                              den op den                              den op inf              den op nan
 +              { O_PHI,                        V_DENORM,                               V_DENORM,                               V_DENORM,               V_DENORM },
 +              { O_SELECT,                     V_DENORM,                               V_DENORM,                               V_DENORM,               V_DENORM },
 +              { O_ADD,                        V_ONE,                                  V_DENORM_TIMES_TWO,             V_INF,                  V_NAN },
 +              { O_SUB,                        V_MINUS_ONE_OR_CLOSE,   V_ZERO,                                 V_MINUS_INF,    V_NAN },
 +              { O_MUL,                        V_DENORM,                               V_ZERO,                                 V_INF,                  V_NAN },
 +              { O_VEC_MUL_S,          V_DENORM,                               V_ZERO,                                 V_INF,                  V_NAN },
 +              { O_VEC_MUL_M,          V_DENORM_TIMES_TWO,             V_ZERO,                                 V_INF,                  V_NAN },
 +              { O_MAT_MUL_S,          V_DENORM,                               V_ZERO,                                 V_INF,                  V_NAN },
 +              { O_MAT_MUL_V,          V_DENORM_TIMES_TWO,             V_ZERO,                                 V_INF,                  V_NAN },
 +              { O_MAT_MUL_M,          V_DENORM_TIMES_TWO,             V_ZERO,                                 V_INF,                  V_NAN },
 +              { O_OUT_PROD,           V_DENORM,                               V_ZERO,                                 V_INF,                  V_NAN },
 +              { O_DOT,                        V_DENORM_TIMES_TWO,             V_ZERO,                                 V_INF,                  V_NAN },
 +              { O_MIX,                        V_HALF,                                 V_DENORM,                               V_INF,                  V_NAN },
 +              { O_FMA,                        V_HALF,                                 V_HALF,                                 V_INF,                  V_NAN },
 +              { O_MIN,                        V_DENORM,                               V_DENORM,                               V_DENORM,               V_UNUSED },
 +              { O_MAX,                        V_ONE,                                  V_DENORM,                               V_INF,                  V_UNUSED },
 +              { O_CLAMP,                      V_ONE,                                  V_DENORM,                               V_INF,                  V_UNUSED },
 +              { O_NMIN,                       V_DENORM,                               V_DENORM,                               V_DENORM,               V_DENORM },
 +              { O_NMAX,                       V_ONE,                                  V_DENORM,                               V_INF,                  V_DENORM },
 +              { O_NCLAMP,                     V_ONE,                                  V_DENORM,                               V_INF,                  V_DENORM },
 +      };
 +
 +      const UnaryCase unaryOpDenormPreserveArr[] = {
 +              //operation                     op den
 +              { O_RETURN_VAL,         V_DENORM },
 +              { O_D_EXTRACT,          V_DENORM },
 +              { O_D_INSERT,           V_DENORM },
 +              { O_SHUFFLE,            V_DENORM },
 +              { O_COMPOSITE,          V_DENORM },
 +              { O_COMPOSITE_INS,      V_DENORM },
 +              { O_COPY,                       V_DENORM },
 +              { O_TRANSPOSE,          V_DENORM },
 +              { O_NEGATE,                     V_DENORM },
 +              { O_ABS,                        V_DENORM },
 +              { O_SIGN,                       V_ONE },
 +              { O_RADIANS,            V_DENORM },
 +              { O_DEGREES,            V_DEGREES_DENORM },
 +      };
 +
 +      binaryOpFTZ.insert(binaryOpFTZ.begin(), binaryOpFTZArr,
 +                                         binaryOpFTZArr + DE_LENGTH_OF_ARRAY(binaryOpFTZArr));
 +      unaryOpFTZ.insert(unaryOpFTZ.begin(), unaryOpFTZArr,
 +                                        unaryOpFTZArr + DE_LENGTH_OF_ARRAY(unaryOpFTZArr));
 +      binaryOpDenormPreserve.insert(binaryOpDenormPreserve.begin(), binaryOpDenormPreserveArr,
 +                                                                binaryOpDenormPreserveArr + DE_LENGTH_OF_ARRAY(binaryOpDenormPreserveArr));
 +      unaryOpDenormPreserve.insert(unaryOpDenormPreserve.begin(), unaryOpDenormPreserveArr,
 +                                                               unaryOpDenormPreserveArr + DE_LENGTH_OF_ARRAY(unaryOpDenormPreserveArr));
 +}
 +
 +template<>
 +TypeTestResults<float>::TypeTestResults()
 +{
 +      m_floatType = FP32;
 +
 +      const BinaryCase binaryOpFTZArr[] = {
 +              //operation             den op one              den op den              den op inf              den op nan
 +              { O_ADD,                V_ONE,                  V_ZERO,                 V_INF,                  V_UNUSED },
 +              { O_SUB,                V_MINUS_ONE,    V_ZERO,                 V_MINUS_INF,    V_UNUSED },
 +              { O_MUL,                V_ZERO,                 V_ZERO,                 V_UNUSED,               V_UNUSED },
 +              { O_DIV,                V_ZERO,                 V_UNUSED,               V_ZERO,                 V_UNUSED },
 +              { O_REM,                V_ZERO,                 V_UNUSED,               V_UNUSED,               V_UNUSED },
 +              { O_MOD,                V_ZERO,                 V_UNUSED,               V_UNUSED,               V_UNUSED },
 +              { O_VEC_MUL_S,  V_ZERO,                 V_ZERO,                 V_UNUSED,               V_UNUSED },
 +              { O_VEC_MUL_M,  V_ZERO,                 V_ZERO,                 V_UNUSED,               V_UNUSED },
 +              { O_MAT_MUL_S,  V_ZERO,                 V_ZERO,                 V_UNUSED,               V_UNUSED },
 +              { O_MAT_MUL_V,  V_ZERO,                 V_ZERO,                 V_UNUSED,               V_UNUSED },
 +              { O_MAT_MUL_M,  V_ZERO,                 V_ZERO,                 V_UNUSED,               V_UNUSED },
 +              { O_OUT_PROD,   V_ZERO,                 V_ZERO,                 V_UNUSED,               V_UNUSED },
 +              { O_DOT,                V_ZERO,                 V_ZERO,                 V_UNUSED,               V_UNUSED },
 +              { O_ATAN2,              V_ZERO,                 V_UNUSED,               V_ZERO,                 V_UNUSED },
 +              { O_POW,                V_ZERO,                 V_UNUSED,               V_ZERO,                 V_UNUSED },
 +              { O_MIX,                V_HALF,                 V_ZERO,                 V_INF,                  V_UNUSED },
 +              { O_MIN,                V_ZERO,                 V_ZERO,                 V_ZERO,                 V_UNUSED },
 +              { O_MAX,                V_ONE,                  V_ZERO,                 V_INF,                  V_UNUSED },
 +              { O_CLAMP,              V_ONE,                  V_ZERO,                 V_INF,                  V_UNUSED },
 +              { O_STEP,               V_ONE,                  V_ONE,                  V_ONE,                  V_UNUSED },
 +              { O_SSTEP,              V_HALF,                 V_ONE,                  V_ZERO,                 V_UNUSED },
 +              { O_FMA,                V_HALF,                 V_HALF,                 V_UNUSED,               V_UNUSED },
 +              { O_FACE_FWD,   V_MINUS_ONE,    V_MINUS_ONE,    V_MINUS_ONE,    V_MINUS_ONE },
 +              { O_NMIN,               V_ZERO,                 V_ZERO,                 V_ZERO,                 V_ZERO },
 +              { O_NMAX,               V_ONE,                  V_ZERO,                 V_INF,                  V_ZERO },
 +              { O_NCLAMP,             V_ONE,                  V_ZERO,                 V_INF,                  V_ZERO },
 +              { O_DIST,               V_ONE,                  V_ZERO,                 V_INF,                  V_UNUSED },
 +              { O_CROSS,              V_ZERO,                 V_ZERO,                 V_UNUSED,               V_UNUSED },
 +      };
 +
 +      const UnaryCase unaryOpFTZArr[] = {
 +              //operation                     op den
 +              { O_NEGATE,                     V_MINUS_ZERO },
 +              { O_ROUND,                      V_ZERO },
 +              { O_ROUND_EV,           V_ZERO },
 +              { O_TRUNC,                      V_ZERO },
 +              { O_ABS,                        V_ZERO },
 +              { O_FLOOR,                      V_ZERO },
 +              { O_CEIL,                       V_ZERO },
 +              { O_FRACT,                      V_ZERO },
 +              { O_RADIANS,            V_ZERO },
 +              { O_DEGREES,            V_ZERO },
 +              { O_SIN,                        V_ZERO },
 +              { O_COS,                        V_ONE },
 +              { O_TAN,                        V_ZERO },
 +              { O_ASIN,                       V_ZERO },
 +              { O_ACOS,                       V_PI_DIV_2 },
 +              { O_ATAN,                       V_ZERO },
 +              { O_SINH,                       V_ZERO },
 +              { O_COSH,                       V_ONE },
 +              { O_TANH,                       V_ZERO },
 +              { O_ASINH,                      V_ZERO },
 +              { O_ACOSH,                      V_UNUSED },
 +              { O_ATANH,                      V_ZERO },
 +              { O_EXP,                        V_ONE },
 +              { O_LOG,                        V_MINUS_INF },
 +              { O_EXP2,                       V_ONE },
 +              { O_LOG2,                       V_MINUS_INF },
 +              { O_SQRT,                       V_ZERO },
 +              { O_INV_SQRT,           V_INF },
 +              { O_MAT_DET,            V_ZERO },
 +              { O_MAT_INV,            V_ZERO_OR_MINUS_ZERO },
 +              { O_MODF,                       V_ZERO },
 +              { O_MODF_ST,            V_ZERO },
 +              { O_NORMALIZE,          V_ZERO },
 +              { O_REFLECT,            V_ZERO },
 +              { O_REFRACT,            V_ZERO },
 +              { O_LENGHT,                     V_ZERO },
 +      };
 +
 +      const BinaryCase binaryOpDenormPreserveArr[] = {
 +              //operation                     den op one                      den op den                              den op inf              den op nan
 +              { O_PHI,                        V_DENORM,                       V_DENORM,                               V_DENORM,               V_DENORM },
 +              { O_SELECT,                     V_DENORM,                       V_DENORM,                               V_DENORM,               V_DENORM },
 +              { O_ADD,                        V_ONE,                          V_DENORM_TIMES_TWO,             V_INF,                  V_NAN },
 +              { O_SUB,                        V_MINUS_ONE,            V_ZERO,                                 V_MINUS_INF,    V_NAN },
 +              { O_MUL,                        V_DENORM,                       V_ZERO,                                 V_INF,                  V_NAN },
 +              { O_VEC_MUL_S,          V_DENORM,                       V_ZERO,                                 V_INF,                  V_NAN },
 +              { O_VEC_MUL_M,          V_DENORM,                       V_ZERO,                                 V_INF,                  V_NAN },
 +              { O_MAT_MUL_S,          V_DENORM,                       V_ZERO,                                 V_INF,                  V_NAN },
 +              { O_MAT_MUL_V,          V_DENORM,                       V_ZERO,                                 V_INF,                  V_NAN },
 +              { O_MAT_MUL_M,          V_DENORM,                       V_ZERO,                                 V_INF,                  V_NAN },
 +              { O_OUT_PROD,           V_DENORM,                       V_ZERO,                                 V_INF,                  V_NAN },
 +              { O_DOT,                        V_DENORM_TIMES_TWO,     V_ZERO,                                 V_INF,                  V_NAN },
 +              { O_MIX,                        V_HALF,                         V_DENORM,                               V_INF,                  V_NAN },
 +              { O_FMA,                        V_HALF,                         V_HALF,                                 V_INF,                  V_NAN },
 +              { O_MIN,                        V_DENORM,                       V_DENORM,                               V_DENORM,               V_UNUSED },
 +              { O_MAX,                        V_ONE,                          V_DENORM,                               V_INF,                  V_UNUSED },
 +              { O_CLAMP,                      V_ONE,                          V_DENORM,                               V_INF,                  V_UNUSED },
 +              { O_NMIN,                       V_DENORM,                       V_DENORM,                               V_DENORM,               V_DENORM },
 +              { O_NMAX,                       V_ONE,                          V_DENORM,                               V_INF,                  V_DENORM },
 +              { O_NCLAMP,                     V_ONE,                          V_DENORM,                               V_INF,                  V_DENORM },
 +      };
 +
 +      const UnaryCase unaryOpDenormPreserveArr[] = {
 +              //operation                     op den
 +              { O_RETURN_VAL,         V_DENORM },
 +              { O_D_EXTRACT,          V_DENORM },
 +              { O_D_INSERT,           V_DENORM },
 +              { O_SHUFFLE,            V_DENORM },
 +              { O_COMPOSITE,          V_DENORM },
 +              { O_COMPOSITE_INS,      V_DENORM },
 +              { O_COPY,                       V_DENORM },
 +              { O_TRANSPOSE,          V_DENORM },
 +              { O_NEGATE,                     V_DENORM },
 +              { O_ABS,                        V_DENORM },
 +              { O_SIGN,                       V_ONE },
 +              { O_RADIANS,            V_DENORM },
 +              { O_DEGREES,            V_DEGREES_DENORM },
 +      };
 +
 +      binaryOpFTZ.insert(binaryOpFTZ.begin(), binaryOpFTZArr,
 +                                         binaryOpFTZArr + DE_LENGTH_OF_ARRAY(binaryOpFTZArr));
 +      unaryOpFTZ.insert(unaryOpFTZ.begin(), unaryOpFTZArr,
 +                                        unaryOpFTZArr + DE_LENGTH_OF_ARRAY(unaryOpFTZArr));
 +      binaryOpDenormPreserve.insert(binaryOpDenormPreserve.begin(), binaryOpDenormPreserveArr,
 +                                                                binaryOpDenormPreserveArr + DE_LENGTH_OF_ARRAY(binaryOpDenormPreserveArr));
 +      unaryOpDenormPreserve.insert(unaryOpDenormPreserve.begin(), unaryOpDenormPreserveArr,
 +                                                               unaryOpDenormPreserveArr + DE_LENGTH_OF_ARRAY(unaryOpDenormPreserveArr));
 +}
 +
 +template<>
 +TypeTestResults<double>::TypeTestResults()
 +{
 +      m_floatType = FP64;
 +
 +      // fp64 is supported by fewer operations then fp16 and fp32
 +      // e.g. Radians and Degrees functions are not supported
 +      const BinaryCase binaryOpFTZArr[] = {
 +              //operation             den op one              den op den              den op inf              den op nan
 +              { O_ADD,                V_ONE,                  V_ZERO,                 V_INF,                  V_UNUSED },
 +              { O_SUB,                V_MINUS_ONE,    V_ZERO,                 V_MINUS_INF,    V_UNUSED },
 +              { O_MUL,                V_ZERO,                 V_ZERO,                 V_UNUSED,               V_UNUSED },
 +              { O_DIV,                V_ZERO,                 V_UNUSED,               V_ZERO,                 V_UNUSED },
 +              { O_REM,                V_ZERO,                 V_UNUSED,               V_UNUSED,               V_UNUSED },
 +              { O_MOD,                V_ZERO,                 V_UNUSED,               V_UNUSED,               V_UNUSED },
 +              { O_VEC_MUL_S,  V_ZERO,                 V_ZERO,                 V_UNUSED,               V_UNUSED },
 +              { O_VEC_MUL_M,  V_ZERO,                 V_ZERO,                 V_UNUSED,               V_UNUSED },
 +              { O_MAT_MUL_S,  V_ZERO,                 V_ZERO,                 V_UNUSED,               V_UNUSED },
 +              { O_MAT_MUL_V,  V_ZERO,                 V_ZERO,                 V_UNUSED,               V_UNUSED },
 +              { O_MAT_MUL_M,  V_ZERO,                 V_ZERO,                 V_UNUSED,               V_UNUSED },
 +              { O_OUT_PROD,   V_ZERO,                 V_ZERO,                 V_UNUSED,               V_UNUSED },
 +              { O_DOT,                V_ZERO,                 V_ZERO,                 V_UNUSED,               V_UNUSED },
 +              { O_MIX,                V_HALF,                 V_ZERO,                 V_INF,                  V_UNUSED },
 +              { O_MIN,                V_ZERO,                 V_ZERO,                 V_ZERO,                 V_UNUSED },
 +              { O_MAX,                V_ONE,                  V_ZERO,                 V_INF,                  V_UNUSED },
 +              { O_CLAMP,              V_ONE,                  V_ZERO,                 V_INF,                  V_UNUSED },
 +              { O_STEP,               V_ONE,                  V_ONE,                  V_ONE,                  V_UNUSED },
 +              { O_SSTEP,              V_HALF,                 V_ONE,                  V_ZERO,                 V_UNUSED },
 +              { O_FMA,                V_HALF,                 V_HALF,                 V_UNUSED,               V_UNUSED },
 +              { O_FACE_FWD,   V_MINUS_ONE,    V_MINUS_ONE,    V_MINUS_ONE,    V_MINUS_ONE },
 +              { O_NMIN,               V_ZERO,                 V_ZERO,                 V_ZERO,                 V_ZERO },
 +              { O_NMAX,               V_ONE,                  V_ZERO,                 V_INF,                  V_ZERO },
 +              { O_NCLAMP,             V_ONE,                  V_ZERO,                 V_INF,                  V_ZERO },
 +              { O_DIST,               V_ONE,                  V_ZERO,                 V_INF,                  V_UNUSED },
 +              { O_CROSS,              V_ZERO,                 V_ZERO,                 V_UNUSED,               V_UNUSED },
 +      };
 +
 +      const UnaryCase unaryOpFTZArr[] = {
 +              //operation                     op den
 +              { O_NEGATE,                     V_MINUS_ZERO },
 +              { O_ROUND,                      V_ZERO },
 +              { O_ROUND_EV,           V_ZERO },
 +              { O_TRUNC,                      V_ZERO },
 +              { O_ABS,                        V_ZERO },
 +              { O_FLOOR,                      V_ZERO },
 +              { O_CEIL,                       V_ZERO },
 +              { O_FRACT,                      V_ZERO },
 +              { O_SQRT,                       V_ZERO },
 +              { O_INV_SQRT,           V_INF },
 +              { O_MAT_DET,            V_ZERO },
 +              { O_MAT_INV,            V_ZERO_OR_MINUS_ZERO },
 +              { O_MODF,                       V_ZERO },
 +              { O_MODF_ST,            V_ZERO },
 +              { O_NORMALIZE,          V_ZERO },
 +              { O_REFLECT,            V_ZERO },
 +              { O_LENGHT,                     V_ZERO },
 +      };
 +
 +      const BinaryCase binaryOpDenormPreserveArr[] = {
 +              //operation                     den op one                      den op den                              den op inf              den op nan
 +              { O_PHI,                        V_DENORM,                       V_DENORM,                               V_DENORM,               V_DENORM },
 +              { O_SELECT,                     V_DENORM,                       V_DENORM,                               V_DENORM,               V_DENORM },
 +              { O_ADD,                        V_ONE,                          V_DENORM_TIMES_TWO,             V_INF,                  V_NAN },
 +              { O_SUB,                        V_MINUS_ONE,            V_ZERO,                                 V_MINUS_INF,    V_NAN },
 +              { O_MUL,                        V_DENORM,                       V_ZERO,                                 V_INF,                  V_NAN },
 +              { O_VEC_MUL_S,          V_DENORM,                       V_ZERO,                                 V_INF,                  V_NAN },
 +              { O_VEC_MUL_M,          V_DENORM_TIMES_TWO,     V_ZERO,                                 V_INF,                  V_NAN },
 +              { O_MAT_MUL_S,          V_DENORM,                       V_ZERO,                                 V_INF,                  V_NAN },
 +              { O_MAT_MUL_V,          V_DENORM_TIMES_TWO,     V_ZERO,                                 V_INF,                  V_NAN },
 +              { O_MAT_MUL_M,          V_DENORM_TIMES_TWO,     V_ZERO,                                 V_INF,                  V_NAN },
 +              { O_OUT_PROD,           V_DENORM,                       V_ZERO,                                 V_INF,                  V_NAN },
 +              { O_DOT,                        V_DENORM_TIMES_TWO,     V_ZERO,                                 V_INF,                  V_NAN },
 +              { O_MIX,                        V_HALF,                         V_DENORM,                               V_INF,                  V_NAN },
 +              { O_FMA,                        V_HALF,                         V_HALF,                                 V_INF,                  V_NAN },
 +              { O_MIN,                        V_DENORM,                       V_DENORM,                               V_DENORM,               V_UNUSED },
 +              { O_MAX,                        V_ONE,                          V_DENORM,                               V_INF,                  V_UNUSED },
 +              { O_CLAMP,                      V_ONE,                          V_DENORM,                               V_INF,                  V_UNUSED },
 +              { O_NMIN,                       V_DENORM,                       V_DENORM,                               V_DENORM,               V_DENORM },
 +              { O_NMAX,                       V_ONE,                          V_DENORM,                               V_INF,                  V_DENORM },
 +              { O_NCLAMP,                     V_ONE,                          V_DENORM,                               V_INF,                  V_DENORM },
 +      };
 +
 +      const UnaryCase unaryOpDenormPreserveArr[] = {
 +              //operation                     op den
 +              { O_RETURN_VAL,         V_DENORM },
 +              { O_D_EXTRACT,          V_DENORM },
 +              { O_D_INSERT,           V_DENORM },
 +              { O_SHUFFLE,            V_DENORM },
 +              { O_COMPOSITE,          V_DENORM },
 +              { O_COMPOSITE_INS,      V_DENORM },
 +              { O_COPY,                       V_DENORM },
 +              { O_TRANSPOSE,          V_DENORM },
 +              { O_NEGATE,                     V_DENORM },
 +              { O_ABS,                        V_DENORM },
 +              { O_SIGN,                       V_ONE },
 +      };
 +
 +      binaryOpFTZ.insert(binaryOpFTZ.begin(), binaryOpFTZArr,
 +                                         binaryOpFTZArr + DE_LENGTH_OF_ARRAY(binaryOpFTZArr));
 +      unaryOpFTZ.insert(unaryOpFTZ.begin(), unaryOpFTZArr,
 +                                        unaryOpFTZArr + DE_LENGTH_OF_ARRAY(unaryOpFTZArr));
 +      binaryOpDenormPreserve.insert(binaryOpDenormPreserve.begin(), binaryOpDenormPreserveArr,
 +                                                                binaryOpDenormPreserveArr + DE_LENGTH_OF_ARRAY(binaryOpDenormPreserveArr));
 +      unaryOpDenormPreserve.insert(unaryOpDenormPreserve.begin(), unaryOpDenormPreserveArr,
 +                                                               unaryOpDenormPreserveArr + DE_LENGTH_OF_ARRAY(unaryOpDenormPreserveArr));
 +}
 +
 +// Operation structure holds data needed to test specified SPIR-V operation. This class contains
 +// additional annotations, additional types and aditional constants that should be properly included
 +// in SPIR-V code. Commands attribute in this structure contains code that performs tested operation
 +// on given arguments, in some cases verification is also performed there.
 +// All snipets stroed in this structure are generic and can be specialized for fp16, fp32 or fp64,
 +// thanks to that this data can be shared by many OperationTestCase instances (testing diferent
 +// float behaviours on diferent float widths).
 +struct Operation
 +{
 +      // operation name is included in test case name
 +      const char*     name;
 +
 +      // operation specific spir-v snippets that will be
 +      // placed in proper places in final test shader
 +      const char*     annotations;
 +      const char*     types;
 +      const char*     constants;
 +      const char*     commands;
 +
 +      // conversion operations operate on one float type and produce float
 +      // type with different bit width; restrictedInputType is used only when
 +      // isInputTypeRestricted is set to true and it restricts usega of this
 +      // operation to specified input type
 +      bool            isInputTypeRestricted;
 +      FloatType       restrictedInputType;
 +
 +      // arguments for OpSpecConstant need to be specified also as constant
 +      bool            isSpecConstant;
 +
 +      Operation()             {}
 +
 +      // Minimal constructor - used by most of operations
 +      Operation(const char* _name, const char* _commands)
 +              : name(_name)
 +              , annotations("")
 +              , types("")
 +              , constants("")
 +              , commands(_commands)
 +              , isInputTypeRestricted(false)
 +              , restrictedInputType(FP16)             // not used as isInputTypeRestricted is false
 +              , isSpecConstant(false)
 +      {}
 +
 +      // Conversion operations constructor (used also by conversions done in SpecConstantOp)
 +      Operation(const char* _name,
 +                        bool specConstant,
 +                        FloatType _inputType,
 +                        const char* _constants,
 +                        const char* _commands)
 +              : name(_name)
 +              , annotations("")
 +              , types("")
 +              , constants(_constants)
 +              , commands(_commands)
 +              , isInputTypeRestricted(true)
 +              , restrictedInputType(_inputType)
 +              , isSpecConstant(specConstant)
 +      {}
 +
 +      // Full constructor - used by few operations, that are more complex to test
 +      Operation(const char* _name,
 +                        const char* _annotations,
 +                        const char* _types,
 +                        const char* _constants,
 +                        const char* _commands)
 +              : name(_name)
 +              , annotations(_annotations)
 +              , types(_types)
 +              , constants(_constants)
 +              , commands(_commands)
 +              , isInputTypeRestricted(false)
 +              , restrictedInputType(FP16)             // not used as isInputTypeRestricted is false
 +              , isSpecConstant(false)
 +      {}
 +
 +      // Full constructor - used by rounding override cases
 +      Operation(const char* _name,
 +                        FloatType _inputType,
 +                        const char* _annotations,
 +                        const char* _types,
 +                        const char* _constants,
 +                        const char* _commands)
 +              : name(_name)
 +              , annotations(_annotations)
 +              , types(_types)
 +              , constants(_constants)
 +              , commands(_commands)
 +              , isInputTypeRestricted(true)
 +              , restrictedInputType(_inputType)
 +              , isSpecConstant(false)
 +      {}
 +};
 +
 +// Class storing input that will be passed to operation and expected
 +// output that should be generated for specified behaviour.
 +class OperationTestCase
 +{
 +public:
 +
 +      OperationTestCase()             {}
 +
 +      OperationTestCase(const char*   _baseName,
 +                                        BehaviorId    _behaviorId,
 +                                        OperationId   _operatinId,
 +                                        ValueId               _input1,
 +                                        ValueId               _input2,
 +                                        ValueId               _expectedOutput)
 +              : baseName(_baseName)
 +              , behaviorId(_behaviorId)
 +              , operationId(_operatinId)
 +              , expectedOutput(_expectedOutput)
 +      {
 +              input[0] = _input1;
 +              input[1] = _input2;
 +      }
 +
 +public:
 +
 +      string                                  baseName;
 +      BehaviorId                              behaviorId;
 +      OperationId                             operationId;
 +      ValueId                                 input[2];
 +      ValueId                                 expectedOutput;
 +};
 +
 +// Helper structure used to store specialized operation
 +// data. This data is ready to be used during shader assembly.
 +struct SpecializedOperation
 +{
 +      string constans;
 +      string annotations;
 +      string types;
 +      string arguments;
 +      string commands;
 +
 +      FloatType               inFloatType;
 +      TypeSnippetsSP  inTypeSnippets;
 +      TypeSnippetsSP  outTypeSnippets;
 +};
 +
 +// Class responsible for constructing list of test cases for specified
 +// float type and specified way of preparation of arguments.
 +// Arguments can be either read from input SSBO or generated via math
 +// operations in spir-v code.
 +class TestCasesBuilder
 +{
 +public:
 +
 +      void init();
 +      void build(vector<OperationTestCase>& testCases, TypeTestResultsSP typeTestResults, bool argumentsFromInput);
 +      const Operation& getOperation(OperationId id) const;
 +
 +private:
 +
 +      void createUnaryTestCases(vector<OperationTestCase>& testCases,
 +                                                        OperationId operationId,
 +                                                        ValueId denormPreserveResult,
 +                                                        ValueId denormFTZResult) const;
 +
 +private:
 +
 +      // Operations are shared betwean test cases so they are
 +      // passed to them as pointers to data stored in TestCasesBuilder.
 +      typedef OperationTestCase OTC;
 +      typedef Operation Op;
 +      map<int, Op> m_operations;
 +};
 +
 +void TestCasesBuilder::init()
 +{
 +      map<int, Op>& mo = m_operations;
 +
 +      // predefine operations repeatedly used in tests; note that "_float"
 +      // in every operation command will be replaced with either "_f16",
 +      // "_f32" or "_f64" - StringTemplate is not used here because it
 +      // would make code less readable
 +      // m_operations contains generic operation definitions that can be
 +      // used for all float types
 +
 +      mo[O_NEGATE]            = Op("negate",          "%result             = OpFNegate %type_float %arg1\n");
 +      mo[O_COMPOSITE]         = Op("composite",       "%vec1               = OpCompositeConstruct %type_float_vec2 %arg1 %arg1\n"
 +                                                                                      "%result             = OpCompositeExtract %type_float %vec1 0\n");
 +      mo[O_COMPOSITE_INS]     = Op("comp_ins",        "%vec1               = OpCompositeConstruct %type_float_vec2 %c_float_0 %c_float_0\n"
 +                                                                                      "%vec2               = OpCompositeInsert %type_float_vec2 %arg1 %vec1 0\n"
 +                                                                                      "%result             = OpCompositeExtract %type_float %vec2 0\n");
 +      mo[O_COPY]                      = Op("copy",            "%result             = OpCopyObject %type_float %arg1\n");
 +      mo[O_D_EXTRACT]         = Op("extract",         "%vec1               = OpCompositeConstruct %type_float_vec2 %arg1 %arg1\n"
 +                                                                                      "%result             = OpVectorExtractDynamic %type_float %vec1 %c_i32_0\n");
 +      mo[O_D_INSERT]          = Op("insert",          "%tmpVec             = OpCompositeConstruct %type_float_vec2 %c_float_2 %c_float_2\n"
 +                                                                                      "%vec1               = OpVectorInsertDynamic %type_float_vec2 %tmpVec %arg1 %c_i32_0\n"
 +                                                                                      "%result             = OpCompositeExtract %type_float %vec1 0\n");
 +      mo[O_SHUFFLE]           = Op("shuffle",         "%tmpVec1            = OpCompositeConstruct %type_float_vec2 %arg1 %arg1\n"
 +                                                                                      "%tmpVec2            = OpCompositeConstruct %type_float_vec2 %c_float_2 %c_float_2\n"   // NOTE: its impossible to test shuffle with denorms flushed
 +                                                                                      "%vec1               = OpVectorShuffle %type_float_vec2 %tmpVec1 %tmpVec2 0 2\n"                //       to zero as this will be done by earlier operation
 +                                                                                      "%result             = OpCompositeExtract %type_float %vec1 0\n");                                              //       (this also applies to few other operations)
 +      mo[O_TRANSPOSE]         = Op("transpose",       "%col                = OpCompositeConstruct %type_float_vec2 %arg1 %arg1\n"
 +                                                                                      "%mat                = OpCompositeConstruct %type_float_mat2x2 %col %col\n"
 +                                                                                      "%tmat               = OpTranspose %type_float_mat2x2 %mat\n"
 +                                                                                      "%tcol               = OpCompositeExtract %type_float_vec2 %tmat 0\n"
 +                                                                                      "%result             = OpCompositeExtract %type_float %tcol 0\n");
 +      mo[O_RETURN_VAL]        = Op("ret_val",         "",
 +                                                                                      "%type_test_fun      = OpTypeFunction %type_float %type_float\n",
 +                                                                                      "%test_fun = OpFunction %type_float None %type_test_fun\n"
 +                                                                                      "%param = OpFunctionParameter %type_float\n"
 +                                                                                      "%entry = OpLabel\n"
 +                                                                                      "OpReturnValue %param\n"
 +                                                                                      "OpFunctionEnd\n",
 +                                                                                      "%result             = OpFunctionCall %type_float %test_fun %arg1\n");
 +
 +      // conversion operations that are meant to be used only for single output type (defined by the second number in name)
 +      const char* convertSource =                             "%result             = OpFConvert %type_float %arg1\n";
 +      mo[O_CONV_FROM_FP16]    = Op("conv_from_fp16", false, FP16, "", convertSource);
 +      mo[O_CONV_FROM_FP32]    = Op("conv_from_fp32", false, FP32, "", convertSource);
 +      mo[O_CONV_FROM_FP64]    = Op("conv_from_fp64", false, FP64, "", convertSource);
 +
 +      // from all operands supported by OpSpecConstantOp we can only test FConvert opcode with literals as everything
 +      // else requires Karnel capability (OpenCL); values of literals used in SPIR-V code must be equiwalent to
 +      // V_CONV_FROM_FP32_ARG and V_CONV_FROM_FP64_ARG so we can use same expected rounded values as for regular OpFConvert
 +      mo[O_SCONST_CONV_FROM_FP32_TO_FP16]
 +                                              = Op("sconst_conv_from_fp32", true, FP32,
 +                                                                                      "%c_arg              = OpConstant %type_f32 1.22334445\n"
 +                                                                                      "%result             = OpSpecConstantOp %type_f16 FConvert %c_arg\n",
 +                                                                                      "");
 +      mo[O_SCONST_CONV_FROM_FP64_TO_FP32]
 +                                              = Op("sconst_conv_from_fp64", true, FP64,
 +                                                                                      "%c_arg              = OpConstant %type_f64 1.22334455\n"
 +                                                                                      "%result             = OpSpecConstantOp %type_f32 FConvert %c_arg\n",
 +                                                                                      "");
 +      mo[O_SCONST_CONV_FROM_FP64_TO_FP16]
 +                                              = Op("sconst_conv_from_fp64", true, FP64,
 +                                                                                      "%c_arg              = OpConstant %type_f64 1.22334445\n"
 +                                                                                      "%result             = OpSpecConstantOp %type_f16 FConvert %c_arg\n",
 +                                                                                      "");
 +
 +      mo[O_ADD]                       = Op("add",                     "%result             = OpFAdd %type_float %arg1 %arg2\n");
 +      mo[O_SUB]                       = Op("sub",                     "%result             = OpFSub %type_float %arg1 %arg2\n");
 +      mo[O_MUL]                       = Op("mul",                     "%result             = OpFMul %type_float %arg1 %arg2\n");
 +      mo[O_DIV]                       = Op("div",                     "%result             = OpFDiv %type_float %arg1 %arg2\n");
 +      mo[O_REM]                       = Op("rem",                     "%result             = OpFRem %type_float %arg1 %arg2\n");
 +      mo[O_MOD]                       = Op("mod",                     "%result             = OpFMod %type_float %arg1 %arg2\n");
 +      mo[O_PHI]                       = Op("phi",                     "%comp               = OpFOrdGreaterThan %type_bool %arg1 %arg2\n"
 +                                                                                      "                      OpSelectionMerge %comp_merge None\n"
 +                                                                                      "                      OpBranchConditional %comp %true_branch %false_branch\n"
 +                                                                                      "%true_branch        = OpLabel\n"
 +                                                                                      "                      OpBranch %comp_merge\n"
 +                                                                                      "%false_branch       = OpLabel\n"
 +                                                                                      "                      OpBranch %comp_merge\n"
 +                                                                                      "%comp_merge         = OpLabel\n"
 +                                                                                      "%result             = OpPhi %type_float %arg2 %true_branch %arg1 %false_branch\n");
 +      mo[O_SELECT]            = Op("select",          "%always_true        = OpFOrdGreaterThan %type_bool %c_float_1 %c_float_0\n"
 +                                                                                      "%result             = OpSelect %type_float %always_true %arg1 %arg2\n");
 +      mo[O_DOT]                       = Op("dot",                     "%vec1               = OpCompositeConstruct %type_float_vec2 %arg1 %arg1\n"
 +                                                                                      "%vec2               = OpCompositeConstruct %type_float_vec2 %arg2 %arg2\n"
 +                                                                                      "%result             = OpDot %type_float %vec1 %vec2\n");
 +      mo[O_VEC_MUL_S]         = Op("vmuls",           "%vec                = OpCompositeConstruct %type_float_vec2 %arg1 %arg1\n"
 +                                                                                      "%tmpVec             = OpVectorTimesScalar %type_float_vec2 %vec %arg2\n"
 +                                                                                      "%result             = OpCompositeExtract %type_float %tmpVec 0\n");
 +      mo[O_VEC_MUL_M]         = Op("vmulm",           "%col                = OpCompositeConstruct %type_float_vec2 %arg1 %arg1\n"
 +                                                                                      "%mat                = OpCompositeConstruct %type_float_mat2x2 %col %col\n"
 +                                                                                      "%vec                = OpCompositeConstruct %type_float_vec2 %arg2 %arg2\n"
 +                                                                                      "%tmpVec             = OpVectorTimesMatrix %type_float_vec2 %vec %mat\n"
 +                                                                                      "%result             = OpCompositeExtract %type_float %tmpVec 0\n");
 +      mo[O_MAT_MUL_S]         = Op("mmuls",           "%col                = OpCompositeConstruct %type_float_vec2 %arg1 %arg1\n"
 +                                                                                      "%mat                = OpCompositeConstruct %type_float_mat2x2 %col %col\n"
 +                                                                                      "%mulMat             = OpMatrixTimesScalar %type_float_mat2x2 %mat %arg2\n"
 +                                                                                      "%extCol             = OpCompositeExtract %type_float_vec2 %mulMat 0\n"
 +                                                                                      "%result             = OpCompositeExtract %type_float %extCol 0\n");
 +      mo[O_MAT_MUL_V]         = Op("mmulv",           "%col                = OpCompositeConstruct %type_float_vec2 %arg1 %arg1\n"
 +                                                                                      "%mat                = OpCompositeConstruct %type_float_mat2x2 %col %col\n"
 +                                                                                      "%vec                = OpCompositeConstruct %type_float_vec2 %arg2 %arg2\n"
 +                                                                                      "%mulVec             = OpMatrixTimesVector %type_float_vec2 %mat %vec\n"
 +                                                                                      "%result             = OpCompositeExtract %type_float %mulVec 0\n");
 +      mo[O_MAT_MUL_M]         = Op("mmulm",           "%col1               = OpCompositeConstruct %type_float_vec2 %arg1 %arg1\n"
 +                                                                                      "%mat1               = OpCompositeConstruct %type_float_mat2x2 %col1 %col1\n"
 +                                                                                      "%col2               = OpCompositeConstruct %type_float_vec2 %arg2 %arg2\n"
 +                                                                                      "%mat2               = OpCompositeConstruct %type_float_mat2x2 %col2 %col2\n"
 +                                                                                      "%mulMat             = OpMatrixTimesMatrix %type_float_mat2x2 %mat1 %mat2\n"
 +                                                                                      "%extCol             = OpCompositeExtract %type_float_vec2 %mulMat 0\n"
 +                                                                                      "%result             = OpCompositeExtract %type_float %extCol 0\n");
 +      mo[O_OUT_PROD]          = Op("out_prod",        "%vec1               = OpCompositeConstruct %type_float_vec2 %arg1 %arg1\n"
 +                                                                                      "%vec2               = OpCompositeConstruct %type_float_vec2 %arg2 %arg2\n"
 +                                                                                      "%mulMat             = OpOuterProduct %type_float_mat2x2 %vec1 %vec2\n"
 +                                                                                      "%extCol             = OpCompositeExtract %type_float_vec2 %mulMat 0\n"
 +                                                                                      "%result             = OpCompositeExtract %type_float %extCol 0\n");
 +
 +      // comparison operations
 +      mo[O_ORD_EQ]            = Op("ord_eq",          "%boolVal           = OpFOrdEqual %type_bool %arg1 %arg2\n"
 +                                                                                      "%result            = OpSelect %type_float %boolVal %c_float_1 %c_float_0\n");
 +      mo[O_UORD_EQ]           = Op("uord_eq",         "%boolVal           = OpFUnordEqual %type_bool %arg1 %arg2\n"
 +                                                                                      "%result            = OpSelect %type_float %boolVal %c_float_1 %c_float_0\n");
 +      mo[O_ORD_NEQ]           = Op("ord_neq",         "%boolVal           = OpFOrdNotEqual %type_bool %arg1 %arg2\n"
 +                                                                                      "%result            = OpSelect %type_float %boolVal %c_float_1 %c_float_0\n");
 +      mo[O_UORD_NEQ]          = Op("uord_neq",        "%boolVal           = OpFUnordNotEqual %type_bool %arg1 %arg2\n"
 +                                                                                      "%result            = OpSelect %type_float %boolVal %c_float_1 %c_float_0\n");
 +      mo[O_ORD_LS]            = Op("ord_ls",          "%boolVal           = OpFOrdLessThan %type_bool %arg1 %arg2\n"
 +                                                                                      "%result            = OpSelect %type_float %boolVal %c_float_1 %c_float_0\n");
 +      mo[O_UORD_LS]           = Op("uord_ls",         "%boolVal           = OpFUnordLessThan %type_bool %arg1 %arg2\n"
 +                                                                                      "%result            = OpSelect %type_float %boolVal %c_float_1 %c_float_0\n");
 +      mo[O_ORD_GT]            = Op("ord_gt",          "%boolVal           = OpFOrdGreaterThan %type_bool %arg1 %arg2\n"
 +                                                                                      "%result            = OpSelect %type_float %boolVal %c_float_1 %c_float_0\n");
 +      mo[O_UORD_GT]           = Op("uord_gt",         "%boolVal           = OpFUnordGreaterThan %type_bool %arg1 %arg2\n"
 +                                                                                      "%result            = OpSelect %type_float %boolVal %c_float_1 %c_float_0\n");
 +      mo[O_ORD_LE]            = Op("ord_le",          "%boolVal           = OpFOrdLessThanEqual %type_bool %arg1 %arg2\n"
 +                                                                                      "%result            = OpSelect %type_float %boolVal %c_float_1 %c_float_0\n");
 +      mo[O_UORD_LE]           = Op("uord_le",         "%boolVal           = OpFUnordLessThanEqual %type_bool %arg1 %arg2\n"
 +                                                                                      "%result            = OpSelect %type_float %boolVal %c_float_1 %c_float_0\n");
 +      mo[O_ORD_GE]            = Op("ord_ge",          "%boolVal           = OpFOrdGreaterThanEqual %type_bool %arg1 %arg2\n"
 +                                                                                      "%result            = OpSelect %type_float %boolVal %c_float_1 %c_float_0\n");
 +      mo[O_UORD_GE]           = Op("uord_ge",         "%boolVal           = OpFUnordGreaterThanEqual %type_bool %arg1 %arg2\n"
 +                                                                                      "%result            = OpSelect %type_float %boolVal %c_float_1 %c_float_0\n");
 +
 +      mo[O_ATAN2]                     = Op("atan2",           "%result             = OpExtInst %type_float %std450 Atan2 %arg1 %arg2\n");
 +      mo[O_POW]                       = Op("pow",                     "%result             = OpExtInst %type_float %std450 Pow %arg1 %arg2\n");
 +      mo[O_MIX]                       = Op("mix",                     "%result             = OpExtInst %type_float %std450 FMix %arg1 %arg2 %c_float_0_5\n");
 +      mo[O_FMA]                       = Op("fma",                     "%result             = OpExtInst %type_float %std450 Fma %arg1 %arg2 %c_float_0_5\n");
 +      mo[O_MIN]                       = Op("min",                     "%result             = OpExtInst %type_float %std450 FMin %arg1 %arg2\n");
 +      mo[O_MAX]                       = Op("max",                     "%result             = OpExtInst %type_float %std450 FMax %arg1 %arg2\n");
 +      mo[O_CLAMP]                     = Op("clamp",           "%result             = OpExtInst %type_float %std450 FClamp %arg1 %arg2 %arg2\n");
 +      mo[O_STEP]                      = Op("step",            "%result             = OpExtInst %type_float %std450 Step %arg1 %arg2\n");
 +      mo[O_SSTEP]                     = Op("sstep",           "%result             = OpExtInst %type_float %std450 SmoothStep %arg1 %arg2 %c_float_0_5\n");
 +      mo[O_DIST]                      = Op("distance",        "%result             = OpExtInst %type_float %std450 Distance %arg1 %arg2\n");
 +      mo[O_CROSS]                     = Op("cross",           "%vec1               = OpCompositeConstruct %type_float_vec3 %arg1 %arg1 %arg1\n"
 +                                                                                      "%vec2               = OpCompositeConstruct %type_float_vec3 %arg2 %arg2 %arg2\n"
 +                                                                                      "%tmpVec             = OpExtInst %type_float_vec3 %std450 Cross %vec1 %vec2\n"
 +                                                                                      "%result             = OpCompositeExtract %type_float %tmpVec 0\n");
 +      mo[O_FACE_FWD]          = Op("face_fwd",        "%result             = OpExtInst %type_float %std450 FaceForward %c_float_1 %arg1 %arg2\n");
 +      mo[O_NMIN]                      = Op("nmin",            "%result             = OpExtInst %type_float %std450 NMin %arg1 %arg2\n");
 +      mo[O_NMAX]                      = Op("nmax",            "%result             = OpExtInst %type_float %std450 NMax %arg1 %arg2\n");
 +      mo[O_NCLAMP]            = Op("nclamp",          "%result             = OpExtInst %type_float %std450 NClamp %arg2 %arg1 %arg2\n");
 +
 +      mo[O_ROUND]                     = Op("round",           "%result             = OpExtInst %type_float %std450 Round %arg1\n");
 +      mo[O_ROUND_EV]          = Op("round_ev",        "%result             = OpExtInst %type_float %std450 RoundEven %arg1\n");
 +      mo[O_TRUNC]                     = Op("trunc",           "%result             = OpExtInst %type_float %std450 Trunc %arg1\n");
 +      mo[O_ABS]                       = Op("abs",                     "%result             = OpExtInst %type_float %std450 FAbs %arg1\n");
 +      mo[O_SIGN]                      = Op("sign",            "%result             = OpExtInst %type_float %std450 FSign %arg1\n");
 +      mo[O_FLOOR]                     = Op("floor",           "%result             = OpExtInst %type_float %std450 Floor %arg1\n");
 +      mo[O_CEIL]                      = Op("ceil",            "%result             = OpExtInst %type_float %std450 Ceil %arg1\n");
 +      mo[O_FRACT]                     = Op("fract",           "%result             = OpExtInst %type_float %std450 Fract %arg1\n");
 +      mo[O_RADIANS]           = Op("radians",         "%result             = OpExtInst %type_float %std450 Radians %arg1\n");
 +      mo[O_DEGREES]           = Op("degrees",         "%result             = OpExtInst %type_float %std450 Degrees %arg1\n");
 +      mo[O_SIN]                       = Op("sin",                     "%result             = OpExtInst %type_float %std450 Sin %arg1\n");
 +      mo[O_COS]                       = Op("cos",                     "%result             = OpExtInst %type_float %std450 Cos %arg1\n");
 +      mo[O_TAN]                       = Op("tan",                     "%result             = OpExtInst %type_float %std450 Tan %arg1\n");
 +      mo[O_ASIN]                      = Op("asin",            "%result             = OpExtInst %type_float %std450 Asin %arg1\n");
 +      mo[O_ACOS]                      = Op("acos",            "%result             = OpExtInst %type_float %std450 Acos %arg1\n");
 +      mo[O_ATAN]                      = Op("atan",            "%result             = OpExtInst %type_float %std450 Atan %arg1\n");
 +      mo[O_SINH]                      = Op("sinh",            "%result             = OpExtInst %type_float %std450 Sinh %arg1\n");
 +      mo[O_COSH]                      = Op("cosh",            "%result             = OpExtInst %type_float %std450 Cosh %arg1\n");
 +      mo[O_TANH]                      = Op("tanh",            "%result             = OpExtInst %type_float %std450 Tanh %arg1\n");
 +      mo[O_ASINH]                     = Op("asinh",           "%result             = OpExtInst %type_float %std450 Asinh %arg1\n");
 +      mo[O_ACOSH]                     = Op("acosh",           "%result             = OpExtInst %type_float %std450 Acosh %arg1\n");
 +      mo[O_ATANH]                     = Op("atanh",           "%result             = OpExtInst %type_float %std450 Atanh %arg1\n");
 +      mo[O_EXP]                       = Op("exp",                     "%result             = OpExtInst %type_float %std450 Exp %arg1\n");
 +      mo[O_LOG]                       = Op("log",                     "%result             = OpExtInst %type_float %std450 Log %arg1\n");
 +      mo[O_EXP2]                      = Op("exp2",            "%result             = OpExtInst %type_float %std450 Exp2 %arg1\n");
 +      mo[O_LOG2]                      = Op("log2",            "%result             = OpExtInst %type_float %std450 Log2 %arg1\n");
 +      mo[O_SQRT]                      = Op("sqrt",            "%result             = OpExtInst %type_float %std450 Sqrt %arg1\n");
 +      mo[O_INV_SQRT]          = Op("inv_sqrt",        "%result             = OpExtInst %type_float %std450 InverseSqrt %arg1\n");
 +      mo[O_MODF]                      = Op("modf",            "%tmpVarPtr          = OpVariable %type_float_fptr Function\n"
 +                                                                                      "%result             = OpExtInst %type_float %std450 Modf %arg1 %tmpVarPtr\n");
 +      mo[O_MODF_ST]           = Op("modf_st",         "OpMemberDecorate %struct_ff 0 Offset ${float_width}\n"
 +                                                                                      "OpMemberDecorate %struct_ff 1 Offset ${float_width}\n",
 +                                                                                      "%struct_ff          = OpTypeStruct %type_float %type_float\n"
 +                                                                                      "%struct_ff_fptr     = OpTypePointer Function %struct_ff\n",
 +                                                                                      "",
 +                                                                                      "%tmpStruct          = OpExtInst %struct_ff %std450 ModfStruct %arg1\n"
 +                                                                                      "%tmpStructPtr       = OpVariable %struct_ff_fptr Function\n"
 +                                                                                      "                      OpStore %tmpStructPtr %tmpStruct\n"
 +                                                                                      "%tmpLoc             = OpAccessChain %type_float_fptr %tmpStructPtr %c_i32_0\n"
 +                                                                                      "%result             = OpLoad %type_float %tmpLoc\n");
 +      mo[O_FREXP]                     = Op("frexp",           "%tmpVarPtr          = OpVariable %type_i32_fptr Function\n"
 +                                                                                      "%result             = OpExtInst %type_float %std450 Frexp %arg1 %tmpVarPtr\n");
 +      mo[O_FREXP_ST]          = Op("frexp_st",        "OpMemberDecorate %struct_fi 0 Offset ${float_width}\n"
 +                                                                                      "OpMemberDecorate %struct_fi 1 Offset 32\n",
 +                                                                                      "%struct_fi          = OpTypeStruct %type_float %type_i32\n"
 +                                                                                      "%struct_fi_fptr     = OpTypePointer Function %struct_fi\n",
 +                                                                                      "",
 +                                                                                      "%tmpStruct          = OpExtInst %struct_fi %std450 FrexpStruct %arg1\n"
 +                                                                                      "%tmpStructPtr       = OpVariable %struct_fi_fptr Function\n"
 +                                                                                      "                      OpStore %tmpStructPtr %tmpStruct\n"
 +                                                                                      "%tmpLoc             = OpAccessChain %type_float_fptr %tmpStructPtr %c_i32_0\n"
 +                                                                                      "%result             = OpLoad %type_float %tmpLoc\n");
 +      mo[O_LENGHT]            = Op("length",          "%result             = OpExtInst %type_float %std450 Length %arg1\n");
 +      mo[O_NORMALIZE]         = Op("normalize",       "%vec1               = OpCompositeConstruct %type_float_vec2 %arg1 %c_float_2\n"
 +                                                                                      "%tmpVec             = OpExtInst %type_float_vec2 %std450 Normalize %vec1\n"
 +                                                                                      "%result             = OpCompositeExtract %type_float %tmpVec 0\n");
 +      mo[O_REFLECT]           = Op("reflect",         "%vec1               = OpCompositeConstruct %type_float_vec2 %arg1 %arg1\n"
 +                                                                                      "%vecN               = OpCompositeConstruct %type_float_vec2 %c_float_0 %c_float_n1\n"
 +                                                                                      "%tmpVec             = OpExtInst %type_float_vec2 %std450 Reflect %vec1 %vecN\n"
 +                                                                                      "%result             = OpCompositeExtract %type_float %tmpVec 0\n");
 +      mo[O_REFRACT]           = Op("refract",         "%vec1               = OpCompositeConstruct %type_float_vec2 %arg1 %arg1\n"
 +                                                                                      "%vecN               = OpCompositeConstruct %type_float_vec2 %c_float_0 %c_float_n1\n"
 +                                                                                      "%tmpVec             = OpExtInst %type_float_vec2 %std450 Refract %vec1 %vecN %c_float_0_5\n"
 +                                                                                      "%result             = OpCompositeExtract %type_float %tmpVec 0\n");
 +      mo[O_MAT_DET]           = Op("mat_det",         "%col                = OpCompositeConstruct %type_float_vec2 %arg1 %arg1\n"
 +                                                                                      "%mat                = OpCompositeConstruct %type_float_mat2x2 %col %col\n"
 +                                                                                      "%result             = OpExtInst %type_float %std450 Determinant %mat\n");
 +      mo[O_MAT_INV]           = Op("mat_inv",         "%col1               = OpCompositeConstruct %type_float_vec2 %arg1 %c_float_1\n"
 +                                                                                      "%col2               = OpCompositeConstruct %type_float_vec2 %c_float_1 %c_float_1\n"
 +                                                                                      "%mat                = OpCompositeConstruct %type_float_mat2x2 %col1 %col2\n"
 +                                                                                      "%invMat             = OpExtInst %type_float_mat2x2 %std450 MatrixInverse %mat\n"
 +                                                                                      "%extCol             = OpCompositeExtract %type_float_vec2 %invMat 1\n"
 +                                                                                      "%result             = OpCompositeExtract %type_float %extCol 1\n");
 +
 +      // PackHalf2x16 is a special case as it operates on fp32 vec2 and returns unsigned int,
 +      // the verification is done in SPIR-V code (if result is correct 1.0 will be written to SSBO)
 +      mo[O_PH_DENORM]         = Op("ph_denorm",       "",
 +                                                                                      "",
 +                                                                                      "%c_fp32_denorm_fp16 = OpConstant %type_f32 6.01e-5\n"          // fp32 representation of fp16 denorm value
 +                                                                                      "%c_ref              = OpConstant %type_u32 66061296\n",
 +                                                                                      "%srcVec             = OpCompositeConstruct %type_f32_vec2 %c_fp32_denorm_fp16 %c_fp32_denorm_fp16\n"
 +                                                                                      "%packedInt          = OpExtInst %type_u32 %std450 PackHalf2x16 %srcVec\n"
 +                                                                                      "%boolVal            = OpIEqual %type_bool %c_ref %packedInt\n"
 +                                                                                      "%result             = OpSelect %type_f32 %boolVal %c_f32_1 %c_f32_0\n");
 +
 +      // UnpackHalf2x16 is a special case that operates on uint32 and returns two 32-bit floats,
 +      // this function is tested using constants
 +      mo[O_UPH_DENORM]        = Op("uph_denorm",      "",
 +                                                                                      "",
 +                                                                                      "%c_u32_2_16_pack    = OpConstant %type_u32 66061296\n", // == packHalf2x16(vec2(denorm))
 +                                                                                      "%tmpVec             = OpExtInst %type_f32_vec2 %std450 UnpackHalf2x16 %c_u32_2_16_pack\n"
 +                                                                                      "%result             = OpCompositeExtract %type_f32 %tmpVec 0\n");
 +
 +      // PackDouble2x32 is a special case that operates on two uint32 and returns
 +      // double, this function is tested using constants
 +      mo[O_PD_DENORM]         = Op("pd_denorm",       "",
 +                                                                                      "",
 +                                                                                      "%c_p1               = OpConstant %type_u32 0\n"
 +                                                                                      "%c_p2               = OpConstant %type_u32 262144\n",          // == UnpackDouble2x32(denorm)
 +                                                                                      "%srcVec             = OpCompositeConstruct %type_u32_vec2 %c_p1 %c_p2\n"
 +                                                                                      "%result             = OpExtInst %type_f64 %std450 PackDouble2x32 %srcVec\n");
 +
 +      // UnpackDouble2x32 is a special case as it operates only on FP64 and returns two ints,
 +      // the verification is done in SPIR-V code (if result is correct 1.0 will be written to SSBO)
 +      const char* unpackDouble2x32Types       =       "%type_bool_vec2     = OpTypeVector %type_bool 2\n";
 +      const char* unpackDouble2x32Source      =       "%refVec2            = OpCompositeConstruct %type_u32_vec2 %c_p1 %c_p2\n"
 +                                                                                      "%resVec2            = OpExtInst %type_u32_vec2 %std450 UnpackDouble2x32 %arg1\n"
 +                                                                                      "%boolVec2           = OpIEqual %type_bool_vec2 %refVec2 %resVec2\n"
 +                                                                                      "%boolVal            = OpAll %type_bool %boolVec2\n"
 +                                                                                      "%result             = OpSelect %type_f64 %boolVal %c_f64_1 %c_f64_0\n";
 +      mo[O_UPD_DENORM_FLUSH]          = Op("upd_denorm",      "",
 +                                                                                      unpackDouble2x32Types,
 +                                                                                      "%c_p1               = OpConstant %type_u32 0\n"
 +                                                                                      "%c_p2               = OpConstant %type_u32 0\n",
 +                                                                                      unpackDouble2x32Source);
 +      mo[O_UPD_DENORM_PRESERVE]       = Op("upd_denorm",      "",
 +                                                                                      unpackDouble2x32Types,
 +                                                                                      "%c_p1               = OpConstant %type_u32 1008\n"
 +                                                                                      "%c_p2               = OpConstant %type_u32 0\n",
 +                                                                                      unpackDouble2x32Source);
 +
 +      mo[O_ORTE_ROUND]        = Op("orte_round",      FP32,
 +                                                                                      "OpDecorate %result FPRoundingMode RTE\n",
 +                                                                                      "",
 +                                                                                      "",
 +                                                                                      "%result             = OpFConvert %type_f16 %arg1\n");
 +      mo[O_ORTZ_ROUND]        = Op("ortz_round",      FP32,
 +                                                                                      "OpDecorate %result FPRoundingMode RTZ\n",
 +                                                                                      "",
 +                                                                                      "",
 +                                                                                      "%result             = OpFConvert %type_f16 %arg1\n");
 +}
 +
 +void TestCasesBuilder::build(vector<OperationTestCase>& testCases, TypeTestResultsSP typeTestResults, bool argumentsFromInput)
 +{
 +      // this method constructs a list of test cases; this list is a bit different
 +      // for every combination of float type, arguments preparation method and tested float control
 +
 +      testCases.reserve(750);
 +
 +      // Denorm - FlushToZero - binary operations
 +      for (size_t i = 0 ; i < typeTestResults->binaryOpFTZ.size() ; ++i)
 +      {
 +              const BinaryCase&       binaryCase      = typeTestResults->binaryOpFTZ[i];
 +              OperationId                     operation       = binaryCase.operationId;
 +              testCases.push_back(OTC("denorm_op_var_flush_to_zero",          B_DENORM_FLUSH, operation, V_DENORM, V_ONE,             binaryCase.opVarResult));
 +              testCases.push_back(OTC("denorm_op_denorm_flush_to_zero",       B_DENORM_FLUSH, operation, V_DENORM, V_DENORM,  binaryCase.opDenormResult));
 +              testCases.push_back(OTC("denorm_op_inf_flush_to_zero",          B_DENORM_FLUSH, operation, V_DENORM, V_INF,             binaryCase.opInfResult));
 +              testCases.push_back(OTC("denorm_op_nan_flush_to_zero",          B_DENORM_FLUSH, operation, V_DENORM, V_NAN,             binaryCase.opNanResult));
 +      }
 +
 +      // Denorm - FlushToZero - unary operations
 +      for (size_t i = 0 ; i < typeTestResults->unaryOpFTZ.size() ; ++i)
 +      {
 +              const UnaryCase&        unaryCase = typeTestResults->unaryOpFTZ[i];
 +              OperationId                     operation = unaryCase.operationId;
 +              testCases.push_back(OTC("op_denorm_flush_to_zero", B_DENORM_FLUSH, operation, V_DENORM, V_UNUSED, unaryCase.result));
 +      }
 +
 +      // Denom - Preserve - binary operations
 +      for (size_t i = 0 ; i < typeTestResults->binaryOpDenormPreserve.size() ; ++i)
 +      {
 +              const BinaryCase&       binaryCase      = typeTestResults->binaryOpDenormPreserve[i];
 +              OperationId                     operation       = binaryCase.operationId;
 +              testCases.push_back(OTC("denorm_op_var_preserve",                       B_DENORM_PERSERVE, operation, V_DENORM, V_ONE,          binaryCase.opVarResult));
 +              testCases.push_back(OTC("denorm_op_denorm_preserve",            B_DENORM_PERSERVE, operation, V_DENORM, V_DENORM,       binaryCase.opDenormResult));
 +              testCases.push_back(OTC("denorm_op_inf_preserve",                       B_DENORM_PERSERVE, operation, V_DENORM, V_INF,          binaryCase.opInfResult));
 +              testCases.push_back(OTC("denorm_op_nan_preserve",                       B_DENORM_PERSERVE, operation, V_DENORM, V_NAN,          binaryCase.opNanResult));
 +      }
 +
 +      // Denom - Preserve - unary operations
 +      for (size_t i = 0 ; i < typeTestResults->unaryOpDenormPreserve.size() ; ++i)
 +      {
 +              const UnaryCase&        unaryCase       = typeTestResults->unaryOpDenormPreserve[i];
 +              OperationId                     operation       = unaryCase.operationId;
 +              testCases.push_back(OTC("op_denorm_preserve", B_DENORM_PERSERVE, operation, V_DENORM, V_UNUSED, unaryCase.result));
 +      }
 +
 +      struct ZINCase
 +      {
 +              OperationId     operationId;
 +              bool            supportedByFP64;
 +              ValueId         secondArgument;
 +              ValueId         preserveZeroResult;
 +              ValueId         preserveSZeroResult;
 +              ValueId         preserveInfResult;
 +              ValueId         preserveSInfResult;
 +              ValueId         preserveNanResult;
 +      };
 +
 +      const ZINCase binaryOpZINPreserve[] = {
 +              // operation            fp64    second arg              preserve zero   preserve szero          preserve inf    preserve sinf           preserve nan
 +              { O_PHI,                        true,   V_INF,                  V_ZERO,                 V_MINUS_ZERO,           V_INF,                  V_MINUS_INF,            V_NAN },
 +              { O_SELECT,                     true,   V_ONE,                  V_ZERO,                 V_MINUS_ZERO,           V_INF,                  V_MINUS_INF,            V_NAN },
 +              { O_ADD,                        true,   V_ZERO,                 V_ZERO,                 V_ZERO,                         V_INF,                  V_MINUS_INF,            V_NAN },
 +              { O_SUB,                        true,   V_ZERO,                 V_ZERO,                 V_MINUS_ZERO,           V_INF,                  V_MINUS_INF,            V_NAN },
 +              { O_MUL,                        true,   V_ONE,                  V_ZERO,                 V_MINUS_ZERO,           V_INF,                  V_MINUS_INF,            V_NAN },
 +      };
 +
 +      const ZINCase unaryOpZINPreserve[] = {
 +              // operation                            fp64    second arg              preserve zero   preserve szero          preserve inf    preserve sinf           preserve nan
 +              { O_RETURN_VAL,                         true,   V_UNUSED,               V_ZERO,                 V_MINUS_ZERO,           V_INF,                  V_MINUS_INF,            V_NAN },
 +              { O_D_EXTRACT,                          true,   V_UNUSED,               V_ZERO,                 V_MINUS_ZERO,           V_INF,                  V_MINUS_INF,            V_NAN },
 +              { O_D_INSERT,                           true,   V_UNUSED,               V_ZERO,                 V_MINUS_ZERO,           V_INF,                  V_MINUS_INF,            V_NAN },
 +              { O_SHUFFLE,                            true,   V_UNUSED,               V_ZERO,                 V_MINUS_ZERO,           V_INF,                  V_MINUS_INF,            V_NAN },
 +              { O_COMPOSITE,                          true,   V_UNUSED,               V_ZERO,                 V_MINUS_ZERO,           V_INF,                  V_MINUS_INF,            V_NAN },
 +              { O_COMPOSITE_INS,                      true,   V_UNUSED,               V_ZERO,                 V_MINUS_ZERO,           V_INF,                  V_MINUS_INF,            V_NAN },
 +              { O_COPY,                                       true,   V_UNUSED,               V_ZERO,                 V_MINUS_ZERO,           V_INF,                  V_MINUS_INF,            V_NAN },
 +              { O_TRANSPOSE,                          true,   V_UNUSED,               V_ZERO,                 V_MINUS_ZERO,           V_INF,                  V_MINUS_INF,            V_NAN },
 +              { O_NEGATE,                                     true,   V_UNUSED,               V_MINUS_ZERO,   V_ZERO,                         V_MINUS_INF,    V_INF,                          V_NAN },
 +      };
 +
 +      bool isFP64 = typeTestResults->floatType() == FP64;
 +
 +      // Signed Zero Inf Nan - Preserve - binary operations
 +      for (size_t i = 0 ; i < DE_LENGTH_OF_ARRAY(binaryOpZINPreserve) ; ++i)
 +      {
 +              const ZINCase& zc = binaryOpZINPreserve[i];
 +              if (isFP64 && !zc.supportedByFP64)
 +                      continue;
 +
 +              testCases.push_back(OTC("zero_op_var_preserve",                         B_ZIN_PERSERVE, zc.operationId, V_ZERO,                 zc.secondArgument,      zc.preserveZeroResult));
 +              testCases.push_back(OTC("signed_zero_op_var_preserve",          B_ZIN_PERSERVE, zc.operationId, V_MINUS_ZERO,   zc.secondArgument,      zc.preserveSZeroResult));
 +              testCases.push_back(OTC("inf_op_var_preserve",                          B_ZIN_PERSERVE, zc.operationId, V_INF,                  zc.secondArgument,      zc.preserveInfResult));
 +              testCases.push_back(OTC("signed_inf_op_var_preserve",           B_ZIN_PERSERVE, zc.operationId, V_MINUS_INF,    zc.secondArgument,      zc.preserveSInfResult));
 +              testCases.push_back(OTC("nan_op_var_preserve",                          B_ZIN_PERSERVE, zc.operationId, V_NAN,                  zc.secondArgument,      zc.preserveNanResult));
 +      }
 +
 +      // Signed Zero Inf Nan - Preserve - unary operations
 +      for (size_t i = 0 ; i < DE_LENGTH_OF_ARRAY(unaryOpZINPreserve) ; ++i)
 +      {
 +              const ZINCase& zc = unaryOpZINPreserve[i];
 +              if (isFP64 && !zc.supportedByFP64)
 +                      continue;
 +
 +              testCases.push_back(OTC("op_zero_preserve",                     B_ZIN_PERSERVE,zc.operationId, V_ZERO,                  V_UNUSED,       zc.preserveZeroResult));
 +              testCases.push_back(OTC("op_signed_zero_preserve",      B_ZIN_PERSERVE,zc.operationId, V_MINUS_ZERO,    V_UNUSED,       zc.preserveSZeroResult));
 +              testCases.push_back(OTC("op_inf_preserve",                      B_ZIN_PERSERVE,zc.operationId, V_INF,                   V_UNUSED,       zc.preserveInfResult));
 +              testCases.push_back(OTC("op_signed_inf_preserve",       B_ZIN_PERSERVE,zc.operationId, V_MINUS_INF,             V_UNUSED,       zc.preserveSInfResult));
 +              testCases.push_back(OTC("op_nan_preserve",                      B_ZIN_PERSERVE,zc.operationId, V_NAN,                   V_UNUSED,       zc.preserveNanResult));
 +      }
 +
 +      // comparison operations - tested differently because they return true/false
 +      struct ComparisonCase
 +      {
 +              OperationId     operationId;
 +              ValueId         denormPreserveResult;
 +      };
 +      const ComparisonCase comparisonCases[] =
 +      {
 +              // operation    denorm
 +              { O_ORD_EQ,             V_ZERO },
 +              { O_UORD_EQ,    V_ZERO },
 +              { O_ORD_NEQ,    V_ONE  },
 +              { O_UORD_NEQ,   V_ONE  },
 +              { O_ORD_LS,             V_ONE  },
 +              { O_UORD_LS,    V_ONE  },
 +              { O_ORD_GT,             V_ZERO },
 +              { O_UORD_GT,    V_ZERO },
 +              { O_ORD_LE,             V_ONE  },
 +              { O_UORD_LE,    V_ONE  },
 +              { O_ORD_GE,             V_ZERO },
 +              { O_UORD_GE,    V_ZERO }
 +      };
 +      for (int op = 0 ; op < DE_LENGTH_OF_ARRAY(comparisonCases) ; ++op)
 +      {
 +              const ComparisonCase& cc = comparisonCases[op];
 +              testCases.push_back(OTC("denorm_op_var_preserve", B_DENORM_PERSERVE, cc.operationId, V_DENORM, V_ONE, cc.denormPreserveResult));
 +      }
 +
 +      if (argumentsFromInput)
 +      {
 +              struct RoundingModeCase
 +              {
 +                      OperationId     operationId;
 +                      ValueId         arg1;
 +                      ValueId         arg2;
 +                      ValueId         expectedRTEResult;
 +                      ValueId         expectedRTZResult;
 +              };
 +
 +              const RoundingModeCase roundingCases[] =
 +              {
 +                      { O_ADD,                        V_ADD_ARG_A,    V_ADD_ARG_B,    V_ADD_RTE_RESULT,       V_ADD_RTZ_RESULT },
 +                      { O_SUB,                        V_SUB_ARG_A,    V_SUB_ARG_B,    V_SUB_RTE_RESULT,       V_SUB_RTZ_RESULT },
 +                      { O_MUL,                        V_MUL_ARG_A,    V_MUL_ARG_B,    V_MUL_RTE_RESULT,       V_MUL_RTZ_RESULT },
 +                      { O_DOT,                        V_DOT_ARG_A,    V_DOT_ARG_B,    V_DOT_RTE_RESULT,       V_DOT_RTZ_RESULT },
 +
 +                      // in vect/mat multiplication by scalar operations only first element of result is checked
 +                      // so argument and result values prepared for multiplication can be reused for those cases
 +                      { O_VEC_MUL_S,          V_MUL_ARG_A,    V_MUL_ARG_B,    V_MUL_RTE_RESULT,       V_MUL_RTZ_RESULT },
 +                      { O_MAT_MUL_S,          V_MUL_ARG_A,    V_MUL_ARG_B,    V_MUL_RTE_RESULT,       V_MUL_RTZ_RESULT },
 +                      { O_OUT_PROD,           V_MUL_ARG_A,    V_MUL_ARG_B,    V_MUL_RTE_RESULT,       V_MUL_RTZ_RESULT },
 +
 +                      // in SPIR-V code we return first element of operation result so for following
 +                      // cases argument and result values prepared for dot product can be reused
 +                      { O_VEC_MUL_M,          V_DOT_ARG_A,    V_DOT_ARG_B,    V_DOT_RTE_RESULT,       V_DOT_RTZ_RESULT },
 +                      { O_MAT_MUL_V,          V_DOT_ARG_A,    V_DOT_ARG_B,    V_DOT_RTE_RESULT,       V_DOT_RTZ_RESULT },
 +                      { O_MAT_MUL_M,          V_DOT_ARG_A,    V_DOT_ARG_B,    V_DOT_RTE_RESULT,       V_DOT_RTZ_RESULT },
 +
 +                      // conversion operations are added separately - depending on float type width
 +              };
 +
 +              for (int c = 0 ; c < DE_LENGTH_OF_ARRAY(roundingCases) ; ++c)
 +              {
 +                      const RoundingModeCase& rmc = roundingCases[c];
 +                      testCases.push_back(OTC("rounding_rte_op", B_RTE_ROUNDING, rmc.operationId, rmc.arg1, rmc.arg2, rmc.expectedRTEResult));
 +                      testCases.push_back(OTC("rounding_rtz_op", B_RTZ_ROUNDING, rmc.operationId, rmc.arg1, rmc.arg2, rmc.expectedRTZResult));
 +              }
 +      }
 +
 +      // special cases
 +      if (typeTestResults->floatType() == FP16)
 +      {
 +              if (argumentsFromInput)
 +              {
 +                      testCases.push_back(OTC("rounding_rte_conv_from_fp32", B_RTE_ROUNDING, O_CONV_FROM_FP32, V_CONV_FROM_FP32_ARG, V_UNUSED, V_CONV_TO_FP16_RTE_RESULT));
 +                      testCases.push_back(OTC("rounding_rtz_conv_from_fp32", B_RTZ_ROUNDING, O_CONV_FROM_FP32, V_CONV_FROM_FP32_ARG, V_UNUSED, V_CONV_TO_FP16_RTZ_RESULT));
 +                      testCases.push_back(OTC("rounding_rte_conv_from_fp64", B_RTE_ROUNDING, O_CONV_FROM_FP64, V_CONV_FROM_FP64_ARG, V_UNUSED, V_CONV_TO_FP16_RTE_RESULT));
 +                      testCases.push_back(OTC("rounding_rtz_conv_from_fp64", B_RTZ_ROUNDING, O_CONV_FROM_FP64, V_CONV_FROM_FP64_ARG, V_UNUSED, V_CONV_TO_FP16_RTZ_RESULT));
 +
 +                      testCases.push_back(OTC("rounding_rte_sconst_conv_from_fp32", B_RTE_ROUNDING, O_SCONST_CONV_FROM_FP32_TO_FP16, V_UNUSED, V_UNUSED, V_CONV_TO_FP16_RTE_RESULT));
 +                      testCases.push_back(OTC("rounding_rtz_sconst_conv_from_fp32", B_RTZ_ROUNDING, O_SCONST_CONV_FROM_FP32_TO_FP16, V_UNUSED, V_UNUSED, V_CONV_TO_FP16_RTZ_RESULT));
 +                      testCases.push_back(OTC("rounding_rte_sconst_conv_from_fp64", B_RTE_ROUNDING, O_SCONST_CONV_FROM_FP64_TO_FP16, V_UNUSED, V_UNUSED, V_CONV_TO_FP16_RTE_RESULT));
 +                      testCases.push_back(OTC("rounding_rtz_sconst_conv_from_fp64", B_RTZ_ROUNDING, O_SCONST_CONV_FROM_FP64_TO_FP16, V_UNUSED, V_UNUSED, V_CONV_TO_FP16_RTZ_RESULT));
 +
 +                      // verify that VkShaderFloatingPointRoundingModeKHR can be overridden for a given instruction by the FPRoundingMode decoration
 +                      testCases.push_back(OTC("rounding_rte_override", B_RTE_ROUNDING, O_ORTZ_ROUND, V_CONV_FROM_FP32_ARG, V_UNUSED, V_CONV_TO_FP16_RTZ_RESULT));
 +                      testCases.push_back(OTC("rounding_rtz_override", B_RTZ_ROUNDING, O_ORTE_ROUND, V_CONV_FROM_FP32_ARG, V_UNUSED, V_CONV_TO_FP16_RTE_RESULT));
 +              }
 +
 +              createUnaryTestCases(testCases, O_CONV_FROM_FP32, V_CONV_DENORM_SMALLER, V_ZERO);
 +              createUnaryTestCases(testCases, O_CONV_FROM_FP64, V_CONV_DENORM_BIGGER, V_ZERO);
 +      }
 +      else if (typeTestResults->floatType() == FP32)
 +      {
 +              if (argumentsFromInput)
 +              {
 +                      // convert from fp64 to fp32
 +                      testCases.push_back(OTC("rounding_rte_conv_from_fp64", B_RTE_ROUNDING, O_CONV_FROM_FP64, V_CONV_FROM_FP64_ARG, V_UNUSED, V_CONV_TO_FP32_RTE_RESULT));
 +                      testCases.push_back(OTC("rounding_rtz_conv_from_fp64", B_RTZ_ROUNDING, O_CONV_FROM_FP64, V_CONV_FROM_FP64_ARG, V_UNUSED, V_CONV_TO_FP32_RTZ_RESULT));
 +
 +                      testCases.push_back(OTC("rounding_rte_sconst_conv_from_fp64", B_RTE_ROUNDING, O_SCONST_CONV_FROM_FP64_TO_FP32, V_UNUSED, V_UNUSED, V_CONV_TO_FP32_RTE_RESULT));
 +                      testCases.push_back(OTC("rounding_rtz_sconst_conv_from_fp64", B_RTZ_ROUNDING, O_SCONST_CONV_FROM_FP64_TO_FP32, V_UNUSED, V_UNUSED, V_CONV_TO_FP32_RTZ_RESULT));
 +              }
 +              else
 +              {
 +                      // PackHalf2x16 - verification done in SPIR-V
 +                      testCases.push_back(OTC("pack_half_denorm_preserve",            B_DENORM_PERSERVE,      O_PH_DENORM,    V_UNUSED, V_UNUSED, V_ONE));
 +
 +                      // UnpackHalf2x16 - custom arguments defined as constants
 +                      testCases.push_back(OTC("upack_half_denorm_flush_to_zero",      B_DENORM_FLUSH,         O_UPH_DENORM,   V_UNUSED, V_UNUSED, V_ZERO));
 +                      testCases.push_back(OTC("upack_half_denorm_preserve",           B_DENORM_PERSERVE,      O_UPH_DENORM,   V_UNUSED, V_UNUSED, V_CONV_DENORM_SMALLER));
 +              }
 +
 +              createUnaryTestCases(testCases, O_CONV_FROM_FP16, V_CONV_DENORM_SMALLER, V_ZERO_OR_FP16_DENORM_TO_FP32);
 +              createUnaryTestCases(testCases, O_CONV_FROM_FP64, V_CONV_DENORM_BIGGER, V_ZERO);
 +      }
 +      else // FP64
 +      {
 +              if (!argumentsFromInput)
 +              {
 +                      // PackDouble2x32 - custom arguments defined as constants
 +                      testCases.push_back(OTC("pack_double_denorm_preserve",                  B_DENORM_PERSERVE,      O_PD_DENORM,                    V_UNUSED, V_UNUSED, V_DENORM));
 +
 +                      // UnpackDouble2x32 - verification done in SPIR-V
 +                      testCases.push_back(OTC("upack_double_denorm_flush_to_zero",    B_DENORM_FLUSH,         O_UPD_DENORM_FLUSH,             V_DENORM, V_UNUSED, V_ONE));
 +                      testCases.push_back(OTC("upack_double_denorm_preserve",                 B_DENORM_PERSERVE,      O_UPD_DENORM_PRESERVE,  V_DENORM, V_UNUSED, V_ONE));
 +              }
 +
 +              createUnaryTestCases(testCases, O_CONV_FROM_FP16, V_CONV_DENORM_SMALLER, V_ZERO_OR_FP16_DENORM_TO_FP64);
 +              createUnaryTestCases(testCases, O_CONV_FROM_FP32, V_CONV_DENORM_BIGGER, V_ZERO_OR_FP32_DENORM_TO_FP64);
 +      }
 +}
 +
 +const Operation& TestCasesBuilder::getOperation(OperationId id) const
 +{
 +      return m_operations.at(id);
 +}
 +
 +void TestCasesBuilder::createUnaryTestCases(vector<OperationTestCase>& testCases, OperationId operationId, ValueId denormPreserveResult, ValueId denormFTZResult) const
 +{
 +      // Denom - Preserve
 +      testCases.push_back(OTC("op_denorm_preserve",           B_DENORM_PERSERVE,      operationId, V_DENORM,  V_UNUSED, denormPreserveResult));
 +
 +      // Denorm - FlushToZero
 +      testCases.push_back(OTC("op_denorm_flush_to_zero",      B_DENORM_FLUSH,         operationId, V_DENORM,  V_UNUSED, denormFTZResult));
 +
 +      // Signed Zero Inf Nan - Preserve
 +      testCases.push_back(OTC("op_zero_preserve",                     B_ZIN_PERSERVE,         operationId, V_ZERO,            V_UNUSED, V_ZERO));
 +      testCases.push_back(OTC("op_signed_zero_preserve",      B_ZIN_PERSERVE,         operationId, V_MINUS_ZERO,      V_UNUSED, V_MINUS_ZERO));
 +      testCases.push_back(OTC("op_inf_preserve",                      B_ZIN_PERSERVE,         operationId, V_INF,                     V_UNUSED, V_INF));
 +      testCases.push_back(OTC("op_nan_preserve",                      B_ZIN_PERSERVE,         operationId, V_NAN,                     V_UNUSED, V_NAN));
 +}
 +
 +template <typename TYPE, typename FLOAT_TYPE>
 +bool isZeroOrOtherValue(const TYPE& returnedFloat, ValueId secondAcceptableResult, TestLog& log)
 +{
 +      if (returnedFloat.isZero() && !returnedFloat.signBit())
 +              return true;
 +
 +      TypeValues<FLOAT_TYPE> typeValues;
 +      typedef typename TYPE::StorageType SType;
 +      typename RawConvert<FLOAT_TYPE, SType>::Value value;
 +      value.fp = typeValues.getValue(secondAcceptableResult);
 +
 +      if (returnedFloat.bits() == value.ui)
 +              return true;
 +
 +      log << TestLog::Message << "Expected 0 or " << toHex(value.ui)
 +              << " (" << value.fp << ")" << TestLog::EndMessage;
 +      return false;
 +}
 +
 +template <typename TYPE>
 +bool isAcosResultCorrect(const TYPE& returnedFloat, TestLog& log)
 +{
 +      // pi/2 is result of acos(0) which in the specs is defined as equivalent to
 +      // atan2(sqrt(1.0 - x^2), x), where atan2 has 4096 ULP, sqrt is equivalent to
 +      // 1.0 /inversesqrt(), inversesqrt() is 2 ULP and rcp is another 2.5 ULP
 +
 +      double precision = 0;
 +      const double piDiv2 = 3.14159265358979323846 / 2;
 +      if (returnedFloat.MANTISSA_BITS == 23)
 +      {
 +              FloatFormat fp32Format(-126, 127, 23, true, tcu::MAYBE, tcu::YES, tcu::MAYBE);
 +              precision = fp32Format.ulp(piDiv2, 4096.0);
 +      }
 +      else
 +      {
 +              FloatFormat fp16Format(-14, 15, 10, true, tcu::MAYBE);
 +              precision = fp16Format.ulp(piDiv2, 5.0);
 +      }
 +
 +      if (deAbs(returnedFloat.asDouble() - piDiv2) < precision)
 +              return true;
 +
 +      log << TestLog::Message << "Expected result to be in range"
 +              << " (" << piDiv2 - precision << ", " << piDiv2 + precision << "), got "
 +              << returnedFloat.asDouble() << TestLog::EndMessage;
 +      return false;
 +}
 +
 +// Function used to compare test result with expected output.
 +// TYPE can be Float16, Float32 or Float64.
 +// FLOAT_TYPE can be deFloat16, float, double.
 +template <typename TYPE, typename FLOAT_TYPE>
 +bool compareBytes(vector<deUint8>& expectedBytes, AllocationSp outputAlloc, TestLog& log)
 +{
 +      const TYPE* returned    = static_cast<const TYPE*>(outputAlloc->getHostPtr());
 +      const TYPE* fValueId    = reinterpret_cast<const TYPE*>(&expectedBytes.front());
 +
 +      // all test return single value
 +      DE_ASSERT((expectedBytes.size() / sizeof(TYPE)) == 1);
 +
 +      // during test setup we do not store expected value but id that can be used to
 +      // retrieve actual value - this is done to handle special cases like multiple
 +      // allowed results or epsilon checks for some cases
 +      // note that this is workaround - this should be done by changing
 +      // ComputerShaderCase and GraphicsShaderCase so that additional arguments can
 +      // be passed to this verification callback
 +      typedef typename TYPE::StorageType SType;
 +      SType           expectedInt             = fValueId[0].bits();
 +      ValueId         expectedValueId = static_cast<ValueId>(expectedInt);
 +
 +      // something went wrong, expected value cant be V_UNUSED,
 +      // if this is the case then test shouldn't be created at all
 +      DE_ASSERT(expectedValueId != V_UNUSED);
 +
 +      TYPE returnedFloat = returned[0];
 +
 +      log << TestLog::Message << "Calculated result: " << toHex(returnedFloat.bits())
 +              << " (" << returnedFloat.asFloat() << ")" << TestLog::EndMessage;
 +
 +      if (expectedValueId == V_NAN)
 +      {
 +              if (returnedFloat.isNaN())
 +                      return true;
 +
 +              log << TestLog::Message << "Expected NaN" << TestLog::EndMessage;
 +              return false;
 +      }
 +
 +      if (expectedValueId == V_DENORM)
 +      {
 +              if (returnedFloat.isDenorm())
 +                      return true;
 +
 +              log << TestLog::Message << "Expected Denorm" << TestLog::EndMessage;
 +              return false;
 +      }
 +
 +      // handle multiple acceptable results cases
 +      if (expectedValueId == V_ZERO_OR_MINUS_ZERO)
 +      {
 +              if (returnedFloat.isZero())
 +                      return true;
 +
 +              log << TestLog::Message << "Expected 0 or -0" << TestLog::EndMessage;
 +              return false;
 +      }
 +      if ((expectedValueId == V_ZERO_OR_FP16_DENORM_TO_FP32) || (expectedValueId == V_ZERO_OR_FP16_DENORM_TO_FP64))
 +              return isZeroOrOtherValue<TYPE, FLOAT_TYPE>(returnedFloat, V_CONV_DENORM_SMALLER, log);
 +      if (expectedValueId == V_ZERO_OR_FP32_DENORM_TO_FP64)
 +              return isZeroOrOtherValue<TYPE, FLOAT_TYPE>(returnedFloat, V_CONV_DENORM_BIGGER, log);
 +      if (expectedValueId == V_MINUS_ONE_OR_CLOSE)
 +      {
 +              // this expected value is only needed for fp16
 +              DE_ASSERT(returnedFloat.EXPONENT_BIAS == 15);
 +              typename TYPE::StorageType returnedValue = returnedFloat.bits();
 +              return (returnedValue == 0xbc00) || (returnedValue == 0xbbff);
 +      }
 +
 +      // handle acos(0) case
 +      if (expectedValueId == V_PI_DIV_2)
 +              return isAcosResultCorrect<TYPE>(returnedFloat, log);
 +
 +      TypeValues<FLOAT_TYPE> typeValues;
 +      typename RawConvert<FLOAT_TYPE, SType>::Value value;
 +      value.fp = typeValues.getValue(expectedValueId);
 +
 +      if (returnedFloat.bits() == value.ui)
 +              return true;
 +
 +      log << TestLog::Message << "Expected " << toHex(value.ui)
 +              << " (" << value.fp << ")" << TestLog::EndMessage;
 +      return false;
 +}
 +
 +template <typename TYPE, typename FLOAT_TYPE>
 +bool checkFloats (const vector<Resource>&             ,
 +                                                const vector<AllocationSp>&   outputAllocs,
 +                                                const vector<Resource>&               expectedOutputs,
 +                                                TestLog&                                              log)
 +{
 +      if (outputAllocs.size() != expectedOutputs.size())
 +              return false;
 +
 +      for (deUint32 outputNdx = 0; outputNdx < outputAllocs.size(); ++outputNdx)
 +      {
 +              vector<deUint8> expectedBytes;
 +              expectedOutputs[outputNdx].getBytes(expectedBytes);
 +
 +              if (!compareBytes<TYPE, FLOAT_TYPE>(expectedBytes, outputAllocs[outputNdx], log))
 +                      return false;
 +      }
 +
 +      return true;
 +}
 +
 +// Base class for ComputeTestGroupBuilder and GrephicstestGroupBuilder classes.
 +// It contains all functionalities that are used by both child classes.
 +class TestGroupBuilderBase
 +{
 +public:
 +
 +      TestGroupBuilderBase();
 +      virtual ~TestGroupBuilderBase() {}
 +
 +      void init();
 +
 +      virtual void createTests(TestCaseGroup* group,
 +                                                       FloatType floatType,
 +                                                       bool argumentsFromInput) = 0;
 +
 +protected:
 +
 +      typedef vector<OperationTestCase> TestCaseVect;
 +
 +      // Structure containing all data required to create single test.
 +      struct TestCaseInfo
 +      {
 +              FloatType                                       outFloatType;
 +              bool                                            argumentsFromInput;
 +              VkShaderStageFlagBits           testedStage;
 +              const Operation&                        operation;
 +              const OperationTestCase&        testCase;
 +      };
 +
 +      void specializeOperation(const TestCaseInfo&    testCaseInfo,
 +                                                       SpecializedOperation&  specializedOperation) const;
 +
 +      void getBehaviorCapabilityAndExecutionMode(BehaviorId behaviorId,
 +                                                                                         const string inBitWidth,
 +                                                                                         const string outBitWidth,
 +                                                                                         string& capability,
 +                                                                                         string& executionMode) const;
 +
 +      void setupVulkanFeatures(FloatType                      inFloatType,
 +                                                       FloatType                      outFloatType,
 +                                                       BehaviorId                     behaviorId,
 +                                                       bool                           float64FeatureRequired,
 +                                                       VulkanFeatures&        features) const;
 +
 +protected:
 +
 +      struct TypeData
 +      {
 +              TypeValuesSP            values;
 +              TypeSnippetsSP          snippets;
 +              TypeTestResultsSP       testResults;
 +      };
 +
 +      // Type specific parameters are stored in this map.
 +      map<FloatType, TypeData> m_typeData;
 +
 +      // Map converting behaviuor id to OpCapability instruction
 +      map<BehaviorId, string> m_behaviorToName;
 +};
 +
 +TestGroupBuilderBase::TestGroupBuilderBase()
 +{
 +      m_typeData[FP16] = TypeData();
 +      m_typeData[FP16].values                 = TypeValuesSP(new TypeValues<deFloat16>);
 +      m_typeData[FP16].snippets               = TypeSnippetsSP(new TypeSnippets<deFloat16>);
 +      m_typeData[FP16].testResults    = TypeTestResultsSP(new TypeTestResults<deFloat16>);
 +      m_typeData[FP32] = TypeData();
 +      m_typeData[FP32].values                 = TypeValuesSP(new TypeValues<float>);
 +      m_typeData[FP32].snippets               = TypeSnippetsSP(new TypeSnippets<float>);
 +      m_typeData[FP32].testResults    = TypeTestResultsSP(new TypeTestResults<float>);
 +      m_typeData[FP64] = TypeData();
 +      m_typeData[FP64].values                 = TypeValuesSP(new TypeValues<double>);
 +      m_typeData[FP64].snippets               = TypeSnippetsSP(new TypeSnippets<double>);
 +      m_typeData[FP64].testResults    = TypeTestResultsSP(new TypeTestResults<double>);
 +
 +      m_behaviorToName[B_DENORM_PERSERVE]     = "DenormPreserve";
 +      m_behaviorToName[B_DENORM_FLUSH]        = "DenormFlushToZero";
 +      m_behaviorToName[B_ZIN_PERSERVE]        = "SignedZeroInfNanPreserve";
 +      m_behaviorToName[B_RTE_ROUNDING]        = "RoundingModeRTE";
 +      m_behaviorToName[B_RTZ_ROUNDING]        = "RoundingModeRTZ";
 +}
 +
 +void TestGroupBuilderBase::specializeOperation(const TestCaseInfo&            testCaseInfo,
 +                                                                                         SpecializedOperation&        specializedOperation) const
 +{
 +      const string            typeToken               = "_float";
 +      const string            widthToken              = "${float_width}";
 +
 +      FloatType                               outFloatType    = testCaseInfo.outFloatType;
 +      const Operation&                operation               = testCaseInfo.operation;
 +      const TypeSnippetsSP    outTypeSnippets = m_typeData.at(outFloatType).snippets;
 +      const bool                              inputRestricted = operation.isInputTypeRestricted;
 +      FloatType                               inFloatType             = operation.restrictedInputType;
 +
 +      // usually input type is same as output but this is not the case for conversion
 +      // operations; in those cases operation definitions have restricted input type
 +      inFloatType = inputRestricted ? inFloatType : outFloatType;
 +
 +      TypeSnippetsSP inTypeSnippets = m_typeData.at(inFloatType).snippets;
 +
 +      const string inTypePrefix       = string("_f") + inTypeSnippets->bitWidth;
 +      const string outTypePrefix      = string("_f") + outTypeSnippets->bitWidth;
 +
 +      specializedOperation.constans           = replace(operation.constants, typeToken, inTypePrefix);
 +      specializedOperation.annotations        = replace(operation.annotations, widthToken, outTypeSnippets->bitWidth);
 +      specializedOperation.types                      = replace(operation.types, typeToken, outTypePrefix);
 +      specializedOperation.commands           = replace(operation.commands, typeToken, outTypePrefix);
 +
 +      specializedOperation.inFloatType                = inFloatType;
 +      specializedOperation.inTypeSnippets             = inTypeSnippets;
 +      specializedOperation.outTypeSnippets    = outTypeSnippets;
 +
 +      if (operation.isSpecConstant)
 +              return;
 +
 +      // select way arguments are prepared
 +      if (testCaseInfo.argumentsFromInput)
 +      {
 +              // read arguments from input SSBO in main function
 +              specializedOperation.arguments = inTypeSnippets->argumentsFromInputSnippet;
 +      }
 +      else
 +      {
 +              // generate proper values in main function
 +              const string arg1 = "%arg1                 = ";
 +              const string arg2 = "%arg2                 = ";
 +
 +              const ValueId* inputArguments = testCaseInfo.testCase.input;
 +              if (inputArguments[0] != V_UNUSED)
 +                      specializedOperation.arguments  = arg1 + inTypeSnippets->valueIdToSnippetArgMap.at(inputArguments[0]);
 +              if (inputArguments[1] != V_UNUSED)
 +                      specializedOperation.arguments += arg2 + inTypeSnippets->valueIdToSnippetArgMap.at(inputArguments[1]);
 +      }
 +}
 +
 +
 +void TestGroupBuilderBase::getBehaviorCapabilityAndExecutionMode(BehaviorId behaviorId,
 +                                                                                                                               const string inBitWidth,
 +                                                                                                                               const string outBitWidth,
 +                                                                                                                               string& capability,
 +                                                                                                                               string& executionMode) const
 +{
 +      const string    behaviorName    = m_behaviorToName.at(behaviorId);
 +      bool                    rounding                = (behaviorId == B_RTE_ROUNDING) || (behaviorId == B_RTZ_ROUNDING);
 +
 +      capability              = "OpCapability " + behaviorName + "\n";
 +
 +      // rounding mode should be obeyed for destination type
 +      executionMode   = "OpExecutionMode %main " + behaviorName + " " +
 +                                        (rounding ? outBitWidth : inBitWidth) + "\n";
 +}
 +
 +void TestGroupBuilderBase::setupVulkanFeatures(FloatType              inFloatType,
 +                                                                                         FloatType            outFloatType,
 +                                                                                         BehaviorId           behaviorId,
 +                                                                                         bool                         float64FeatureRequired,
 +                                                                                         VulkanFeatures&      features) const
 +{
 +      features.coreFeatures.shaderFloat64 = float64FeatureRequired;
 +
 +      // request proper float controls features
 +      ExtensionFloatControlsFeatures& floatControls = features.floatControlsProperties;
 +
 +      // rounding mode should obey the destination type
 +      bool rteRounding = (behaviorId == B_RTE_ROUNDING);
 +      bool rtzRounding = (behaviorId == B_RTZ_ROUNDING);
 +      if (rteRounding || rtzRounding)
 +      {
 +              switch(outFloatType)
 +              {
 +              case FP16:
 +                      floatControls.shaderRoundingModeRTEFloat16 = rteRounding;
 +                      floatControls.shaderRoundingModeRTZFloat16 = rtzRounding;
 +                      return;
 +              case FP32:
 +                      floatControls.shaderRoundingModeRTEFloat32 = rteRounding;
 +                      floatControls.shaderRoundingModeRTZFloat32 = rtzRounding;
 +                      return;
 +              case FP64:
 +                      floatControls.shaderRoundingModeRTEFloat64 = rteRounding;
 +                      floatControls.shaderRoundingModeRTZFloat64 = rtzRounding;
 +                      return;
 +              }
 +      }
 +
 +      switch(inFloatType)
 +      {
 +      case FP16:
 +              floatControls.shaderDenormPreserveFloat16                       = behaviorId == B_DENORM_PERSERVE;
 +              floatControls.shaderDenormFlushToZeroFloat16            = behaviorId == B_DENORM_FLUSH;
 +              floatControls.shaderSignedZeroInfNanPreserveFloat16     = behaviorId == B_ZIN_PERSERVE;
 +              return;
 +      case FP32:
 +              floatControls.shaderDenormPreserveFloat32                       = behaviorId == B_DENORM_PERSERVE;
 +              floatControls.shaderDenormFlushToZeroFloat32            = behaviorId == B_DENORM_FLUSH;
 +              floatControls.shaderSignedZeroInfNanPreserveFloat32     = behaviorId == B_ZIN_PERSERVE;
 +              return;
 +      case FP64:
 +              floatControls.shaderDenormPreserveFloat64                       = behaviorId == B_DENORM_PERSERVE;
 +              floatControls.shaderDenormFlushToZeroFloat64            = behaviorId == B_DENORM_FLUSH;
 +              floatControls.shaderSignedZeroInfNanPreserveFloat64     = behaviorId == B_ZIN_PERSERVE;
 +              return;
 +      }
 +}
 +
 +// ComputeTestGroupBuilder contains logic that creates compute shaders
 +// for all test cases. As most tests in spirv-assembly it uses functionality
 +// implemented in vktSpvAsmComputeShaderTestUtil.cpp.
 +class ComputeTestGroupBuilder: public TestGroupBuilderBase
 +{
 +public:
 +
 +      void init();
 +
 +      void createTests(TestCaseGroup* group, FloatType floatType, bool argumentsFromInput);
 +
 +protected:
 +
 +      void fillShaderSpec(const TestCaseInfo&         testCaseInfo,
 +                                              ComputeShaderSpec&              csSpec) const;
 +
 +private:
 +
 +
 +      StringTemplate          m_shaderTemplate;
 +      TestCasesBuilder        m_testCaseBuilder;
 +};
 +
 +void ComputeTestGroupBuilder::init()
 +{
 +      m_testCaseBuilder.init();
 +
 +      // geenric compute shader template that has code common for all
 +      // float types and all possible operations listed in OperationId enum
 +      m_shaderTemplate.setString(
 +              "OpCapability Shader\n"
 +              "${capabilities}"
 +
 +              "OpExtension \"SPV_KHR_float_controls\"\n"
 +              "${extensions}"
 +
 +              "%std450            = OpExtInstImport \"GLSL.std.450\"\n"
 +              "OpMemoryModel Logical GLSL450\n"
 +              "OpEntryPoint GLCompute %main \"main\" %id\n"
 +              "OpExecutionMode %main LocalSize 1 1 1\n"
 +              "${execution_mode}"
 +
 +              "OpDecorate %id BuiltIn GlobalInvocationId\n"
 +
 +              // some tests require additional annotations
 +              "${annotations}"
 +
 +              "%type_void            = OpTypeVoid\n"
 +              "%type_voidf           = OpTypeFunction %type_void\n"
 +              "%type_bool            = OpTypeBool\n"
 +              "%type_u32             = OpTypeInt 32 0\n"
 +              "%type_i32             = OpTypeInt 32 1\n"
 +              "%type_i32_fptr        = OpTypePointer Function %type_i32\n"
 +              "%type_u32_vec2        = OpTypeVector %type_u32 2\n"
 +              "%type_u32_vec3        = OpTypeVector %type_u32 3\n"
 +              "%type_u32_vec3_ptr    = OpTypePointer Input %type_u32_vec3\n"
 +
 +              "%c_i32_0              = OpConstant %type_i32 0\n"
 +              "%c_i32_1              = OpConstant %type_i32 1\n"
 +              "%c_i32_2              = OpConstant %type_i32 2\n"
 +              "%c_u32_1              = OpConstant %type_u32 1\n"
 +
 +              // if input float type has different width then output then
 +              // both types are defined here along with all types derived from
 +              // them that are commonly used by tests; some tests also define
 +              // their own types (those that are needed just by this single test)
 +              "${types}"
 +
 +              // SSBO definitions
 +              "${io_definitions}"
 +
 +              "%id                   = OpVariable %type_u32_vec3_ptr Input\n"
 +
 +              // set of default constants per float type is placed here,
 +              // operation tests can also define additional constants;
 +              // note that O_RETURN_VAL defines function here and becouse
 +              // of that this token needs to be directly before main function
 +              "${constants}"
 +
 +              "%main                 = OpFunction %type_void None %type_voidf\n"
 +              "%label                = OpLabel\n"
 +
 +              // depending on test case arguments are either read from input ssbo
 +              // or generated in spir-v code - in later case shader input is not used
 +              "${arguments}"
 +
 +              // perform test commands
 +              "${commands}"
 +
 +              // save result to SSBO
 +              "${save_result}"
 +
 +              "OpReturn\n"
 +              "OpFunctionEnd\n");
 +}
 +
 +void ComputeTestGroupBuilder::createTests(TestCaseGroup* group, FloatType floatType, bool argumentsFromInput)
 +{
 +      TestContext& testCtx = group->getTestContext();
 +      TestCaseVect testCases;
 +      m_testCaseBuilder.build(testCases, m_typeData[floatType].testResults, argumentsFromInput);
 +
 +      TestCaseVect::const_iterator currTestCase = testCases.begin();
 +      TestCaseVect::const_iterator lastTestCase = testCases.end();
 +      while(currTestCase != lastTestCase)
 +      {
 +              const OperationTestCase& testCase = *currTestCase;
 +              ++currTestCase;
 +
 +              // skip cases with undefined output
 +              if (testCase.expectedOutput == V_UNUSED)
 +                      continue;
 +
 +              TestCaseInfo testCaseInfo =
 +              {
 +                      floatType,
 +                      argumentsFromInput,
 +                      VK_SHADER_STAGE_COMPUTE_BIT,
 +                      m_testCaseBuilder.getOperation(testCase.operationId),
 +                      testCase
 +              };
 +
 +              ComputeShaderSpec       csSpec;
 +
 +              fillShaderSpec(testCaseInfo, csSpec);
 +
 +              string testName = replace(testCase.baseName, "op", testCaseInfo.operation.name);
 +              group->addChild(new SpvAsmComputeShaderCase(testCtx, testName.c_str(), "", csSpec));
 +      }
 +}
 +
 +void ComputeTestGroupBuilder::fillShaderSpec(const TestCaseInfo& testCaseInfo,
 +                                                                                       ComputeShaderSpec& csSpec) const
 +{
 +      // LUT storing functions used to verify test results
 +      const VerifyIOFunc checkFloatsLUT[] =
 +      {
 +              checkFloats<Float16, deFloat16>,
 +              checkFloats<Float32, float>,
 +              checkFloats<Float64, double>
 +      };
 +
 +      const Operation&                        testOperation   = testCaseInfo.operation;
 +      const OperationTestCase&        testCase                = testCaseInfo.testCase;
 +      FloatType                                       outFloatType    = testCaseInfo.outFloatType;
 +
 +      SpecializedOperation specOpData;
 +      specializeOperation(testCaseInfo, specOpData);
 +
 +      TypeSnippetsSP  inTypeSnippets          = specOpData.inTypeSnippets;
 +      TypeSnippetsSP  outTypeSnippets         = specOpData.outTypeSnippets;
 +      FloatType               inFloatType                     = specOpData.inFloatType;
 +
 +      // UnpackHalf2x16 is a corner case - it returns two 32-bit floats but
 +      // internaly operates on fp16 and this type should be used by float controls
 +      FloatType               inFloatTypeForCaps              = inFloatType;
 +      string                  inFloatWidthForCaps             = inTypeSnippets->bitWidth;
 +      if (testCase.operationId == O_UPH_DENORM)
 +      {
 +              inFloatTypeForCaps      = FP16;
 +              inFloatWidthForCaps     = "16";
 +      }
 +
 +      string behaviorCapability;
 +      string behaviorExecutionMode;
 +      getBehaviorCapabilityAndExecutionMode(testCase.behaviorId,
 +                                                                                inFloatWidthForCaps,
 +                                                                                outTypeSnippets->bitWidth,
 +                                                                                behaviorCapability,
 +                                                                                behaviorExecutionMode);
 +
 +      string capabilities             = behaviorCapability + outTypeSnippets->capabilities;
 +      string extensions               = outTypeSnippets->extensions;
 +      string annotations              = inTypeSnippets->inputAnnotationsSnippet + outTypeSnippets->outputAnnotationsSnippet +
 +                                                        outTypeSnippets->typeAnnotationsSnippet;
 +      string types                    = outTypeSnippets->typeDefinitionsSnippet;
 +      string constants                = outTypeSnippets->constantsDefinitionsSnippet;
 +      string ioDefinitions    = inTypeSnippets->inputDefinitionsSnippet + outTypeSnippets->outputDefinitionsSnippet;
 +
 +      if (testOperation.isInputTypeRestricted)
 +      {
 +              annotations             += inTypeSnippets->typeAnnotationsSnippet;
 +              capabilities    += inTypeSnippets->capabilities;
 +              extensions              += inTypeSnippets->extensions;
 +              types                   += inTypeSnippets->typeDefinitionsSnippet;
 +              constants               += inTypeSnippets->constantsDefinitionsSnippet;
 +      }
 +
 +      map<string, string> specializations;
 +      specializations["capabilities"]         = capabilities;
 +      specializations["extensions"]           = extensions;
 +      specializations["execution_mode"]       = behaviorExecutionMode;
 +      specializations["annotations"]          = annotations + specOpData.annotations;
 +      specializations["types"]                        = types + specOpData.types;
 +      specializations["constants"]            = constants + specOpData.constans;
 +      specializations["io_definitions"]       = ioDefinitions;
 +      specializations["arguments"]            = specOpData.arguments;
 +      specializations["commands"]                     = specOpData.commands;
 +      specializations["save_result"]          = outTypeSnippets->storeResultsSnippet;
 +
 +      // specialize shader
 +      const string shaderCode = m_shaderTemplate.specialize(specializations);
 +
 +      // construct input and output buffers of proper types
 +      TypeValuesSP inTypeValues       = m_typeData.at(inFloatType).values;
 +      TypeValuesSP outTypeValues      = m_typeData.at(outFloatType).values;
 +      BufferSp inBufferSp                     = inTypeValues->constructInputBuffer(testCase.input);
 +      BufferSp outBufferSp            = outTypeValues->constructOutputBuffer(testCase.expectedOutput);
 +      csSpec.inputs.push_back(Resource(inBufferSp, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
 +      csSpec.outputs.push_back(Resource(outBufferSp));
 +
 +      // check which format features are needed
 +      bool float16FeatureRequired = (outFloatType == FP16) || (inFloatType == FP16);
 +      bool float64FeatureRequired = (outFloatType == FP64) || (inFloatType == FP64);
 +
 +      setupVulkanFeatures(inFloatTypeForCaps,         // usualy same as inFloatType - different only for UnpackHalf2x16
 +                                              outFloatType,
 +                                              testCase.behaviorId,
 +                                              float64FeatureRequired,
 +                                              csSpec.requestedVulkanFeatures);
 +
 +      csSpec.assembly                 = shaderCode;
 +      csSpec.numWorkGroups    = IVec3(1, 1, 1);
 +      csSpec.verifyIO                 = checkFloatsLUT[outFloatType];
 +
 +      csSpec.extensions.push_back("VK_KHR_shader_float_controls");
 +      if (float16FeatureRequired)
 +      {
 +              csSpec.extensions.push_back("VK_KHR_16bit_storage");
 +              csSpec.requestedVulkanFeatures.ext16BitStorage = EXT16BITSTORAGEFEATURES_UNIFORM_BUFFER_BLOCK;
 +      }
 +      if (float64FeatureRequired)
 +              csSpec.requestedVulkanFeatures.coreFeatures.shaderFloat64 = VK_TRUE;
 +}
 +
 +void getGraphicsShaderCode (vk::SourceCollections& dst, InstanceContext context)
 +{
 +      // this function is used only by GraphicsTestGroupBuilder but it couldn't
 +      // be implemented as a method because of how addFunctionCaseWithPrograms
 +      // was implemented
 +
-               << SpirVAsmBuildOptions(targetSpirvVersion);
++      SpirvVersion    targetSpirvVersion      = context.resources.spirvVersion;
++      const deUint32  vulkanVersion           = dst.usedVulkanVersion;
 +
 +      static const string vertexTemplate =
 +              "OpCapability Shader\n"
 +              "OpCapability ClipDistance\n"
 +              "OpCapability CullDistance\n"
 +              "${vert_capabilities}"
 +
 +              "OpExtension \"SPV_KHR_float_controls\"\n"
 +              "${vert_extensions}"
 +
 +              "%std450            = OpExtInstImport \"GLSL.std.450\"\n"
 +              "OpMemoryModel Logical GLSL450\n"
 +              "OpEntryPoint Vertex %main \"main\" %BP_stream %BP_position %BP_color %BP_gl_VertexIndex %BP_gl_InstanceIndex %BP_vertex_color %BP_vertex_result \n"
 +              "${vert_execution_mode}"
 +
 +              "OpMemberDecorate %BP_gl_PerVertex 0 BuiltIn Position\n"
 +              "OpMemberDecorate %BP_gl_PerVertex 1 BuiltIn PointSize\n"
 +              "OpMemberDecorate %BP_gl_PerVertex 2 BuiltIn ClipDistance\n"
 +              "OpMemberDecorate %BP_gl_PerVertex 3 BuiltIn CullDistance\n"
 +              "OpDecorate %BP_gl_PerVertex Block\n"
 +              "OpDecorate %BP_position Location 0\n"
 +              "OpDecorate %BP_color Location 1\n"
 +              "OpDecorate %BP_vertex_color Location 1\n"
 +              "OpDecorate %BP_vertex_result Location 2\n"
 +              "OpDecorate %BP_vertex_result Flat\n"
 +              "OpDecorate %BP_gl_VertexIndex BuiltIn VertexIndex\n"
 +              "OpDecorate %BP_gl_InstanceIndex BuiltIn InstanceIndex\n"
 +
 +              // some tests require additional annotations
 +              "${vert_annotations}"
 +
 +              // types required by most of tests
 +              "%type_void            = OpTypeVoid\n"
 +              "%type_voidf           = OpTypeFunction %type_void\n"
 +              "%type_bool            = OpTypeBool\n"
 +              "%type_i32             = OpTypeInt 32 1\n"
 +              "%type_u32             = OpTypeInt 32 0\n"
 +              "%type_u32_vec2        = OpTypeVector %type_u32 2\n"
 +              "%type_i32_iptr        = OpTypePointer Input %type_i32\n"
 +              "%type_i32_optr        = OpTypePointer Output %type_i32\n"
 +              "%type_i32_fptr        = OpTypePointer Function %type_i32\n"
 +
 +              // constants required by most of tests
 +              "%c_i32_0              = OpConstant %type_i32 0\n"
 +              "%c_i32_1              = OpConstant %type_i32 1\n"
 +              "%c_i32_2              = OpConstant %type_i32 2\n"
 +              "%c_u32_1              = OpConstant %type_u32 1\n"
 +
 +              // if input float type has different width then output then
 +              // both types are defined here along with all types derived from
 +              // them that are commonly used by tests; some tests also define
 +              // their own types (those that are needed just by this single test)
 +              "${vert_types}"
 +
 +              // SSBO is not universally supported for storing
 +              // data in vertex stages - it is onle read here
 +              "${vert_io_definitions}"
 +
 +              "%BP_gl_PerVertex      = OpTypeStruct %type_f32_vec4 %type_f32 %type_f32_arr_1 %type_f32_arr_1\n"
 +              "%BP_gl_PerVertex_optr = OpTypePointer Output %BP_gl_PerVertex\n"
 +              "%BP_stream            = OpVariable %BP_gl_PerVertex_optr Output\n"
 +              "%BP_position          = OpVariable %type_f32_vec4_iptr Input\n"
 +              "%BP_color             = OpVariable %type_f32_vec4_iptr Input\n"
 +              "%BP_gl_VertexIndex    = OpVariable %type_i32_iptr Input\n"
 +              "%BP_gl_InstanceIndex  = OpVariable %type_i32_iptr Input\n"
 +              "%BP_vertex_color      = OpVariable %type_f32_vec4_optr Output\n"
 +
 +              // set of default constants per float type is placed here,
 +              // operation tests can also define additional constants;
 +              // note that O_RETURN_VAL defines function here and becouse
 +              // of that this token needs to be directly before main function
 +              "${vert_constants}"
 +
 +              "%main                 = OpFunction %type_void None %type_voidf\n"
 +              "%label                = OpLabel\n"
 +              "%position             = OpLoad %type_f32_vec4 %BP_position\n"
 +              "%gl_pos               = OpAccessChain %type_f32_vec4_optr %BP_stream %c_i32_0\n"
 +              "OpStore %gl_pos %position\n"
 +              "%color                = OpLoad %type_f32_vec4 %BP_color\n"
 +              "OpStore %BP_vertex_color %color\n"
 +
 +              // this token is filled only when vertex stage is tested;
 +              // depending on test case arguments are either read from input ssbo
 +              // or generated in spir-v code - in later case ssbo is not used
 +              "${vert_arguments}"
 +
 +              // when vertex shader is tested then test operations are performed
 +              // here and passed to fragment stage; if fragment stage ts tested
 +              // then ${comands} and ${vert_process_result} are rplaced with nop
 +              "${vert_commands}"
 +
 +              "${vert_process_result}"
 +
 +              "OpReturn\n"
 +              "OpFunctionEnd\n";
 +
 +
 +      static const string fragmentTemplate =
 +              "OpCapability Shader\n"
 +              "${frag_capabilities}"
 +
 +              "OpExtension \"SPV_KHR_float_controls\"\n"
 +              "${frag_extensions}"
 +
 +              "%std450            = OpExtInstImport \"GLSL.std.450\"\n"
 +              "OpMemoryModel Logical GLSL450\n"
 +              "OpEntryPoint Fragment %main \"main\" %BP_vertex_color %BP_vertex_result %BP_fragColor %BP_gl_FragCoord \n"
 +              "OpExecutionMode %main OriginUpperLeft\n"
 +              "${frag_execution_mode}"
 +
 +              "OpDecorate %BP_fragColor Location 0\n"
 +              "OpDecorate %BP_vertex_color Location 1\n"
 +              "OpDecorate %BP_vertex_result Location 2\n"
 +              "OpDecorate %BP_vertex_result Flat\n"
 +              "OpDecorate %BP_gl_FragCoord BuiltIn FragCoord\n"
 +
 +              // some tests require additional annotations
 +              "${frag_annotations}"
 +
 +              // types required by most of tests
 +              "%type_void            = OpTypeVoid\n"
 +              "%type_voidf           = OpTypeFunction %type_void\n"
 +              "%type_bool            = OpTypeBool\n"
 +              "%type_i32             = OpTypeInt 32 1\n"
 +              "%type_u32             = OpTypeInt 32 0\n"
 +              "%type_u32_vec2        = OpTypeVector %type_u32 2\n"
 +              "%type_i32_iptr        = OpTypePointer Input %type_i32\n"
 +              "%type_i32_optr        = OpTypePointer Output %type_i32\n"
 +              "%type_i32_fptr        = OpTypePointer Function %type_i32\n"
 +
 +              // constants required by most of tests
 +              "%c_i32_0              = OpConstant %type_i32 0\n"
 +              "%c_i32_1              = OpConstant %type_i32 1\n"
 +              "%c_i32_2              = OpConstant %type_i32 2\n"
 +              "%c_u32_1              = OpConstant %type_u32 1\n"
 +
 +              // if input float type has different width then output then
 +              // both types are defined here along with all types derived from
 +              // them that are commonly used by tests; some tests also define
 +              // their own types (those that are needed just by this single test)
 +              "${frag_types}"
 +
 +              "%BP_gl_FragCoord      = OpVariable %type_f32_vec4_iptr Input\n"
 +              "%BP_vertex_color      = OpVariable %type_f32_vec4_iptr Input\n"
 +              "%BP_fragColor         = OpVariable %type_f32_vec4_optr Output\n"
 +
 +              // SSBO definitions
 +              "${frag_io_definitions}"
 +
 +              // set of default constants per float type is placed here,
 +              // operation tests can also define additional constants;
 +              // note that O_RETURN_VAL defines function here and becouse
 +              // of that this token needs to be directly before main function
 +              "${frag_constants}"
 +
 +              "%main                 = OpFunction %type_void None %type_voidf\n"
 +              "%label                = OpLabel\n"
 +
 +              // just pass vertex color - rendered image is not important in our case
 +              "%vertex_color         = OpLoad %type_f32_vec4 %BP_vertex_color\n"
 +              "OpStore %BP_fragColor %vertex_color\n"
 +
 +              // this token is filled only when fragment stage is tested;
 +              // depending on test case arguments are either read from input ssbo or
 +              // generated in spir-v code - in later case ssbo is used only for output
 +              "${frag_arguments}"
 +
 +              // when fragment shader is tested then test operations are performed
 +              // here and saved to ssbo; if vertex stage was tested then its
 +              // result is just saved to ssbo here
 +              "${frag_commands}"
 +              "${frag_process_result}"
 +
 +              "OpReturn\n"
 +              "OpFunctionEnd\n";
 +
 +      dst.spirvAsmSources.add("vert", DE_NULL)
 +              << StringTemplate(vertexTemplate).specialize(context.testCodeFragments)
-               << SpirVAsmBuildOptions(targetSpirvVersion);
++              << SpirVAsmBuildOptions(vulkanVersion, targetSpirvVersion);
 +      dst.spirvAsmSources.add("frag", DE_NULL)
 +              << StringTemplate(fragmentTemplate).specialize(context.testCodeFragments)
++              << SpirVAsmBuildOptions(vulkanVersion, targetSpirvVersion);
 +}
 +
 +// GraphicsTestGroupBuilder iterates over all test cases and creates test for both
 +// vertex and fragment stages. As in most spirv-assembly tests, tests here are also
 +// executed using functionality defined in vktSpvAsmGraphicsShaderTestUtil.cpp but
 +// because one of requirements during development was that SSBO wont be used in
 +// vertex stage we couldn't use createTestForStage functions - we need a custom
 +// version for both vertex and fragmen shaders at the same time. This was required
 +// as we needed to pass result from vertex stage to fragment stage where it could
 +// be saved to ssbo. To achieve that InstanceContext is created manually in
 +// createInstanceContext method.
 +class GraphicsTestGroupBuilder: public TestGroupBuilderBase
 +{
 +public:
 +
 +      void init();
 +
 +      void createTests(TestCaseGroup* group, FloatType floatType, bool argumentsFromInput);
 +
 +protected:
 +
 +      InstanceContext createInstanceContext(const TestCaseInfo& testCaseInfo) const;
 +
 +private:
 +
 +      TestCasesBuilder        m_testCaseBuilder;
 +};
 +
 +void GraphicsTestGroupBuilder::init()
 +{
 +      m_testCaseBuilder.init();
 +}
 +
 +void GraphicsTestGroupBuilder::createTests(TestCaseGroup* group, FloatType floatType, bool argumentsFromInput)
 +{
 +      // create test cases for vertex stage
 +      TestCaseVect testCases;
 +      m_testCaseBuilder.build(testCases, m_typeData[floatType].testResults, argumentsFromInput);
 +
 +      TestCaseVect::const_iterator currTestCase = testCases.begin();
 +      TestCaseVect::const_iterator lastTestCase = testCases.end();
 +      while(currTestCase != lastTestCase)
 +      {
 +              const OperationTestCase& testCase = *currTestCase;
 +              ++currTestCase;
 +
 +              // skip cases with undefined output
 +              if (testCase.expectedOutput == V_UNUSED)
 +                      continue;
 +
 +              TestCaseInfo testCaseInfo =
 +              {
 +                      floatType,
 +                      argumentsFromInput,
 +                      VK_SHADER_STAGE_VERTEX_BIT,
 +                      m_testCaseBuilder.getOperation(testCase.operationId),
 +                      testCase
 +              };
 +
 +              InstanceContext ctxVertex       = createInstanceContext(testCaseInfo);
 +              string                  testName        = replace(testCase.baseName, "op", testCaseInfo.operation.name);
 +
 +              addFunctionCaseWithPrograms<InstanceContext>(group, testName + "_vert", "", getGraphicsShaderCode, runAndVerifyDefaultPipeline, ctxVertex);
 +      }
 +
 +      // create test cases for fragment stage
 +      testCases.clear();
 +      m_testCaseBuilder.build(testCases, m_typeData[floatType].testResults, argumentsFromInput);
 +
 +      currTestCase = testCases.begin();
 +      lastTestCase = testCases.end();
 +      while(currTestCase != lastTestCase)
 +      {
 +              const OperationTestCase& testCase = *currTestCase;
 +              ++currTestCase;
 +
 +              // skip cases with undefined output
 +              if (testCase.expectedOutput == V_UNUSED)
 +                      continue;
 +
 +              TestCaseInfo testCaseInfo =
 +              {
 +                      floatType,
 +                      argumentsFromInput,
 +                      VK_SHADER_STAGE_FRAGMENT_BIT,
 +                      m_testCaseBuilder.getOperation(testCase.operationId),
 +                      testCase
 +              };
 +
 +              InstanceContext ctxFragment     = createInstanceContext(testCaseInfo);
 +              string                  testName        = replace(testCase.baseName, "op", testCaseInfo.operation.name);
 +
 +              addFunctionCaseWithPrograms<InstanceContext>(group, testName + "_frag", "", getGraphicsShaderCode, runAndVerifyDefaultPipeline, ctxFragment);
 +      }
 +}
 +
 +InstanceContext GraphicsTestGroupBuilder::createInstanceContext(const TestCaseInfo& testCaseInfo) const
 +{
 +      // LUT storing functions used to verify test results
 +      const VerifyIOFunc checkFloatsLUT[] =
 +      {
 +              checkFloats<Float16, deFloat16>,
 +              checkFloats<Float32, float>,
 +              checkFloats<Float64, double>
 +      };
 +
 +      // 32-bit float types are always needed for standard operations on color
 +      // if tested operation does not require fp32 for either input or output
 +      // then this minimal type definitions must be appended to types section
 +      const string f32TypeMinimalRequired =
 +              "%type_f32             = OpTypeFloat 32\n"
 +              "%type_f32_arr_1       = OpTypeArray %type_f32 %c_i32_1\n"
 +              "%type_f32_iptr        = OpTypePointer Input %type_f32\n"
 +              "%type_f32_optr        = OpTypePointer Output %type_f32\n"
 +              "%type_f32_vec4        = OpTypeVector %type_f32 4\n"
 +              "%type_f32_vec4_iptr   = OpTypePointer Input %type_f32_vec4\n"
 +              "%type_f32_vec4_optr   = OpTypePointer Output %type_f32_vec4\n";
 +
 +      const Operation&                        testOperation   = testCaseInfo.operation;
 +      const OperationTestCase&        testCase                = testCaseInfo.testCase;
 +      FloatType                                       outFloatType    = testCaseInfo.outFloatType;
 +      VkShaderStageFlagBits           testedStage             = testCaseInfo.testedStage;
 +
 +      DE_ASSERT((testedStage == VK_SHADER_STAGE_VERTEX_BIT) || (testedStage == VK_SHADER_STAGE_FRAGMENT_BIT));
 +
 +      SpecializedOperation specOpData;
 +      specializeOperation(testCaseInfo, specOpData);
 +
 +      TypeSnippetsSP  inTypeSnippets          = specOpData.inTypeSnippets;
 +      TypeSnippetsSP  outTypeSnippets         = specOpData.outTypeSnippets;
 +      FloatType               inFloatType                     = specOpData.inFloatType;
 +
 +      // UnpackHalf2x16 is a corner case - it returns two 32-bit floats but
 +      // internaly operates on fp16 and this type should be used by float controls
 +      FloatType               inFloatTypeForCaps              = inFloatType;
 +      string                  inFloatWidthForCaps             = inTypeSnippets->bitWidth;
 +      if (testCase.operationId == O_UPH_DENORM)
 +      {
 +              inFloatTypeForCaps      = FP16;
 +              inFloatWidthForCaps     = "16";
 +      }
 +
 +      string behaviorCapability;
 +      string behaviorExecutionMode;
 +      getBehaviorCapabilityAndExecutionMode(testCase.behaviorId,
 +                                                                                inFloatWidthForCaps,
 +                                                                                outTypeSnippets->bitWidth,
 +                                                                                behaviorCapability,
 +                                                                                behaviorExecutionMode);
 +
 +      // check which format features are needed
 +      bool float16FeatureRequired = (inFloatType == FP16) || (outFloatType == FP16);
 +      bool float64FeatureRequired = (inFloatType == FP64) || (outFloatType == FP64);
 +
 +      string vertExecutionMode;
 +      string fragExecutionMode;
 +      string vertCapabilities;
 +      string fragCapabilities;
 +      string vertExtensions;
 +      string fragExtensions;
 +      string vertAnnotations;
 +      string fragAnnotations;
 +      string vertTypes;
 +      string fragTypes;
 +      string vertConstants;
 +      string fragConstants;
 +      string vertIODefinitions;
 +      string fragIODefinitions;
 +      string vertArguments;
 +      string fragArguments;
 +      string vertCommands;
 +      string fragCommands;
 +      string vertProcessResult;
 +      string fragProcessResult;
 +
 +      // check if operation should be executed in vertex stage
 +      if (testedStage == VK_SHADER_STAGE_VERTEX_BIT)
 +      {
 +              vertAnnotations = inTypeSnippets->inputAnnotationsSnippet + inTypeSnippets->typeAnnotationsSnippet;
 +              fragAnnotations = outTypeSnippets->outputAnnotationsSnippet + outTypeSnippets->typeAnnotationsSnippet;
 +
 +              // check if input type is different from tested type (conversion operations)
 +              if (testOperation.isInputTypeRestricted)
 +              {
 +                      vertCapabilities        = behaviorCapability + inTypeSnippets->capabilities + outTypeSnippets->capabilities;
 +                      fragCapabilities        = outTypeSnippets->capabilities;
 +                      vertExtensions          = inTypeSnippets->extensions + outTypeSnippets->extensions;
 +                      fragExtensions          = outTypeSnippets->extensions;
 +                      vertTypes                       = inTypeSnippets->typeDefinitionsSnippet + outTypeSnippets->typeDefinitionsSnippet + outTypeSnippets->varyingsTypesSnippet;
 +                      fragTypes                       = outTypeSnippets->typeDefinitionsSnippet + outTypeSnippets->varyingsTypesSnippet;
 +                      vertConstants           = inTypeSnippets->constantsDefinitionsSnippet + outTypeSnippets->constantsDefinitionsSnippet;
 +                      fragConstants           = outTypeSnippets->constantsDefinitionsSnippet;
 +              }
 +              else
 +              {
 +                      // input and output types are the same (majority of operations)
 +
 +                      vertCapabilities        = behaviorCapability + outTypeSnippets->capabilities;
 +                      fragCapabilities        = vertCapabilities;
 +                      vertExtensions          = outTypeSnippets->extensions;
 +                      fragExtensions          = vertExtensions;
 +                      vertTypes                       = outTypeSnippets->typeDefinitionsSnippet + outTypeSnippets->varyingsTypesSnippet;
 +                      fragTypes                       = vertTypes;
 +                      vertConstants           = outTypeSnippets->constantsDefinitionsSnippet;
 +                      fragConstants           = vertConstants;
 +              }
 +
 +              if (outFloatType != FP32)
 +              {
 +                      fragTypes += f32TypeMinimalRequired;
 +                      if (inFloatType != FP32)
 +                              vertTypes += f32TypeMinimalRequired;
 +              }
 +
 +              vertAnnotations += specOpData.annotations;
 +              vertTypes               += specOpData.types;
 +              vertConstants   += specOpData.constans;
 +
 +              vertExecutionMode               = behaviorExecutionMode;
 +              fragExecutionMode               = "";
 +              vertIODefinitions               = inTypeSnippets->inputDefinitionsSnippet + outTypeSnippets->outputVaryingsSnippet;
 +              fragIODefinitions               = outTypeSnippets->outputDefinitionsSnippet + outTypeSnippets->inputVaryingsSnippet;
 +              vertArguments                   = specOpData.arguments;
 +              fragArguments                   = "";
 +              vertCommands                    = specOpData.commands;
 +              fragCommands                    = "";
 +              vertProcessResult               = outTypeSnippets->storeVertexResultSnippet;
 +              fragProcessResult               = outTypeSnippets->loadVertexResultSnippet + outTypeSnippets->storeResultsSnippet;
 +      }
 +      else // perform test in fragment stage - vertex stage is empty
 +      {
 +              fragAnnotations = inTypeSnippets->inputAnnotationsSnippet + inTypeSnippets->typeAnnotationsSnippet +
 +                                                outTypeSnippets->outputAnnotationsSnippet + outTypeSnippets->typeAnnotationsSnippet;
 +
 +              // check if input type is different from tested type
 +              if (testOperation.isInputTypeRestricted)
 +              {
 +                      fragCapabilities        = behaviorCapability + inTypeSnippets->capabilities + outTypeSnippets->capabilities;
 +                      fragExtensions          = inTypeSnippets->extensions + outTypeSnippets->extensions;
 +                      fragTypes                       = inTypeSnippets->typeDefinitionsSnippet + outTypeSnippets->typeDefinitionsSnippet;
 +                      fragConstants           = inTypeSnippets->constantsDefinitionsSnippet + outTypeSnippets->constantsDefinitionsSnippet;
 +              }
 +              else
 +              {
 +                      // input and output types are the same
 +
 +                      fragCapabilities        = behaviorCapability + outTypeSnippets->capabilities;
 +                      fragExtensions          = outTypeSnippets->extensions;
 +                      fragTypes                       = outTypeSnippets->typeDefinitionsSnippet;
 +                      fragConstants           = outTypeSnippets->constantsDefinitionsSnippet;
 +              }
 +
 +              // varying is not used but it needs to be specified so lets use type_i32 for it
 +              string dummyVertVarying = "%BP_vertex_result     = OpVariable %type_i32_optr Output\n";
 +              string dummyFragVarying = "%BP_vertex_result     = OpVariable %type_i32_iptr Input\n";
 +
 +              vertCapabilities        = "";
 +              vertExtensions          = "";
 +              vertAnnotations         = "OpDecorate %type_f32_arr_1 ArrayStride 4\n";
 +              vertTypes                       = f32TypeMinimalRequired;
 +              vertConstants           = "";
 +
 +              if ((outFloatType != FP32) && (inFloatType != FP32))
 +                      fragTypes += f32TypeMinimalRequired;
 +
 +              fragAnnotations += specOpData.annotations;
 +              fragTypes               += specOpData.types;
 +              fragConstants   += specOpData.constans;
 +
 +              vertExecutionMode       = "";
 +              fragExecutionMode       = behaviorExecutionMode;
 +              vertIODefinitions       = dummyVertVarying;
 +              fragIODefinitions       = inTypeSnippets->inputDefinitionsSnippet +
 +                                                        outTypeSnippets->outputDefinitionsSnippet + dummyFragVarying;
 +              vertArguments           = "";
 +              fragArguments           = specOpData.arguments;
 +              vertCommands            = "";
 +              fragCommands            = specOpData.commands;
 +              vertProcessResult       = "";
 +              fragProcessResult       = outTypeSnippets->storeResultsSnippet;
 +      }
 +
 +      map<string, string> specializations;
 +      specializations["vert_capabilities"]    = vertCapabilities;
 +      specializations["vert_extensions"]              = vertExtensions;
 +      specializations["vert_execution_mode"]  = vertExecutionMode;
 +      specializations["vert_annotations"]             = vertAnnotations;
 +      specializations["vert_types"]                   = vertTypes;
 +      specializations["vert_constants"]               = vertConstants;
 +      specializations["vert_io_definitions"]  = vertIODefinitions;
 +      specializations["vert_arguments"]               = vertArguments;
 +      specializations["vert_commands"]                = vertCommands;
 +      specializations["vert_process_result"]  = vertProcessResult;
 +      specializations["frag_capabilities"]    = fragCapabilities;
 +      specializations["frag_extensions"]              = fragExtensions;
 +      specializations["frag_execution_mode"]  = fragExecutionMode;
 +      specializations["frag_annotations"]             = fragAnnotations;
 +      specializations["frag_types"]                   = fragTypes;
 +      specializations["frag_constants"]               = fragConstants;
 +      specializations["frag_io_definitions"]  = fragIODefinitions;
 +      specializations["frag_arguments"]               = fragArguments;
 +      specializations["frag_commands"]                = fragCommands;
 +      specializations["frag_process_result"]  = fragProcessResult;
 +
 +      // colors are not used by the test - input is passed via uniform buffer
 +      RGBA defaultColors[4] = { RGBA::white(), RGBA::red(), RGBA::green(), RGBA::blue() };
 +
 +      // construct input and output buffers of proper types
 +      TypeValuesSP inTypeValues       = m_typeData.at(inFloatType).values;
 +      TypeValuesSP outTypeValues      = m_typeData.at(outFloatType).values;
 +      BufferSp inBufferSp                     = inTypeValues->constructInputBuffer(testCase.input);
 +      BufferSp outBufferSp            = outTypeValues->constructOutputBuffer(testCase.expectedOutput);
 +
 +      vkt::SpirVAssembly::GraphicsResources resources;
 +      resources.inputs.push_back( Resource(inBufferSp, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
 +      resources.outputs.push_back(Resource(outBufferSp, VK_DESCRIPTOR_TYPE_STORAGE_BUFFER));
 +      resources.verifyIO = checkFloatsLUT[outFloatType];
 +
 +      vector<string>                  features;
 +      StageToSpecConstantMap  noSpecConstants;
 +      PushConstants                   noPushConstants;
 +      GraphicsInterfaces              noInterfaces;
 +
 +      VulkanFeatures vulkanFeatures;
 +      setupVulkanFeatures(inFloatTypeForCaps,         // usualy same as inFloatType - different only for UnpackHalf2x16
 +                                              outFloatType,
 +                                              testCase.behaviorId,
 +                                              float64FeatureRequired,
 +                                              vulkanFeatures);
 +      vulkanFeatures.coreFeatures.fragmentStoresAndAtomics = true;
 +
 +      vector<string> extensions;
 +      extensions.push_back("VK_KHR_shader_float_controls");
 +      if (float16FeatureRequired)
 +      {
 +              extensions.push_back("VK_KHR_16bit_storage");
 +              vulkanFeatures.ext16BitStorage = EXT16BITSTORAGEFEATURES_UNIFORM_BUFFER_BLOCK;
 +      }
 +
 +      InstanceContext ctx(defaultColors,
 +                                              defaultColors,
 +                                              specializations,
 +                                              noSpecConstants,
 +                                              noPushConstants,
 +                                              resources,
 +                                              noInterfaces,
 +                                              extensions,
 +                                              features,
 +                                              vulkanFeatures,
 +                                              testedStage);
 +
 +      ctx.moduleMap["vert"].push_back(std::make_pair("main", VK_SHADER_STAGE_VERTEX_BIT));
 +      ctx.moduleMap["frag"].push_back(std::make_pair("main", VK_SHADER_STAGE_FRAGMENT_BIT));
 +
 +      ctx.requiredStages                      = static_cast<VkShaderStageFlagBits>(VK_SHADER_STAGE_VERTEX_BIT | VK_SHADER_STAGE_FRAGMENT_BIT);
 +      ctx.failResult                          = QP_TEST_RESULT_FAIL;
 +      ctx.failMessageTemplate         = "Output doesn't match with expected";
 +
 +      return ctx;
 +}
 +
 +} // anonymous
 +
 +tcu::TestCaseGroup* createFloatControlsTestGroup (TestContext& testCtx, TestGroupBuilderBase* groupBuilder)
 +{
 +      de::MovePtr<TestCaseGroup>      group(new TestCaseGroup(testCtx, "float_controls", "Tests for VK_KHR_shader_float_controls extension"));
 +
 +      struct TestGroup
 +      {
 +              FloatType               floatType;
 +              const char*             groupName;
 +      };
 +      TestGroup testGroups[] =
 +      {
 +              { FP16, "fp16" },
 +              { FP32, "fp32" },
 +              { FP64, "fp64" },
 +      };
 +
 +      for (int i = 0 ; i < DE_LENGTH_OF_ARRAY(testGroups) ; ++i)
 +      {
 +              const TestGroup& testGroup = testGroups[i];
 +              TestCaseGroup* typeGroup = new TestCaseGroup(testCtx, testGroup.groupName, "");
 +              group->addChild(typeGroup);
 +
 +              TestCaseGroup* inputArgsGroup = new TestCaseGroup(testCtx, "input_args", "");
 +              groupBuilder->createTests(inputArgsGroup, testGroup.floatType, true);
 +              typeGroup->addChild(inputArgsGroup);
 +
 +              TestCaseGroup* generatedArgsGroup = new TestCaseGroup(testCtx, "generated_args", "");
 +              groupBuilder->createTests(generatedArgsGroup, testGroup.floatType, false);
 +              typeGroup->addChild(generatedArgsGroup);
 +      }
 +
 +      return group.release();
 +}
 +
 +tcu::TestCaseGroup* createFloatControlsComputeGroup (TestContext& testCtx)
 +{
 +      ComputeTestGroupBuilder computeTestGroupBuilder;
 +      computeTestGroupBuilder.init();
 +
 +      return createFloatControlsTestGroup(testCtx, &computeTestGroupBuilder);
 +}
 +
 +tcu::TestCaseGroup* createFloatControlsGraphicsGroup (TestContext& testCtx)
 +{
 +      GraphicsTestGroupBuilder graphicsTestGroupBuilder;
 +      graphicsTestGroupBuilder.init();
 +
 +      return createFloatControlsTestGroup(testCtx, &graphicsTestGroupBuilder);
 +}
 +
 +} // SpirVAssembly
 +} // vkt
@@@ -311,10 -311,9 +311,10 @@@ public
        VkPhysicalDevice8BitStorageFeaturesKHR                          eightBitStorageFeatures;
        VkPhysicalDevice16BitStorageFeatures                            sixteenBitStorageFeatures;
        VkPhysicalDeviceVariablePointerFeatures                         variablePointerFeatures;
-       VkPhysicalDeviceDescriptorIndexingFeaturesEXT           descriptorIndexingFeatures;
-       VkPhysicalDeviceInlineUniformBlockFeaturesEXT           inlineUniformBlockFeatures;
 +      VkPhysicalDeviceFloat16Int8FeaturesKHR                          float16Int8Features;
        VkPhysicalDeviceVertexAttributeDivisorFeaturesEXT       vertexAttributeDivisorFeatures;
+       VkPhysicalDeviceDescriptorIndexingFeaturesEXT           descriptorIndexingFeatures;
+       VkPhysicalDeviceInlineUniformBlockFeaturesEXT           inlineUniformBlockFeatures;
  
        DeviceFeatures (const InstanceInterface&        vki,
                                        const deUint32                          apiVersion,
                deMemset(&eightBitStorageFeatures, 0, sizeof(eightBitStorageFeatures));
                deMemset(&sixteenBitStorageFeatures, 0, sizeof(sixteenBitStorageFeatures));
                deMemset(&variablePointerFeatures, 0, sizeof(variablePointerFeatures));
 +              deMemset(&descriptorIndexingFeatures, 0, sizeof(descriptorIndexingFeatures));
 +              deMemset(&inlineUniformBlockFeatures, 0, sizeof(inlineUniformBlockFeatures));
 +              deMemset(&float16Int8Features, 0, sizeof(float16Int8Features));
                deMemset(&vertexAttributeDivisorFeatures, 0, sizeof(vertexAttributeDivisorFeatures));
+               deMemset(&descriptorIndexingFeatures, 0, sizeof(descriptorIndexingFeatures));
+               deMemset(&inlineUniformBlockFeatures, 0, sizeof(inlineUniformBlockFeatures));
  
                coreFeatures.sType                                              = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2;
                samplerYCbCrConversionFeatures.sType    = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SAMPLER_YCBCR_CONVERSION_FEATURES;
                eightBitStorageFeatures.sType                   = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_8BIT_STORAGE_FEATURES_KHR;
                sixteenBitStorageFeatures.sType                 = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_16BIT_STORAGE_FEATURES_KHR;
                variablePointerFeatures.sType                   = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VARIABLE_POINTER_FEATURES_KHR;
 +              descriptorIndexingFeatures.sType                = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_INDEXING_FEATURES_EXT;
 +              inlineUniformBlockFeatures.sType                = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_INLINE_UNIFORM_BLOCK_FEATURES_EXT;
 +              float16Int8Features.sType                               = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FLOAT16_INT8_FEATURES_KHR;
                vertexAttributeDivisorFeatures.sType    = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VERTEX_ATTRIBUTE_DIVISOR_FEATURES_EXT;
+               descriptorIndexingFeatures.sType                = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DESCRIPTOR_INDEXING_FEATURES_EXT;
+               inlineUniformBlockFeatures.sType                = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_INLINE_UNIFORM_BLOCK_FEATURES_EXT;
  
 +
                if (isPhysicalDeviceFeatures2Supported(apiVersion, instanceExtensions))
                {
                        void** nextPtr = &coreFeatures.pNext;