1 //
2 // Copyright 2019 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
7 // MultisampleTest: Tests of multisampled default framebuffer
8
9 #include "test_utils/ANGLETest.h"
10
11 #include "test_utils/gl_raii.h"
12 #include "util/OSWindow.h"
13 #include "util/shader_utils.h"
14
15 using namespace angle;
16
17 namespace
18 {
19 class MultisampleTest : public ANGLETest
20 {
21 protected:
testSetUp()22 void testSetUp() override
23 {
24 // Get display.
25 EGLint dispattrs[] = {EGL_PLATFORM_ANGLE_TYPE_ANGLE, GetParam().getRenderer(), EGL_NONE};
26 mDisplay = eglGetPlatformDisplayEXT(
27 EGL_PLATFORM_ANGLE_ANGLE, reinterpret_cast<void *>(EGL_DEFAULT_DISPLAY), dispattrs);
28 ASSERT_TRUE(mDisplay != EGL_NO_DISPLAY);
29
30 ASSERT_TRUE(eglInitialize(mDisplay, nullptr, nullptr) == EGL_TRUE);
31
32 // Nexus 5X and 6P fail to eglMakeCurrent with a config they advertise they support.
33 // http://anglebug.com/3464
34 ANGLE_SKIP_TEST_IF(IsNexus5X() || IsNexus6P());
35
36 // Find a config that uses RGBA8 and allows 4x multisampling.
37 const EGLint configAttributes[] = {
38 EGL_RED_SIZE, 8, EGL_GREEN_SIZE, 8, EGL_BLUE_SIZE, 8,
39 EGL_ALPHA_SIZE, 8, EGL_DEPTH_SIZE, 24, EGL_STENCIL_SIZE, 8,
40 EGL_SAMPLE_BUFFERS, 1, EGL_SAMPLES, 4, EGL_NONE};
41
42 EGLint configCount;
43 EGLConfig multisampledConfig;
44 EGLint ret =
45 eglChooseConfig(mDisplay, configAttributes, &multisampledConfig, 1, &configCount);
46 mMultisampledConfigExists = ret && configCount > 0;
47
48 if (!mMultisampledConfigExists)
49 {
50 return;
51 }
52
53 // Create a window, context and surface if multisampling is possible.
54 mOSWindow = OSWindow::New();
55 mOSWindow->initialize("MultisampleTest", kWindowSize, kWindowSize);
56 setWindowVisible(mOSWindow, true);
57
58 EGLint contextAttributes[] = {
59 EGL_CONTEXT_MAJOR_VERSION_KHR,
60 GetParam().majorVersion,
61 EGL_CONTEXT_MINOR_VERSION_KHR,
62 GetParam().minorVersion,
63 EGL_NONE,
64 };
65
66 mContext =
67 eglCreateContext(mDisplay, multisampledConfig, EGL_NO_CONTEXT, contextAttributes);
68 ASSERT_TRUE(mContext != EGL_NO_CONTEXT);
69
70 mSurface = eglCreateWindowSurface(mDisplay, multisampledConfig,
71 mOSWindow->getNativeWindow(), nullptr);
72 ASSERT_EGL_SUCCESS();
73
74 eglMakeCurrent(mDisplay, mSurface, mSurface, mContext);
75 ASSERT_EGL_SUCCESS();
76 }
77
testTearDown()78 void testTearDown() override
79 {
80 if (mSurface)
81 {
82 eglSwapBuffers(mDisplay, mSurface);
83 }
84
85 eglMakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
86
87 if (mSurface)
88 {
89 eglDestroySurface(mDisplay, mSurface);
90 ASSERT_EGL_SUCCESS();
91 }
92
93 if (mContext != EGL_NO_CONTEXT)
94 {
95 eglDestroyContext(mDisplay, mContext);
96 ASSERT_EGL_SUCCESS();
97 }
98
99 if (mOSWindow)
100 {
101 OSWindow::Delete(&mOSWindow);
102 }
103
104 eglTerminate(mDisplay);
105 }
106
prepareVertexBuffer(GLBuffer & vertexBuffer,const Vector3 * vertices,size_t vertexCount,GLint positionLocation)107 void prepareVertexBuffer(GLBuffer &vertexBuffer,
108 const Vector3 *vertices,
109 size_t vertexCount,
110 GLint positionLocation)
111 {
112 glBindBuffer(GL_ARRAY_BUFFER, vertexBuffer);
113 glBufferData(GL_ARRAY_BUFFER, sizeof(*vertices) * vertexCount, vertices, GL_STATIC_DRAW);
114 glVertexAttribPointer(positionLocation, 3, GL_FLOAT, GL_FALSE, 0, nullptr);
115 glEnableVertexAttribArray(positionLocation);
116 }
117
118 protected:
119 static constexpr int kWindowSize = 8;
120
121 OSWindow *mOSWindow = nullptr;
122 EGLDisplay mDisplay = EGL_NO_DISPLAY;
123 EGLContext mContext = EGL_NO_CONTEXT;
124 EGLSurface mSurface = EGL_NO_SURFACE;
125 bool mMultisampledConfigExists = false;
126 };
127
128 // Test point rendering on a multisampled surface. GLES2 section 3.3.1.
TEST_P(MultisampleTest,Point)129 TEST_P(MultisampleTest, Point)
130 {
131 ANGLE_SKIP_TEST_IF(!mMultisampledConfigExists);
132 // http://anglebug.com/3470
133 ANGLE_SKIP_TEST_IF(IsAndroid() && IsNVIDIAShield() && IsOpenGLES());
134
135 constexpr char kPointsVS[] = R"(precision highp float;
136 attribute vec4 a_position;
137
138 void main()
139 {
140 gl_PointSize = 3.0;
141 gl_Position = a_position;
142 })";
143
144 ANGLE_GL_PROGRAM(program, kPointsVS, essl1_shaders::fs::Red());
145 glUseProgram(program);
146 const GLint positionLocation = glGetAttribLocation(program, "a_position");
147
148 GLBuffer vertexBuffer;
149 const Vector3 vertices[1] = {{0.0f, 0.0f, 0.0f}};
150 prepareVertexBuffer(vertexBuffer, vertices, 1, positionLocation);
151
152 glClear(GL_COLOR_BUFFER_BIT);
153 glDrawArrays(GL_POINTS, 0, 1);
154
155 ASSERT_GL_NO_ERROR();
156
157 // The center pixels should be all red.
158 EXPECT_PIXEL_COLOR_EQ(kWindowSize / 2, kWindowSize / 2, GLColor::red);
159 EXPECT_PIXEL_COLOR_EQ(kWindowSize / 2 - 1, kWindowSize / 2, GLColor::red);
160 EXPECT_PIXEL_COLOR_EQ(kWindowSize / 2, kWindowSize / 2 - 1, GLColor::red);
161 EXPECT_PIXEL_COLOR_EQ(kWindowSize / 2 - 1, kWindowSize / 2 - 1, GLColor::red);
162
163 // Border pixels should be between red and black, and not exactly either; corners are darker and
164 // sides are brighter.
165 const GLColor kSideColor = {128, 0, 0, 128};
166 const GLColor kCornerColor = {64, 0, 0, 64};
167 constexpr int kErrorMargin = 16;
168 EXPECT_PIXEL_COLOR_NEAR(kWindowSize / 2 - 2, kWindowSize / 2 - 2, kCornerColor, kErrorMargin);
169 EXPECT_PIXEL_COLOR_NEAR(kWindowSize / 2 - 2, kWindowSize / 2 + 1, kCornerColor, kErrorMargin);
170 EXPECT_PIXEL_COLOR_NEAR(kWindowSize / 2 + 1, kWindowSize / 2 - 2, kCornerColor, kErrorMargin);
171 EXPECT_PIXEL_COLOR_NEAR(kWindowSize / 2 + 1, kWindowSize / 2 + 1, kCornerColor, kErrorMargin);
172
173 EXPECT_PIXEL_COLOR_NEAR(kWindowSize / 2 - 2, kWindowSize / 2 - 1, kSideColor, kErrorMargin);
174 EXPECT_PIXEL_COLOR_NEAR(kWindowSize / 2 - 2, kWindowSize / 2, kSideColor, kErrorMargin);
175 EXPECT_PIXEL_COLOR_NEAR(kWindowSize / 2 - 1, kWindowSize / 2 - 2, kSideColor, kErrorMargin);
176 EXPECT_PIXEL_COLOR_NEAR(kWindowSize / 2 - 1, kWindowSize / 2 + 1, kSideColor, kErrorMargin);
177 EXPECT_PIXEL_COLOR_NEAR(kWindowSize / 2, kWindowSize / 2 - 2, kSideColor, kErrorMargin);
178 EXPECT_PIXEL_COLOR_NEAR(kWindowSize / 2, kWindowSize / 2 + 1, kSideColor, kErrorMargin);
179 EXPECT_PIXEL_COLOR_NEAR(kWindowSize / 2 + 1, kWindowSize / 2 - 1, kSideColor, kErrorMargin);
180 EXPECT_PIXEL_COLOR_NEAR(kWindowSize / 2 + 1, kWindowSize / 2, kSideColor, kErrorMargin);
181 }
182
183 // Test line rendering on a multisampled surface. GLES2 section 3.4.4.
TEST_P(MultisampleTest,Line)184 TEST_P(MultisampleTest, Line)
185 {
186 ANGLE_SKIP_TEST_IF(!mMultisampledConfigExists);
187 ANGLE_SKIP_TEST_IF(IsARM64() && IsWindows() && IsD3D());
188
189 ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), essl1_shaders::fs::Red());
190 glUseProgram(program);
191 const GLint positionLocation = glGetAttribLocation(program, essl1_shaders::PositionAttrib());
192
193 GLBuffer vertexBuffer;
194 const Vector3 vertices[2] = {{-1.0f, -0.3f, 0.0f}, {1.0f, 0.3f, 0.0f}};
195 prepareVertexBuffer(vertexBuffer, vertices, 2, positionLocation);
196
197 glClear(GL_COLOR_BUFFER_BIT);
198 glDrawArrays(GL_LINES, 0, 2);
199
200 ASSERT_GL_NO_ERROR();
201
202 // The line goes from left to right at about -17 degrees slope. It renders as such (captured
203 // with renderdoc):
204 //
205 // D D = Dark Red (0.25) or (0.5)
206 // BRA R = Red (1.0)
207 // ARB M = Middle Red (0.75)
208 // D B = Bright Red (1.0 or 0.75)
209 // A = Any red (0.5, 0.75 or 1.0)
210 //
211 // Verify that rendering is done as above.
212
213 const GLColor kDarkRed = {128, 0, 0, 128};
214 const GLColor kMidRed = {192, 0, 0, 192};
215 constexpr int kErrorMargin = 16;
216 constexpr int kLargeMargin = 80;
217
218 static_assert(kWindowSize == 8, "Verification code written for 8x8 window");
219 EXPECT_PIXEL_COLOR_NEAR(0, 2, kDarkRed, kLargeMargin);
220 EXPECT_PIXEL_COLOR_NEAR(1, 3, GLColor::red, kLargeMargin);
221 EXPECT_PIXEL_COLOR_NEAR(2, 3, GLColor::red, kErrorMargin);
222 EXPECT_PIXEL_COLOR_NEAR(3, 3, kMidRed, kLargeMargin);
223 EXPECT_PIXEL_COLOR_NEAR(4, 4, kMidRed, kLargeMargin);
224 EXPECT_PIXEL_COLOR_NEAR(5, 4, GLColor::red, kErrorMargin);
225 EXPECT_PIXEL_COLOR_NEAR(6, 4, GLColor::red, kLargeMargin);
226 EXPECT_PIXEL_COLOR_NEAR(7, 5, kDarkRed, kLargeMargin);
227 }
228
229 // Test polygon rendering on a multisampled surface. GLES2 section 3.5.3.
TEST_P(MultisampleTest,Triangle)230 TEST_P(MultisampleTest, Triangle)
231 {
232 ANGLE_SKIP_TEST_IF(!mMultisampledConfigExists);
233 // http://anglebug.com/3470
234 ANGLE_SKIP_TEST_IF(IsAndroid() && IsNVIDIAShield() && IsOpenGLES());
235
236 ANGLE_GL_PROGRAM(program, essl1_shaders::vs::Simple(), essl1_shaders::fs::Red());
237 glUseProgram(program);
238 const GLint positionLocation = glGetAttribLocation(program, essl1_shaders::PositionAttrib());
239
240 GLBuffer vertexBuffer;
241 const Vector3 vertices[3] = {{-1.0f, -1.0f, 0.0f}, {-1.0f, 1.0f, 0.0f}, {1.0f, -1.0f, 0.0f}};
242 prepareVertexBuffer(vertexBuffer, vertices, 3, positionLocation);
243
244 glClear(GL_COLOR_BUFFER_BIT);
245 glDrawArrays(GL_TRIANGLES, 0, 3);
246
247 ASSERT_GL_NO_ERROR();
248
249 // Top-left pixels should be all red.
250 EXPECT_PIXEL_COLOR_EQ(0, 0, GLColor::red);
251 EXPECT_PIXEL_COLOR_EQ(kWindowSize / 4, kWindowSize / 4, GLColor::red);
252
253 // Diagonal pixels from bottom-left to top-right are between red and black. Pixels above the
254 // diagonal are red and pixels below it are black.
255 const GLColor kMidRed = {128, 0, 0, 128};
256 constexpr int kErrorMargin = 16;
257
258 for (int i = 1; i + 1 < kWindowSize; ++i)
259 {
260 int j = kWindowSize - 1 - i;
261 EXPECT_PIXEL_COLOR_NEAR(i, j, kMidRed, kErrorMargin);
262 EXPECT_PIXEL_COLOR_EQ(i, j - 1, GLColor::red);
263 EXPECT_PIXEL_COLOR_EQ(i, j + 1, GLColor::transparentBlack);
264 }
265 }
266
267 ANGLE_INSTANTIATE_TEST(MultisampleTest,
268 WithNoFixture(ES2_D3D11()),
269 WithNoFixture(ES3_D3D11()),
270 WithNoFixture(ES31_D3D11()),
271 WithNoFixture(ES2_OPENGL()),
272 WithNoFixture(ES3_OPENGL()),
273 WithNoFixture(ES31_OPENGL()),
274 WithNoFixture(ES2_OPENGLES()),
275 WithNoFixture(ES3_OPENGLES()),
276 WithNoFixture(ES31_OPENGLES()),
277 WithNoFixture(ES2_VULKAN()),
278 WithNoFixture(ES3_VULKAN()));
279 } // anonymous namespace
280