Martin Radev | 14a26ae | 2017-07-24 15:56:29 +0300 | [diff] [blame] | 1 | // |
| 2 | // Copyright 2017 The ANGLE Project Authors. All rights reserved. |
| 3 | // Use of this source code is governed by a BSD-style license that can be |
| 4 | // found in the LICENSE file. |
| 5 | // |
| 6 | // Multiview draw tests: |
| 7 | // Test issuing multiview Draw* commands. |
| 8 | // |
| 9 | |
| 10 | #include "test_utils/ANGLETest.h" |
| 11 | #include "test_utils/gl_raii.h" |
| 12 | |
| 13 | using namespace angle; |
| 14 | |
| 15 | class MultiviewDrawTest : public ANGLETest |
| 16 | { |
| 17 | protected: |
| 18 | MultiviewDrawTest() |
| 19 | { |
| 20 | setWindowWidth(128); |
| 21 | setWindowHeight(128); |
| 22 | setWebGLCompatibilityEnabled(true); |
| 23 | } |
Martin Radev | 7cf6166 | 2017-07-26 17:10:53 +0300 | [diff] [blame] | 24 | virtual ~MultiviewDrawTest() {} |
Martin Radev | 14a26ae | 2017-07-24 15:56:29 +0300 | [diff] [blame] | 25 | |
| 26 | void SetUp() override |
| 27 | { |
| 28 | ANGLETest::SetUp(); |
| 29 | |
| 30 | glRequestExtensionANGLE = reinterpret_cast<PFNGLREQUESTEXTENSIONANGLEPROC>( |
| 31 | eglGetProcAddress("glRequestExtensionANGLE")); |
| 32 | } |
| 33 | |
| 34 | // Requests the ANGLE_multiview extension and returns true if the operation succeeds. |
| 35 | bool requestMultiviewExtension() |
| 36 | { |
| 37 | if (extensionRequestable("GL_ANGLE_multiview")) |
| 38 | { |
| 39 | glRequestExtensionANGLE("GL_ANGLE_multiview"); |
| 40 | } |
| 41 | |
| 42 | if (!extensionEnabled("GL_ANGLE_multiview")) |
| 43 | { |
| 44 | std::cout << "Test skipped due to missing GL_ANGLE_multiview." << std::endl; |
| 45 | return false; |
| 46 | } |
| 47 | return true; |
| 48 | } |
| 49 | |
| 50 | PFNGLREQUESTEXTENSIONANGLEPROC glRequestExtensionANGLE = nullptr; |
| 51 | }; |
| 52 | |
Martin Radev | 7cf6166 | 2017-07-26 17:10:53 +0300 | [diff] [blame] | 53 | class MultiviewDrawValidationTest : public MultiviewDrawTest |
| 54 | { |
| 55 | protected: |
| 56 | MultiviewDrawValidationTest() {} |
| 57 | |
| 58 | void SetUp() override |
| 59 | { |
| 60 | MultiviewDrawTest::SetUp(); |
| 61 | |
| 62 | glBindFramebuffer(GL_FRAMEBUFFER, mFramebuffer); |
| 63 | |
| 64 | glBindTexture(GL_TEXTURE_2D, mTex2d); |
| 65 | glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, 1, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); |
| 66 | |
| 67 | glBindVertexArray(mVao); |
| 68 | |
| 69 | const float kVertexData[3] = {0.0f}; |
| 70 | glBindBuffer(GL_ARRAY_BUFFER, mVbo); |
| 71 | glBufferData(GL_ARRAY_BUFFER, sizeof(float) * 3u, &kVertexData[0], GL_STATIC_DRAW); |
| 72 | |
| 73 | const unsigned int kIndices[3] = {0u, 1u, 2u}; |
| 74 | glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mIbo); |
| 75 | glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(unsigned int) * 3, &kIndices[0], |
| 76 | GL_STATIC_DRAW); |
| 77 | ASSERT_GL_NO_ERROR(); |
| 78 | } |
| 79 | |
| 80 | GLTexture mTex2d; |
| 81 | GLVertexArray mVao; |
| 82 | GLBuffer mVbo; |
| 83 | GLBuffer mIbo; |
| 84 | GLFramebuffer mFramebuffer; |
| 85 | }; |
| 86 | |
Martin Radev | 14a26ae | 2017-07-24 15:56:29 +0300 | [diff] [blame] | 87 | // The test verifies that glDraw*Indirect: |
| 88 | // 1) generates an INVALID_OPERATION error if the number of views in the draw framebuffer is greater |
| 89 | // than 1. |
| 90 | // 2) does not generate any error if the draw framebuffer has exactly 1 view. |
Martin Radev | 7cf6166 | 2017-07-26 17:10:53 +0300 | [diff] [blame] | 91 | TEST_P(MultiviewDrawValidationTest, IndirectDraw) |
Martin Radev | 14a26ae | 2017-07-24 15:56:29 +0300 | [diff] [blame] | 92 | { |
| 93 | if (!requestMultiviewExtension()) |
| 94 | { |
| 95 | return; |
| 96 | } |
| 97 | |
Martin Radev | 14a26ae | 2017-07-24 15:56:29 +0300 | [diff] [blame] | 98 | const GLint viewportOffsets[4] = {0, 0, 2, 0}; |
Martin Radev | 14a26ae | 2017-07-24 15:56:29 +0300 | [diff] [blame] | 99 | |
| 100 | const std::string fsSource = |
| 101 | "#version 300 es\n" |
| 102 | "#extension GL_OVR_multiview : require\n" |
| 103 | "precision mediump float;\n" |
| 104 | "void main()\n" |
| 105 | "{}\n"; |
| 106 | |
Martin Radev | 14a26ae | 2017-07-24 15:56:29 +0300 | [diff] [blame] | 107 | GLBuffer commandBuffer; |
| 108 | glBindBuffer(GL_DRAW_INDIRECT_BUFFER, commandBuffer); |
| 109 | const GLuint commandData[] = {1u, 1u, 0u, 0u, 0u}; |
| 110 | glBufferData(GL_DRAW_INDIRECT_BUFFER, sizeof(GLuint) * 5u, &commandData[0], GL_STATIC_DRAW); |
| 111 | ASSERT_GL_NO_ERROR(); |
| 112 | |
Martin Radev | 14a26ae | 2017-07-24 15:56:29 +0300 | [diff] [blame] | 113 | // Check for a GL_INVALID_OPERATION error with the framebuffer having 2 views. |
| 114 | { |
| 115 | const std::string &vsSource = |
| 116 | "#version 300 es\n" |
| 117 | "#extension GL_OVR_multiview : require\n" |
| 118 | "layout(num_views = 2) in;\n" |
| 119 | "void main()\n" |
| 120 | "{}\n"; |
| 121 | ANGLE_GL_PROGRAM(program, vsSource, fsSource); |
| 122 | glUseProgram(program); |
| 123 | |
Martin Radev | 7cf6166 | 2017-07-26 17:10:53 +0300 | [diff] [blame] | 124 | glFramebufferTextureMultiviewSideBySideANGLE(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, mTex2d, |
| 125 | 0, 2, &viewportOffsets[0]); |
Martin Radev | 14a26ae | 2017-07-24 15:56:29 +0300 | [diff] [blame] | 126 | |
| 127 | glDrawArraysIndirect(GL_TRIANGLES, nullptr); |
| 128 | EXPECT_GL_ERROR(GL_INVALID_OPERATION); |
| 129 | |
| 130 | glDrawElementsIndirect(GL_TRIANGLES, GL_UNSIGNED_INT, nullptr); |
| 131 | EXPECT_GL_ERROR(GL_INVALID_OPERATION); |
| 132 | } |
| 133 | |
| 134 | // Check that no errors are generated if the number of views is 1. |
| 135 | { |
| 136 | const std::string &vsSource = |
| 137 | "#version 300 es\n" |
| 138 | "#extension GL_OVR_multiview : require\n" |
| 139 | "layout(num_views = 1) in;\n" |
| 140 | "void main()\n" |
| 141 | "{}\n"; |
| 142 | ANGLE_GL_PROGRAM(program, vsSource, fsSource); |
| 143 | glUseProgram(program); |
| 144 | |
Martin Radev | 7cf6166 | 2017-07-26 17:10:53 +0300 | [diff] [blame] | 145 | glFramebufferTextureMultiviewSideBySideANGLE(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, mTex2d, |
| 146 | 0, 1, &viewportOffsets[0]); |
Martin Radev | 14a26ae | 2017-07-24 15:56:29 +0300 | [diff] [blame] | 147 | |
| 148 | glDrawArraysIndirect(GL_TRIANGLES, nullptr); |
| 149 | EXPECT_GL_NO_ERROR(); |
| 150 | |
| 151 | glDrawElementsIndirect(GL_TRIANGLES, GL_UNSIGNED_INT, nullptr); |
| 152 | EXPECT_GL_NO_ERROR(); |
| 153 | } |
| 154 | } |
| 155 | |
Martin Radev | 7cf6166 | 2017-07-26 17:10:53 +0300 | [diff] [blame] | 156 | // The test verifies that glDraw*: |
| 157 | // 1) generates an INVALID_OPERATION error if the number of views in the active draw framebuffer and |
| 158 | // program differs. |
| 159 | // 2) does not generate any error if the number of views is the same. |
| 160 | // 3) does not generate any error if the program does not use the multiview extension. |
| 161 | TEST_P(MultiviewDrawValidationTest, NumViewsMismatch) |
| 162 | { |
| 163 | if (!requestMultiviewExtension()) |
| 164 | { |
| 165 | return; |
| 166 | } |
| 167 | |
| 168 | const GLint viewportOffsets[4] = {0, 0, 2, 0}; |
| 169 | |
| 170 | const std::string &vsSource = |
| 171 | "#version 300 es\n" |
| 172 | "#extension GL_OVR_multiview : require\n" |
| 173 | "layout(num_views = 2) in;\n" |
| 174 | "void main()\n" |
| 175 | "{}\n"; |
| 176 | const std::string &fsSource = |
| 177 | "#version 300 es\n" |
| 178 | "#extension GL_OVR_multiview : require\n" |
| 179 | "precision mediump float;\n" |
| 180 | "void main()\n" |
| 181 | "{}\n"; |
| 182 | ANGLE_GL_PROGRAM(program, vsSource, fsSource); |
| 183 | glUseProgram(program); |
| 184 | |
| 185 | // Check for a GL_INVALID_OPERATION error with the framebuffer and program having different |
| 186 | // number of views. |
| 187 | { |
| 188 | // The framebuffer has only 1 view. |
| 189 | glFramebufferTextureMultiviewSideBySideANGLE(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, mTex2d, |
| 190 | 0, 1, &viewportOffsets[0]); |
| 191 | |
| 192 | glDrawArrays(GL_TRIANGLES, 0, 3); |
| 193 | EXPECT_GL_ERROR(GL_INVALID_OPERATION); |
| 194 | |
| 195 | glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_INT, nullptr); |
| 196 | EXPECT_GL_ERROR(GL_INVALID_OPERATION); |
| 197 | } |
| 198 | |
| 199 | // Check that no errors are generated if the number of views in both program and draw |
| 200 | // framebuffer matches. |
| 201 | { |
| 202 | glFramebufferTextureMultiviewSideBySideANGLE(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, mTex2d, |
| 203 | 0, 2, &viewportOffsets[0]); |
| 204 | |
| 205 | glDrawArrays(GL_TRIANGLES, 0, 3); |
| 206 | EXPECT_GL_NO_ERROR(); |
| 207 | |
| 208 | glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_INT, nullptr); |
| 209 | EXPECT_GL_NO_ERROR(); |
| 210 | } |
| 211 | |
| 212 | // Check that no errors are generated if the program does not use the multiview extension. |
| 213 | { |
| 214 | const std::string &vsSourceNoMultiview = |
| 215 | "#version 300 es\n" |
| 216 | "void main()\n" |
| 217 | "{}\n"; |
| 218 | const std::string &fsSourceNoMultiview = |
| 219 | "#version 300 es\n" |
| 220 | "precision mediump float;\n" |
| 221 | "void main()\n" |
| 222 | "{}\n"; |
| 223 | ANGLE_GL_PROGRAM(programNoMultiview, vsSourceNoMultiview, fsSourceNoMultiview); |
| 224 | glUseProgram(programNoMultiview); |
| 225 | |
| 226 | glFramebufferTextureMultiviewSideBySideANGLE(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, mTex2d, |
| 227 | 0, 2, &viewportOffsets[0]); |
| 228 | |
| 229 | glDrawArrays(GL_TRIANGLES, 0, 3); |
| 230 | EXPECT_GL_NO_ERROR(); |
| 231 | |
| 232 | glDrawElements(GL_TRIANGLES, 3, GL_UNSIGNED_INT, nullptr); |
| 233 | EXPECT_GL_NO_ERROR(); |
| 234 | } |
| 235 | } |
| 236 | |
Martin Radev | 7e69f76 | 2017-07-27 14:54:13 +0300 | [diff] [blame] | 237 | // The test verifies that glDraw*: |
| 238 | // 1) generates an INVALID_OPERATION error if the number of views in the active draw framebuffer is |
| 239 | // greater than 1 and there is an active transform feedback object. |
| 240 | // 2) does not generate any error if the number of views in the draw framebuffer is 1. |
| 241 | TEST_P(MultiviewDrawValidationTest, ActiveTransformFeedback) |
| 242 | { |
| 243 | if (!requestMultiviewExtension()) |
| 244 | { |
| 245 | return; |
| 246 | } |
| 247 | |
| 248 | const GLint viewportOffsets[4] = {0, 0, 2, 0}; |
| 249 | |
| 250 | const std::string &vsSource = |
| 251 | "#version 300 es\n" |
| 252 | "void main()\n" |
| 253 | "{}\n"; |
| 254 | const std::string &fsSource = |
| 255 | "#version 300 es\n" |
| 256 | "precision mediump float;\n" |
| 257 | "void main()\n" |
| 258 | "{}\n"; |
| 259 | ANGLE_GL_PROGRAM(program, vsSource, fsSource); |
| 260 | glUseProgram(program); |
| 261 | |
| 262 | GLBuffer tbo; |
| 263 | glBindBuffer(GL_TRANSFORM_FEEDBACK_BUFFER, tbo); |
| 264 | glBufferData(GL_TRANSFORM_FEEDBACK_BUFFER, sizeof(float) * 4u, nullptr, GL_STATIC_DRAW); |
| 265 | |
| 266 | GLTransformFeedback transformFeedback; |
| 267 | glBindTransformFeedback(GL_TRANSFORM_FEEDBACK, transformFeedback); |
| 268 | glBeginTransformFeedback(GL_TRIANGLES); |
| 269 | ASSERT_GL_NO_ERROR(); |
| 270 | |
| 271 | // Check that drawArrays generates an error when there is an active transform feedback object |
| 272 | // and the number of views in the draw framebuffer is greater than 1. |
| 273 | { |
| 274 | glFramebufferTextureMultiviewSideBySideANGLE(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, mTex2d, |
| 275 | 0, 2, &viewportOffsets[0]); |
| 276 | glDrawArrays(GL_TRIANGLES, 0, 3); |
| 277 | EXPECT_GL_ERROR(GL_INVALID_OPERATION); |
| 278 | } |
| 279 | |
| 280 | // Check that drawArrays does not generate an error when the number of views in the draw |
| 281 | // framebuffer is 1. |
| 282 | { |
| 283 | glFramebufferTextureMultiviewSideBySideANGLE(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, mTex2d, |
| 284 | 0, 1, &viewportOffsets[0]); |
| 285 | glDrawArrays(GL_TRIANGLES, 0, 3); |
| 286 | EXPECT_GL_NO_ERROR(); |
| 287 | } |
| 288 | |
| 289 | glEndTransformFeedback(); |
| 290 | } |
| 291 | |
Martin Radev | ffe754b | 2017-07-31 10:38:07 +0300 | [diff] [blame^] | 292 | // The test verifies that glDraw*: |
| 293 | // 1) generates an INVALID_OPERATION error if the number of views in the active draw framebuffer is |
| 294 | // greater than 1 and there is an active query for target GL_TIME_ELAPSED_EXT. |
| 295 | // 2) does not generate any error if the number of views in the draw framebuffer is 1. |
| 296 | TEST_P(MultiviewDrawValidationTest, ActiveTimeElapsedQuery) |
| 297 | { |
| 298 | if (!requestMultiviewExtension()) |
| 299 | { |
| 300 | return; |
| 301 | } |
| 302 | |
| 303 | if (!extensionEnabled("GL_EXT_disjoint_timer_query")) |
| 304 | { |
| 305 | std::cout << "Test skipped because GL_EXT_disjoint_timer_query is not available." |
| 306 | << std::endl; |
| 307 | return; |
| 308 | } |
| 309 | |
| 310 | const GLint viewportOffsets[4] = {0, 0, 2, 0}; |
| 311 | const std::string &vsSource = |
| 312 | "#version 300 es\n" |
| 313 | "void main()\n" |
| 314 | "{}\n"; |
| 315 | const std::string &fsSource = |
| 316 | "#version 300 es\n" |
| 317 | "precision mediump float;\n" |
| 318 | "void main()\n" |
| 319 | "{}\n"; |
| 320 | ANGLE_GL_PROGRAM(program, vsSource, fsSource); |
| 321 | glUseProgram(program); |
| 322 | |
| 323 | GLuint query = 0u; |
| 324 | glGenQueriesEXT(1, &query); |
| 325 | glBeginQueryEXT(GL_TIME_ELAPSED_EXT, query); |
| 326 | |
| 327 | // Check first case. |
| 328 | { |
| 329 | glFramebufferTextureMultiviewSideBySideANGLE(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, mTex2d, |
| 330 | 0, 2, &viewportOffsets[0]); |
| 331 | glDrawArrays(GL_TRIANGLES, 0, 3); |
| 332 | EXPECT_GL_ERROR(GL_INVALID_OPERATION); |
| 333 | } |
| 334 | |
| 335 | // Check second case. |
| 336 | { |
| 337 | glFramebufferTextureMultiviewSideBySideANGLE(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, mTex2d, |
| 338 | 0, 1, &viewportOffsets[0]); |
| 339 | glDrawArrays(GL_TRIANGLES, 0, 3); |
| 340 | EXPECT_GL_NO_ERROR(); |
| 341 | } |
| 342 | |
| 343 | glEndQueryEXT(GL_TIME_ELAPSED_EXT); |
| 344 | glDeleteQueries(1, &query); |
| 345 | } |
| 346 | |
Martin Radev | 7cf6166 | 2017-07-26 17:10:53 +0300 | [diff] [blame] | 347 | ANGLE_INSTANTIATE_TEST(MultiviewDrawValidationTest, ES31_OPENGL()); |