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