2 * Copyright (C) 2015 The Android Open Source Project
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 #include "anbox/graphics/emugl/TextureResize.h"
18 #include "anbox/graphics/emugl/DispatchTables.h"
19 #include "anbox/logger.h"
26 #define MAX_FACTOR_POWER 4
28 static const char kCommonShaderSource[] =
29 "precision mediump float;\n"
30 "varying vec2 vUV00, vUV01;\n"
32 "varying vec2 vUV02, vUV03;\n"
34 "varying vec2 vUV04, vUV05, vUV06, vUV07;\n"
36 "varying vec2 vUV08, vUV09, vUV10, vUV11, vUV12, vUV13, vUV14, vUV15;\n"
41 static const char kVertexShaderSource[] =
42 "attribute vec2 aPosition;\n"
45 " gl_Position = vec4(aPosition, 0, 1);\n"
46 " vec2 uv = ((aPosition + 1.0) / 2.0) + 0.5 / kDimension;\n"
48 " #ifdef HORIZONTAL\n"
49 " vUV01 = uv + vec2( 1.0 / kDimension.x, 0);\n"
51 " vUV02 = uv + vec2( 2.0 / kDimension.x, 0);\n"
52 " vUV03 = uv + vec2( 3.0 / kDimension.x, 0);\n"
54 " vUV04 = uv + vec2( 4.0 / kDimension.x, 0);\n"
55 " vUV05 = uv + vec2( 5.0 / kDimension.x, 0);\n"
56 " vUV06 = uv + vec2( 6.0 / kDimension.x, 0);\n"
57 " vUV07 = uv + vec2( 7.0 / kDimension.x, 0);\n"
59 " vUV08 = uv + vec2( 8.0 / kDimension.x, 0);\n"
60 " vUV09 = uv + vec2( 9.0 / kDimension.x, 0);\n"
61 " vUV10 = uv + vec2(10.0 / kDimension.x, 0);\n"
62 " vUV11 = uv + vec2(11.0 / kDimension.x, 0);\n"
63 " vUV12 = uv + vec2(12.0 / kDimension.x, 0);\n"
64 " vUV13 = uv + vec2(13.0 / kDimension.x, 0);\n"
65 " vUV14 = uv + vec2(14.0 / kDimension.x, 0);\n"
66 " vUV15 = uv + vec2(15.0 / kDimension.x, 0);\n"
67 " #endif\n" // FACTOR > 8
68 " #endif\n" // FACTOR > 4
69 " #endif\n" // FACTOR > 2
72 " vUV01 = uv + vec2(0, 1.0 / kDimension.y);\n"
74 " vUV02 = uv + vec2(0, 2.0 / kDimension.y);\n"
75 " vUV03 = uv + vec2(0, 3.0 / kDimension.y);\n"
77 " vUV04 = uv + vec2(0, 4.0 / kDimension.y);\n"
78 " vUV05 = uv + vec2(0, 5.0 / kDimension.y);\n"
79 " vUV06 = uv + vec2(0, 6.0 / kDimension.y);\n"
80 " vUV07 = uv + vec2(0, 7.0 / kDimension.y);\n"
82 " vUV08 = uv + vec2(0, 8.0 / kDimension.y);\n"
83 " vUV09 = uv + vec2(0, 9.0 / kDimension.y);\n"
84 " vUV10 = uv + vec2(0, 10.0 / kDimension.y);\n"
85 " vUV11 = uv + vec2(0, 11.0 / kDimension.y);\n"
86 " vUV12 = uv + vec2(0, 12.0 / kDimension.y);\n"
87 " vUV13 = uv + vec2(0, 13.0 / kDimension.y);\n"
88 " vUV14 = uv + vec2(0, 14.0 / kDimension.y);\n"
89 " vUV15 = uv + vec2(0, 15.0 / kDimension.y);\n"
90 " #endif\n" // FACTOR > 8
91 " #endif\n" // FACTOR > 4
92 " #endif\n" // FACTOR > 2
93 " #endif\n" // HORIZONTAL/VERTICAL
96 const char kFragmentShaderSource[] =
97 "uniform sampler2D uTexture;\n"
99 "vec4 read(vec2 uv) {\n"
100 " vec4 r = texture2D(uTexture, uv);\n"
101 " #ifdef HORIZONTAL\n"
102 " r.rgb = pow(r.rgb, vec3(2.2));\n"
108 " vec4 sum = read(vUV00) + read(vUV01);\n"
110 " sum += read(vUV02) + read(vUV03);\n"
112 " sum += read(vUV04) + read(vUV05) + read(vUV06) + read(vUV07);\n"
114 " sum += read(vUV08) + read(vUV09) + read(vUV10) + read(vUV11) +"
115 " read(vUV12) + read(vUV13) + read(vUV14) + read(vUV15);\n"
119 " sum /= float(FACTOR);\n"
121 " sum.rgb = pow(sum.rgb, vec3(1.0 / 2.2));\n"
123 " gl_FragColor = sum;\n"
126 static const float kVertexData[] = {-1, -1, 3, -1, -1, 3};
128 static void detachShaders(GLuint program) {
129 GLuint shaders[2] = {};
131 s_gles2.glGetAttachedShaders(program, 2, &count, shaders);
132 if (s_gles2.glGetError() == GL_NO_ERROR) {
133 for (GLsizei i = 0; i < count; i++) {
134 s_gles2.glDetachShader(program, shaders[i]);
135 s_gles2.glDeleteShader(shaders[i]);
140 static GLuint createShader(GLenum type,
141 const std::initializer_list<const char*>& source) {
142 GLint success, infoLength;
144 GLuint shader = s_gles2.glCreateShader(type);
146 s_gles2.glShaderSource(shader, source.size(), source.begin(), nullptr);
147 s_gles2.glCompileShader(shader);
148 s_gles2.glGetShaderiv(shader, GL_COMPILE_STATUS, &success);
149 if (success == GL_FALSE) {
150 s_gles2.glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &infoLength);
151 std::string infoLog(infoLength + 1, '\0');
152 s_gles2.glGetShaderInfoLog(shader, infoLength, nullptr, &infoLog[0]);
153 ERROR("%s shader compile failed: %s", (type == GL_VERTEX_SHADER) ? "Vertex" : "Fragment", infoLog.c_str());
154 s_gles2.glDeleteShader(shader);
161 static void attachShaders(TextureResize::Framebuffer* fb,
162 const char* factorDefine, const char* dimensionDefine,
163 GLuint width, GLuint height) {
164 std::ostringstream dimensionConst;
165 dimensionConst << "const vec2 kDimension = vec2(" << width << ", " << height
168 GLuint vShader = createShader(
169 GL_VERTEX_SHADER, {factorDefine, dimensionDefine, kCommonShaderSource,
170 dimensionConst.str().c_str(), kVertexShaderSource});
171 GLuint fShader = createShader(
172 GL_FRAGMENT_SHADER, {factorDefine, dimensionDefine, kCommonShaderSource,
173 kFragmentShaderSource});
175 if (!vShader || !fShader) {
179 s_gles2.glAttachShader(fb->program, vShader);
180 s_gles2.glAttachShader(fb->program, fShader);
181 s_gles2.glLinkProgram(fb->program);
183 s_gles2.glUseProgram(fb->program);
184 fb->aPosition = s_gles2.glGetAttribLocation(fb->program, "aPosition");
185 fb->uTexture = s_gles2.glGetUniformLocation(fb->program, "uTexture");
188 TextureResize::TextureResize(GLuint width, GLuint height)
192 s_gles2.glGenTextures(1, &mFBWidth.texture);
193 s_gles2.glBindTexture(GL_TEXTURE_2D, mFBWidth.texture);
194 s_gles2.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
195 s_gles2.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
196 s_gles2.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
197 s_gles2.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
199 s_gles2.glGenTextures(1, &mFBHeight.texture);
200 s_gles2.glBindTexture(GL_TEXTURE_2D, mFBHeight.texture);
201 s_gles2.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
202 s_gles2.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
203 s_gles2.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
204 s_gles2.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
206 s_gles2.glGenFramebuffers(1, &mFBWidth.framebuffer);
207 s_gles2.glGenFramebuffers(1, &mFBHeight.framebuffer);
209 mFBWidth.program = s_gles2.glCreateProgram();
210 mFBHeight.program = s_gles2.glCreateProgram();
212 s_gles2.glGenBuffers(1, &mVertexBuffer);
213 s_gles2.glBindBuffer(GL_ARRAY_BUFFER, mVertexBuffer);
214 s_gles2.glBufferData(GL_ARRAY_BUFFER, sizeof(kVertexData), kVertexData,
218 TextureResize::~TextureResize() {
219 GLuint fb[2] = {mFBWidth.framebuffer, mFBHeight.framebuffer};
220 s_gles2.glDeleteFramebuffers(2, fb);
222 GLuint tex[2] = {mFBWidth.texture, mFBHeight.texture};
223 s_gles2.glDeleteTextures(2, tex);
225 s_gles2.glDeleteProgram(mFBWidth.program);
226 s_gles2.glDeleteProgram(mFBHeight.program);
228 s_gles2.glDeleteBuffers(1, &mVertexBuffer);
231 GLuint TextureResize::update(GLuint texture) {
232 // Store the viewport. The viewport is clobbered due to the framebuffers.
236 s_gles2.glGetIntegerv(GL_VIEWPORT, vport);
238 // Correctly deal with rotated screens.
239 GLint tWidth = vport[2], tHeight = vport[3];
240 if ((mWidth < mHeight) != (tWidth < tHeight)) {
241 std::swap(tWidth, tHeight);
244 // Compute the scaling factor needed to get an image just larger than the
246 unsigned int factor = 1;
247 for (int i = 0, w = mWidth / 2, h = mHeight / 2;
248 i < MAX_FACTOR_POWER && w >= tWidth && h >= tHeight;
249 i++, w /= 2, h /= 2, factor *= 2) {
252 // No resizing needed.
257 s_gles2.glGetError(); // Clear any GL errors.
258 setupFramebuffers(factor);
260 s_gles2.glViewport(vport[0], vport[1], vport[2],
261 vport[3]); // Restore the viewport.
263 // If there was an error while resizing, just use the unscaled texture.
264 GLenum error = s_gles2.glGetError();
265 if (error != GL_NO_ERROR) {
266 ERROR("GL error while resizing: 0x%x (ignored)", error);
270 return mFBHeight.texture;
273 void TextureResize::setupFramebuffers(unsigned int factor) {
274 if (factor == mFactor) {
275 // The factor hasn't changed, no need to update the framebuffers.
279 // Update the framebuffer sizes to match the new factor.
280 s_gles2.glBindTexture(GL_TEXTURE_2D, mFBWidth.texture);
281 s_gles2.glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, mWidth / factor, mHeight, 0,
282 GL_RGBA, GL_FLOAT, nullptr);
283 s_gles2.glBindFramebuffer(GL_FRAMEBUFFER, mFBWidth.framebuffer);
284 s_gles2.glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
285 GL_TEXTURE_2D, mFBWidth.texture, 0);
287 s_gles2.glBindTexture(GL_TEXTURE_2D, mFBHeight.texture);
288 s_gles2.glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, mWidth / factor,
289 mHeight / factor, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr);
290 s_gles2.glBindFramebuffer(GL_FRAMEBUFFER, mFBHeight.framebuffer);
291 s_gles2.glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0,
292 GL_TEXTURE_2D, mFBHeight.texture, 0);
294 // Update the shaders to the new factor. First detach the old shaders...
295 detachShaders(mFBWidth.program);
296 detachShaders(mFBHeight.program);
298 // ... then attach the new ones.
299 std::ostringstream factorDefine;
300 factorDefine << "#define FACTOR " << factor << "\n";
301 attachShaders(&mFBWidth, factorDefine.str().c_str(), "#define HORIZONTAL\n",
303 attachShaders(&mFBHeight, factorDefine.str().c_str(), "#define VERTICAL\n",
309 void TextureResize::resize(GLuint texture) {
310 s_gles2.glBindBuffer(GL_ARRAY_BUFFER, mVertexBuffer);
311 s_gles2.glActiveTexture(GL_TEXTURE0);
313 // First scale the horizontal dimension by rendering the input texture to a
314 // scaled framebuffer.
315 s_gles2.glBindFramebuffer(GL_FRAMEBUFFER, mFBWidth.framebuffer);
316 s_gles2.glViewport(0, 0, mWidth / mFactor, mHeight);
317 s_gles2.glUseProgram(mFBWidth.program);
318 s_gles2.glEnableVertexAttribArray(mFBWidth.aPosition);
319 s_gles2.glVertexAttribPointer(mFBWidth.aPosition, 2, GL_FLOAT, GL_FALSE, 0,
321 s_gles2.glBindTexture(GL_TEXTURE_2D, texture);
323 // Store the current texture filters and set to nearest for scaling.
324 GLint mag_filter, min_filter;
325 s_gles2.glGetTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,
327 s_gles2.glGetTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER,
329 s_gles2.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
330 s_gles2.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
331 s_gles2.glUniform1i(mFBWidth.uTexture, 0);
332 s_gles2.glDrawArrays(GL_TRIANGLES, 0,
333 sizeof(kVertexData) / (2 * sizeof(float)));
335 // Restore the previous texture filters.
336 s_gles2.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, mag_filter);
337 s_gles2.glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, min_filter);
339 // Secondly, scale the vertical dimension using the second framebuffer.
340 s_gles2.glBindFramebuffer(GL_FRAMEBUFFER, mFBHeight.framebuffer);
341 s_gles2.glViewport(0, 0, mWidth / mFactor, mHeight / mFactor);
342 s_gles2.glUseProgram(mFBHeight.program);
343 s_gles2.glEnableVertexAttribArray(mFBHeight.aPosition);
344 s_gles2.glVertexAttribPointer(mFBHeight.aPosition, 2, GL_FLOAT, GL_FALSE, 0,
346 s_gles2.glBindTexture(GL_TEXTURE_2D, mFBWidth.texture);
347 s_gles2.glUniform1i(mFBHeight.uTexture, 0);
348 s_gles2.glDrawArrays(GL_TRIANGLES, 0,
349 sizeof(kVertexData) / (2 * sizeof(float)));
351 // Clear the bindings.
352 s_gles2.glBindBuffer(GL_ARRAY_BUFFER, 0);
353 s_gles2.glBindFramebuffer(GL_FRAMEBUFFER, 0);
354 s_gles2.glBindTexture(GL_TEXTURE_2D, 0);