1 /*------------------------------------------------------------------------
2 * Vulkan Conformance Tests
3 * ------------------------
4 *
5 * Copyright (c) 2023 The Khronos Group Inc.
6 * Copyright (c) 2023 Valve Corporation.
7 *
8 * Licensed under the Apache License, Version 2.0 (the "License");
9 * you may not use this file except in compliance with the License.
10 * You may obtain a copy of the License at
11 *
12 * http://www.apache.org/licenses/LICENSE-2.0
13 *
14 * Unless required by applicable law or agreed to in writing, software
15 * distributed under the License is distributed on an "AS IS" BASIS,
16 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
17 * See the License for the specific language governing permissions and
18 * limitations under the License.
19 *
20 *//*!
21 * \file
22 * \brief Vulkan Fragment Shader Invocation and Sample Cound Tests
23 *//*--------------------------------------------------------------------*/
24 #include "vktQueryPoolFragInvocationTests.hpp"
25 #include "tcuImageCompare.hpp"
26 #include "vkBarrierUtil.hpp"
27 #include "vkCmdUtil.hpp"
28 #include "vkImageUtil.hpp"
29 #include "vkObjUtil.hpp"
30 #include "vkTypeUtil.hpp"
31 #include "vktTestCaseUtil.hpp"
32
33 #include <sstream>
34
35 namespace vkt
36 {
37 namespace QueryPool
38 {
39
40 namespace
41 {
42
43 using namespace vk;
44
45 enum class QueryType { INVOCATIONS = 0, OCCLUSION };
46
getQueryTypeName(const QueryType qType)47 std::string getQueryTypeName (const QueryType qType)
48 {
49 switch (qType)
50 {
51 case QueryType::INVOCATIONS: return "frag_invs";
52 case QueryType::OCCLUSION: return "occlusion";
53 default: break;
54 }
55
56 DE_ASSERT(false);
57 return "";
58 }
59
60 struct TestParams
61 {
62 const QueryType queryType;
63 const bool secondary;
64 };
65
getFlatColor(void)66 tcu::Vec4 getFlatColor (void)
67 {
68 return tcu::Vec4(0.0f, 0.0f, 1.0f, 1.0f);
69 }
70
getClearColor(void)71 tcu::Vec4 getClearColor (void)
72 {
73 return tcu::Vec4(0.0f, 0.0f, 0.0f, 0.0f);
74 }
75
checkSupport(Context & context,TestParams params)76 void checkSupport (Context& context, TestParams params)
77 {
78 if (params.secondary)
79 context.requireDeviceCoreFeature(DEVICE_CORE_FEATURE_INHERITED_QUERIES);
80
81 if (params.queryType == QueryType::OCCLUSION)
82 context.requireDeviceCoreFeature(DEVICE_CORE_FEATURE_OCCLUSION_QUERY_PRECISE);
83 else if (params.queryType == QueryType::INVOCATIONS)
84 context.requireDeviceCoreFeature(DEVICE_CORE_FEATURE_PIPELINE_STATISTICS_QUERY);
85 }
86
initPrograms(vk::SourceCollections & programCollection,TestParams)87 void initPrograms (vk::SourceCollections& programCollection, TestParams)
88 {
89 std::ostringstream vert;
90 vert
91 << "#version 460\n"
92 << "vec2 positions[3] = vec2[](\n"
93 << " vec2(-1.0, -1.0),"
94 << " vec2(3.0, -1.0),"
95 << " vec2(-1.0, 3.0)"
96 << ");\n"
97 << "void main() {\n"
98 << " gl_Position = vec4(positions[gl_VertexIndex % 3], 0.0, 1.0);\n"
99 << "}"
100 ;
101 programCollection.glslSources.add("vert") << glu::VertexSource(vert.str());
102
103 std::ostringstream frag;
104 frag
105 << "#version 460\n"
106 << "layout (location=0) out vec4 outColor;\n"
107 << "void main() {\n"
108 << " outColor = vec4" << getFlatColor() << ";\n"
109 << "}";
110 ;
111 programCollection.glslSources.add("frag") << glu::FragmentSource(frag.str());
112 }
113
recordRenderPassCommands(const DeviceInterface & vkd,const VkCommandBuffer cmdBuffer,const VkPipelineBindPoint bindPoint,const VkPipeline pipeline)114 void recordRenderPassCommands (const DeviceInterface& vkd, const VkCommandBuffer cmdBuffer, const VkPipelineBindPoint bindPoint, const VkPipeline pipeline)
115 {
116 vkd.cmdBindPipeline(cmdBuffer, bindPoint, pipeline);
117 vkd.cmdDraw(cmdBuffer, 3u, 1u, 0u, 0u);
118 }
119
testInvocations(Context & context,const TestParams params)120 tcu::TestStatus testInvocations (Context& context, const TestParams params)
121 {
122 const auto ctx = context.getContextCommonData();
123 const tcu::IVec3 fbExtent (64, 64, 1);
124 const auto vkExtent = makeExtent3D(fbExtent);
125 const auto colorFormat = VK_FORMAT_R8G8B8A8_UNORM;
126 const auto colorSRR = makeDefaultImageSubresourceRange();
127 const auto colorSRL = makeDefaultImageSubresourceLayers();
128 const auto colorUsage = (VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT);
129 const auto imageType = VK_IMAGE_TYPE_2D;
130 const auto bindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;
131
132 ImageWithBuffer colorBuffer (ctx.vkd, ctx.device, ctx.allocator, vkExtent, colorFormat, colorUsage, imageType, colorSRR);
133
134 const auto& binaries = context.getBinaryCollection();
135 const auto vertModule = createShaderModule(ctx.vkd, ctx.device, binaries.get("vert"));
136 const auto fragModule = createShaderModule(ctx.vkd, ctx.device, binaries.get("frag"));
137
138 const auto pipelineLayout = makePipelineLayout(ctx.vkd, ctx.device);
139 const auto renderPass = makeRenderPass(ctx.vkd, ctx.device, colorFormat);
140 const auto framebuffer = makeFramebuffer(ctx.vkd, ctx.device, renderPass.get(), colorBuffer.getImageView(), vkExtent.width, vkExtent.height);
141
142 const bool isInvQuery = (params.queryType == QueryType::INVOCATIONS);
143 const auto queryType = (isInvQuery ? VK_QUERY_TYPE_PIPELINE_STATISTICS : VK_QUERY_TYPE_OCCLUSION);
144 const auto statFlags = (isInvQuery ? static_cast<VkQueryPipelineStatisticFlags>(VK_QUERY_PIPELINE_STATISTIC_FRAGMENT_SHADER_INVOCATIONS_BIT) : 0u);
145 const auto controlFlags = (isInvQuery ? 0u : static_cast<VkQueryControlFlags>(VK_QUERY_CONTROL_PRECISE_BIT));
146
147 const VkQueryPoolCreateInfo queryPoolCreateInfo =
148 {
149 VK_STRUCTURE_TYPE_QUERY_POOL_CREATE_INFO, // VkStructureType sType;
150 nullptr, // const void* pNext;
151 0u, // VkQueryPoolCreateFlags flags;
152 queryType, // VkQueryType queryType;
153 1u, // uint32_t queryCount;
154 statFlags, // VkQueryPipelineStatisticFlags pipelineStatistics;
155 };
156 const auto queryPool = createQueryPool(ctx.vkd, ctx.device, &queryPoolCreateInfo);
157
158 const std::vector<VkViewport> viewports (1u, makeViewport(vkExtent));
159 const std::vector<VkRect2D> scissors (1u, makeRect2D(fbExtent));
160
161 const VkPipelineVertexInputStateCreateInfo inputStateCreateInfo = initVulkanStructure();
162
163 const auto pipeline = makeGraphicsPipeline(ctx.vkd, ctx.device, pipelineLayout.get(),
164 vertModule.get(), VK_NULL_HANDLE, VK_NULL_HANDLE, VK_NULL_HANDLE, fragModule.get(), renderPass.get(),
165 viewports, scissors, VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST, 0u, 0u,
166 &inputStateCreateInfo);
167
168 CommandPoolWithBuffer cmd (ctx.vkd, ctx.device, ctx.qfIndex);
169 VkCommandBuffer primaryCmdBuffer = cmd.cmdBuffer.get();
170 Move<VkCommandBuffer> secCmdBufferPtr;
171
172 if (params.secondary)
173 {
174 secCmdBufferPtr = allocateCommandBuffer(ctx.vkd, ctx.device, cmd.cmdPool.get(), VK_COMMAND_BUFFER_LEVEL_SECONDARY);
175 const auto secCmdBuffer = secCmdBufferPtr.get();
176
177 const VkCommandBufferInheritanceInfo inheritanceInfo =
178 {
179 VK_STRUCTURE_TYPE_COMMAND_BUFFER_INHERITANCE_INFO, // VkStructureType sType;
180 nullptr, // const void* pNext;
181 renderPass.get(), // VkRenderPass renderPass;
182 0u, // uint32_t subpass;
183 framebuffer.get(), // VkFramebuffer framebuffer;
184 ((queryType == VK_QUERY_TYPE_OCCLUSION) ? VK_TRUE : VK_FALSE), // VkBool32 occlusionQueryEnable;
185 controlFlags, // VkQueryControlFlags queryFlags;
186 statFlags, // VkQueryPipelineStatisticFlags pipelineStatistics;
187 };
188
189 const auto usageFlags = (VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT | VK_COMMAND_BUFFER_USAGE_RENDER_PASS_CONTINUE_BIT);
190 const VkCommandBufferBeginInfo beginInfo =
191 {
192 VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, // VkStructureType sType;
193 nullptr, // const void* pNext;
194 usageFlags, // VkCommandBufferUsageFlags flags;
195 &inheritanceInfo, // const VkCommandBufferInheritanceInfo* pInheritanceInfo;
196 };
197
198 VK_CHECK(ctx.vkd.beginCommandBuffer(secCmdBuffer, &beginInfo));
199 recordRenderPassCommands(ctx.vkd, secCmdBuffer, bindPoint, pipeline.get());
200 endCommandBuffer(ctx.vkd, secCmdBuffer);
201 }
202
203 const auto subpassContents = (params.secondary ? VK_SUBPASS_CONTENTS_SECONDARY_COMMAND_BUFFERS : VK_SUBPASS_CONTENTS_INLINE);
204 const auto clearColor = makeClearValueColor(getClearColor());
205
206 beginCommandBuffer(ctx.vkd, primaryCmdBuffer);
207 ctx.vkd.cmdResetQueryPool(primaryCmdBuffer, queryPool.get(), 0u, 1u);
208 ctx.vkd.cmdBeginQuery(primaryCmdBuffer, queryPool.get(), 0u, controlFlags);
209 beginRenderPass(ctx.vkd, primaryCmdBuffer, renderPass.get(), framebuffer.get(), scissors.at(0), clearColor, subpassContents);
210 if (!params.secondary)
211 recordRenderPassCommands(ctx.vkd, primaryCmdBuffer, bindPoint, pipeline.get());
212 else
213 ctx.vkd.cmdExecuteCommands(primaryCmdBuffer, 1u, &secCmdBufferPtr.get());
214 endRenderPass(ctx.vkd, primaryCmdBuffer);
215 ctx.vkd.cmdEndQuery(primaryCmdBuffer, queryPool.get(), 0u);
216 {
217 const auto preTransferBarrier = makeImageMemoryBarrier(
218 VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT,
219 VK_ACCESS_TRANSFER_READ_BIT,
220 VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
221 VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
222 colorBuffer.getImage(), colorSRR);
223 cmdPipelineImageMemoryBarrier(ctx.vkd, primaryCmdBuffer, VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, &preTransferBarrier);
224
225 const auto copyRegion = makeBufferImageCopy(vkExtent, colorSRL);
226 ctx.vkd.cmdCopyImageToBuffer(primaryCmdBuffer, colorBuffer.getImage(), VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, colorBuffer.getBuffer(), 1u, ©Region);
227
228 const auto preHostBarrier = makeMemoryBarrier(VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_HOST_READ_BIT);
229 cmdPipelineMemoryBarrier(ctx.vkd, primaryCmdBuffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_HOST_BIT, &preHostBarrier);
230 }
231 endCommandBuffer(ctx.vkd, primaryCmdBuffer);
232 submitCommandsAndWait(ctx.vkd, ctx.device, ctx.queue, primaryCmdBuffer);
233
234 const auto resultAllocation = colorBuffer.getBufferAllocation();
235 invalidateAlloc(ctx.vkd, ctx.device, resultAllocation);
236
237 uint32_t queryResult = 0u;
238 VK_CHECK(ctx.vkd.getQueryPoolResults(ctx.device, queryPool.get(), 0u, 1u, sizeof(queryResult), &queryResult, static_cast<VkDeviceSize>(sizeof(queryResult)), VK_QUERY_RESULT_WAIT_BIT));
239
240 const auto expectedResult = vkExtent.width * vkExtent.height * vkExtent.depth;
241 const bool needsExact = (!isInvQuery);
242
243 if (needsExact)
244 {
245 if (expectedResult != queryResult)
246 {
247 std::ostringstream msg;
248 msg << "Framebuffer size: " << vkExtent.width << "x" << vkExtent.height << "; expected query result to be " << expectedResult << " but found " << queryResult;
249 return tcu::TestStatus::fail(msg.str());
250 }
251 }
252 else
253 {
254 if (queryResult < expectedResult)
255 {
256 std::ostringstream msg;
257 msg << "Framebuffer size: " << vkExtent.width << "x" << vkExtent.height << "; expected query result to be at least " << expectedResult << " but found " << queryResult;
258 return tcu::TestStatus::fail(msg.str());
259 }
260 }
261
262 const auto tcuFormat = mapVkFormat(colorFormat);
263 auto& log = context.getTestContext().getLog();
264 const tcu::Vec4 colorThreshold (0.0f, 0.0f, 0.0f, 0.0f); // Expect exact color result.
265 tcu::ConstPixelBufferAccess resultAccess (tcuFormat, fbExtent, resultAllocation.getHostPtr());
266
267 if (!tcu::floatThresholdCompare(log, "Result", "", getFlatColor(), resultAccess, colorThreshold, tcu::COMPARE_LOG_ON_ERROR))
268 return tcu::TestStatus::fail("Unexpected results in color buffer -- check log for details");
269
270 return tcu::TestStatus::pass("Pass");
271 }
272
273 } // anonymous namespace
274
createFragInvocationTests(tcu::TestContext & testContext)275 tcu::TestCaseGroup* createFragInvocationTests (tcu::TestContext& testContext)
276 {
277 using GroupPtr = de::MovePtr<tcu::TestCaseGroup>;
278
279 // Test implementations do not optimize out fragment shader invocations
280 GroupPtr mainGroup (new tcu::TestCaseGroup(testContext, "frag_invocations"));
281
282 for (const auto queryType : { QueryType::OCCLUSION, QueryType::INVOCATIONS })
283 {
284 const auto groupName = getQueryTypeName(queryType);
285 GroupPtr queryTypeGroup (new tcu::TestCaseGroup(testContext, groupName.c_str()));
286
287 for (const auto secondaryCase : { false, true })
288 {
289 const auto testName = (secondaryCase ? "secondary" : "primary");
290 const TestParams params { queryType, secondaryCase };
291 addFunctionCaseWithPrograms(queryTypeGroup.get(), testName, checkSupport, initPrograms, testInvocations, params);
292 }
293
294 mainGroup->addChild(queryTypeGroup.release());
295 }
296
297 return mainGroup.release();
298 }
299
300 } // QueryPool
301 } // vkt
302