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