1 //
2 // Copyright 2015 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 #include <cmath>
8
9 #include "test_utils/ANGLETest.h"
10 #include "test_utils/gl_raii.h"
11
12 using namespace angle;
13
14 class MaxTextureSizeTest : public ANGLETest<>
15 {
16 protected:
MaxTextureSizeTest()17 MaxTextureSizeTest()
18 {
19 setWindowWidth(512);
20 setWindowHeight(512);
21 setConfigRedBits(8);
22 setConfigGreenBits(8);
23 setConfigBlueBits(8);
24 setConfigAlphaBits(8);
25 }
26
testSetUp()27 void testSetUp() override
28 {
29 constexpr char kVS[] = R"(precision highp float;
30 attribute vec4 position;
31 varying vec2 texcoord;
32
33 void main()
34 {
35 gl_Position = position;
36 texcoord = (position.xy * 0.5) + 0.5;
37 })";
38
39 constexpr char kTextureFS[] = R"(precision highp float;
40 uniform sampler2D tex;
41 varying vec2 texcoord;
42
43 void main()
44 {
45 gl_FragColor = texture2D(tex, texcoord);
46 })";
47
48 constexpr char kBlueFS[] = R"(precision highp float;
49 void main()
50 {
51 gl_FragColor = vec4(0.0, 0.0, 1.0, 1.0);
52 })";
53
54 mTextureProgram = CompileProgram(kVS, kTextureFS);
55 mBlueProgram = CompileProgram(kVS, kBlueFS);
56 if (mTextureProgram == 0 || mBlueProgram == 0)
57 {
58 FAIL() << "shader compilation failed.";
59 }
60
61 mTextureUniformLocation = glGetUniformLocation(mTextureProgram, "tex");
62
63 glGetIntegerv(GL_MAX_TEXTURE_SIZE, &mMaxTexture2DSize);
64 glGetIntegerv(GL_MAX_CUBE_MAP_TEXTURE_SIZE, &mMaxTextureCubeSize);
65 glGetIntegerv(GL_MAX_RENDERBUFFER_SIZE, &mMaxRenderbufferSize);
66
67 ASSERT_GL_NO_ERROR();
68 }
69
testTearDown()70 void testTearDown() override
71 {
72 glDeleteProgram(mTextureProgram);
73 glDeleteProgram(mBlueProgram);
74 }
75
76 GLuint mTextureProgram;
77 GLint mTextureUniformLocation;
78
79 GLuint mBlueProgram;
80
81 GLint mMaxTexture2DSize;
82 GLint mMaxTextureCubeSize;
83 GLint mMaxRenderbufferSize;
84 };
85
TEST_P(MaxTextureSizeTest,SpecificationTexImage)86 TEST_P(MaxTextureSizeTest, SpecificationTexImage)
87 {
88 GLuint tex;
89 glGenTextures(1, &tex);
90 glBindTexture(GL_TEXTURE_2D, tex);
91
92 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
93 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
94 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
95 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
96
97 GLsizei textureWidth = mMaxTexture2DSize;
98 GLsizei textureHeight = 64;
99
100 std::vector<GLubyte> data(textureWidth * textureHeight * 4);
101 for (int y = 0; y < textureHeight; y++)
102 {
103 for (int x = 0; x < textureWidth; x++)
104 {
105 GLubyte *pixel = &data[0] + ((y * textureWidth + x) * 4);
106
107 // Draw a gradient, red in direction, green in y direction
108 pixel[0] = static_cast<GLubyte>((float(x) / textureWidth) * 255);
109 pixel[1] = static_cast<GLubyte>((float(y) / textureHeight) * 255);
110 pixel[2] = 0;
111 pixel[3] = 255;
112 }
113 }
114
115 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, textureWidth, textureHeight, 0, GL_RGBA,
116 GL_UNSIGNED_BYTE, &data[0]);
117 EXPECT_GL_NO_ERROR();
118
119 glUseProgram(mTextureProgram);
120 glUniform1i(mTextureUniformLocation, 0);
121
122 drawQuad(mTextureProgram, "position", 0.5f);
123
124 std::vector<GLubyte> pixels(getWindowWidth() * getWindowHeight() * 4);
125 glReadPixels(0, 0, getWindowWidth(), getWindowHeight(), GL_RGBA, GL_UNSIGNED_BYTE, &pixels[0]);
126
127 for (int y = 1; y < getWindowHeight(); y++)
128 {
129 for (int x = 1; x < getWindowWidth(); x++)
130 {
131 const GLubyte *prevPixel = &pixels[0] + (((y - 1) * getWindowWidth() + (x - 1)) * 4);
132 const GLubyte *curPixel = &pixels[0] + ((y * getWindowWidth() + x) * 4);
133
134 EXPECT_GE(curPixel[0], prevPixel[0]);
135 EXPECT_GE(curPixel[1], prevPixel[1]);
136 EXPECT_EQ(curPixel[2], prevPixel[2]);
137 EXPECT_EQ(curPixel[3], prevPixel[3]);
138 }
139 }
140 }
141
TEST_P(MaxTextureSizeTest,SpecificationTexStorage)142 TEST_P(MaxTextureSizeTest, SpecificationTexStorage)
143 {
144 if (getClientMajorVersion() < 3 && (!IsGLExtensionEnabled("GL_EXT_texture_storage") ||
145 !IsGLExtensionEnabled("GL_OES_rgb8_rgba8")))
146 {
147 return;
148 }
149
150 GLuint tex;
151 glGenTextures(1, &tex);
152 glBindTexture(GL_TEXTURE_2D, tex);
153
154 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
155 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
156 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
157 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
158
159 GLsizei textureWidth = 64;
160 GLsizei textureHeight = mMaxTexture2DSize;
161
162 std::vector<GLubyte> data(textureWidth * textureHeight * 4);
163 for (int y = 0; y < textureHeight; y++)
164 {
165 for (int x = 0; x < textureWidth; x++)
166 {
167 GLubyte *pixel = &data[0] + ((y * textureWidth + x) * 4);
168
169 // Draw a gradient, red in direction, green in y direction
170 pixel[0] = static_cast<GLubyte>((float(x) / textureWidth) * 255);
171 pixel[1] = static_cast<GLubyte>((float(y) / textureHeight) * 255);
172 pixel[2] = 0;
173 pixel[3] = 255;
174 }
175 }
176
177 if (getClientMajorVersion() < 3)
178 {
179 glTexStorage2DEXT(GL_TEXTURE_2D, 1, GL_RGBA8_OES, textureWidth, textureHeight);
180 }
181 else
182 {
183 glTexStorage2D(GL_TEXTURE_2D, 1, GL_RGBA8_OES, textureWidth, textureHeight);
184 }
185 EXPECT_GL_NO_ERROR();
186
187 glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, textureWidth, textureHeight, GL_RGBA, GL_UNSIGNED_BYTE,
188 &data[0]);
189 EXPECT_GL_NO_ERROR();
190
191 glUseProgram(mTextureProgram);
192 glUniform1i(mTextureUniformLocation, 0);
193
194 drawQuad(mTextureProgram, "position", 0.5f);
195
196 std::vector<GLubyte> pixels(getWindowWidth() * getWindowHeight() * 4);
197 glReadPixels(0, 0, getWindowWidth(), getWindowHeight(), GL_RGBA, GL_UNSIGNED_BYTE, &pixels[0]);
198
199 for (int y = 1; y < getWindowHeight(); y++)
200 {
201 for (int x = 1; x < getWindowWidth(); x++)
202 {
203 const GLubyte *prevPixel = &pixels[0] + (((y - 1) * getWindowWidth() + (x - 1)) * 4);
204 const GLubyte *curPixel = &pixels[0] + ((y * getWindowWidth() + x) * 4);
205
206 EXPECT_GE(curPixel[0], prevPixel[0]);
207 EXPECT_GE(curPixel[1], prevPixel[1]);
208 EXPECT_EQ(curPixel[2], prevPixel[2]);
209 EXPECT_EQ(curPixel[3], prevPixel[3]);
210 }
211 }
212 }
213
TEST_P(MaxTextureSizeTest,RenderToTexture)214 TEST_P(MaxTextureSizeTest, RenderToTexture)
215 {
216 ANGLE_SKIP_TEST_IF(getClientMajorVersion() < 3 &&
217 (!IsGLExtensionEnabled("GL_ANGLE_framebuffer_blit")));
218
219 GLuint fbo = 0;
220 GLuint textureId = 0;
221 // create a 1-level texture at maximum size
222 glGenTextures(1, &textureId);
223 glBindTexture(GL_TEXTURE_2D, textureId);
224
225 GLsizei textureWidth = 64;
226 GLsizei textureHeight = mMaxTexture2DSize;
227
228 // texture setup code
229 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
230 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
231 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
232 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
233 glTexImage2D(GL_TEXTURE_2D, 0, GL_BGRA_EXT, textureWidth, textureHeight, 0, GL_BGRA_EXT,
234 GL_UNSIGNED_BYTE, nullptr);
235 EXPECT_GL_NO_ERROR();
236
237 // create an FBO and attach the texture
238 glGenFramebuffers(1, &fbo);
239 glBindFramebuffer(GL_FRAMEBUFFER, fbo);
240 glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, textureId, 0);
241
242 EXPECT_GL_NO_ERROR();
243 EXPECT_GLENUM_EQ(GL_FRAMEBUFFER_COMPLETE, glCheckFramebufferStatus(GL_FRAMEBUFFER));
244
245 const int frameCount = 64;
246 for (int i = 0; i < frameCount; i++)
247 {
248 // clear the screen
249 glBindFramebuffer(GL_FRAMEBUFFER, 0);
250
251 GLubyte clearRed = static_cast<GLubyte>((float(i) / frameCount) * 255);
252 GLubyte clearGreen = 255 - clearRed;
253 GLubyte clearBlue = 0;
254
255 glClearColor(clearRed / 255.0f, clearGreen / 255.0f, clearBlue / 255.0f, 1.0f);
256 glClear(GL_COLOR_BUFFER_BIT);
257
258 // render blue into the texture
259 glBindFramebuffer(GL_FRAMEBUFFER, fbo);
260 drawQuad(mBlueProgram, "position", 0.5f);
261
262 // copy corner of texture to LL corner of window
263 glBindFramebuffer(GL_DRAW_FRAMEBUFFER_ANGLE, 0);
264 glBindFramebuffer(GL_READ_FRAMEBUFFER_ANGLE, fbo);
265 glBlitFramebufferANGLE(0, 0, textureWidth - 1, getWindowHeight() - 1, 0, 0,
266 textureWidth - 1, getWindowHeight() - 1, GL_COLOR_BUFFER_BIT,
267 GL_NEAREST);
268 glBindFramebuffer(GL_READ_FRAMEBUFFER_ANGLE, 0);
269 EXPECT_GL_NO_ERROR();
270
271 EXPECT_PIXEL_EQ(textureWidth / 2, getWindowHeight() / 2, 0, 0, 255, 255);
272 EXPECT_PIXEL_EQ(textureWidth + 10, getWindowHeight() / 2, clearRed, clearGreen, clearBlue,
273 255);
274
275 swapBuffers();
276 }
277
278 glBindFramebuffer(GL_FRAMEBUFFER, 0);
279 glBindTexture(GL_TEXTURE_2D, 0);
280
281 glDeleteFramebuffers(1, &fbo);
282 glDeleteTextures(1, &textureId);
283 }
284
TEST_P(MaxTextureSizeTest,Render1xTexture)285 TEST_P(MaxTextureSizeTest, Render1xTexture)
286 {
287 // This is not spec compliant but checking for now anyway. We can fix it
288 // if we find a driver for which this is not true.
289 float power = std::roundf(std::log(static_cast<float>(mMaxTexture2DSize)) / std::log(2.0f));
290 EXPECT_EQ(std::pow(2.0f, power), mMaxTexture2DSize);
291
292 // Make a the largest possbile texture but not too big.
293 const GLint testSize = std::min(mMaxTexture2DSize, 128 * 1024);
294
295 glUseProgram(mTextureProgram);
296 glUniform1i(mTextureUniformLocation, 0);
297
298 constexpr GLColor testColor(0, 255, 128, 255);
299 std::vector<GLColor> data(testSize, testColor);
300
301 // Test a wide texture.
302 {
303 GLTexture texture1Id;
304 glBindTexture(GL_TEXTURE_2D, texture1Id);
305 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, mMaxTexture2DSize, 1, 0, GL_RGBA, GL_UNSIGNED_BYTE,
306 data.data());
307 glGenerateMipmap(GL_TEXTURE_2D);
308
309 glClear(GL_COLOR_BUFFER_BIT);
310 drawQuad(mTextureProgram, "position", 0.5f);
311
312 EXPECT_PIXEL_RECT_EQ(0, 0, getWindowWidth(), getWindowHeight(), testColor);
313 }
314
315 // Test a tall texture.
316 {
317 GLTexture texture2Id;
318 glBindTexture(GL_TEXTURE_2D, texture2Id);
319 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 1, mMaxTexture2DSize, 0, GL_RGBA, GL_UNSIGNED_BYTE,
320 data.data());
321 glGenerateMipmap(GL_TEXTURE_2D);
322
323 glClear(GL_COLOR_BUFFER_BIT);
324 drawQuad(mTextureProgram, "position", 0.5f);
325 }
326
327 EXPECT_PIXEL_RECT_EQ(0, 0, getWindowWidth(), getWindowHeight(), testColor);
328
329 EXPECT_GL_NO_ERROR();
330 }
331
332 // TODO(geofflang): Fix the dependence on glBlitFramebufferANGLE without checks and assuming the
333 // default framebuffer is BGRA to enable the GL and GLES backends. (http://anglebug.com/42260299)
334
335 // Use this to select which configurations (e.g. which renderer, which GLES major version) these
336 // tests should be run against.
337 ANGLE_INSTANTIATE_TEST(MaxTextureSizeTest, ES2_D3D9(), ES2_D3D11(), ES2_VULKAN(), ES2_METAL());
338
339 // This test suite is not instantiated on some OSes.
340 GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(MaxTextureSizeTest);
341