1/*-------------------------------------------------------------------------
2 * drawElements Quality Program OpenGL ES 3.0 Module
3 * -------------------------------------------------
4 *
5 * Copyright 2018 The Android Open Source Project
6 *
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
10 *
11 *      http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
18 *
19 */ /*!
20 * \file
21 * \brief Multiview tests.
22 * Tests functionality provided by the three multiview extensions.
23 */ /*--------------------------------------------------------------------*/
24
25#include "es3fMultiviewTests.hpp"
26
27#include "deString.h"
28#include "deStringUtil.hpp"
29#include "gluContextInfo.hpp"
30#include "gluPixelTransfer.hpp"
31#include "gluShaderProgram.hpp"
32#include "glw.h"
33#include "glwEnums.hpp"
34#include "glwFunctions.hpp"
35#include "tcuRenderTarget.hpp"
36#include "tcuSurface.hpp"
37#include "tcuTestLog.hpp"
38#include "tcuVector.hpp"
39
40using tcu::TestLog;
41using tcu::Vec4;
42
43namespace deqp
44{
45namespace gles3
46{
47namespace Functional
48{
49
50static const int   NUM_CASE_ITERATIONS = 1;
51static const float UNIT_SQUARE[16]	 = {
52	1.0f,  1.0f,  0.05f, 1.0f, // Vertex 0
53	1.0f,  -1.0f, 0.05f, 1.0f, // Vertex 1
54	-1.0f, 1.0f,  0.05f, 1.0f, // Vertex 2
55	-1.0f, -1.0f, 0.05f, 1.0f  // Vertex 3
56};
57static const float COLOR_VALUES[] = {
58	1, 0, 0, 1, // Red for level 0
59	0, 1, 0, 1, // Green for level 1
60};
61
62class MultiviewCase : public TestCase
63{
64public:
65	MultiviewCase(Context& context, const char* name, const char* description, int numSamples);
66	~MultiviewCase();
67	void		  init();
68	void		  deinit();
69	IterateResult iterate();
70
71private:
72	MultiviewCase(const MultiviewCase& other);
73	MultiviewCase& operator=(const MultiviewCase& other);
74	void setupFramebufferObjects();
75	void deleteFramebufferObjects();
76
77	glu::ShaderProgram* m_multiviewProgram;
78	deUint32			m_multiviewFbo;
79	deUint32			m_arrayTexture;
80
81	glu::ShaderProgram* m_finalProgram;
82
83	int		  m_caseIndex;
84	const int m_numSamples;
85	const int m_width;
86	const int m_height;
87};
88
89MultiviewCase::MultiviewCase(Context& context, const char* name, const char* description, int numSamples)
90	: TestCase(context, name, description)
91	, m_multiviewProgram(DE_NULL)
92	, m_multiviewFbo(0)
93	, m_arrayTexture(0)
94	, m_finalProgram(DE_NULL)
95	, m_caseIndex(0)
96	, m_numSamples(numSamples)
97	, m_width(512)
98	, m_height(512)
99{
100}
101
102MultiviewCase::~MultiviewCase()
103{
104	MultiviewCase::deinit();
105}
106
107void MultiviewCase::setupFramebufferObjects()
108{
109	const glw::Functions& gl = m_context.getRenderContext().getFunctions();
110
111	// First create the array texture and multiview FBO.
112
113	gl.genTextures(1, &m_arrayTexture);
114	gl.bindTexture(GL_TEXTURE_2D_ARRAY, m_arrayTexture);
115	gl.texStorage3D(GL_TEXTURE_2D_ARRAY, 1 /* num mipmaps */, GL_RGBA8, m_width / 2, m_height, 2 /* num levels */);
116	gl.texParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
117	gl.texParameteri(GL_TEXTURE_2D_ARRAY, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
118	GLU_EXPECT_NO_ERROR(gl.getError(), "Create array texture");
119
120	gl.genFramebuffers(1, &m_multiviewFbo);
121	gl.bindFramebuffer(GL_FRAMEBUFFER, m_multiviewFbo);
122	if (m_numSamples == 1)
123	{
124		gl.framebufferTextureMultiviewOVR(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, m_arrayTexture, 0 /* mip level */,
125										  0 /* base view index */, 2 /* num views */);
126	}
127	else
128	{
129		gl.framebufferTextureMultisampleMultiviewOVR(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, m_arrayTexture,
130													 0 /* mip level */, m_numSamples /* samples */,
131													 0 /* base view index */, 2 /* num views */);
132	}
133	GLU_EXPECT_NO_ERROR(gl.getError(), "Create multiview FBO");
134	deUint32 fboStatus = glCheckFramebufferStatus(GL_FRAMEBUFFER);
135	if (fboStatus == GL_FRAMEBUFFER_UNSUPPORTED)
136	{
137		throw tcu::NotSupportedError("Framebuffer unsupported", "", __FILE__, __LINE__);
138	}
139	else if (fboStatus != GL_FRAMEBUFFER_COMPLETE)
140	{
141		throw tcu::TestError("Failed to create framebuffer object", "", __FILE__, __LINE__);
142	}
143}
144
145void MultiviewCase::deleteFramebufferObjects()
146{
147	const glw::Functions& gl = m_context.getRenderContext().getFunctions();
148	gl.deleteTextures(1, &m_arrayTexture);
149	gl.deleteFramebuffers(1, &m_multiviewFbo);
150}
151
152void MultiviewCase::init()
153{
154	const glu::ContextInfo& contextInfo = m_context.getContextInfo();
155	bool					mvsupported = contextInfo.isExtensionSupported("GL_OVR_multiview");
156	if (!mvsupported)
157	{
158		TCU_THROW(NotSupportedError, "Multiview is not supported");
159	}
160
161	if (m_numSamples > 1)
162	{
163		bool msaasupported = contextInfo.isExtensionSupported("GL_OVR_multiview_multisampled_render_to_texture");
164		if (!msaasupported)
165		{
166			TCU_THROW(NotSupportedError, "Implicit MSAA multiview is not supported");
167		}
168	}
169
170	const char* multiviewVertexShader = "#version 300 es\n"
171										"#extension GL_OVR_multiview : enable\n"
172										"layout(num_views=2) in;\n"
173										"layout(location = 0) in mediump vec4 a_position;\n"
174										"uniform mediump vec4 uColor[2];\n"
175										"out mediump vec4 vColor;\n"
176										"void main() {\n"
177										"  vColor = uColor[gl_ViewID_OVR];\n"
178										"  gl_Position = a_position;\n"
179										"}\n";
180
181	const char* multiviewFragmentShader = "#version 300 es\n"
182										  "layout(location = 0) out mediump vec4 dEQP_FragColor;\n"
183										  "in mediump vec4 vColor;\n"
184										  "void main() {\n"
185										  "  dEQP_FragColor = vColor;\n"
186										  "}\n";
187
188	m_multiviewProgram = new glu::ShaderProgram(
189		m_context.getRenderContext(), glu::makeVtxFragSources(multiviewVertexShader, multiviewFragmentShader));
190	DE_ASSERT(m_multiviewProgram);
191	if (!m_multiviewProgram->isOk())
192	{
193		m_testCtx.getLog() << *m_multiviewProgram;
194		TCU_FAIL("Failed to compile multiview shader");
195	}
196
197	// Draw the first layer on the left half of the screen and the second layer
198	// on the right half.
199	const char* finalVertexShader = "#version 300 es\n"
200									"layout(location = 0) in mediump vec4 a_position;\n"
201									"out highp vec3 vTexCoord;\n"
202									"void main() {\n"
203									"  vTexCoord.x = fract(a_position.x + 1.0);\n"
204									"  vTexCoord.y = .5 * (a_position.y + 1.0);\n"
205									"  vTexCoord.z = a_position.x;\n"
206									"  gl_Position = a_position;\n"
207									"}\n";
208
209	const char* finalFragmentShader = "#version 300 es\n"
210									  "layout(location = 0) out mediump vec4 dEQP_FragColor;\n"
211									  "uniform lowp sampler2DArray uArrayTexture;\n"
212									  "in highp vec3 vTexCoord;\n"
213									  "void main() {\n"
214									  "  highp vec3 uvw = vTexCoord;\n"
215									  "  uvw.z = floor(vTexCoord.z + 1.0);\n"
216									  "  dEQP_FragColor = texture(uArrayTexture, uvw);\n"
217									  "}\n";
218
219	m_finalProgram = new glu::ShaderProgram(m_context.getRenderContext(),
220											glu::makeVtxFragSources(finalVertexShader, finalFragmentShader));
221	DE_ASSERT(m_finalProgram);
222	if (!m_finalProgram->isOk())
223	{
224		m_testCtx.getLog() << *m_finalProgram;
225		TCU_FAIL("Failed to compile final shader");
226	}
227
228	m_testCtx.setTestResult(QP_TEST_RESULT_PASS, "Pass");
229	GLU_CHECK_MSG("Case initialization finished");
230}
231
232void MultiviewCase::deinit()
233{
234	deleteFramebufferObjects();
235	delete m_multiviewProgram;
236	m_multiviewProgram = DE_NULL;
237	delete m_finalProgram;
238	m_finalProgram = DE_NULL;
239}
240
241MultiviewCase::IterateResult MultiviewCase::iterate()
242{
243	TestLog&	log			 = m_testCtx.getLog();
244	deUint32	colorUniform = glGetUniformLocation(m_multiviewProgram->getProgram(), "uColor");
245	std::string header = "Case iteration " + de::toString(m_caseIndex + 1) + " / " + de::toString(NUM_CASE_ITERATIONS);
246	log << TestLog::Section(header, header);
247
248	DE_ASSERT(m_multiviewProgram);
249
250	// Create and bind the multiview FBO.
251
252	try
253	{
254		setupFramebufferObjects();
255	}
256	catch (tcu::NotSupportedError& e)
257	{
258		log << TestLog::Message << "ERROR: " << e.what() << "." << TestLog::EndMessage << TestLog::EndSection;
259		m_testCtx.setTestResult(QP_TEST_RESULT_NOT_SUPPORTED, "Not supported");
260		return STOP;
261	}
262	catch (tcu::InternalError& e)
263	{
264		log << TestLog::Message << "ERROR: " << e.what() << "." << TestLog::EndMessage << TestLog::EndSection;
265		m_testCtx.setTestResult(QP_TEST_RESULT_INTERNAL_ERROR, "Error");
266		return STOP;
267	}
268
269	log << TestLog::EndSection;
270
271	// Draw full screen quad into the multiview framebuffer.
272	// The quad should be instanced into both layers of the array texture.
273
274	const glw::Functions& gl = m_context.getRenderContext().getFunctions();
275	gl.bindFramebuffer(GL_FRAMEBUFFER, m_multiviewFbo);
276	gl.viewport(0, 0, m_width / 2, m_height);
277	gl.useProgram(m_multiviewProgram->getProgram());
278	gl.uniform4fv(colorUniform, 2, COLOR_VALUES);
279	gl.enableVertexAttribArray(0);
280	gl.vertexAttribPointer(0, 4, GL_FLOAT, GL_FALSE, 0, &UNIT_SQUARE[0]);
281	gl.drawArrays(GL_TRIANGLE_STRIP, 0, 4);
282
283	// Sample from the array texture to draw a quad into the backbuffer.
284
285	const int backbufferWidth  = m_context.getRenderTarget().getWidth();
286	const int backbufferHeight = m_context.getRenderTarget().getHeight();
287	gl.bindFramebuffer(GL_FRAMEBUFFER, 0);
288	gl.viewport(0, 0, backbufferWidth, backbufferHeight);
289	gl.useProgram(m_finalProgram->getProgram());
290	gl.bindTexture(GL_TEXTURE_2D_ARRAY, m_arrayTexture);
291	gl.drawArrays(GL_TRIANGLE_STRIP, 0, 4);
292
293	// Read back the framebuffer, ensure that the left half is red and the
294	// right half is green.
295
296	tcu::Surface pixels(backbufferWidth, backbufferHeight);
297	glu::readPixels(m_context.getRenderContext(), 0, 0, pixels.getAccess());
298	bool failed = false;
299	for (int y = 0; y < backbufferHeight; y++)
300	{
301		for (int x = 0; x < backbufferWidth; x++)
302		{
303			tcu::RGBA pixel = pixels.getPixel(x, y);
304			if (x < backbufferWidth / 2)
305			{
306				if (pixel.getRed() != 255 || pixel.getGreen() != 0 || pixel.getBlue() != 0)
307				{
308					failed = true;
309				}
310			}
311			else if (x > backbufferWidth / 2)
312			{
313				if (pixel.getRed() != 0 || pixel.getGreen() != 255 || pixel.getBlue() != 0)
314				{
315					failed = true;
316				}
317			}
318			if (failed)
319			{
320				break;
321			}
322		}
323	}
324
325	deleteFramebufferObjects();
326
327	if (failed)
328	{
329		log << TestLog::Image("Result image", "Result image", pixels);
330	}
331
332	log << TestLog::Message << "Test result: " << (failed ? "Failed!" : "Passed!") << TestLog::EndMessage;
333
334	if (failed)
335	{
336		m_testCtx.setTestResult(QP_TEST_RESULT_FAIL, "Fail");
337		return STOP;
338	}
339
340	return (++m_caseIndex < NUM_CASE_ITERATIONS) ? CONTINUE : STOP;
341}
342
343MultiviewTests::MultiviewTests(Context& context) : TestCaseGroup(context, "multiview", "Multiview Tests")
344{
345}
346
347MultiviewTests::~MultiviewTests()
348{
349}
350
351void MultiviewTests::init()
352{
353	addChild(new MultiviewCase(m_context, "samples_1", "Multiview test without multisampling", 1));
354	addChild(new MultiviewCase(m_context, "samples_2", "Multiview test with MSAAx2", 2));
355	addChild(new MultiviewCase(m_context, "samples_4", "Multiview test without MSAAx4", 4));
356}
357
358} // namespace Functional
359} // namespace gles3
360} // namespace deqp
361