1 /*------------------------------------------------------------------------
2 * Vulkan Conformance Tests
3 * ------------------------
4 *
5 * Copyright (c) 2016 The Khronos Group Inc.
6 * Copyright (c) 2014 The Android Open Source Project
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 Scissor multi viewport tests
23 *//*--------------------------------------------------------------------*/
24
25 #include "vktFragmentOperationsScissorMultiViewportTests.hpp"
26 #include "vktTestCaseUtil.hpp"
27
28 #include "vkDefs.hpp"
29 #include "vkRefUtil.hpp"
30 #include "vkTypeUtil.hpp"
31 #include "vkMemUtil.hpp"
32 #include "vkPrograms.hpp"
33 #include "vkImageUtil.hpp"
34 #include "vkQueryUtil.hpp"
35 #include "vkCmdUtil.hpp"
36 #include "vkObjUtil.hpp"
37
38 #include "tcuTestLog.hpp"
39 #include "tcuVector.hpp"
40 #include "tcuImageCompare.hpp"
41 #include "tcuTextureUtil.hpp"
42
43 #include "deUniquePtr.hpp"
44 #include "deMath.h"
45
46 namespace vkt
47 {
48 namespace FragmentOperations
49 {
50 using namespace vk;
51 using de::MovePtr;
52 using de::UniquePtr;
53 using tcu::IVec2;
54 using tcu::IVec4;
55 using tcu::Vec2;
56 using tcu::Vec4;
57
58 namespace
59 {
60
61 enum Constants
62 {
63 MIN_MAX_VIEWPORTS = 16, //!< Minimum number of viewports for an implementation supporting multiViewport.
64 };
65
66 template <typename T>
sizeInBytes(const std::vector<T> & vec)67 inline VkDeviceSize sizeInBytes(const std::vector<T> &vec)
68 {
69 return vec.size() * sizeof(vec[0]);
70 }
71
makeImageCreateInfo(const VkFormat format,const IVec2 & size,VkImageUsageFlags usage)72 VkImageCreateInfo makeImageCreateInfo(const VkFormat format, const IVec2 &size, VkImageUsageFlags usage)
73 {
74 const VkImageCreateInfo imageParams = {
75 VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO, // VkStructureType sType;
76 DE_NULL, // const void* pNext;
77 (VkImageCreateFlags)0, // VkImageCreateFlags flags;
78 VK_IMAGE_TYPE_2D, // VkImageType imageType;
79 format, // VkFormat format;
80 makeExtent3D(size.x(), size.y(), 1), // VkExtent3D extent;
81 1u, // uint32_t mipLevels;
82 1u, // uint32_t arrayLayers;
83 VK_SAMPLE_COUNT_1_BIT, // VkSampleCountFlagBits samples;
84 VK_IMAGE_TILING_OPTIMAL, // VkImageTiling tiling;
85 usage, // VkImageUsageFlags usage;
86 VK_SHARING_MODE_EXCLUSIVE, // VkSharingMode sharingMode;
87 0u, // uint32_t queueFamilyIndexCount;
88 DE_NULL, // const uint32_t* pQueueFamilyIndices;
89 VK_IMAGE_LAYOUT_UNDEFINED, // VkImageLayout initialLayout;
90 };
91 return imageParams;
92 }
93
makeGraphicsPipeline(const DeviceInterface & vk,const VkDevice device,const VkPipelineLayout pipelineLayout,const VkRenderPass renderPass,const VkShaderModule vertexModule,const VkShaderModule geometryModule,const VkShaderModule fragmentModule,const IVec2 renderSize,const int numViewports,const std::vector<IVec4> scissors)94 Move<VkPipeline> makeGraphicsPipeline(const DeviceInterface &vk, const VkDevice device,
95 const VkPipelineLayout pipelineLayout, const VkRenderPass renderPass,
96 const VkShaderModule vertexModule, const VkShaderModule geometryModule,
97 const VkShaderModule fragmentModule, const IVec2 renderSize,
98 const int numViewports, const std::vector<IVec4> scissors)
99 {
100 const VkViewport defaultViewport = makeViewport(renderSize);
101 const std::vector<VkViewport> viewports(numViewports, defaultViewport);
102
103 DE_ASSERT(numViewports == static_cast<int>(scissors.size()));
104
105 std::vector<VkRect2D> rectScissors;
106 rectScissors.reserve(numViewports);
107
108 for (std::vector<IVec4>::const_iterator it = scissors.begin(); it != scissors.end(); ++it)
109 {
110 const VkRect2D rect = {
111 makeOffset2D(it->x(), it->y()),
112 makeExtent2D(static_cast<uint32_t>(it->z()), static_cast<uint32_t>(it->w())),
113 };
114 rectScissors.push_back(rect);
115 }
116
117 return vk::makeGraphicsPipeline(vk, // const DeviceInterface& vk
118 device, // const VkDevice device
119 pipelineLayout, // const VkPipelineLayout pipelineLayout
120 vertexModule, // const VkShaderModule vertexShaderModule
121 DE_NULL, // const VkShaderModule tessellationControlModule
122 DE_NULL, // const VkShaderModule tessellationEvalModule
123 geometryModule, // const VkShaderModule geometryShaderModule
124 fragmentModule, // const VkShaderModule fragmentShaderModule
125 renderPass, // const VkRenderPass renderPass
126 viewports, // const std::vector<VkViewport>& viewports
127 rectScissors, // const std::vector<VkRect2D>& scissors
128 VK_PRIMITIVE_TOPOLOGY_POINT_LIST); // const VkPrimitiveTopology topology
129 }
130
generateScissors(const int numScissors,const IVec2 & renderSize)131 std::vector<IVec4> generateScissors(const int numScissors, const IVec2 &renderSize)
132 {
133 // Scissor rects will be arranged in a grid-like fashion.
134
135 const int numCols = deCeilFloatToInt32(deFloatSqrt(static_cast<float>(numScissors)));
136 const int numRows = deCeilFloatToInt32(static_cast<float>(numScissors) / static_cast<float>(numCols));
137 const int rectWidth = renderSize.x() / numCols;
138 const int rectHeight = renderSize.y() / numRows;
139
140 std::vector<IVec4> scissors;
141 scissors.reserve(numScissors);
142
143 int x = 0;
144 int y = 0;
145
146 for (int scissorNdx = 0; scissorNdx < numScissors; ++scissorNdx)
147 {
148 const bool nextRow = (scissorNdx != 0) && (scissorNdx % numCols == 0);
149 if (nextRow)
150 {
151 x = 0;
152 y += rectHeight;
153 }
154
155 scissors.push_back(IVec4(x, y, rectWidth, rectHeight));
156
157 x += rectWidth;
158 }
159
160 return scissors;
161 }
162
generateColors(const int numColors)163 std::vector<Vec4> generateColors(const int numColors)
164 {
165 const Vec4 colors[] = {
166 Vec4(0.18f, 0.42f, 0.17f, 1.0f), Vec4(0.29f, 0.62f, 0.28f, 1.0f), Vec4(0.59f, 0.84f, 0.44f, 1.0f),
167 Vec4(0.96f, 0.95f, 0.72f, 1.0f), Vec4(0.94f, 0.55f, 0.39f, 1.0f), Vec4(0.82f, 0.19f, 0.12f, 1.0f),
168 Vec4(0.46f, 0.15f, 0.26f, 1.0f), Vec4(0.24f, 0.14f, 0.24f, 1.0f), Vec4(0.49f, 0.31f, 0.26f, 1.0f),
169 Vec4(0.78f, 0.52f, 0.33f, 1.0f), Vec4(0.94f, 0.82f, 0.31f, 1.0f), Vec4(0.98f, 0.65f, 0.30f, 1.0f),
170 Vec4(0.22f, 0.65f, 0.53f, 1.0f), Vec4(0.67f, 0.81f, 0.91f, 1.0f), Vec4(0.43f, 0.44f, 0.75f, 1.0f),
171 Vec4(0.26f, 0.24f, 0.48f, 1.0f),
172 };
173
174 DE_ASSERT(numColors <= DE_LENGTH_OF_ARRAY(colors));
175
176 return std::vector<Vec4>(colors, colors + numColors);
177 }
178
179 //! Renders a colorful grid of rectangles.
generateReferenceImage(const tcu::TextureFormat format,const IVec2 & renderSize,const Vec4 & clearColor,const std::vector<IVec4> & scissors,const std::vector<Vec4> & scissorColors)180 tcu::TextureLevel generateReferenceImage(const tcu::TextureFormat format, const IVec2 &renderSize,
181 const Vec4 &clearColor, const std::vector<IVec4> &scissors,
182 const std::vector<Vec4> &scissorColors)
183 {
184 DE_ASSERT(scissors.size() == scissorColors.size());
185
186 tcu::TextureLevel image(format, renderSize.x(), renderSize.y());
187 tcu::clear(image.getAccess(), clearColor);
188
189 for (std::size_t i = 0; i < scissors.size(); ++i)
190 {
191 tcu::clear(
192 tcu::getSubregion(image.getAccess(), scissors[i].x(), scissors[i].y(), scissors[i].z(), scissors[i].w()),
193 scissorColors[i]);
194 }
195
196 return image;
197 }
198
initPrograms(SourceCollections & programCollection,const int numViewports)199 void initPrograms(SourceCollections &programCollection, const int numViewports)
200 {
201 DE_UNREF(numViewports);
202
203 // Vertex shader
204 {
205 std::ostringstream src;
206 src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_450) << "\n"
207 << "\n"
208 << "layout(location = 0) in vec4 in_color;\n"
209 << "layout(location = 0) out vec4 out_color;\n"
210 << "\n"
211 << "void main(void)\n"
212 << "{\n"
213 << " out_color = in_color;\n"
214 << "}\n";
215
216 programCollection.glslSources.add("vert") << glu::VertexSource(src.str());
217 }
218
219 // Geometry shader
220 {
221 // Each input point generates a fullscreen quad.
222
223 std::ostringstream src;
224 src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_450) << "\n"
225 << "\n"
226 << "layout(points) in;\n"
227 << "layout(triangle_strip, max_vertices=4) out;\n"
228 << "\n"
229 << "out gl_PerVertex {\n"
230 << " vec4 gl_Position;\n"
231 << "};\n"
232 << "\n"
233 << "layout(location = 0) in vec4 in_color[];\n"
234 << "layout(location = 0) out vec4 out_color;\n"
235 << "\n"
236 << "void main(void)\n"
237 << "{\n"
238 << " gl_ViewportIndex = gl_PrimitiveIDIn;\n"
239 << " gl_Position = vec4(-1.0, -1.0, 0.0, 1.0);\n"
240 << " out_color = in_color[0];\n"
241 << " EmitVertex();"
242 << "\n"
243 << " gl_ViewportIndex = gl_PrimitiveIDIn;\n"
244 << " gl_Position = vec4(-1.0, 1.0, 0.0, 1.0);\n"
245 << " out_color = in_color[0];\n"
246 << " EmitVertex();"
247 << "\n"
248 << " gl_ViewportIndex = gl_PrimitiveIDIn;\n"
249 << " gl_Position = vec4(1.0, -1.0, 0.0, 1.0);\n"
250 << " out_color = in_color[0];\n"
251 << " EmitVertex();"
252 << "\n"
253 << " gl_ViewportIndex = gl_PrimitiveIDIn;\n"
254 << " gl_Position = vec4(1.0, 1.0, 0.0, 1.0);\n"
255 << " out_color = in_color[0];\n"
256 << " EmitVertex();"
257 << "}\n";
258
259 programCollection.glslSources.add("geom") << glu::GeometrySource(src.str());
260 }
261
262 // Fragment shader
263 {
264 std::ostringstream src;
265 src << glu::getGLSLVersionDeclaration(glu::GLSL_VERSION_450) << "\n"
266 << "\n"
267 << "layout(location = 0) in vec4 in_color;\n"
268 << "layout(location = 0) out vec4 out_color;\n"
269 << "\n"
270 << "void main(void)\n"
271 << "{\n"
272 << " out_color = in_color;\n"
273 << "}\n";
274
275 programCollection.glslSources.add("frag") << glu::FragmentSource(src.str());
276 }
277 }
278
279 class ScissorRenderer
280 {
281 public:
ScissorRenderer(Context & context,const IVec2 & renderSize,const int numViewports,const std::vector<IVec4> & scissors,const VkFormat colorFormat,const Vec4 & clearColor,const std::vector<Vec4> & vertices)282 ScissorRenderer(Context &context, const IVec2 &renderSize, const int numViewports,
283 const std::vector<IVec4> &scissors, const VkFormat colorFormat, const Vec4 &clearColor,
284 const std::vector<Vec4> &vertices)
285 : m_renderSize(renderSize)
286 , m_colorFormat(colorFormat)
287 , m_colorSubresourceRange(makeImageSubresourceRange(VK_IMAGE_ASPECT_COLOR_BIT, 0u, 1u, 0u, 1u))
288 , m_clearColor(clearColor)
289 , m_numViewports(numViewports)
290 , m_vertexBufferSize(sizeInBytes(vertices))
291 {
292 const DeviceInterface &vk = context.getDeviceInterface();
293 const VkDevice device = context.getDevice();
294 const uint32_t queueFamilyIndex = context.getUniversalQueueFamilyIndex();
295 Allocator &allocator = context.getDefaultAllocator();
296
297 m_colorImage =
298 makeImage(vk, device,
299 makeImageCreateInfo(m_colorFormat, m_renderSize,
300 VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT));
301 m_colorImageAlloc = bindImage(vk, device, allocator, *m_colorImage, MemoryRequirement::Any);
302 m_colorAttachment =
303 makeImageView(vk, device, *m_colorImage, VK_IMAGE_VIEW_TYPE_2D, m_colorFormat, m_colorSubresourceRange);
304
305 m_vertexBuffer = makeBuffer(vk, device, m_vertexBufferSize, VK_BUFFER_USAGE_VERTEX_BUFFER_BIT);
306 m_vertexBufferAlloc = bindBuffer(vk, device, allocator, *m_vertexBuffer, MemoryRequirement::HostVisible);
307
308 {
309 deMemcpy(m_vertexBufferAlloc->getHostPtr(), &vertices[0], static_cast<std::size_t>(m_vertexBufferSize));
310 flushAlloc(vk, device, *m_vertexBufferAlloc);
311 }
312
313 m_vertexModule = createShaderModule(vk, device, context.getBinaryCollection().get("vert"), 0u);
314 m_geometryModule = createShaderModule(vk, device, context.getBinaryCollection().get("geom"), 0u);
315 m_fragmentModule = createShaderModule(vk, device, context.getBinaryCollection().get("frag"), 0u);
316 m_renderPass = makeRenderPass(vk, device, m_colorFormat);
317 m_framebuffer =
318 makeFramebuffer(vk, device, *m_renderPass, m_colorAttachment.get(), static_cast<uint32_t>(m_renderSize.x()),
319 static_cast<uint32_t>(m_renderSize.y()));
320 m_pipelineLayout = makePipelineLayout(vk, device);
321 m_pipeline = makeGraphicsPipeline(vk, device, *m_pipelineLayout, *m_renderPass, *m_vertexModule,
322 *m_geometryModule, *m_fragmentModule, m_renderSize, m_numViewports, scissors);
323 m_cmdPool = createCommandPool(vk, device, VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT, queueFamilyIndex);
324 m_cmdBuffer = allocateCommandBuffer(vk, device, *m_cmdPool, VK_COMMAND_BUFFER_LEVEL_PRIMARY);
325 }
326
draw(Context & context,const VkBuffer colorBuffer) const327 void draw(Context &context, const VkBuffer colorBuffer) const
328 {
329 const DeviceInterface &vk = context.getDeviceInterface();
330 const VkDevice device = context.getDevice();
331 const VkQueue queue = context.getUniversalQueue();
332
333 beginCommandBuffer(vk, *m_cmdBuffer);
334
335 beginRenderPass(vk, *m_cmdBuffer, *m_renderPass, *m_framebuffer,
336 makeRect2D(0, 0, m_renderSize.x(), m_renderSize.y()), m_clearColor);
337
338 vk.cmdBindPipeline(*m_cmdBuffer, VK_PIPELINE_BIND_POINT_GRAPHICS, *m_pipeline);
339 {
340 const VkDeviceSize vertexBufferOffset = 0ull;
341 vk.cmdBindVertexBuffers(*m_cmdBuffer, 0u, 1u, &m_vertexBuffer.get(), &vertexBufferOffset);
342 }
343 vk.cmdDraw(*m_cmdBuffer, static_cast<uint32_t>(m_numViewports), 1u, 0u, 0u); // one vertex per viewport
344 endRenderPass(vk, *m_cmdBuffer);
345
346 copyImageToBuffer(vk, *m_cmdBuffer, *m_colorImage, colorBuffer, m_renderSize);
347
348 endCommandBuffer(vk, *m_cmdBuffer);
349 submitCommandsAndWait(vk, device, queue, *m_cmdBuffer);
350 }
351
352 private:
353 const IVec2 m_renderSize;
354 const VkFormat m_colorFormat;
355 const VkImageSubresourceRange m_colorSubresourceRange;
356 const Vec4 m_clearColor;
357 const int m_numViewports;
358 const VkDeviceSize m_vertexBufferSize;
359
360 Move<VkImage> m_colorImage;
361 MovePtr<Allocation> m_colorImageAlloc;
362 Move<VkImageView> m_colorAttachment;
363 Move<VkBuffer> m_vertexBuffer;
364 MovePtr<Allocation> m_vertexBufferAlloc;
365 Move<VkShaderModule> m_vertexModule;
366 Move<VkShaderModule> m_geometryModule;
367 Move<VkShaderModule> m_fragmentModule;
368 Move<VkRenderPass> m_renderPass;
369 Move<VkFramebuffer> m_framebuffer;
370 Move<VkPipelineLayout> m_pipelineLayout;
371 Move<VkPipeline> m_pipeline;
372 Move<VkCommandPool> m_cmdPool;
373 Move<VkCommandBuffer> m_cmdBuffer;
374
375 // "deleted"
376 ScissorRenderer(const ScissorRenderer &);
377 ScissorRenderer &operator=(const ScissorRenderer &);
378 };
379
test(Context & context,const int numViewports)380 tcu::TestStatus test(Context &context, const int numViewports)
381 {
382 const DeviceInterface &vk = context.getDeviceInterface();
383 const VkDevice device = context.getDevice();
384 Allocator &allocator = context.getDefaultAllocator();
385
386 const IVec2 renderSize(128, 128);
387 const VkFormat colorFormat = VK_FORMAT_R8G8B8A8_UNORM;
388 const Vec4 clearColor(0.5f, 0.5f, 0.5f, 1.0f);
389 const std::vector<Vec4> vertexColors = generateColors(numViewports);
390 const std::vector<IVec4> scissors = generateScissors(numViewports, renderSize);
391
392 const VkDeviceSize colorBufferSize = renderSize.x() * renderSize.y() * tcu::getPixelSize(mapVkFormat(colorFormat));
393 const Unique<VkBuffer> colorBuffer(makeBuffer(vk, device, colorBufferSize, VK_BUFFER_USAGE_TRANSFER_DST_BIT));
394 const UniquePtr<Allocation> colorBufferAlloc(
395 bindBuffer(vk, device, allocator, *colorBuffer, MemoryRequirement::HostVisible));
396
397 zeroBuffer(vk, device, *colorBufferAlloc, colorBufferSize);
398
399 {
400 context.getTestContext().getLog()
401 << tcu::TestLog::Message << "Rendering a colorful grid of " << numViewports << " rectangle(s)."
402 << tcu::TestLog::EndMessage << tcu::TestLog::Message << "Not covered area will be filled with a gray color."
403 << tcu::TestLog::EndMessage;
404 }
405
406 // Draw
407 {
408 const ScissorRenderer renderer(context, renderSize, numViewports, scissors, colorFormat, clearColor,
409 vertexColors);
410 renderer.draw(context, *colorBuffer);
411 }
412
413 // Log image
414 {
415 invalidateAlloc(vk, device, *colorBufferAlloc);
416
417 const tcu::ConstPixelBufferAccess resultImage(mapVkFormat(colorFormat), renderSize.x(), renderSize.y(), 1u,
418 colorBufferAlloc->getHostPtr());
419 const tcu::TextureLevel referenceImage =
420 generateReferenceImage(mapVkFormat(colorFormat), renderSize, clearColor, scissors, vertexColors);
421
422 // Images should now match.
423 if (!tcu::floatThresholdCompare(context.getTestContext().getLog(), "color", "Image compare",
424 referenceImage.getAccess(), resultImage, Vec4(0.02f), tcu::COMPARE_LOG_RESULT))
425 return tcu::TestStatus::fail("Rendered image is not correct");
426 }
427
428 return tcu::TestStatus::pass("OK");
429 }
430
checkSupport(Context & context,const int)431 void checkSupport(Context &context, const int)
432 {
433 context.requireDeviceCoreFeature(DEVICE_CORE_FEATURE_GEOMETRY_SHADER);
434 context.requireDeviceCoreFeature(DEVICE_CORE_FEATURE_MULTI_VIEWPORT);
435
436 if (context.getDeviceProperties().limits.maxViewports < MIN_MAX_VIEWPORTS)
437 TCU_THROW(NotSupportedError, "Implementation doesn't support minimum required number of viewports");
438 }
439
440 } // namespace
441
createScissorMultiViewportTests(tcu::TestContext & testCtx)442 tcu::TestCaseGroup *createScissorMultiViewportTests(tcu::TestContext &testCtx)
443 {
444 MovePtr<tcu::TestCaseGroup> group(new tcu::TestCaseGroup(testCtx, "multi_viewport"));
445
446 for (int numViewports = 1; numViewports <= MIN_MAX_VIEWPORTS; ++numViewports)
447 addFunctionCaseWithPrograms(group.get(), "scissor_" + de::toString(numViewports), checkSupport, initPrograms,
448 test, numViewports);
449
450 return group.release();
451 }
452
453 } // namespace FragmentOperations
454 } // namespace vkt
455