1 /*-------------------------------------------------------------------------
2  * Vulkan Conformance Tests
3  * ------------------------
4  *
5  * Copyright (c) 2017 The Khronos Group Inc.
6  *
7  * Licensed under the Apache License, Version 2.0 (the "License");
8  * you may not use this file except in compliance with the License.
9  * You may obtain a copy of the License at
10  *
11  *      http://www.apache.org/licenses/LICENSE-2.0
12  *
13  * Unless required by applicable law or agreed to in writing, software
14  * distributed under the License is distributed on an "AS IS" BASIS,
15  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16  * See the License for the specific language governing permissions and
17  * limitations under the License.
18  *
19  *//*!
20  * \file
21  * \brief SPIR-V Loop Control for DependencyLength qualifier tests
22  *//*--------------------------------------------------------------------*/
23 
24 #include "vkApiVersion.hpp"
25 
26 #include "vktSpvAsmLoopDepLenTests.hpp"
27 #include "vktTestCase.hpp"
28 #include "vktSpvAsmComputeShaderCase.hpp"
29 
30 #include "deRandom.hpp"
31 
32 namespace vkt
33 {
34 namespace SpirVAssembly
35 {
36 
37 using namespace vk;
38 using std::map;
39 using std::string;
40 using std::vector;
41 
42 // Assembly code used for testing loop control with dependencies is based on GLSL source code:
43 // #version 430
44 //
45 // layout(std140, set = 0, binding = 0) readonly buffer Input {
46 //   float elements[];
47 // } input_data;
48 // layout(std140, set = 0, binding = 1) writeonly buffer Output {
49 //   float elements[];
50 // } output_data;
51 //
52 // void main() {
53 //   const uint n = 12;
54 //   float c[n];
55 //   uint x = gl_GlobalInvocationID.x;
56 //
57 //   for (uint i = 0; i < 6; ++i)
58 //     c[i] = float(i) * input_data.elements[x];
59 //
60 //   for (uint i = 6; i < n; ++i)
61 //     c[i] = c[i - 4] + c[i - 5] + c[i - 6];
62 //
63 //   output_data.elements[x] = c[n - 1];
64 // }
getComputeSourceCode(std::string & computeSourceCode)65 static void getComputeSourceCode(std::string &computeSourceCode)
66 {
67     computeSourceCode = string(getComputeAsmShaderPreamble()) +
68 
69                         "OpSource GLSL 430\n"
70                         "OpName %main \"main\"\n"
71                         "OpName %id \"gl_GlobalInvocationID\"\n"
72 
73                         "OpDecorate %id BuiltIn GlobalInvocationId\n"
74 
75                         + string(getComputeAsmInputOutputBufferTraits()) + string(getComputeAsmCommonTypes()) +
76                         string(getComputeAsmInputOutputBuffer()) +
77 
78                         "%u32ptr        = OpTypePointer Function %u32\n"
79 
80                         "%id            = OpVariable %uvec3ptr Input\n"
81                         "%zero          = OpConstant %i32 0\n"
82                         "%uzero         = OpConstant %u32 0\n"
83                         "%one           = OpConstant %i32 1\n"
84 
85                         "%four          = OpConstant %u32 4\n"
86                         "%five          = OpConstant %u32 5\n"
87                         "%six           = OpConstant %u32 6\n"
88                         "%elleven       = OpConstant %u32 11\n"
89                         "%twelve        = OpConstant %u32 12\n"
90 
91                         "%f32arr12_t    = OpTypeArray %f32 %twelve\n"
92                         "%f32arr12ptr_t = OpTypePointer Function %f32arr12_t\n"
93                         "%f32funcptr    = OpTypePointer Function %f32\n"
94 
95                         "%main          = OpFunction %void None %voidf\n"
96                         "%entry         = OpLabel\n"
97 
98                         "%f32arr12      = OpVariable %f32arr12ptr_t Function\n"
99 
100                         "%i1            = OpVariable %u32ptr Function\n"
101                         "%i2            = OpVariable %u32ptr Function\n"
102                         "                 OpStore %i1 %uzero\n"
103                         "                 OpStore %i2 %six\n"
104 
105                         "%idval         = OpLoad %uvec3 %id\n"
106                         "%x             = OpCompositeExtract %u32 %idval 0\n"
107                         "%inloc         = OpAccessChain %f32ptr %indata %zero %x\n"
108                         "%inval         = OpLoad %f32 %inloc\n"
109 
110                         // for (uint i = 0; i < 6; ++i) c[i] = float(i) * input_data.elements[x];
111                         "                 OpBranch %loop1_entry\n"
112                         "%loop1_entry   = OpLabel\n"
113                         "%i1_val        = OpLoad %u32 %i1\n"
114                         "%cmp1_lt       = OpULessThan %bool %i1_val %six\n"
115                         "                 OpLoopMerge %loop1_merge %loop1_body None\n"
116                         "                 OpBranchConditional %cmp1_lt %loop1_body %loop1_merge\n"
117                         "%loop1_body    = OpLabel\n"
118                         "%i1_valf32     = OpConvertUToF %f32 %i1_val\n"
119                         "%mulf1         = OpFMul %f32 %i1_valf32 %inval\n"
120                         "%outloc1       = OpAccessChain %f32funcptr %f32arr12 %i1_val\n"
121                         "                 OpStore %outloc1 %mulf1\n"
122                         "%new1_i        = OpIAdd %u32 %i1_val %one\n"
123                         "                 OpStore %i1 %new1_i\n"
124                         "                 OpBranch %loop1_entry\n"
125                         "%loop1_merge   = OpLabel\n"
126 
127                         //   for (uint i = 6; i < n; ++i) c[i] = c[i - 4] + c[i - 5] + c[i - 6];
128                         "                 OpBranch %loop2_entry\n"
129                         "%loop2_entry   = OpLabel\n"
130                         "%i2_val        = OpLoad %u32 %i2\n"
131                         "%cmp2_lt       = OpULessThan %bool %i2_val %twelve\n"
132                         "                 OpLoopMerge %loop2_merge %loop2_body DependencyLength 3\n"
133                         "                 OpBranchConditional %cmp2_lt %loop2_body %loop2_merge\n"
134                         "%loop2_body    = OpLabel\n"
135                         "%i2_m4         = OpISub %u32 %i2_val %four\n"
136                         "%arr1_i2m4loc  = OpAccessChain %f32funcptr %f32arr12 %i2_m4\n"
137                         "%arr1_i2m4val  = OpLoad %f32 %arr1_i2m4loc\n"
138                         "%i2_m5         = OpISub %u32 %i2_val %five\n"
139                         "%arr1_i2m5loc  = OpAccessChain %f32funcptr %f32arr12 %i2_m5\n"
140                         "%arr1_i2m5val  = OpLoad %f32 %arr1_i2m5loc\n"
141                         "%f32add1       = OpFAdd %f32 %arr1_i2m4val %arr1_i2m5val\n"
142                         "%i2_m6         = OpISub %u32 %i2_val %six\n"
143                         "%arr1_i2m6loc  = OpAccessChain %f32funcptr %f32arr12 %i2_m6\n"
144                         "%arr1_i2m6val  = OpLoad %f32 %arr1_i2m6loc\n"
145                         "%f32add2       = OpFAdd %f32 %f32add1 %arr1_i2m6val\n"
146                         "%outloc2       = OpAccessChain %f32funcptr %f32arr12 %i2_val\n"
147                         "                 OpStore %outloc2 %f32add2\n"
148                         "%new_i2        = OpIAdd %u32 %i2_val %one\n"
149                         "                 OpStore %i2 %new_i2\n"
150                         "                 OpBranch %loop2_entry\n"
151                         "%loop2_merge   = OpLabel\n"
152 
153                         //   output_data.elements[x] = c[n - 1];
154                         "%arr1locq      = OpAccessChain %f32funcptr %f32arr12 %elleven\n"
155                         "%arr1valq      = OpLoad %f32 %arr1locq\n"
156                         "%outlocq       = OpAccessChain %f32ptr %outdata %zero %x\n"
157                         "                 OpStore %outlocq %arr1valq\n"
158                         "                 OpReturn\n"
159                         "                 OpFunctionEnd\n";
160 }
161 
getComputeShaderSpec()162 static ComputeShaderSpec getComputeShaderSpec()
163 {
164     de::Random rnd(0xABC);
165     const int numElements = 100;
166     vector<float> inputFloats(numElements, 0);
167     vector<float> outputFloats(numElements, 0);
168     ComputeShaderSpec spec;
169 
170     for (size_t ndx = 0; ndx < numElements; ++ndx)
171         inputFloats[ndx] = rnd.getFloat(1.0f, 100.0f);
172 
173     for (size_t ndx = 0; ndx < numElements; ++ndx)
174     {
175         const uint32_t n = 12;
176         float c[n];
177 
178         for (uint32_t i = 0; i < 6; ++i)
179             c[i] = float(i) * inputFloats[ndx];
180 
181         for (uint32_t i = 6; i < n; ++i)
182             c[i] = c[i - 4] + c[i - 5] + c[i - 6];
183 
184         outputFloats[ndx] = c[n - 1];
185     }
186 
187     // Shader source code can be retrieved to complete definition of ComputeShaderSpec, though it is not required at this stage
188     // getComputeSourceCode (spec.assembly);
189 
190     spec.inputs.push_back(BufferSp(new Float32Buffer(inputFloats)));
191     spec.outputs.push_back(BufferSp(new Float32Buffer(outputFloats)));
192     spec.numWorkGroups = tcu::IVec3(numElements, 1, 1);
193     spec.verifyIO      = &verifyOutput;
194 
195     return spec;
196 }
197 
198 class SpvAsmLoopControlDependencyLengthInstance : public ComputeShaderSpec, public SpvAsmComputeShaderInstance
199 {
200 public:
201     SpvAsmLoopControlDependencyLengthInstance(Context &ctx);
202 };
203 
SpvAsmLoopControlDependencyLengthInstance(Context & ctx)204 SpvAsmLoopControlDependencyLengthInstance::SpvAsmLoopControlDependencyLengthInstance(Context &ctx)
205     : ComputeShaderSpec(getComputeShaderSpec())
206     , SpvAsmComputeShaderInstance(ctx, *this)
207 {
208 }
209 
SpvAsmLoopControlDependencyLengthCase(tcu::TestContext & testCtx,const char * name)210 SpvAsmLoopControlDependencyLengthCase::SpvAsmLoopControlDependencyLengthCase(tcu::TestContext &testCtx,
211                                                                              const char *name)
212     : TestCase(testCtx, name)
213 {
214 }
215 
initPrograms(SourceCollections & programCollection) const216 void SpvAsmLoopControlDependencyLengthCase::initPrograms(SourceCollections &programCollection) const
217 {
218     std::string comp;
219 
220     getComputeSourceCode(comp);
221 
222     programCollection.spirvAsmSources.add("compute")
223         << SpirVAsmBuildOptions(programCollection.usedVulkanVersion, SPIRV_VERSION_1_3) << comp;
224 }
225 
createInstance(Context & context) const226 TestInstance *SpvAsmLoopControlDependencyLengthCase::createInstance(Context &context) const
227 {
228     if (!context.contextSupports(vk::ApiVersion(0, 1, 1, 0)))
229         TCU_THROW(NotSupportedError, "SPIR-V higher than 1.3 is required for this test to run");
230 
231     return new SpvAsmLoopControlDependencyLengthInstance(context);
232 }
233 
234 } // namespace SpirVAssembly
235 } // namespace vkt
236