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