blob: c35b28c85a4f0d5619ae42b909f6be717a687e1a [file] [log] [blame]
/*-------------------------------------------------------------------------
* Vulkan Conformance Tests
* ------------------------
*
* Copyright (c) 2017 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 SPIR-V Loop Control for DependencyLength qualifier tests
*//*--------------------------------------------------------------------*/
#include "vkApiVersion.hpp"
#include "vktSpvAsmLoopDepLenTests.hpp"
#include "vktTestCase.hpp"
#include "vktSpvAsmComputeShaderCase.hpp"
#include "deRandom.hpp"
namespace vkt
{
namespace SpirVAssembly
{
using namespace vk;
using std::map;
using std::string;
using std::vector;
// Assembly code used for testing loop control with dependencies is based on GLSL source code:
// #version 430
//
// layout(std140, set = 0, binding = 0) readonly buffer Input {
// float elements[];
// } input_data;
// layout(std140, set = 0, binding = 1) writeonly buffer Output {
// float elements[];
// } output_data;
//
// void main() {
// const uint n = 12;
// float c[n];
// uint x = gl_GlobalInvocationID.x;
//
// for (uint i = 0; i < 6; ++i)
// c[i] = float(i) * input_data.elements[x];
//
// for (uint i = 6; i < n; ++i)
// c[i] = c[i - 4] + c[i - 5] + c[i - 6];
//
// output_data.elements[x] = c[n - 1];
// }
static void getComputeSourceCode (std::string& computeSourceCode)
{
computeSourceCode =
string(getComputeAsmShaderPreamble()) +
"OpSource GLSL 430\n"
"OpName %main \"main\"\n"
"OpName %id \"gl_GlobalInvocationID\"\n"
"OpDecorate %id BuiltIn GlobalInvocationId\n"
+ string(getComputeAsmInputOutputBufferTraits()) + string(getComputeAsmCommonTypes()) + string(getComputeAsmInputOutputBuffer()) +
"%u32ptr = OpTypePointer Function %u32\n"
"%id = OpVariable %uvec3ptr Input\n"
"%zero = OpConstant %i32 0\n"
"%uzero = OpConstant %u32 0\n"
"%one = OpConstant %i32 1\n"
"%four = OpConstant %u32 4\n"
"%five = OpConstant %u32 5\n"
"%six = OpConstant %u32 6\n"
"%elleven = OpConstant %u32 11\n"
"%twelve = OpConstant %u32 12\n"
"%f32arr12_t = OpTypeArray %f32 %twelve\n"
"%f32arr12ptr_t = OpTypePointer Function %f32arr12_t\n"
"%f32funcptr = OpTypePointer Function %f32\n"
"%main = OpFunction %void None %voidf\n"
"%entry = OpLabel\n"
"%f32arr12 = OpVariable %f32arr12ptr_t Function\n"
"%i1 = OpVariable %u32ptr Function\n"
"%i2 = OpVariable %u32ptr Function\n"
" OpStore %i1 %uzero\n"
" OpStore %i2 %six\n"
"%idval = OpLoad %uvec3 %id\n"
"%x = OpCompositeExtract %u32 %idval 0\n"
"%inloc = OpAccessChain %f32ptr %indata %zero %x\n"
"%inval = OpLoad %f32 %inloc\n"
// for (uint i = 0; i < 6; ++i) c[i] = float(i) * input_data.elements[x];
" OpBranch %loop1_entry\n"
"%loop1_entry = OpLabel\n"
"%i1_val = OpLoad %u32 %i1\n"
"%cmp1_lt = OpULessThan %bool %i1_val %six\n"
" OpLoopMerge %loop1_merge %loop1_body None\n"
" OpBranchConditional %cmp1_lt %loop1_body %loop1_merge\n"
"%loop1_body = OpLabel\n"
"%i1_valf32 = OpConvertUToF %f32 %i1_val\n"
"%mulf1 = OpFMul %f32 %i1_valf32 %inval\n"
"%outloc1 = OpAccessChain %f32funcptr %f32arr12 %i1_val\n"
" OpStore %outloc1 %mulf1\n"
"%new1_i = OpIAdd %u32 %i1_val %one\n"
" OpStore %i1 %new1_i\n"
" OpBranch %loop1_entry\n"
"%loop1_merge = OpLabel\n"
// for (uint i = 6; i < n; ++i) c[i] = c[i - 4] + c[i - 5] + c[i - 6];
" OpBranch %loop2_entry\n"
"%loop2_entry = OpLabel\n"
"%i2_val = OpLoad %u32 %i2\n"
"%cmp2_lt = OpULessThan %bool %i2_val %twelve\n"
" OpLoopMerge %loop2_merge %loop2_body DependencyLength 3\n"
" OpBranchConditional %cmp2_lt %loop2_body %loop2_merge\n"
"%loop2_body = OpLabel\n"
"%i2_m4 = OpISub %u32 %i2_val %four\n"
"%arr1_i2m4loc = OpAccessChain %f32funcptr %f32arr12 %i2_m4\n"
"%arr1_i2m4val = OpLoad %f32 %arr1_i2m4loc\n"
"%i2_m5 = OpISub %u32 %i2_val %five\n"
"%arr1_i2m5loc = OpAccessChain %f32funcptr %f32arr12 %i2_m5\n"
"%arr1_i2m5val = OpLoad %f32 %arr1_i2m5loc\n"
"%f32add1 = OpFAdd %f32 %arr1_i2m4val %arr1_i2m5val\n"
"%i2_m6 = OpISub %u32 %i2_val %six\n"
"%arr1_i2m6loc = OpAccessChain %f32funcptr %f32arr12 %i2_m6\n"
"%arr1_i2m6val = OpLoad %f32 %arr1_i2m6loc\n"
"%f32add2 = OpFAdd %f32 %f32add1 %arr1_i2m6val\n"
"%outloc2 = OpAccessChain %f32funcptr %f32arr12 %i2_val\n"
" OpStore %outloc2 %f32add2\n"
"%new_i2 = OpIAdd %u32 %i2_val %one\n"
" OpStore %i2 %new_i2\n"
" OpBranch %loop2_entry\n"
"%loop2_merge = OpLabel\n"
// output_data.elements[x] = c[n - 1];
"%arr1locq = OpAccessChain %f32funcptr %f32arr12 %elleven\n"
"%arr1valq = OpLoad %f32 %arr1locq\n"
"%outlocq = OpAccessChain %f32ptr %outdata %zero %x\n"
" OpStore %outlocq %arr1valq\n"
" OpReturn\n"
" OpFunctionEnd\n";
}
static ComputeShaderSpec getComputeShaderSpec ()
{
de::Random rnd (0xABC);
const int numElements = 100;
vector<float> inputFloats (numElements, 0);
vector<float> outputFloats (numElements, 0);
ComputeShaderSpec spec;
for (size_t ndx = 0; ndx < numElements; ++ndx)
inputFloats[ndx] = rnd.getFloat(1.0f, 100.0f);
for (size_t ndx = 0; ndx < numElements; ++ndx)
{
const deUint32 n = 12;
float c[n];
for (deUint32 i = 0; i < 6; ++i)
c[i] = float(i) * inputFloats[ndx];
for (deUint32 i = 6; i < n; ++i)
c[i] = c[i - 4] + c[i - 5] + c[i - 6];
outputFloats[ndx] = c[n - 1];
}
// Shader source code can be retrieved to complete definition of ComputeShaderSpec, though it is not required at this stage
// getComputeSourceCode (spec.assembly);
spec.inputs.push_back(BufferSp(new Float32Buffer(inputFloats)));
spec.outputs.push_back(BufferSp(new Float32Buffer(outputFloats)));
spec.numWorkGroups = tcu::IVec3(numElements, 1, 1);
spec.verifyIO = &verifyOutput;
return spec;
}
class SpvAsmLoopControlDependencyLengthInstance : public ComputeShaderSpec, public SpvAsmComputeShaderInstance
{
public:
SpvAsmLoopControlDependencyLengthInstance (Context& ctx);
};
SpvAsmLoopControlDependencyLengthInstance::SpvAsmLoopControlDependencyLengthInstance (Context& ctx)
: ComputeShaderSpec(getComputeShaderSpec())
, SpvAsmComputeShaderInstance(ctx, *this)
{
}
SpvAsmLoopControlDependencyLengthCase::SpvAsmLoopControlDependencyLengthCase (tcu::TestContext& testCtx, const char* name, const char* description)
: TestCase (testCtx, name, description)
{
}
void SpvAsmLoopControlDependencyLengthCase::initPrograms (SourceCollections& programCollection) const
{
std::string comp;
getComputeSourceCode(comp);
programCollection.spirvAsmSources.add("compute") << SpirVAsmBuildOptions(programCollection.usedVulkanVersion, SPIRV_VERSION_1_3) << comp;
}
TestInstance* SpvAsmLoopControlDependencyLengthCase::createInstance (Context& context) const
{
if (!context.contextSupports(vk::ApiVersion(1, 1, 0)))
TCU_THROW(NotSupportedError, "SPIR-V higher than 1.3 is required for this test to run");
return new SpvAsmLoopControlDependencyLengthInstance(context);
}
} // SpirVAssembly
} // vkt