1 //
2 // Copyright 2016 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 // CHROMIUMFramebufferMixedSamplesTest
7 // Test CHROMIUM subset of NV_framebuffer_mixed_samples.
8 // This extension allows rendering to a framebuffer that has different
9 // sample counts for different render buffers (stencil, depth, color)
10
11 #include "test_utils/ANGLETest.h"
12 #include "shader_utils.h"
13
14 using namespace angle;
15
16 namespace
17 {
18
19 const GLuint kWidth = 100;
20 const GLuint kHeight = 100;
21
22 class CHROMIUMFramebufferMixedSamplesTest : public ANGLETest
23 {
24 protected:
25 enum SetupFBOType
26 {
27 MixedSampleFBO, // 1 color sample, N stencil samples.
28 SingleSampleFBO, // 1 color sample, 1 stencil sample.
29 };
30
isApplicable() const31 bool isApplicable() const
32 {
33 return extensionEnabled("GL_CHROMIUM_framebuffer_mixed_samples") &&
34 extensionEnabled("GL_OES_rgb8_rgba8");
35 }
36
SetUp()37 void SetUp() override
38 {
39 ANGLETest::SetUp();
40
41 // clang-format off
42 static const char* kVertexShaderSource =
43 "attribute mediump vec4 position;\n"
44 "void main() {\n"
45 " gl_Position = position;\n"
46 "}\n";
47
48 static const char* kFragmentShaderSource =
49 "uniform mediump vec4 color;\n"
50 "void main() {\n"
51 " gl_FragColor = color;\n"
52 "}\n";
53
54 // clang-format on
55 mProgram = CompileProgram(kVertexShaderSource, kFragmentShaderSource);
56
57 GLuint position_loc = glGetAttribLocation(mProgram, "position");
58 mColorLoc = glGetUniformLocation(mProgram, "color");
59
60 glGenBuffers(1, &mVBO);
61 glBindBuffer(GL_ARRAY_BUFFER, mVBO);
62
63 static float vertices[] = {
64 1.0f, 1.0f, -1.0f, 1.0f, -1.0f, -1.0f, -1.0f, 1.0f, -1.0f,
65 -1.0f, 1.0f, -1.0f, -1.0f, -1.0f, 1.0f, -1.0f, 1.0f, 1.0f,
66 };
67 glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW);
68 glEnableVertexAttribArray(position_loc);
69 glVertexAttribPointer(position_loc, 2, GL_FLOAT, GL_FALSE, 0, 0);
70
71 ASSERT_GL_NO_ERROR();
72 }
73
TearDown()74 void TearDown() override
75 {
76 glDeleteBuffers(1, &mVBO);
77 glDeleteProgram(mProgram);
78
79 ASSERT_GL_NO_ERROR();
80
81 ANGLETest::TearDown();
82 }
83
prepareForDraw(SetupFBOType fbo_type)84 void prepareForDraw(SetupFBOType fbo_type)
85 {
86 glActiveTexture(GL_TEXTURE0);
87 glGenTextures(1, &mTexture);
88 glBindTexture(GL_TEXTURE_2D, mTexture);
89 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
90 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
91 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
92 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
93 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, kWidth, kHeight, 0, GL_RGBA, GL_UNSIGNED_BYTE,
94 NULL);
95 glBindTexture(GL_TEXTURE_2D, 0);
96
97 glGenRenderbuffers(1, &mStencilRB);
98 glBindRenderbuffer(GL_RENDERBUFFER, mStencilRB);
99
100 if (fbo_type == MixedSampleFBO)
101 {
102 // Create a sample buffer.
103 GLsizei num_samples = 8, max_samples = 0;
104 glGetIntegerv(GL_MAX_SAMPLES, &max_samples);
105 num_samples = std::min(num_samples, max_samples);
106 glRenderbufferStorageMultisampleANGLE(GL_RENDERBUFFER, num_samples, GL_STENCIL_INDEX8,
107 kWidth, kHeight);
108 GLint param = 0;
109 glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_SAMPLES, ¶m);
110 EXPECT_GT(param, 1);
111 }
112 else
113 {
114 glRenderbufferStorage(GL_RENDERBUFFER, GL_STENCIL_INDEX8, kWidth, kHeight);
115 }
116 glBindRenderbuffer(GL_RENDERBUFFER, 0);
117
118 glGenFramebuffers(1, &mSampleFBO);
119 glBindFramebuffer(GL_FRAMEBUFFER, mSampleFBO);
120 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mTexture, 0);
121 glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER,
122 mStencilRB);
123 EXPECT_EQ(static_cast<GLenum>(GL_FRAMEBUFFER_COMPLETE),
124 glCheckFramebufferStatus(GL_FRAMEBUFFER));
125
126 glUseProgram(mProgram);
127 glBindBuffer(GL_ARRAY_BUFFER, 0);
128 glViewport(0, 0, kWidth, kHeight);
129 glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
130 glClearStencil(1);
131 glClear(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT);
132 glEnable(GL_STENCIL_TEST);
133 glEnable(GL_BLEND);
134 glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
135 glStencilMask(0xffffffff);
136 glStencilFunc(GL_EQUAL, 1, 0xffffffff);
137 glStencilOp(GL_KEEP, GL_KEEP, GL_ZERO);
138
139 ASSERT_GL_NO_ERROR();
140 }
141
cleanup()142 void cleanup()
143 {
144 glBindFramebuffer(GL_FRAMEBUFFER, 0);
145 glDeleteFramebuffers(1, &mSampleFBO);
146 glDeleteTextures(1, &mTexture);
147 glDeleteRenderbuffers(1, &mStencilRB);
148
149 ASSERT_GL_NO_ERROR();
150 }
151
152 GLuint mSampleFBO;
153 GLuint mStencilRB;
154 GLuint mTexture;
155
156 GLuint mProgram;
157 GLuint mVBO;
158 GLint mColorLoc;
159 };
160
161 } //
162
TEST_P(CHROMIUMFramebufferMixedSamplesTest,StateSettingTest)163 TEST_P(CHROMIUMFramebufferMixedSamplesTest, StateSettingTest)
164 {
165 if (!isApplicable())
166 {
167 return;
168 }
169
170 GLint value = -1;
171 glGetIntegerv(GL_COVERAGE_MODULATION_CHROMIUM, &value);
172 EXPECT_EQ(GL_NONE, value);
173 GLenum kValues[] = {GL_NONE, GL_RGB, GL_RGBA, GL_ALPHA};
174 for (auto expect : kValues)
175 {
176 glCoverageModulationCHROMIUM(expect);
177 value = -1;
178 glGetIntegerv(GL_COVERAGE_MODULATION_CHROMIUM, &value);
179 EXPECT_EQ(expect, static_cast<GLenum>(value));
180 EXPECT_EQ(static_cast<GLenum>(GL_NO_ERROR), glGetError());
181 }
182
183 glCoverageModulationCHROMIUM(GL_BYTE);
184 EXPECT_EQ(static_cast<GLenum>(GL_INVALID_ENUM), glGetError());
185 value = -1;
186 glGetIntegerv(GL_COVERAGE_MODULATION_CHROMIUM, &value);
187 EXPECT_EQ(static_cast<GLenum>(GL_ALPHA), static_cast<GLenum>(value));
188 EXPECT_EQ(static_cast<GLenum>(GL_NO_ERROR), glGetError());
189 }
190
191 // The test pattern is as follows:
192 // A green triangle (bottom left, top right, top left).
193 // A blue triangle (top left, bottom right, bottom left).
194 // The triangles will overlap but overlap only contains green pixels,
195 // due to each draw erasing its area from stencil.
196 // The blue triangle will fill only the area (bottom left, center,
197 // bottom right).
198
199 // The test tests that CoverageModulation call works.
200 // The fractional pixels of both triangles end up being modulated
201 // by the coverage of the fragment. Test that drawing with and without
202 // CoverageModulation causes the result to be different.
TEST_P(CHROMIUMFramebufferMixedSamplesTest,CoverageModulation)203 TEST_P(CHROMIUMFramebufferMixedSamplesTest, CoverageModulation)
204 {
205 if (!isApplicable())
206 {
207 return;
208 }
209 static const float kBlue[] = {0.0f, 0.0f, 1.0f, 1.0f};
210 static const float kGreen[] = {0.0f, 1.0f, 0.0f, 1.0f};
211 std::unique_ptr<uint8_t[]> results[3];
212 const GLint kResultSize = kWidth * kHeight * 4;
213
214 for (int pass = 0; pass < 3; ++pass)
215 {
216 prepareForDraw(MixedSampleFBO);
217 if (pass == 1)
218 {
219 glCoverageModulationCHROMIUM(GL_RGBA);
220 }
221 glUniform4fv(mColorLoc, 1, kGreen);
222 glDrawArrays(GL_TRIANGLES, 0, 3);
223
224 glUniform4fv(mColorLoc, 1, kBlue);
225 glDrawArrays(GL_TRIANGLES, 3, 3);
226 if (pass == 1)
227 {
228 glCoverageModulationCHROMIUM(GL_NONE);
229 }
230 results[pass].reset(new uint8_t[kResultSize]);
231 memset(results[pass].get(), 123u, kResultSize);
232 glReadPixels(0, 0, kWidth, kHeight, GL_RGBA, GL_UNSIGNED_BYTE, results[pass].get());
233
234 cleanup();
235 }
236
237 EXPECT_NE(0, memcmp(results[0].get(), results[1].get(), kResultSize));
238 // Verify that rendering is deterministic, so that the pass above does not
239 // come from non-deterministic rendering.
240 EXPECT_EQ(0, memcmp(results[0].get(), results[2].get(), kResultSize));
241 }
242
243 // The test tests that the stencil buffer can be multisampled, even though the
244 // color buffer is single-sampled. Draws the same pattern with single-sample
245 // stencil buffer and with multisample stencil buffer. The images should differ.
TEST_P(CHROMIUMFramebufferMixedSamplesTest,MultisampleStencilEffective)246 TEST_P(CHROMIUMFramebufferMixedSamplesTest, MultisampleStencilEffective)
247 {
248 if (!isApplicable())
249 {
250 return;
251 }
252
253 static const float kBlue[] = {0.0f, 0.0f, 1.0f, 1.0f};
254 static const float kGreen[] = {0.0f, 1.0f, 0.0f, 1.0f};
255
256 std::unique_ptr<uint8_t[]> results[3];
257 const GLint kResultSize = kWidth * kHeight * 4;
258
259 for (int pass = 0; pass < 3; ++pass)
260 {
261 if (pass == 1)
262 {
263 prepareForDraw(MixedSampleFBO);
264 }
265 else
266 {
267 prepareForDraw(SingleSampleFBO);
268 }
269 glUniform4fv(mColorLoc, 1, kGreen);
270 glDrawArrays(GL_TRIANGLES, 0, 3);
271
272 glUniform4fv(mColorLoc, 1, kBlue);
273 glDrawArrays(GL_TRIANGLES, 3, 3);
274
275 results[pass].reset(new uint8_t[kResultSize]);
276 memset(results[pass].get(), 12u, kResultSize);
277 glReadPixels(0, 0, kWidth, kHeight, GL_RGBA, GL_UNSIGNED_BYTE, results[pass].get());
278
279 cleanup();
280 }
281
282 EXPECT_NE(0, memcmp(results[0].get(), results[1].get(), kResultSize));
283 // Verify that rendering is deterministic, so that the pass above does not
284 // come from non-deterministic rendering.
285 EXPECT_EQ(0, memcmp(results[0].get(), results[2].get(), kResultSize));
286 }
287
288 ANGLE_INSTANTIATE_TEST(CHROMIUMFramebufferMixedSamplesTest,
289 ES2_OPENGL(),
290 ES2_OPENGLES(),
291 ES3_OPENGL());
292