• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 &params) : 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 &params)
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