blob: dc17a3eda7a91335cd3b64f10dafc9661ca084eb [file] [log] [blame]
/*------------------------------------------------------------------------
* Vulkan Conformance Tests
* ------------------------
*
* Copyright (c) 2020 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 Test copying struct which contains an empty struct.
Test pointer comparisons of empty struct members.
*//*--------------------------------------------------------------------*/
#include "vktSpvAsmComputeShaderTestUtil.hpp"
#include "vktSpvAsmComputeShaderCase.hpp"
#include "vktSpvAsmEmptyStructTests.hpp"
#include "tcuStringTemplate.hpp"
#include "vktTestGroupUtil.hpp"
#include "vktSpvAsmUtils.hpp"
namespace vkt
{
namespace SpirVAssembly
{
namespace
{
bool verifyResult(const std::vector<Resource>&,
const std::vector<AllocationSp>& outputAllocs,
const std::vector<Resource>& expectedOutputs,
tcu::TestLog&)
{
for (deUint32 outputNdx = 0; outputNdx < static_cast<deUint32>(outputAllocs.size()); ++outputNdx)
{
std::vector<deUint8> expectedBytes;
expectedOutputs[outputNdx].getBytes(expectedBytes);
const deUint32 itemCount = static_cast<deUint32>(expectedBytes.size()) / 4u;
const deUint32* returned = static_cast<const deUint32*>(outputAllocs[outputNdx]->getHostPtr());
const deUint32* expected = reinterpret_cast<const deUint32*>(&expectedBytes.front());
for (deUint32 i = 0; i < itemCount; ++i)
{
// skip items with 0 as this is used to mark empty structure
if (expected[i] == 0)
continue;
if (expected[i] != returned[i])
return false;
}
}
return true;
}
void addCopyingComputeGroup(tcu::TestCaseGroup* group)
{
const tcu::StringTemplate shaderTemplate(
"OpCapability Shader\n"
"OpExtension \"SPV_KHR_storage_buffer_storage_class\"\n"
"OpMemoryModel Logical GLSL450\n"
"OpEntryPoint GLCompute %main \"main\" %var_id\n"
"OpExecutionMode %main LocalSize 1 1 1\n"
"OpDecorate %var_id BuiltIn GlobalInvocationId\n"
"OpDecorate %var_input Binding 0\n"
"OpDecorate %var_input DescriptorSet 0\n"
"OpDecorate %var_outdata Binding 1\n"
"OpDecorate %var_outdata DescriptorSet 0\n"
"OpMemberDecorate %type_container_struct 0 Offset 0\n"
"OpMemberDecorate %type_container_struct 1 Offset ${OFFSET_1}\n"
"OpMemberDecorate %type_container_struct 2 Offset ${OFFSET_2}\n"
"OpMemberDecorate %type_container_struct 3 Offset ${OFFSET_3}\n"
"OpDecorate %type_container_struct Block\n"
+ std::string(getComputeAsmCommonTypes()) +
//struct EmptyStruct {};
//struct ContainerStruct {
// int i;
// A a1;
// A a2;
// int j;
//};
//layout(set=, binding = ) buffer block B b;
// types
"%type_empty_struct = OpTypeStruct\n"
"%type_container_struct = OpTypeStruct %i32 %type_empty_struct %type_empty_struct %i32\n"
"%type_container_struct_ubo_ptr = OpTypePointer Uniform %type_container_struct\n"
"%type_container_struct_ssbo_ptr = OpTypePointer StorageBuffer %type_container_struct\n"
// variables
"%var_id = OpVariable %uvec3ptr Input\n"
"${VARIABLES}\n"
// void main function
"%main = OpFunction %void None %voidf\n"
"%label = OpLabel\n"
"${COPYING_METHOD}"
"OpReturn\n"
"OpFunctionEnd\n");
struct BufferType
{
std::string name;
VkDescriptorType descriptorType;
std::vector<deUint32> offsets;
std::vector<int> input;
std::vector<int> expectedOutput;
std::string spirvVariables;
std::string spirvCopyObject;
};
std::vector<BufferType> bufferTypes
{
{
"ubo",
VK_DESCRIPTOR_TYPE_UNIFORM_BUFFER,
// structure decorated as Block for variable in Uniform storage class
// must follow relaxed uniform buffer layout rules and be aligned to 16
{0, 16, 32, 48},
{2, 0, 0, 0, 3, 0, 0, 0, 5, 0, 0, 0, 7, 0, 0, 0},
{2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 0, 0, 0},
"%var_input = OpVariable %type_container_struct_ubo_ptr Uniform\n"
"%var_outdata = OpVariable %type_container_struct_ssbo_ptr StorageBuffer\n",
"%input_copy = OpCopyObject %type_container_struct_ubo_ptr %var_input\n"
},
{
"ssbo",
VK_DESCRIPTOR_TYPE_STORAGE_BUFFER,
{0, 4, 8, 12},
{2, 3, 5, 7 },
{2, 0, 0, 7 },
"%var_input = OpVariable %type_container_struct_ssbo_ptr StorageBuffer\n"
"%var_outdata = OpVariable %type_container_struct_ssbo_ptr StorageBuffer\n",
"%input_copy = OpCopyObject %type_container_struct_ssbo_ptr %var_input\n"
}
};
struct CopyingMethod
{
std::string name;
std::string spirvCopyCode;
};
std::vector<CopyingMethod> copyingMethods
{
{
"copy_object",
"%result = OpLoad %type_container_struct %input_copy\n"
"OpStore %var_outdata %result\n"
},
{
"copy_memory",
"OpCopyMemory %var_outdata %var_input\n"
}
};
for (const auto& bufferType : bufferTypes)
{
for (const auto& copyingMethod : copyingMethods)
{
std::string name = copyingMethod.name + "_" + bufferType.name;
std::map<std::string, std::string> specializationMap
{
{ "OFFSET_1", de::toString(bufferType.offsets[1]) },
{ "OFFSET_2", de::toString(bufferType.offsets[2]) },
{ "OFFSET_3", de::toString(bufferType.offsets[3]) },
{ "VARIABLES", bufferType.spirvVariables },
// NOTE: to simlify code spirvCopyObject is added also when OpCopyMemory is used
{ "COPYING_METHOD", bufferType.spirvCopyObject + copyingMethod.spirvCopyCode },
};
ComputeShaderSpec spec;
spec.assembly = shaderTemplate.specialize(specializationMap);
spec.numWorkGroups = tcu::IVec3(1, 1, 1);
spec.verifyIO = verifyResult;
spec.inputs.push_back (Resource(BufferSp(new Int32Buffer(bufferType.input)), bufferType.descriptorType));
spec.outputs.push_back(Resource(BufferSp(new Int32Buffer(bufferType.expectedOutput))));
group->addChild(new SpvAsmComputeShaderCase(group->getTestContext(), name.c_str(), "", spec));
}
}
}
void addPointerComparisionComputeGroup(tcu::TestCaseGroup* group)
{
// NOTE: pointer comparison is possible only for StorageBuffer storage class
std::string computeSource =
"OpCapability Shader\n"
"OpCapability VariablePointersStorageBuffer\n"
"OpMemoryModel Logical GLSL450\n"
"OpEntryPoint GLCompute %main \"main\" %var_id %var_input %var_outdata\n"
"OpExecutionMode %main LocalSize 1 1 1\n"
"OpDecorate %var_id BuiltIn GlobalInvocationId\n"
"OpDecorate %var_input Binding 0\n"
"OpDecorate %var_input DescriptorSet 0\n"
"OpDecorate %var_outdata Binding 1\n"
"OpDecorate %var_outdata DescriptorSet 0\n"
"OpMemberDecorate %type_container_struct 0 Offset 0\n"
"OpMemberDecorate %type_container_struct 1 Offset 4\n"
"OpMemberDecorate %type_container_struct 2 Offset 8\n"
"OpMemberDecorate %type_container_struct 3 Offset 12\n"
"OpDecorate %type_container_struct Block\n"
"OpMemberDecorate %type_i32_struct 0 Offset 0\n"
"OpDecorate %type_i32_struct Block\n"
+ std::string(getComputeAsmCommonTypes("StorageBuffer")) +
// struct EmptyStruct {};
// struct ContainerStruct {
// int i;
// A a1;
// A a2;
// int j;
//};
// layout(set=, binding = ) buffer block B b;
// types
"%type_empty_struct = "
"OpTypeStruct\n"
"%type_container_struct = OpTypeStruct %i32 "
"%type_empty_struct %type_empty_struct %i32\n"
"%type_i32_struct = OpTypeStruct %i32\n"
// constants
"%c_i32_0 = OpConstant "
"%i32 0\n"
"%c_i32_1 = OpConstant "
"%i32 1\n"
"%c_i32_2 = OpConstant "
"%i32 2\n"
"%type_container_struct_in_ptr = OpTypePointer StorageBuffer "
"%type_container_struct\n"
"%type_i32_struct_out_ptr = OpTypePointer StorageBuffer "
"%type_i32_struct\n"
"%type_func_struct_ptr_ptr = OpTypePointer "
"StorageBuffer %type_empty_struct\n"
// variables
"%var_id = OpVariable "
"%uvec3ptr Input\n"
"%var_input = "
"OpVariable %type_container_struct_in_ptr StorageBuffer\n"
"%var_outdata = OpVariable "
"%type_i32_struct_out_ptr StorageBuffer\n"
// void main function
"%main = "
"OpFunction %void None %voidf\n"
"%label = "
"OpLabel\n"
// compare pointers to empty structures
"%ptr_to_first = "
"OpAccessChain %type_func_struct_ptr_ptr %var_input %c_i32_1\n"
"%ptr_to_second = "
"OpAccessChain %type_func_struct_ptr_ptr %var_input %c_i32_2\n"
"%pointers_not_equal = OpPtrNotEqual %bool "
"%ptr_to_first %ptr_to_second\n"
"%result = OpSelect "
"%i32 %pointers_not_equal %c_i32_1 %c_i32_0\n"
"%outloc = "
"OpAccessChain %i32ptr %var_outdata %c_i32_0\n"
"OpStore %outloc %result\n"
"OpReturn\n"
"OpFunctionEnd\n";
tcu::TestContext &testCtx = group->getTestContext();
std::vector<int> input = {2, 3, 5, 7};
std::vector<int> expectedOutput = {1};
ComputeShaderSpec spec;
spec.assembly = computeSource;
spec.numWorkGroups = tcu::IVec3(1, 1, 1);
spec.spirvVersion = SPIRV_VERSION_1_4;
spec.requestedVulkanFeatures.extVariablePointers =
EXTVARIABLEPOINTERSFEATURES_VARIABLE_POINTERS_STORAGEBUFFER;
spec.inputs.push_back(Resource(BufferSp(new Int32Buffer(input))));
spec.outputs.push_back(Resource(BufferSp(new Int32Buffer(expectedOutput))));
spec.extensions.push_back("VK_KHR_spirv_1_4");
group->addChild(new SpvAsmComputeShaderCase(testCtx, "ssbo", "", spec));
}
void addFunctionArgumentReturnValueGroup(tcu::TestCaseGroup* group)
{
const tcu::StringTemplate shaderTemplate(
" OpCapability Shader\n"
" %1 = OpExtInstImport \"GLSL.std.450\"\n"
" OpMemoryModel Logical GLSL450\n"
" OpEntryPoint GLCompute %4 \"main\" %29 %42 %51 ${GLOBAL_VARIABLE} %79\n"
" OpExecutionMode %4 LocalSize 2 1 1\n"
" OpSource GLSL 460\n"
" OpDecorate %29 BuiltIn LocalInvocationId\n"
" OpMemberDecorate %40 0 Offset 0\n"
" OpMemberDecorate %40 1 Offset 4\n"
" OpMemberDecorate %40 2 Offset 8\n"
" OpDecorate %40 Block\n"
" OpDecorate %42 DescriptorSet 0\n"
" OpDecorate %42 Binding 1\n"
" OpMemberDecorate %49 0 Offset 0\n"
" OpDecorate %49 Block\n"
" OpDecorate %51 DescriptorSet 0\n"
" OpDecorate %51 Binding 0\n"
" OpMemberDecorate %77 0 Offset 0\n"
" OpMemberDecorate %77 1 Offset 4\n"
" OpMemberDecorate %77 2 Offset 8\n"
" OpDecorate %77 Block\n"
" OpDecorate %79 DescriptorSet 0\n"
" OpDecorate %79 Binding 2\n"
" OpDecorate %96 BuiltIn WorkgroupSize\n"
" %2 = OpTypeVoid\n"
" %3 = OpTypeFunction %2\n"
" %7 = OpTypeStruct\n"
" %8 = OpTypePointer Function %7\n"
" %9 = OpTypeBool\n"
"%10 = OpTypePointer Function %9\n"
"%11 = OpTypeFunction %7 %8 %8 %10\n"
"%26 = OpTypeInt 32 0\n"
"%27 = OpTypeVector %26 3\n"
"%28 = OpTypePointer Input %27\n"
"%29 = OpVariable %28 Input\n"
"%30 = OpConstant %26 0\n"
"%31 = OpTypePointer Input %26\n"
"%34 = OpConstant %26 2\n"
"%39 = OpTypeStruct\n"
"%40 = OpTypeStruct %26 %39 %26\n"
"%41 = OpTypePointer StorageBuffer %40\n"
"%42 = OpVariable %41 StorageBuffer\n"
"%43 = OpTypeInt 32 1\n"
"%44 = OpConstant %43 0\n"
"%45 = OpConstant %26 1\n"
"%46 = OpTypePointer StorageBuffer %26\n"
"%48 = OpConstant %43 1\n"
"%49 = OpTypeStruct %39\n"
"%50 = OpTypePointer StorageBuffer %49\n"
"%51 = OpVariable %50 StorageBuffer\n"
"${VARIABLE_DEFINITION}\n"
"%59 = OpTypePointer StorageBuffer %39\n"
"%69 = OpConstant %43 2\n"
"%77 = OpTypeStruct %26 %39 %26\n"
"%78 = OpTypePointer StorageBuffer %77\n"
"%79 = OpVariable %78 StorageBuffer\n"
"%96 = OpConstantComposite %27 %34 %45 %45\n"
" %4 = OpFunction %2 None %3\n"
" %5 = OpLabel\n"
"${VARIABLE_FUNCTION_DEFINITION}\n"
"%58 = OpVariable %8 Function\n"
"%63 = OpVariable %8 Function\n"
"%65 = OpVariable %10 Function\n"
"%85 = OpVariable %8 Function\n"
"%89 = OpVariable %8 Function\n"
"%91 = OpVariable %10 Function\n"
"%32 = OpAccessChain %31 %29 %30\n"
"%33 = OpLoad %26 %32\n"
"%35 = OpUMod %26 %33 %34\n"
"%36 = OpIEqual %9 %35 %30\n"
" OpSelectionMerge %38 None\n"
" OpBranchConditional %36 %37 %38\n"
"%37 = OpLabel\n"
"%47 = OpAccessChain %46 %42 %44\n"
" OpStore %47 %45\n"
"%54 = OpAccessChain %31 %29 %30\n"
"%55 = OpLoad %26 %54\n"
"%56 = OpUMod %26 %55 %34\n"
"%57 = OpIEqual %9 %56 %30\n"
"%60 = OpAccessChain %59 %51 %44\n"
"%61 = OpLoad %39 %60\n"
"%62 = OpCopyLogical %7 %61\n"
" OpStore %58 %62\n"
"%64 = OpLoad %7 %53\n"
" OpStore %63 %64\n"
" OpStore %65 %57\n"
"%66 = OpFunctionCall %7 %15 %58 %63 %65\n"
"%67 = OpAccessChain %59 %42 %48\n"
"%68 = OpCopyLogical %39 %66\n"
" OpStore %67 %68\n"
"%70 = OpAccessChain %46 %42 %69\n"
" OpStore %70 %45\n"
" OpBranch %38\n"
"%38 = OpLabel\n"
"%71 = OpAccessChain %31 %29 %30\n"
"%72 = OpLoad %26 %71\n"
"%73 = OpUMod %26 %72 %34\n"
"%74 = OpIEqual %9 %73 %45\n"
" OpSelectionMerge %76 None\n"
" OpBranchConditional %74 %75 %76\n"
"%75 = OpLabel\n"
"%80 = OpAccessChain %46 %79 %44\n"
" OpStore %80 %45\n"
"%81 = OpAccessChain %31 %29 %30\n"
"%82 = OpLoad %26 %81\n"
"%83 = OpUMod %26 %82 %34\n"
"%84 = OpIEqual %9 %83 %45\n"
"%86 = OpAccessChain %59 %51 %44\n"
"%87 = OpLoad %39 %86\n"
"%88 = OpCopyLogical %7 %87\n"
" OpStore %85 %88\n"
"%90 = OpLoad %7 %53\n"
" OpStore %89 %90\n"
" OpStore %91 %84\n"
"%92 = OpFunctionCall %7 %15 %85 %89 %91\n"
"%93 = OpAccessChain %59 %79 %48\n"
"%94 = OpCopyLogical %39 %92\n"
" OpStore %93 %94\n"
"%95 = OpAccessChain %46 %79 %69\n"
" OpStore %95 %45\n"
" OpBranch %76\n"
"%76 = OpLabel\n"
" OpReturn\n"
" OpFunctionEnd\n"
"%15 = OpFunction %7 None %11\n"
"%12 = OpFunctionParameter %8\n"
"%13 = OpFunctionParameter %8\n"
"%14 = OpFunctionParameter %10\n"
"%16 = OpLabel\n"
"%17 = OpLoad %9 %14\n"
" OpSelectionMerge %19 None\n"
" OpBranchConditional %17 %18 %22\n"
"%18 = OpLabel\n"
"%20 = OpLoad %7 %12\n"
" OpReturnValue %20\n"
"%22 = OpLabel\n"
"%23 = OpLoad %7 %13\n"
" OpReturnValue %23\n"
"%19 = OpLabel\n"
" OpUnreachable\n"
" OpFunctionEnd\n");
struct VariableDefinition {
std::string name;
std::string globalVariable;
std::string spirvVariableDefinitionCode;
std::string spirvVariableFunctionDefinitionCode;
};
std::vector<VariableDefinition> variableDefinitions
{
{
"global_variable_private",
"%53",
"%52 = OpTypePointer Private %7\n"
"%53 = OpVariable %52 Private\n",
""
},
{
"global_variable_shared",
"%53",
"%52 = OpTypePointer Workgroup %7\n"
"%53 = OpVariable %52 Workgroup\n",
""
},
{
"local_variable",
"",
"",
"%53 = OpVariable %8 Function\n"
},
};
tcu::TestContext &testCtx = group->getTestContext();
std::vector<int> input = {2};
std::vector<deUint32> expectedOutput = {1, 0xffffffff, 1};
for (const auto &variableDefinition : variableDefinitions)
{
std::map<std::string, std::string> specializationMap
{
{ "GLOBAL_VARIABLE", variableDefinition.globalVariable },
{ "VARIABLE_DEFINITION", variableDefinition.spirvVariableDefinitionCode },
{ "VARIABLE_FUNCTION_DEFINITION", variableDefinition.spirvVariableFunctionDefinitionCode },
};
ComputeShaderSpec spec;
spec.assembly = shaderTemplate.specialize(specializationMap);
spec.numWorkGroups = tcu::IVec3(2, 1, 1);
spec.spirvVersion = SPIRV_VERSION_1_4;
spec.requestedVulkanFeatures.extVariablePointers =
EXTVARIABLEPOINTERSFEATURES_VARIABLE_POINTERS_STORAGEBUFFER;
spec.inputs.push_back(Resource(BufferSp(new Int32Buffer(input))));
spec.outputs.push_back(
Resource(BufferSp(new Uint32Buffer(expectedOutput))));
spec.outputs.push_back(
Resource(BufferSp(new Uint32Buffer(expectedOutput))));
spec.extensions.push_back("VK_KHR_spirv_1_4");
group->addChild(new SpvAsmComputeShaderCase(
testCtx, variableDefinition.name.c_str(), "", spec));
}
}
} // anonymous
tcu::TestCaseGroup* createEmptyStructComputeGroup (tcu::TestContext& testCtx)
{
de::MovePtr<tcu::TestCaseGroup> group (new tcu::TestCaseGroup(testCtx, "empty_struct", "Tests empty structs in UBOs and SSBOs"));
addTestGroup(group.get(), "copying", "Test copying struct which contains an empty struct", addCopyingComputeGroup);
addTestGroup(group.get(), "pointer_comparison", "Test pointer comparisons of empty struct members", addPointerComparisionComputeGroup);
addTestGroup(group.get(), "function", "Test empty structs as function arguments or return type", addFunctionArgumentReturnValueGroup);
return group.release();
}
} // SpirVAssembly
} // vkt