1 /*------------------------------------------------------------------------
2 * Vulkan Conformance Tests
3 * ------------------------
4 *
5 * Copyright (c) 2024 The Khronos Group Inc.
6 * Copyright (c) 2024 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 Tests for empty and missing Fragment Shaders.
23 *//*--------------------------------------------------------------------*/
24
25 #include "vktPipelineEmptyFSTests.hpp"
26 #include "tcuImageCompare.hpp"
27 #include "vkCmdUtil.hpp"
28 #include "vkImageUtil.hpp"
29 #include "vkObjUtil.hpp"
30 #include "vkTypeUtil.hpp"
31 #include "vktTestCase.hpp"
32
33 #include <iostream>
34 #include <vector>
35
36 namespace vkt
37 {
38 namespace pipeline
39 {
40
41 using namespace vk;
42
43 namespace
44 {
45
46 struct TestParams
47 {
48 PipelineConstructionType constructionType;
49 VkShaderStageFlagBits lastVertexStage; // Last vertex shader stage: vertex, tessellation or geometry.
50 bool emptyFS; // True: empty FS; False: do not include a fragment shader at all.
51
TestParamsvkt::pipeline::__anon57ea3aa00111::TestParams52 TestParams(PipelineConstructionType type_, VkShaderStageFlagBits lastStage, bool emptyFS_)
53 : constructionType(type_)
54 , lastVertexStage(lastStage)
55 , emptyFS(emptyFS_)
56 {
57 DE_ASSERT(lastIsVertex() || lastIsTessellation() || lastIsGeometry());
58 }
59
lastIsVertexvkt::pipeline::__anon57ea3aa00111::TestParams60 bool lastIsVertex(void) const
61 {
62 return (lastVertexStage == VK_SHADER_STAGE_VERTEX_BIT);
63 }
64
lastIsTessellationvkt::pipeline::__anon57ea3aa00111::TestParams65 bool lastIsTessellation(void) const
66 {
67 return (lastVertexStage == VK_SHADER_STAGE_TESSELLATION_CONTROL_BIT ||
68 lastVertexStage == VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT);
69 }
70
lastIsGeometryvkt::pipeline::__anon57ea3aa00111::TestParams71 bool lastIsGeometry(void) const
72 {
73 return (lastVertexStage == VK_SHADER_STAGE_GEOMETRY_BIT);
74 }
75 };
76
77 class EmptyFSInstance : public vkt::TestInstance
78 {
79 public:
EmptyFSInstance(Context & context,const TestParams & params)80 EmptyFSInstance(Context &context, const TestParams ¶ms) : vkt::TestInstance(context), m_params(params)
81 {
82 }
~EmptyFSInstance(void)83 virtual ~EmptyFSInstance(void)
84 {
85 }
86
87 tcu::TestStatus iterate(void) override;
88
89 protected:
90 const TestParams m_params;
91 };
92
93 class EmptyFSCase : public vkt::TestCase
94 {
95 public:
EmptyFSCase(tcu::TestContext & testCtx,const std::string & name,const TestParams & params)96 EmptyFSCase(tcu::TestContext &testCtx, const std::string &name, const TestParams ¶ms)
97 : vkt::TestCase(testCtx, name)
98 , m_params(params)
99 {
100 }
~EmptyFSCase(void)101 virtual ~EmptyFSCase(void)
102 {
103 }
104
105 void initPrograms(vk::SourceCollections &programCollection) const override;
106 TestInstance *createInstance(Context &context) const override;
107 void checkSupport(Context &context) const override;
108
109 protected:
110 const TestParams m_params;
111 };
112
createInstance(Context & context) const113 TestInstance *EmptyFSCase::createInstance(Context &context) const
114 {
115 return new EmptyFSInstance(context, m_params);
116 }
117
checkSupport(Context & context) const118 void EmptyFSCase::checkSupport(Context &context) const
119 {
120 if (m_params.lastIsTessellation())
121 {
122 context.requireDeviceCoreFeature(DEVICE_CORE_FEATURE_TESSELLATION_SHADER);
123 context.requireDeviceCoreFeature(DEVICE_CORE_FEATURE_SHADER_TESSELLATION_AND_GEOMETRY_POINT_SIZE);
124 }
125
126 if (m_params.lastIsGeometry())
127 context.requireDeviceCoreFeature(DEVICE_CORE_FEATURE_GEOMETRY_SHADER);
128
129 const auto ctx = context.getContextCommonData();
130 checkPipelineConstructionRequirements(ctx.vki, ctx.physicalDevice, m_params.constructionType);
131 }
132
initPrograms(SourceCollections & programCollection) const133 void EmptyFSCase::initPrograms(SourceCollections &programCollection) const
134 {
135 std::stringstream userOutputsDecl;
136 userOutputsDecl << "layout (location=0) out float added;\n"
137 << "layout (location=1) out float multiplied;\n";
138
139 std::ostringstream vert;
140 vert << "#version 460\n"
141 << "layout (location=0) in vec4 inPos;\n"
142 << (m_params.lastIsVertex() ? userOutputsDecl.str() : "") << "out gl_PerVertex\n"
143 << "{\n"
144 << " vec4 gl_Position;\n"
145 << " float gl_PointSize;\n"
146 << "};\n"
147 << "void main (void)\n"
148 << "{\n"
149 << " gl_Position = inPos;\n"
150 << " gl_PointSize = 1.0;\n"
151 << (m_params.lastIsVertex() ? " added = inPos.x + 1000.0;\n"
152 " multiplied = inPos.y * 1000.0;\n" :
153 "")
154 << "}\n";
155 programCollection.glslSources.add("vert") << glu::VertexSource(vert.str());
156
157 if (m_params.lastIsTessellation())
158 {
159 // Add passthrough tessellation control shader.
160 std::ostringstream tesc;
161 tesc << "#version 460\n"
162 << "layout (vertices=3) out;\n"
163 << "in gl_PerVertex\n"
164 << "{\n"
165 << " vec4 gl_Position;\n"
166 << " float gl_PointSize;\n"
167 << "} gl_in[gl_MaxPatchVertices];\n"
168 << "out gl_PerVertex\n"
169 << "{\n"
170 << " vec4 gl_Position;\n"
171 << " float gl_PointSize;\n"
172 << "} gl_out[];\n"
173 << "void main (void)\n"
174 << "{\n"
175 << " gl_TessLevelInner[0] = 1.0;\n"
176 << " gl_TessLevelInner[1] = 1.0;\n"
177 << " gl_TessLevelOuter[0] = 1.0;\n"
178 << " gl_TessLevelOuter[1] = 1.0;\n"
179 << " gl_TessLevelOuter[2] = 1.0;\n"
180 << " gl_TessLevelOuter[3] = 1.0;\n"
181 << " gl_out[gl_InvocationID].gl_Position = gl_in[gl_InvocationID].gl_Position;\n"
182 << " gl_out[gl_InvocationID].gl_PointSize = gl_in[gl_InvocationID].gl_PointSize;\n"
183 << "}\n";
184
185 programCollection.glslSources.add("tesc") << glu::TessellationControlSource(tesc.str());
186
187 std::ostringstream tese;
188 tese << "#version 460\n"
189 << "layout (triangles, fractional_odd_spacing, cw) in;\n"
190 << userOutputsDecl.str() << "in gl_PerVertex\n"
191 << "{\n"
192 << " vec4 gl_Position;\n"
193 << " float gl_PointSize;\n"
194 << "} gl_in[gl_MaxPatchVertices];\n"
195 << "out gl_PerVertex\n"
196 << "{\n"
197 << " vec4 gl_Position;\n"
198 << " float gl_PointSize;\n"
199 << "};\n"
200 << "void main (void)\n"
201 << "{\n"
202 << " vec4 pos = (gl_TessCoord.x * gl_in[0].gl_Position) +\n"
203 << " (gl_TessCoord.y * gl_in[1].gl_Position) +\n"
204 << " (gl_TessCoord.z * gl_in[2].gl_Position);\n"
205 << " gl_Position = pos;\n"
206 << " gl_PointSize = gl_in[0].gl_PointSize;\n"
207 << " added = pos.x + 1000.0;\n"
208 << " multiplied = pos.y * 1000.0;\n"
209 << "}\n";
210
211 programCollection.glslSources.add("tese") << glu::TessellationEvaluationSource(tese.str());
212 }
213
214 if (m_params.lastIsGeometry())
215 {
216 const auto vertexCount = 3u;
217
218 std::ostringstream geom;
219 geom << "#version 450\n"
220 << "layout (triangles) in;\n"
221 << "layout (triangle_strip, max_vertices=" << vertexCount << ") out;\n"
222 << userOutputsDecl.str() << "in gl_PerVertex\n"
223 << "{\n"
224 << " vec4 gl_Position;\n"
225 << " float gl_PointSize;\n"
226 << "} gl_in[" << vertexCount << "];\n"
227 << "out gl_PerVertex\n"
228 << "{\n"
229 << " vec4 gl_Position;\n"
230 << " float gl_PointSize;\n"
231 << "};\n"
232 << "void main() {\n";
233
234 for (uint32_t i = 0u; i < vertexCount; ++i)
235 {
236 geom << " gl_Position = gl_in[" << i << "].gl_Position;\n"
237 << " gl_PointSize = gl_in[" << i << "].gl_PointSize;\n"
238 << " added = gl_in[" << i << "].gl_Position.x + 1000.0;\n"
239 << " multiplied = gl_in[" << i << "].gl_Position.y * 1000.0;\n"
240 << " EmitVertex();\n";
241 }
242
243 geom << "}\n";
244 programCollection.glslSources.add("geom") << glu::GeometrySource(geom.str());
245 }
246
247 if (m_params.emptyFS)
248 {
249 std::ostringstream frag;
250 frag << "#version 460\n"
251 << "layout (location=0) in float added;\n"
252 << "layout (location=1) in float multiplied;\n"
253 << "void main (void) {}\n";
254 programCollection.glslSources.add("frag") << glu::FragmentSource(frag.str());
255 }
256 }
257
iterate(void)258 tcu::TestStatus EmptyFSInstance::iterate(void)
259 {
260 const auto &ctx = m_context.getContextCommonData();
261 const tcu::IVec3 fbExtent(2, 2, 1);
262 const auto pixelCount = fbExtent.x() * fbExtent.y() * fbExtent.z();
263 const auto vkExtent = makeExtent3D(fbExtent);
264 const auto fbFormat = VK_FORMAT_R8G8B8A8_UNORM;
265 const auto dsFormat = VK_FORMAT_D16_UNORM;
266 const auto tcuFormat = mapVkFormat(dsFormat);
267 const float depthThres = 0.000025f; // 1/65535 < depthThres < 2/65535
268 const auto fbUsage = (VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT);
269 const auto dsUsage = (VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT);
270 const tcu::Vec4 clearColor(0.0f, 0.0f, 0.0f, 1.0f);
271
272 // Color buffer.
273 ImageWithBuffer colorBuffer(ctx.vkd, ctx.device, ctx.allocator, vkExtent, fbFormat, fbUsage, VK_IMAGE_TYPE_2D);
274
275 // Depth/stencil buffer.
276 ImageWithBuffer dsBuffer(ctx.vkd, ctx.device, ctx.allocator, vkExtent, dsFormat, dsUsage, VK_IMAGE_TYPE_2D,
277 makeImageSubresourceRange(VK_IMAGE_ASPECT_DEPTH_BIT, 0u, 1u, 0u, 1u));
278
279 // Vertices.
280 const float pixelWidth = 2.0f / static_cast<float>(vkExtent.width);
281 const float pixelHeight = 2.0f / static_cast<float>(vkExtent.height);
282 const float horMargin = pixelWidth / 4.0f;
283 const float vertMargin = pixelHeight / 4.0f;
284
285 const auto calcCenter = [](int i, int size)
286 { return (static_cast<float>(i) + 0.5f) / static_cast<float>(size) * 2.0f - 1.0f; };
287
288 // One triangle per pixel with varying depth.
289 std::vector<tcu::Vec4> vertices;
290 for (int y = 0; y < fbExtent.y(); ++y)
291 for (int x = 0; x < fbExtent.x(); ++x)
292 {
293 const float xCenter = calcCenter(x, fbExtent.x());
294 const float yCenter = calcCenter(y, fbExtent.y());
295 const int pixelId = y * fbExtent.x() + x;
296 const float depth = static_cast<float>(pixelId) / static_cast<float>(pixelCount);
297
298 // Triangle around the center.
299 vertices.emplace_back(xCenter, yCenter - vertMargin, depth, 1.0f);
300 vertices.emplace_back(xCenter - horMargin, yCenter + vertMargin, depth, 1.0f);
301 vertices.emplace_back(xCenter + horMargin, yCenter + vertMargin, depth, 1.0f);
302 }
303
304 // Vertex buffer
305 const auto vbSize = static_cast<VkDeviceSize>(de::dataSize(vertices));
306 const auto vbInfo = makeBufferCreateInfo(vbSize, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT);
307 BufferWithMemory vertexBuffer(ctx.vkd, ctx.device, ctx.allocator, vbInfo, MemoryRequirement::HostVisible);
308 const auto vbAlloc = vertexBuffer.getAllocation();
309 void *vbData = vbAlloc.getHostPtr();
310 const auto vbOffset = static_cast<VkDeviceSize>(0);
311
312 deMemcpy(vbData, de::dataOrNull(vertices), de::dataSize(vertices));
313 flushAlloc(ctx.vkd, ctx.device, vbAlloc); // strictly speaking, not needed.
314
315 // Pipeline layout
316 PipelineLayoutWrapper pipelineLayout(m_params.constructionType, ctx.vkd, ctx.device);
317 RenderPassWrapper renderPass(m_params.constructionType, ctx.vkd, ctx.device, fbFormat, dsFormat);
318 std::vector<VkImage> images{colorBuffer.getImage(), dsBuffer.getImage()};
319 std::vector<VkImageView> imageViews{colorBuffer.getImageView(), dsBuffer.getImageView()};
320
321 DE_ASSERT(images.size() == imageViews.size());
322 renderPass.createFramebuffer(ctx.vkd, ctx.device, de::sizeU32(images), de::dataOrNull(images),
323 de::dataOrNull(imageViews), vkExtent.width, vkExtent.height);
324
325 // Modules.
326 const auto &binaries = m_context.getBinaryCollection();
327 const auto vertModule = ShaderWrapper(ctx.vkd, ctx.device, binaries.get("vert"));
328 const auto tescModule =
329 (m_params.lastIsTessellation() ? ShaderWrapper(ctx.vkd, ctx.device, binaries.get("tesc")) : ShaderWrapper());
330 const auto teseModule =
331 (m_params.lastIsTessellation() ? ShaderWrapper(ctx.vkd, ctx.device, binaries.get("tese")) : ShaderWrapper());
332 const auto geomModule =
333 (m_params.lastIsGeometry() ? ShaderWrapper(ctx.vkd, ctx.device, binaries.get("geom")) : ShaderWrapper());
334 const auto fragModule =
335 (m_params.emptyFS ? ShaderWrapper(ctx.vkd, ctx.device, binaries.get("frag")) : ShaderWrapper());
336
337 const std::vector<VkViewport> viewports(1u, makeViewport(vkExtent));
338 const std::vector<VkRect2D> scissors(1u, makeRect2D(vkExtent));
339
340 VkPipelineRasterizationStateCreateInfo rasterizationStateCreateInfo = initVulkanStructure();
341 rasterizationStateCreateInfo.lineWidth = 1.0f;
342
343 VkPipelineDepthStencilStateCreateInfo depthStencilStateCreateInfo = initVulkanStructure();
344 depthStencilStateCreateInfo.depthTestEnable = VK_TRUE;
345 depthStencilStateCreateInfo.depthWriteEnable = VK_TRUE;
346 depthStencilStateCreateInfo.depthCompareOp = VK_COMPARE_OP_ALWAYS;
347
348 VkPipelineInputAssemblyStateCreateInfo inputAssemblyStateCreateInfo = initVulkanStructure();
349 inputAssemblyStateCreateInfo.topology =
350 (m_params.lastIsTessellation() ? VK_PRIMITIVE_TOPOLOGY_PATCH_LIST : VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST);
351
352 GraphicsPipelineWrapper pipelineWrapper(ctx.vki, ctx.vkd, ctx.physicalDevice, ctx.device,
353 m_context.getDeviceExtensions(), m_params.constructionType);
354 pipelineWrapper.setMonolithicPipelineLayout(pipelineLayout);
355 pipelineWrapper.setDefaultVertexInputState(true);
356 pipelineWrapper.setDefaultColorBlendState();
357 pipelineWrapper.setDefaultMultisampleState();
358 pipelineWrapper.setDefaultPatchControlPoints(3u);
359 pipelineWrapper.setupVertexInputState(nullptr, &inputAssemblyStateCreateInfo);
360 pipelineWrapper.setupPreRasterizationShaderState(viewports, scissors, pipelineLayout, *renderPass, 0u, vertModule,
361 &rasterizationStateCreateInfo, tescModule, teseModule, geomModule);
362 pipelineWrapper.setupFragmentShaderState(pipelineLayout, *renderPass, 0u, fragModule, &depthStencilStateCreateInfo);
363 pipelineWrapper.setupFragmentOutputState(*renderPass);
364 pipelineWrapper.buildPipeline();
365
366 CommandPoolWithBuffer cmd(ctx.vkd, ctx.device, ctx.qfIndex);
367 const auto cmdBuffer = *cmd.cmdBuffer;
368
369 const std::vector<VkClearValue> clearValues{makeClearValueColor(clearColor), makeClearValueDepthStencil(0.0f, 0u)};
370 beginCommandBuffer(ctx.vkd, cmdBuffer);
371 renderPass.begin(ctx.vkd, cmdBuffer, scissors.at(0u), de::sizeU32(clearValues), de::dataOrNull(clearValues));
372 ctx.vkd.cmdBindVertexBuffers(cmdBuffer, 0u, 1u, &vertexBuffer.get(), &vbOffset);
373 pipelineWrapper.bind(cmdBuffer);
374 ctx.vkd.cmdDraw(cmdBuffer, de::sizeU32(vertices), 1u, 0u, 0u);
375 renderPass.end(ctx.vkd, cmdBuffer);
376 copyImageToBuffer(ctx.vkd, cmdBuffer, dsBuffer.getImage(), dsBuffer.getBuffer(), fbExtent.swizzle(0, 1),
377 VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT, VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL,
378 1u, VK_IMAGE_ASPECT_DEPTH_BIT, VK_IMAGE_ASPECT_DEPTH_BIT,
379 (VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT));
380 endCommandBuffer(ctx.vkd, cmdBuffer);
381 submitCommandsAndWait(ctx.vkd, ctx.device, ctx.queue, cmdBuffer);
382
383 // Verify depth output.
384 invalidateAlloc(ctx.vkd, ctx.device, dsBuffer.getBufferAllocation());
385 tcu::PixelBufferAccess resultAccess(tcuFormat, fbExtent, dsBuffer.getBufferAllocation().getHostPtr());
386
387 tcu::TextureLevel referenceLevel(tcuFormat, fbExtent.x(), fbExtent.y());
388 auto referenceAccess = referenceLevel.getAccess();
389
390 for (int y = 0; y < fbExtent.y(); ++y)
391 for (int x = 0; x < fbExtent.x(); ++x)
392 {
393 const int pixelId = y * fbExtent.x() + x;
394 const float depth = static_cast<float>(pixelId) / static_cast<float>(pixelCount);
395
396 referenceAccess.setPixDepth(depth, x, y);
397 }
398
399 auto &log = m_context.getTestContext().getLog();
400 if (!tcu::dsThresholdCompare(log, "DepthResult", "", referenceAccess, resultAccess, depthThres,
401 tcu::COMPARE_LOG_EVERYTHING))
402 return tcu::TestStatus::fail("Unexpected color in result buffer; check log for details");
403
404 return tcu::TestStatus::pass("Pass");
405 }
406
407 } // anonymous namespace
408
createEmptyFSTests(tcu::TestContext & testCtx,PipelineConstructionType pipelineType)409 tcu::TestCaseGroup *createEmptyFSTests(tcu::TestContext &testCtx, PipelineConstructionType pipelineType)
410 {
411 de::MovePtr<tcu::TestCaseGroup> emptyFSTests(new tcu::TestCaseGroup(testCtx, "empty_fs"));
412
413 const struct
414 {
415 VkShaderStageFlagBits shaderStage;
416 const char *name;
417 } vertexStages[] = {
418 {VK_SHADER_STAGE_VERTEX_BIT, "vert"},
419 {VK_SHADER_STAGE_TESSELLATION_EVALUATION_BIT, "tess"},
420 {VK_SHADER_STAGE_VERTEX_BIT, "geom"},
421 };
422
423 for (const auto &stage : vertexStages)
424 for (const bool emptyFS : {false, true})
425 {
426 const std::string suffix(emptyFS ? "_empty_fs" : "_no_fs");
427 const std::string testName = stage.name + suffix;
428 const TestParams params{pipelineType, stage.shaderStage, emptyFS};
429
430 emptyFSTests->addChild(new EmptyFSCase(testCtx, testName, params));
431 }
432
433 return emptyFSTests.release();
434 }
435
436 } // namespace pipeline
437 } // namespace vkt
438