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
36namespace android {
37
38// ---------------------------------------------------------------------------
39
40TextureManager::TextureManager()
41    : mGLExtensions(GLExtensions::getInstance())
42{
43}
44
45GLenum 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
55status_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
76status_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
106bool TextureManager::isSupportedYuvFormat(int format)
107{
108    switch (format) {
109    case HAL_PIXEL_FORMAT_YV12:
110        return true;
111    }
112    return false;
113}
114
115bool 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
134status_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
183status_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
303void 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
329void 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
342