| /* |
| ** |
| ** Copyright 2009, The Android Open Source Project |
| ** |
| ** Licensed under the Apache License, Version 2.0 (the "License"); |
| ** you may not use this file except in compliance with the License. |
| ** You may obtain a copy of the License at |
| ** |
| ** http://www.apache.org/licenses/LICENSE-2.0 |
| ** |
| ** Unless required by applicable law or agreed to in writing, software |
| ** distributed under the License is distributed on an "AS IS" BASIS, |
| ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| ** See the License for the specific language governing permissions and |
| ** limitations under the License. |
| */ |
| |
| #include <stdlib.h> |
| #include <stdio.h> |
| |
| #include "context.h" |
| #include "fp.h" |
| #include "state.h" |
| #include "matrix.h" |
| #include "vertex.h" |
| #include "light.h" |
| #include "primitives.h" |
| #include "texture.h" |
| #include "BufferObjectManager.h" |
| |
| #include "TextureObjectManager.h" |
| #include <hardware/gralloc.h> |
| #include <hardware/copybit.h> |
| #include "gralloc_priv.h" |
| |
| // ---------------------------------------------------------------------------- |
| |
| namespace android { |
| |
| static void textureToCopyBitImage(const GGLSurface* surface, int fd, copybit_image_t* img) { |
| img->w = surface->stride; |
| img->h = surface->height; |
| img->format = surface->format; |
| img->offset = 0; |
| img->base = surface->data; |
| img->fd = fd; |
| } |
| |
| struct clipRectRegion : public copybit_region_t { |
| clipRectRegion(ogles_context_t* c) { |
| next = iterate; |
| int x = c->viewport.scissor.x; |
| int y = c->viewport.scissor.y; |
| r.l = x; |
| r.t = y; |
| r.r = x + c->viewport.scissor.w; |
| r.b = y + c->viewport.scissor.h; |
| firstTime = true; |
| } |
| private: |
| static int iterate(copybit_region_t const * self, copybit_rect_t* rect) { |
| clipRectRegion* myself = (clipRectRegion*) self; |
| if (myself->firstTime) { |
| myself->firstTime = false; |
| *rect = myself->r; |
| return 1; |
| } |
| return 0; |
| } |
| mutable copybit_rect_t r; |
| mutable bool firstTime; |
| }; |
| |
| static bool supportedCopybitsFormat(int format) { |
| switch (format) { |
| case COPYBIT_FORMAT_RGBA_8888: |
| case COPYBIT_FORMAT_RGB_565: |
| case COPYBIT_FORMAT_BGRA_8888: |
| case COPYBIT_FORMAT_RGBA_5551: |
| case COPYBIT_FORMAT_RGBA_4444: |
| case COPYBIT_FORMAT_YCbCr_422_SP: |
| case COPYBIT_FORMAT_YCbCr_420_SP: |
| return true; |
| default: |
| return false; |
| } |
| } |
| |
| static bool hasAlpha(int format) { |
| switch (format) { |
| case COPYBIT_FORMAT_RGBA_8888: |
| case COPYBIT_FORMAT_BGRA_8888: |
| case COPYBIT_FORMAT_RGBA_5551: |
| case COPYBIT_FORMAT_RGBA_4444: |
| return true; |
| default: |
| return false; |
| } |
| } |
| |
| static inline int fixedToByte(GGLfixed val) { |
| return (val - (val >> 8)) >> 8; |
| } |
| |
| /** |
| * Performs a quick check of the rendering state. If this function returns |
| * false we cannot use the copybit driver. |
| */ |
| |
| static bool checkContext(ogles_context_t* c) { |
| |
| // By convenction copybitQuickCheckContext() has already returned true. |
| // avoid checking the same information again. |
| |
| if (c->copybits.blitEngine == NULL |
| || (c->rasterizer.state.enables |
| & (GGL_ENABLE_DEPTH_TEST|GGL_ENABLE_FOG)) != 0) { |
| return false; |
| } |
| |
| // Note: The drawSurfaceFd is only set for destination |
| // surfaces types that are supported by the hardware and |
| // do not have an alpha channel. So we don't have to re-check that here. |
| |
| static const int tmu = 0; |
| texture_unit_t& u(c->textures.tmu[tmu]); |
| EGLTextureObject* textureObject = u.texture; |
| |
| if (!supportedCopybitsFormat(textureObject->surface.format)) { |
| return false; |
| } |
| return true; |
| } |
| |
| |
| static bool copybit(GLint x, GLint y, |
| GLint w, GLint h, |
| EGLTextureObject* textureObject, |
| const GLint* crop_rect, |
| int transform, |
| ogles_context_t* c) |
| { |
| // We assume checkContext has already been called and has already |
| // returned true. |
| |
| const GGLSurface& cbSurface = c->rasterizer.state.buffers.color.s; |
| |
| y = cbSurface.height - (y + h); |
| |
| const GLint Ucr = crop_rect[0]; |
| const GLint Vcr = crop_rect[1]; |
| const GLint Wcr = crop_rect[2]; |
| const GLint Hcr = crop_rect[3]; |
| |
| int32_t dsdx = (Wcr << 16) / w; // dsdx = ((Wcr/w)/Wt)*Wt |
| int32_t dtdy = ((-Hcr) << 16) / h; // dtdy = -((Hcr/h)/Ht)*Ht |
| |
| if (dsdx < c->copybits.minScale || dsdx > c->copybits.maxScale |
| || dtdy < c->copybits.minScale || dtdy > c->copybits.maxScale) { |
| // The requested scale is out of the range the hardware |
| // can support. |
| return false; |
| } |
| |
| int32_t texelArea = gglMulx(dtdy, dsdx); |
| if (texelArea < FIXED_ONE && textureObject->mag_filter != GL_LINEAR) { |
| // Non-linear filtering on a texture enlargement. |
| return false; |
| } |
| |
| if (texelArea > FIXED_ONE && textureObject->min_filter != GL_LINEAR) { |
| // Non-linear filtering on an texture shrink. |
| return false; |
| } |
| |
| const uint32_t enables = c->rasterizer.state.enables; |
| int planeAlpha = 255; |
| static const int tmu = 0; |
| texture_t& tev(c->rasterizer.state.texture[tmu]); |
| bool srcTextureHasAlpha = hasAlpha(textureObject->surface.format); |
| switch (tev.env) { |
| |
| case GGL_REPLACE: |
| if (!srcTextureHasAlpha) { |
| planeAlpha = fixedToByte(c->currentColorClamped.a); |
| } |
| break; |
| |
| case GGL_MODULATE: |
| if (! (c->currentColorClamped.r == FIXED_ONE |
| && c->currentColorClamped.g == FIXED_ONE |
| && c->currentColorClamped.b == FIXED_ONE)) { |
| return false; |
| } |
| planeAlpha = fixedToByte(c->currentColorClamped.a); |
| break; |
| |
| default: |
| // Incompatible texture environment. |
| return false; |
| } |
| |
| bool blending = false; |
| |
| if ((enables & GGL_ENABLE_BLENDING) |
| && !(c->rasterizer.state.blend.src == GL_ONE |
| && c->rasterizer.state.blend.dst == GL_ZERO)) { |
| // Blending is OK if it is |
| // the exact kind of blending that the copybits hardware supports. |
| // Note: The hardware only supports |
| // GL_SRC_ALPHA / GL_ONE_MINUS_SRC_ALPHA, |
| // But the surface flinger uses GL_ONE / GL_ONE_MINUS_SRC_ALPHA. |
| // We substitute GL_SRC_ALPHA / GL_ONE_MINUS_SRC_ALPHA in that case, |
| // because the performance is worth it, even if the results are |
| // not correct. |
| if (!((c->rasterizer.state.blend.src == GL_SRC_ALPHA |
| || c->rasterizer.state.blend.src == GL_ONE) |
| && c->rasterizer.state.blend.dst == GL_ONE_MINUS_SRC_ALPHA |
| && c->rasterizer.state.blend.alpha_separate == 0)) { |
| // Incompatible blend mode. |
| return false; |
| } |
| blending = true; |
| } else { |
| // No blending is OK if we are not using alpha. |
| if (srcTextureHasAlpha || planeAlpha != 255) { |
| // Incompatible alpha |
| return false; |
| } |
| } |
| |
| if (srcTextureHasAlpha && planeAlpha != 255) { |
| // Can't do two types of alpha at once. |
| return false; |
| } |
| |
| // LOGW("calling copybits"); |
| |
| copybit_device_t* copybit = c->copybits.blitEngine; |
| copybit_image_t dst; |
| textureToCopyBitImage(&cbSurface, c->copybits.drawSurfaceFd, &dst); |
| copybit_rect_t drect = {x, y, x+w, y+h}; |
| |
| copybit_image_t src; |
| textureToCopyBitImage(&textureObject->surface, textureObject->copybits_fd, |
| &src); |
| copybit_rect_t srect = { Ucr, Vcr + Hcr, Ucr + Wcr, Vcr }; |
| |
| copybit->set_parameter(copybit, COPYBIT_TRANSFORM, transform); |
| copybit->set_parameter(copybit, COPYBIT_PLANE_ALPHA, planeAlpha); |
| |
| copybit->set_parameter(copybit, COPYBIT_DITHER, |
| (enables & GGL_ENABLE_DITHER) ? COPYBIT_ENABLE : COPYBIT_DISABLE); |
| |
| clipRectRegion it(c); |
| copybit->stretch(copybit, &dst, &src, &drect, &srect, &it); |
| return true; |
| } |
| |
| /* |
| * Try to draw a triangle fan with copybit, return false if we fail. |
| */ |
| bool drawTrangleFanWithCopybit_impl(ogles_context_t* c, GLint first, GLsizei count) { |
| if (! checkContext(c)) { |
| return false; |
| } |
| |
| c->arrays.compileElements(c, c->vc.vBuffer, 0, 4); |
| // Is the result a screen aligned rectangle? |
| int sx[4]; |
| int sy[4]; |
| for (int i = 0; i < 4; i++) { |
| GLfixed x = c->vc.vBuffer[i].window.x; |
| GLfixed y = c->vc.vBuffer[i].window.y; |
| if (x < 0 || y < 0 || (x & 0xf) != 0 || (y & 0xf) != 0) { |
| return false; |
| } |
| sx[i] = x >> 4; |
| sy[i] = y >> 4; |
| } |
| |
| /* |
| * This is the pattern we're looking for: |
| * (2)--(3) |
| * |\ | |
| * | \ | |
| * | \ | |
| * | \| |
| * (1)--(0) |
| * |
| */ |
| int dx[4]; |
| int dy[4]; |
| for (int i = 0; i < 4; i++) { |
| int i1 = (i + 1) & 3; |
| dx[i] = sx[i] - sx[i1]; |
| dy[i] = sy[i] - sy[i1]; |
| } |
| if (dx[1] | dx[3] | dy[0] | dy[2]) { |
| return false; |
| } |
| if (dx[0] != -dx[2] || dy[1] != -dy[3]) { |
| return false; |
| } |
| |
| int x = sx[1]; |
| int y = sy[1]; |
| int w = dx[0]; |
| int h = dy[3]; |
| |
| // We expect the texture coordinates to always be the unit square: |
| |
| static const GLfixed kExpectedUV[8] = { |
| 0, 0, |
| 0, FIXED_ONE, |
| FIXED_ONE, FIXED_ONE, |
| FIXED_ONE, 0 |
| }; |
| { |
| const GLfixed* pExpected = &kExpectedUV[0]; |
| for (int i = 0; i < 4; i++) { |
| GLfixed u = c->vc.vBuffer[i].texture[0].x; |
| GLfixed v = c->vc.vBuffer[i].texture[0].y; |
| if (u != *pExpected++ || v != *pExpected++) { |
| return false; |
| } |
| } |
| } |
| |
| static const int tmu = 0; |
| texture_unit_t& u(c->textures.tmu[tmu]); |
| EGLTextureObject* textureObject = u.texture; |
| |
| GLint tWidth = textureObject->surface.width; |
| GLint tHeight = textureObject->surface.height; |
| GLint crop_rect[4] = {0, tHeight, tWidth, -tHeight}; |
| |
| const GGLSurface& cbSurface = c->rasterizer.state.buffers.color.s; |
| y = cbSurface.height - (y + h); |
| |
| return copybit(x, y, w, h, textureObject, crop_rect, |
| COPYBIT_TRANSFORM_ROT_90, c); |
| } |
| |
| /* |
| * Try to drawTexiOESWithCopybit, return false if we fail. |
| */ |
| |
| bool drawTexiOESWithCopybit_impl(GLint x, GLint y, GLint z, |
| GLint w, GLint h, ogles_context_t* c) |
| { |
| // quickly process empty rects |
| if ((w|h) <= 0) { |
| return true; |
| } |
| |
| if (! checkContext(c)) { |
| return false; |
| } |
| |
| static const int tmu = 0; |
| texture_unit_t& u(c->textures.tmu[tmu]); |
| EGLTextureObject* textureObject = u.texture; |
| |
| return copybit(x, y, w, h, textureObject, textureObject->crop_rect, |
| 0, c); |
| } |
| |
| } // namespace android |
| |