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, &param);
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