2 * Copyright (C) 2010 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.
19 #include <sys/types.h>
21 #include <utils/Errors.h>
22 #include <utils/Log.h>
24 #include <ui/GraphicBuffer.h>
27 #include <GLES/glext.h>
29 #include <hardware/hardware.h>
32 #include "DisplayHardware/DisplayHardware.h"
33 #include "GLExtensions.h"
34 #include "TextureManager.h"
38 // ---------------------------------------------------------------------------
40 TextureManager::TextureManager()
41 : mGLExtensions(GLExtensions::getInstance())
45 GLenum TextureManager::getTextureTarget(const Image* image) {
46 #if defined(GL_OES_EGL_image_external)
47 switch (image->target) {
48 case Texture::TEXTURE_EXTERNAL:
49 return GL_TEXTURE_EXTERNAL_OES;
55 status_t TextureManager::initTexture(Texture* texture)
57 if (texture->name != -1UL)
58 return INVALID_OPERATION;
60 GLuint textureName = -1;
61 glGenTextures(1, &textureName);
62 texture->name = textureName;
66 const GLenum target = GL_TEXTURE_2D;
67 glBindTexture(target, textureName);
68 glTexParameterx(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
69 glTexParameterx(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
70 glTexParameterx(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
71 glTexParameterx(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
76 status_t TextureManager::initTexture(Image* pImage, int32_t format)
78 if (pImage->name != -1UL)
79 return INVALID_OPERATION;
81 GLuint textureName = -1;
82 glGenTextures(1, &textureName);
83 pImage->name = textureName;
87 GLenum target = GL_TEXTURE_2D;
88 #if defined(GL_OES_EGL_image_external)
89 if (GLExtensions::getInstance().haveTextureExternal()) {
90 if (format && isYuvFormat(format)) {
91 target = GL_TEXTURE_EXTERNAL_OES;
92 pImage->target = Texture::TEXTURE_EXTERNAL;
97 glBindTexture(target, textureName);
98 glTexParameterx(target, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
99 glTexParameterx(target, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
100 glTexParameterx(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
101 glTexParameterx(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
106 bool TextureManager::isSupportedYuvFormat(int format)
109 case HAL_PIXEL_FORMAT_YV12:
115 bool TextureManager::isYuvFormat(int format)
118 // supported YUV formats
119 case HAL_PIXEL_FORMAT_YV12:
120 // Legacy/deprecated YUV formats
121 case HAL_PIXEL_FORMAT_YCbCr_422_SP:
122 case HAL_PIXEL_FORMAT_YCrCb_420_SP:
123 case HAL_PIXEL_FORMAT_YCbCr_422_I:
127 // Any OEM format needs to be considered
128 if (format>=0x100 && format<=0x1FF)
134 status_t TextureManager::initEglImage(Image* pImage,
135 EGLDisplay dpy, const sp<GraphicBuffer>& buffer)
137 status_t err = NO_ERROR;
138 if (!pImage->dirty) return err;
140 // free the previous image
141 if (pImage->image != EGL_NO_IMAGE_KHR) {
142 eglDestroyImageKHR(dpy, pImage->image);
143 pImage->image = EGL_NO_IMAGE_KHR;
146 // construct an EGL_NATIVE_BUFFER_ANDROID
147 android_native_buffer_t* clientBuf = buffer->getNativeBuffer();
149 // create the new EGLImageKHR
150 const EGLint attrs[] = {
151 EGL_IMAGE_PRESERVED_KHR, EGL_TRUE,
154 pImage->image = eglCreateImageKHR(
155 dpy, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID,
156 (EGLClientBuffer)clientBuf, attrs);
158 if (pImage->image != EGL_NO_IMAGE_KHR) {
159 if (pImage->name == -1UL) {
160 initTexture(pImage, buffer->format);
162 const GLenum target = getTextureTarget(pImage);
163 glBindTexture(target, pImage->name);
164 glEGLImageTargetTexture2DOES(target, (GLeglImageOES)pImage->image);
165 GLint error = glGetError();
166 if (error != GL_NO_ERROR) {
167 LOGE("glEGLImageTargetTexture2DOES(%p) failed err=0x%04x",
168 pImage->image, error);
169 err = INVALID_OPERATION;
171 // Everything went okay!
172 pImage->dirty = false;
173 pImage->width = clientBuf->width;
174 pImage->height = clientBuf->height;
177 LOGE("eglCreateImageKHR() failed. err=0x%4x", eglGetError());
178 err = INVALID_OPERATION;
183 status_t TextureManager::loadTexture(Texture* texture,
184 const Region& dirty, const GGLSurface& t)
186 if (texture->name == -1UL) {
187 status_t err = initTexture(texture);
188 LOGE_IF(err, "loadTexture failed in initTexture (%s)", strerror(err));
192 if (texture->target != Texture::TEXTURE_2D)
193 return INVALID_OPERATION;
195 glBindTexture(GL_TEXTURE_2D, texture->name);
198 * In OpenGL ES we can't specify a stride with glTexImage2D (however,
199 * GL_UNPACK_ALIGNMENT is a limited form of stride).
200 * So if the stride here isn't representable with GL_UNPACK_ALIGNMENT, we
201 * need to do something reasonable (here creating a bigger texture).
203 * extra pixels = (((stride - width) * pixelsize) / GL_UNPACK_ALIGNMENT);
205 * This situation doesn't happen often, but some h/w have a limitation
206 * for their framebuffer (eg: must be multiple of 8 pixels), and
207 * we need to take that into account when using these buffers as
210 * This should never be a problem with POT textures
213 int unpack = __builtin_ctz(t.stride * bytesPerPixel(t.format));
214 unpack = 1 << ((unpack > 3) ? 3 : unpack);
215 glPixelStorei(GL_UNPACK_ALIGNMENT, unpack);
218 * round to POT if needed
220 if (!mGLExtensions.haveNpot()) {
221 texture->NPOTAdjust = true;
224 if (texture->NPOTAdjust) {
225 // find the smallest power-of-two that will accommodate our surface
226 texture->potWidth = 1 << (31 - clz(t.width));
227 texture->potHeight = 1 << (31 - clz(t.height));
228 if (texture->potWidth < t.width) texture->potWidth <<= 1;
229 if (texture->potHeight < t.height) texture->potHeight <<= 1;
230 texture->wScale = float(t.width) / texture->potWidth;
231 texture->hScale = float(t.height) / texture->potHeight;
233 texture->potWidth = t.width;
234 texture->potHeight = t.height;
237 Rect bounds(dirty.bounds());
239 if (texture->width != t.width || texture->height != t.height) {
240 texture->width = t.width;
241 texture->height = t.height;
243 // texture size changed, we need to create a new one
244 bounds.set(Rect(t.width, t.height));
245 if (t.width == texture->potWidth &&
246 t.height == texture->potHeight) {
247 // we can do it one pass
251 if (t.format == HAL_PIXEL_FORMAT_RGB_565) {
252 glTexImage2D(GL_TEXTURE_2D, 0,
253 GL_RGB, texture->potWidth, texture->potHeight, 0,
254 GL_RGB, GL_UNSIGNED_SHORT_5_6_5, data);
255 } else if (t.format == HAL_PIXEL_FORMAT_RGBA_4444) {
256 glTexImage2D(GL_TEXTURE_2D, 0,
257 GL_RGBA, texture->potWidth, texture->potHeight, 0,
258 GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4, data);
259 } else if (t.format == HAL_PIXEL_FORMAT_RGBA_8888 ||
260 t.format == HAL_PIXEL_FORMAT_RGBX_8888) {
261 glTexImage2D(GL_TEXTURE_2D, 0,
262 GL_RGBA, texture->potWidth, texture->potHeight, 0,
263 GL_RGBA, GL_UNSIGNED_BYTE, data);
264 } else if (isSupportedYuvFormat(t.format)) {
265 // just show the Y plane of YUV buffers
266 glTexImage2D(GL_TEXTURE_2D, 0,
267 GL_LUMINANCE, texture->potWidth, texture->potHeight, 0,
268 GL_LUMINANCE, GL_UNSIGNED_BYTE, data);
270 // oops, we don't handle this format!
271 LOGE("texture=%d, using format %d, which is not "
272 "supported by the GL", texture->name, t.format);
276 if (t.format == HAL_PIXEL_FORMAT_RGB_565) {
277 glTexSubImage2D(GL_TEXTURE_2D, 0,
278 0, bounds.top, t.width, bounds.height(),
279 GL_RGB, GL_UNSIGNED_SHORT_5_6_5,
280 t.data + bounds.top*t.stride*2);
281 } else if (t.format == HAL_PIXEL_FORMAT_RGBA_4444) {
282 glTexSubImage2D(GL_TEXTURE_2D, 0,
283 0, bounds.top, t.width, bounds.height(),
284 GL_RGBA, GL_UNSIGNED_SHORT_4_4_4_4,
285 t.data + bounds.top*t.stride*2);
286 } else if (t.format == HAL_PIXEL_FORMAT_RGBA_8888 ||
287 t.format == HAL_PIXEL_FORMAT_RGBX_8888) {
288 glTexSubImage2D(GL_TEXTURE_2D, 0,
289 0, bounds.top, t.width, bounds.height(),
290 GL_RGBA, GL_UNSIGNED_BYTE,
291 t.data + bounds.top*t.stride*4);
292 } else if (isSupportedYuvFormat(t.format)) {
293 // just show the Y plane of YUV buffers
294 glTexSubImage2D(GL_TEXTURE_2D, 0,
295 0, bounds.top, t.width, bounds.height(),
296 GL_LUMINANCE, GL_UNSIGNED_BYTE,
297 t.data + bounds.top*t.stride);
303 void TextureManager::activateTexture(const Texture& texture, bool filter)
305 const GLenum target = getTextureTarget(&texture);
306 if (target == GL_TEXTURE_2D) {
307 glBindTexture(GL_TEXTURE_2D, texture.name);
308 glEnable(GL_TEXTURE_2D);
309 #if defined(GL_OES_EGL_image_external)
310 if (GLExtensions::getInstance().haveTextureExternal()) {
311 glDisable(GL_TEXTURE_EXTERNAL_OES);
314 glBindTexture(GL_TEXTURE_EXTERNAL_OES, texture.name);
315 glEnable(GL_TEXTURE_EXTERNAL_OES);
316 glDisable(GL_TEXTURE_2D);
321 glTexParameterx(target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
322 glTexParameterx(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
324 glTexParameterx(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
325 glTexParameterx(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
329 void TextureManager::deactivateTextures()
331 glDisable(GL_TEXTURE_2D);
332 #if defined(GL_OES_EGL_image_external)
333 if (GLExtensions::getInstance().haveTextureExternal()) {
334 glDisable(GL_TEXTURE_EXTERNAL_OES);
339 // ---------------------------------------------------------------------------
341 }; // namespace android