blob: 996a683b084e0568f68042458a1e42eda7a17ec2 [file] [log] [blame]
Mathias Agopiand606de62010-05-10 20:06:11 -07001/*
2 * Copyright (C) 2010 The Android Open Source Project
3 *
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
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
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.
15 */
16
17#include <stdlib.h>
18#include <stdint.h>
19#include <sys/types.h>
20
21#include <utils/Errors.h>
22#include <utils/Log.h>
23
24#include <ui/GraphicBuffer.h>
25
26#include <GLES/gl.h>
27#include <GLES/glext.h>
28
29#include <hardware/hardware.h>
30
31#include "clz.h"
32#include "DisplayHardware/DisplayHardware.h"
33#include "TextureManager.h"
34
35namespace android {
36
37// ---------------------------------------------------------------------------
38
39TextureManager::TextureManager(uint32_t flags)
40 : mFlags(flags)
41{
42}
43
Mathias Agopian0a917752010-06-14 21:20:00 -070044GLenum TextureManager::getTextureTarget(const Image* image) {
45#if defined(GL_OES_texture_external)
46 switch (image->target) {
47 case Texture::TEXTURE_EXTERNAL:
48 return GL_TEXTURE_EXTERNAL_OES;
49 }
50#endif
51 return GL_TEXTURE_2D;
52}
53
54status_t TextureManager::initTexture(Texture* texture)
Mathias Agopiand606de62010-05-10 20:06:11 -070055{
Mathias Agopian0a917752010-06-14 21:20:00 -070056 if (texture->name != -1UL)
57 return INVALID_OPERATION;
58
Mathias Agopiand606de62010-05-10 20:06:11 -070059 GLuint textureName = -1;
60 glGenTextures(1, &textureName);
Mathias Agopian0a917752010-06-14 21:20:00 -070061 texture->name = textureName;
62 texture->width = 0;
63 texture->height = 0;
64
65 const GLenum target = GL_TEXTURE_2D;
66 glBindTexture(target, textureName);
67 glTexParameterx(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
68 glTexParameterx(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
69 glTexParameterx(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
70 glTexParameterx(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
71
72 return NO_ERROR;
73}
74
75status_t TextureManager::initTexture(Image* pImage, int32_t format)
76{
77 if (pImage->name != -1UL)
78 return INVALID_OPERATION;
79
80 GLuint textureName = -1;
81 glGenTextures(1, &textureName);
82 pImage->name = textureName;
83 pImage->width = 0;
84 pImage->height = 0;
85
86 GLenum target = GL_TEXTURE_2D;
87#if defined(GL_OES_texture_external)
88 if (format && isSupportedYuvFormat(format)) {
89 target = GL_TEXTURE_EXTERNAL_OES;
90 pImage->target = Texture::TEXTURE_EXTERNAL;
91 }
92#endif
93
94 glBindTexture(target, textureName);
95 glTexParameterx(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
96 glTexParameterx(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
97 glTexParameterx(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
98 glTexParameterx(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
99
100 return NO_ERROR;
Mathias Agopiand606de62010-05-10 20:06:11 -0700101}
102
103bool TextureManager::isSupportedYuvFormat(int format)
104{
Mathias Agopian0a917752010-06-14 21:20:00 -0700105 // TODO: how to we know which YUV formats are supported by the GPU?
106
107 // Adreno 200 supports these
108 // YUVY_adreno
109 // UYVY_adreno
110 // NV21_adreno
111 // YV12_adreno
112 // Adreno 205 adds
113 // NV12_adreno_tiled
114 // NV21_adreno_tiled
115
116 // for now pretend we support them all, failure will happen when
117 // we try to use them.
118 return isYuvFormat(format);
119}
120
121bool TextureManager::isYuvFormat(int format)
122{
Mathias Agopiand606de62010-05-10 20:06:11 -0700123 switch (format) {
Mathias Agopian0a917752010-06-14 21:20:00 -0700124 case HAL_PIXEL_FORMAT_NV16:
125 case HAL_PIXEL_FORMAT_NV21:
126 case HAL_PIXEL_FORMAT_IYUV:
127 case HAL_PIXEL_FORMAT_YUV9:
128 case HAL_PIXEL_FORMAT_YUY2:
129 case HAL_PIXEL_FORMAT_UYVY:
130 case HAL_PIXEL_FORMAT_NV12:
131 case HAL_PIXEL_FORMAT_NV61:
132 case HAL_PIXEL_FORMAT_YV12:
133 case HAL_PIXEL_FORMAT_NV12_ADRENO_TILED:
134 case HAL_PIXEL_FORMAT_NV21_ADRENO_TILED:
135 return true;
Mathias Agopiand606de62010-05-10 20:06:11 -0700136 }
137 return false;
138}
139
Mathias Agopian0a917752010-06-14 21:20:00 -0700140status_t TextureManager::initEglImage(Image* pImage,
Mathias Agopiand606de62010-05-10 20:06:11 -0700141 EGLDisplay dpy, const sp<GraphicBuffer>& buffer)
142{
143 status_t err = NO_ERROR;
Mathias Agopian0a917752010-06-14 21:20:00 -0700144 if (!pImage->dirty) return err;
Mathias Agopiand606de62010-05-10 20:06:11 -0700145
146 // free the previous image
Mathias Agopian0a917752010-06-14 21:20:00 -0700147 if (pImage->image != EGL_NO_IMAGE_KHR) {
148 eglDestroyImageKHR(dpy, pImage->image);
149 pImage->image = EGL_NO_IMAGE_KHR;
Mathias Agopiand606de62010-05-10 20:06:11 -0700150 }
151
152 // construct an EGL_NATIVE_BUFFER_ANDROID
153 android_native_buffer_t* clientBuf = buffer->getNativeBuffer();
154
155 // create the new EGLImageKHR
156 const EGLint attrs[] = {
157 EGL_IMAGE_PRESERVED_KHR, EGL_TRUE,
158 EGL_NONE, EGL_NONE
159 };
Mathias Agopian0a917752010-06-14 21:20:00 -0700160 pImage->image = eglCreateImageKHR(
Mathias Agopiand606de62010-05-10 20:06:11 -0700161 dpy, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID,
162 (EGLClientBuffer)clientBuf, attrs);
163
Mathias Agopian0a917752010-06-14 21:20:00 -0700164 if (pImage->image != EGL_NO_IMAGE_KHR) {
165 if (pImage->name == -1UL) {
166 initTexture(pImage, buffer->format);
Mathias Agopiand606de62010-05-10 20:06:11 -0700167 }
Mathias Agopian0a917752010-06-14 21:20:00 -0700168 const GLenum target = getTextureTarget(pImage);
169 glBindTexture(target, pImage->name);
170 glEGLImageTargetTexture2DOES(target, (GLeglImageOES)pImage->image);
Mathias Agopiand606de62010-05-10 20:06:11 -0700171 GLint error = glGetError();
172 if (error != GL_NO_ERROR) {
173 LOGE("glEGLImageTargetTexture2DOES(%p) failed err=0x%04x",
Mathias Agopian0a917752010-06-14 21:20:00 -0700174 pImage->image, error);
Mathias Agopiand606de62010-05-10 20:06:11 -0700175 err = INVALID_OPERATION;
176 } else {
177 // Everything went okay!
Mathias Agopian0a917752010-06-14 21:20:00 -0700178 pImage->dirty = false;
179 pImage->width = clientBuf->width;
180 pImage->height = clientBuf->height;
Mathias Agopiand606de62010-05-10 20:06:11 -0700181 }
182 } else {
183 LOGE("eglCreateImageKHR() failed. err=0x%4x", eglGetError());
184 err = INVALID_OPERATION;
185 }
186 return err;
187}
188
189status_t TextureManager::loadTexture(Texture* texture,
190 const Region& dirty, const GGLSurface& t)
191{
192 if (texture->name == -1UL) {
Mathias Agopian0a917752010-06-14 21:20:00 -0700193 status_t err = initTexture(texture);
194 LOGE_IF(err, "loadTexture failed in initTexture (%s)", strerror(err));
195 return err;
Mathias Agopiand606de62010-05-10 20:06:11 -0700196 }
197
Mathias Agopian0a917752010-06-14 21:20:00 -0700198 if (texture->target != GL_TEXTURE_2D)
199 return INVALID_OPERATION;
200
Mathias Agopiand606de62010-05-10 20:06:11 -0700201 glBindTexture(GL_TEXTURE_2D, texture->name);
202
203 /*
204 * In OpenGL ES we can't specify a stride with glTexImage2D (however,
205 * GL_UNPACK_ALIGNMENT is a limited form of stride).
206 * So if the stride here isn't representable with GL_UNPACK_ALIGNMENT, we
207 * need to do something reasonable (here creating a bigger texture).
208 *
209 * extra pixels = (((stride - width) * pixelsize) / GL_UNPACK_ALIGNMENT);
210 *
211 * This situation doesn't happen often, but some h/w have a limitation
212 * for their framebuffer (eg: must be multiple of 8 pixels), and
213 * we need to take that into account when using these buffers as
214 * textures.
215 *
216 * This should never be a problem with POT textures
217 */
218
219 int unpack = __builtin_ctz(t.stride * bytesPerPixel(t.format));
220 unpack = 1 << ((unpack > 3) ? 3 : unpack);
221 glPixelStorei(GL_UNPACK_ALIGNMENT, unpack);
222
223 /*
224 * round to POT if needed
225 */
226 if (!(mFlags & DisplayHardware::NPOT_EXTENSION)) {
227 texture->NPOTAdjust = true;
228 }
229
230 if (texture->NPOTAdjust) {
231 // find the smallest power-of-two that will accommodate our surface
232 texture->potWidth = 1 << (31 - clz(t.width));
233 texture->potHeight = 1 << (31 - clz(t.height));
234 if (texture->potWidth < t.width) texture->potWidth <<= 1;
235 if (texture->potHeight < t.height) texture->potHeight <<= 1;
236 texture->wScale = float(t.width) / texture->potWidth;
237 texture->hScale = float(t.height) / texture->potHeight;
238 } else {
239 texture->potWidth = t.width;
240 texture->potHeight = t.height;
241 }
242
243 Rect bounds(dirty.bounds());
244 GLvoid* data = 0;
245 if (texture->width != t.width || texture->height != t.height) {
246 texture->width = t.width;
247 texture->height = t.height;
248
249 // texture size changed, we need to create a new one
250 bounds.set(Rect(t.width, t.height));
251 if (t.width == texture->potWidth &&
252 t.height == texture->potHeight) {
253 // we can do it one pass
254 data = t.data;
255 }
256
257 if (t.format == HAL_PIXEL_FORMAT_RGB_565) {
258 glTexImage2D(GL_TEXTURE_2D, 0,
259 GL_RGB, texture->potWidth, texture->potHeight, 0,
260 GL_RGB, GL_UNSIGNED_SHORT_5_6_5, data);
261 } else if (t.format == HAL_PIXEL_FORMAT_RGBA_4444) {
262 glTexImage2D(GL_TEXTURE_2D, 0,
263 GL_RGBA, texture->potWidth, texture->potHeight, 0,
264 GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4, data);
265 } else if (t.format == HAL_PIXEL_FORMAT_RGBA_8888 ||
266 t.format == HAL_PIXEL_FORMAT_RGBX_8888) {
267 glTexImage2D(GL_TEXTURE_2D, 0,
268 GL_RGBA, texture->potWidth, texture->potHeight, 0,
269 GL_RGBA, GL_UNSIGNED_BYTE, data);
Mathias Agopian0a917752010-06-14 21:20:00 -0700270 } else if (isYuvFormat(t.format)) {
Mathias Agopiand606de62010-05-10 20:06:11 -0700271 // just show the Y plane of YUV buffers
272 glTexImage2D(GL_TEXTURE_2D, 0,
273 GL_LUMINANCE, texture->potWidth, texture->potHeight, 0,
274 GL_LUMINANCE, GL_UNSIGNED_BYTE, data);
275 } else {
276 // oops, we don't handle this format!
277 LOGE("texture=%d, using format %d, which is not "
278 "supported by the GL", texture->name, t.format);
279 }
280 }
281 if (!data) {
282 if (t.format == HAL_PIXEL_FORMAT_RGB_565) {
283 glTexSubImage2D(GL_TEXTURE_2D, 0,
284 0, bounds.top, t.width, bounds.height(),
285 GL_RGB, GL_UNSIGNED_SHORT_5_6_5,
286 t.data + bounds.top*t.stride*2);
287 } else if (t.format == HAL_PIXEL_FORMAT_RGBA_4444) {
288 glTexSubImage2D(GL_TEXTURE_2D, 0,
289 0, bounds.top, t.width, bounds.height(),
290 GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4,
291 t.data + bounds.top*t.stride*2);
292 } else if (t.format == HAL_PIXEL_FORMAT_RGBA_8888 ||
293 t.format == HAL_PIXEL_FORMAT_RGBX_8888) {
294 glTexSubImage2D(GL_TEXTURE_2D, 0,
295 0, bounds.top, t.width, bounds.height(),
296 GL_RGBA, GL_UNSIGNED_BYTE,
297 t.data + bounds.top*t.stride*4);
Mathias Agopian0a917752010-06-14 21:20:00 -0700298 } else if (isYuvFormat(t.format)) {
Mathias Agopiand606de62010-05-10 20:06:11 -0700299 // just show the Y plane of YUV buffers
300 glTexSubImage2D(GL_TEXTURE_2D, 0,
301 0, bounds.top, t.width, bounds.height(),
302 GL_LUMINANCE, GL_UNSIGNED_BYTE,
303 t.data + bounds.top*t.stride);
304 }
305 }
306 return NO_ERROR;
307}
308
Mathias Agopian0a917752010-06-14 21:20:00 -0700309void TextureManager::activateTexture(const Texture& texture, bool filter)
310{
311 const GLenum target = getTextureTarget(&texture);
312
313 glBindTexture(target, texture.name);
314 glEnable(target);
315
316#if defined(GL_OES_texture_external)
317 if (texture.target == Texture::TEXTURE_2D) {
318 glDisable(GL_TEXTURE_EXTERNAL_OES);
319 } else {
320 glDisable(GL_TEXTURE_2D);
321 }
322#endif
323
324 if (filter) {
325 glTexParameterx(target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
326 glTexParameterx(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
327 } else {
328 glTexParameterx(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
329 glTexParameterx(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
330 }
331}
332
333void TextureManager::deactivateTextures()
334{
335 glDisable(GL_TEXTURE_2D);
336#if defined(GL_OES_texture_external)
337 glDisable(GL_TEXTURE_EXTERNAL_OES);
338#endif
339}
340
Mathias Agopiand606de62010-05-10 20:06:11 -0700341// ---------------------------------------------------------------------------
342
343}; // namespace android