diff options
Diffstat (limited to 'opengl/libagl/copybit.cpp')
-rw-r--r-- | opengl/libagl/copybit.cpp | 370 |
1 files changed, 370 insertions, 0 deletions
diff --git a/opengl/libagl/copybit.cpp b/opengl/libagl/copybit.cpp new file mode 100644 index 0000000..427e42a --- /dev/null +++ b/opengl/libagl/copybit.cpp @@ -0,0 +1,370 @@ +/* +** +** 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 + |