OSDN Git Service

am 9df48a03: Merge "NotificationManager: droiddoc documentation improvements"
[android-x86/frameworks-native.git] / services / surfaceflinger / TextureManager.cpp
1 /*
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 "GLExtensions.h"
34 #include "TextureManager.h"
35
36 namespace android {
37
38 // ---------------------------------------------------------------------------
39
40 TextureManager::TextureManager()
41     : mGLExtensions(GLExtensions::getInstance())
42 {
43 }
44
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;
50     }
51 #endif
52     return GL_TEXTURE_2D;
53 }
54
55 status_t TextureManager::initTexture(Texture* texture)
56 {
57     if (texture->name != -1UL)
58         return INVALID_OPERATION;
59
60     GLuint textureName = -1;
61     glGenTextures(1, &textureName);
62     texture->name = textureName;
63     texture->width = 0;
64     texture->height = 0;
65
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);
72
73     return NO_ERROR;
74 }
75
76 status_t TextureManager::initTexture(Image* pImage, int32_t format)
77 {
78     if (pImage->name != -1UL)
79         return INVALID_OPERATION;
80
81     GLuint textureName = -1;
82     glGenTextures(1, &textureName);
83     pImage->name = textureName;
84     pImage->width = 0;
85     pImage->height = 0;
86
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;
93         }
94     }
95 #endif
96
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);
102
103     return NO_ERROR;
104 }
105
106 bool TextureManager::isSupportedYuvFormat(int format)
107 {
108     switch (format) {
109     case HAL_PIXEL_FORMAT_YV12:
110         return true;
111     }
112     return false;
113 }
114
115 bool TextureManager::isYuvFormat(int format)
116 {
117     switch (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:
124         return true;
125     }
126
127     // Any OEM format needs to be considered
128     if (format>=0x100 && format<=0x1FF)
129         return true;
130
131     return false;
132 }
133
134 status_t TextureManager::initEglImage(Image* pImage,
135         EGLDisplay dpy, const sp<GraphicBuffer>& buffer)
136 {
137     status_t err = NO_ERROR;
138     if (!pImage->dirty) return err;
139
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;
144     }
145
146     // construct an EGL_NATIVE_BUFFER_ANDROID
147     android_native_buffer_t* clientBuf = buffer->getNativeBuffer();
148
149     // create the new EGLImageKHR
150     const EGLint attrs[] = {
151             EGL_IMAGE_PRESERVED_KHR,    EGL_TRUE,
152             EGL_NONE,                   EGL_NONE
153     };
154     pImage->image = eglCreateImageKHR(
155             dpy, EGL_NO_CONTEXT, EGL_NATIVE_BUFFER_ANDROID,
156             (EGLClientBuffer)clientBuf, attrs);
157
158     if (pImage->image != EGL_NO_IMAGE_KHR) {
159         if (pImage->name == -1UL) {
160             initTexture(pImage, buffer->format);
161         }
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;
170         } else {
171             // Everything went okay!
172             pImage->dirty  = false;
173             pImage->width  = clientBuf->width;
174             pImage->height = clientBuf->height;
175         }
176     } else {
177         LOGE("eglCreateImageKHR() failed. err=0x%4x", eglGetError());
178         err = INVALID_OPERATION;
179     }
180     return err;
181 }
182
183 status_t TextureManager::loadTexture(Texture* texture,
184         const Region& dirty, const GGLSurface& t)
185 {
186     if (texture->name == -1UL) {
187         status_t err = initTexture(texture);
188         LOGE_IF(err, "loadTexture failed in initTexture (%s)", strerror(err));
189         return err;
190     }
191
192     if (texture->target != Texture::TEXTURE_2D)
193         return INVALID_OPERATION;
194
195     glBindTexture(GL_TEXTURE_2D, texture->name);
196
197     /*
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).
202      *
203      * extra pixels = (((stride - width) * pixelsize) / GL_UNPACK_ALIGNMENT);
204      *
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
208      * textures.
209      *
210      * This should never be a problem with POT textures
211      */
212
213     int unpack = __builtin_ctz(t.stride * bytesPerPixel(t.format));
214     unpack = 1 << ((unpack > 3) ? 3 : unpack);
215     glPixelStorei(GL_UNPACK_ALIGNMENT, unpack);
216
217     /*
218      * round to POT if needed
219      */
220     if (!mGLExtensions.haveNpot()) {
221         texture->NPOTAdjust = true;
222     }
223
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;
232     } else {
233         texture->potWidth  = t.width;
234         texture->potHeight = t.height;
235     }
236
237     Rect bounds(dirty.bounds());
238     GLvoid* data = 0;
239     if (texture->width != t.width || texture->height != t.height) {
240         texture->width  = t.width;
241         texture->height = t.height;
242
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
248             data = t.data;
249         }
250
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);
269         } else {
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);
273         }
274     }
275     if (!data) {
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);
298         }
299     }
300     return NO_ERROR;
301 }
302
303 void TextureManager::activateTexture(const Texture& texture, bool filter)
304 {
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);
312         }
313     } else {
314         glBindTexture(GL_TEXTURE_EXTERNAL_OES, texture.name);
315         glEnable(GL_TEXTURE_EXTERNAL_OES);
316         glDisable(GL_TEXTURE_2D);
317 #endif
318     }
319
320     if (filter) {
321         glTexParameterx(target, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
322         glTexParameterx(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
323     } else {
324         glTexParameterx(target, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
325         glTexParameterx(target, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
326     }
327 }
328
329 void TextureManager::deactivateTextures()
330 {
331     glDisable(GL_TEXTURE_2D);
332 #if defined(GL_OES_EGL_image_external)
333     if (GLExtensions::getInstance().haveTextureExternal()) {
334         glDisable(GL_TEXTURE_EXTERNAL_OES);
335     }
336 #endif
337 }
338
339 // ---------------------------------------------------------------------------
340
341 }; // namespace android