diff options
Diffstat (limited to 'libs/hwui/renderstate')
| -rw-r--r-- | libs/hwui/renderstate/Blend.cpp | 136 | ||||
| -rw-r--r-- | libs/hwui/renderstate/Blend.h | 58 | ||||
| -rw-r--r-- | libs/hwui/renderstate/MeshState.cpp | 175 | ||||
| -rw-r--r-- | libs/hwui/renderstate/MeshState.h | 144 | ||||
| -rw-r--r-- | libs/hwui/renderstate/PixelBufferState.cpp | 45 | ||||
| -rw-r--r-- | libs/hwui/renderstate/PixelBufferState.h | 37 | ||||
| -rw-r--r-- | libs/hwui/renderstate/RenderState.cpp | 354 | ||||
| -rw-r--r-- | libs/hwui/renderstate/RenderState.h | 130 | ||||
| -rw-r--r-- | libs/hwui/renderstate/Scissor.cpp | 91 | ||||
| -rw-r--r-- | libs/hwui/renderstate/Scissor.h | 46 | ||||
| -rw-r--r-- | libs/hwui/renderstate/Stencil.cpp | 133 | ||||
| -rw-r--r-- | libs/hwui/renderstate/Stencil.h | 123 | ||||
| -rw-r--r-- | libs/hwui/renderstate/TextureState.cpp | 103 | ||||
| -rw-r--r-- | libs/hwui/renderstate/TextureState.h | 88 |
14 files changed, 1663 insertions, 0 deletions
diff --git a/libs/hwui/renderstate/Blend.cpp b/libs/hwui/renderstate/Blend.cpp new file mode 100644 index 0000000..29927ed --- /dev/null +++ b/libs/hwui/renderstate/Blend.cpp @@ -0,0 +1,136 @@ +/* + * Copyright (C) 2015 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 <renderstate/Blend.h> +#include "Program.h" + +#include "ShadowTessellator.h" + +namespace android { +namespace uirenderer { + +/** + * Structure mapping Skia xfermodes to OpenGL blending factors. + */ +struct Blender { + SkXfermode::Mode mode; + GLenum src; + GLenum dst; +}; + +// In this array, the index of each Blender equals the value of the first +// entry. For instance, gBlends[1] == gBlends[SkXfermode::kSrc_Mode] +const Blender kBlends[] = { + { SkXfermode::kClear_Mode, GL_ZERO, GL_ONE_MINUS_SRC_ALPHA }, + { SkXfermode::kSrc_Mode, GL_ONE, GL_ZERO }, + { SkXfermode::kDst_Mode, GL_ZERO, GL_ONE }, + { SkXfermode::kSrcOver_Mode, GL_ONE, GL_ONE_MINUS_SRC_ALPHA }, + { SkXfermode::kDstOver_Mode, GL_ONE_MINUS_DST_ALPHA, GL_ONE }, + { SkXfermode::kSrcIn_Mode, GL_DST_ALPHA, GL_ZERO }, + { SkXfermode::kDstIn_Mode, GL_ZERO, GL_SRC_ALPHA }, + { SkXfermode::kSrcOut_Mode, GL_ONE_MINUS_DST_ALPHA, GL_ZERO }, + { SkXfermode::kDstOut_Mode, GL_ZERO, GL_ONE_MINUS_SRC_ALPHA }, + { SkXfermode::kSrcATop_Mode, GL_DST_ALPHA, GL_ONE_MINUS_SRC_ALPHA }, + { SkXfermode::kDstATop_Mode, GL_ONE_MINUS_DST_ALPHA, GL_SRC_ALPHA }, + { SkXfermode::kXor_Mode, GL_ONE_MINUS_DST_ALPHA, GL_ONE_MINUS_SRC_ALPHA }, + { SkXfermode::kPlus_Mode, GL_ONE, GL_ONE }, + { SkXfermode::kModulate_Mode, GL_ZERO, GL_SRC_COLOR }, + { SkXfermode::kScreen_Mode, GL_ONE, GL_ONE_MINUS_SRC_COLOR } +}; + +// This array contains the swapped version of each SkXfermode. For instance +// this array's SrcOver blending mode is actually DstOver. You can refer to +// createLayer() for more information on the purpose of this array. +const Blender kBlendsSwap[] = { + { SkXfermode::kClear_Mode, GL_ONE_MINUS_DST_ALPHA, GL_ZERO }, + { SkXfermode::kSrc_Mode, GL_ZERO, GL_ONE }, + { SkXfermode::kDst_Mode, GL_ONE, GL_ZERO }, + { SkXfermode::kSrcOver_Mode, GL_ONE_MINUS_DST_ALPHA, GL_ONE }, + { SkXfermode::kDstOver_Mode, GL_ONE, GL_ONE_MINUS_SRC_ALPHA }, + { SkXfermode::kSrcIn_Mode, GL_ZERO, GL_SRC_ALPHA }, + { SkXfermode::kDstIn_Mode, GL_DST_ALPHA, GL_ZERO }, + { SkXfermode::kSrcOut_Mode, GL_ZERO, GL_ONE_MINUS_SRC_ALPHA }, + { SkXfermode::kDstOut_Mode, GL_ONE_MINUS_DST_ALPHA, GL_ZERO }, + { SkXfermode::kSrcATop_Mode, GL_ONE_MINUS_DST_ALPHA, GL_SRC_ALPHA }, + { SkXfermode::kDstATop_Mode, GL_DST_ALPHA, GL_ONE_MINUS_SRC_ALPHA }, + { SkXfermode::kXor_Mode, GL_ONE_MINUS_DST_ALPHA, GL_ONE_MINUS_SRC_ALPHA }, + { SkXfermode::kPlus_Mode, GL_ONE, GL_ONE }, + { SkXfermode::kModulate_Mode, GL_DST_COLOR, GL_ZERO }, + { SkXfermode::kScreen_Mode, GL_ONE_MINUS_DST_COLOR, GL_ONE } +}; + +Blend::Blend() + : mEnabled(false) + , mSrcMode(GL_ZERO) + , mDstMode(GL_ZERO) { + // gl blending off by default +} + +void Blend::enable(SkXfermode::Mode mode, ModeOrderSwap modeUsage) { + GLenum srcMode; + GLenum dstMode; + getFactors(mode, modeUsage, &srcMode, &dstMode); + setFactors(srcMode, dstMode); +} + +void Blend::disable() { + if (mEnabled) { + glDisable(GL_BLEND); + mEnabled = false; + } +} + +void Blend::invalidate() { + syncEnabled(); + mSrcMode = mDstMode = GL_ZERO; +} + +void Blend::syncEnabled() { + if (mEnabled) { + glEnable(GL_BLEND); + } else { + glDisable(GL_BLEND); + } +} + +void Blend::getFactors(SkXfermode::Mode mode, ModeOrderSwap modeUsage, GLenum* outSrc, GLenum* outDst) { + *outSrc = (modeUsage == ModeOrderSwap::Swap) ? kBlendsSwap[mode].src : kBlends[mode].src; + *outDst = (modeUsage == ModeOrderSwap::Swap) ? kBlendsSwap[mode].dst : kBlends[mode].dst; +} + +void Blend::setFactors(GLenum srcMode, GLenum dstMode) { + if (srcMode == GL_ZERO && dstMode == GL_ZERO) { + disable(); + } else { + if (!mEnabled) { + glEnable(GL_BLEND); + mEnabled = true; + } + + if (srcMode != mSrcMode || dstMode != mDstMode) { + glBlendFunc(srcMode, dstMode); + mSrcMode = srcMode; + mDstMode = dstMode; + } + } +} + +void Blend::dump() { + ALOGD("Blend: enabled %d, func src %d, dst %d", mEnabled, mSrcMode, mDstMode); +} + +} /* namespace uirenderer */ +} /* namespace android */ + diff --git a/libs/hwui/renderstate/Blend.h b/libs/hwui/renderstate/Blend.h new file mode 100644 index 0000000..dcc681d --- /dev/null +++ b/libs/hwui/renderstate/Blend.h @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2015 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. + */ +#ifndef RENDERSTATE_BLEND_H +#define RENDERSTATE_BLEND_H + +#include "Vertex.h" + +#include <GLES2/gl2.h> +#include <GLES2/gl2ext.h> +#include <SkXfermode.h> +#include <memory> + +namespace android { +namespace uirenderer { + +class Blend { + friend class RenderState; +public: + // dictates whether to swap src/dst + enum class ModeOrderSwap { + NoSwap, + Swap, + }; + + void enable(SkXfermode::Mode mode, ModeOrderSwap modeUsage); + void disable(); + void syncEnabled(); + + static void getFactors(SkXfermode::Mode mode, ModeOrderSwap modeUsage, + GLenum* outSrc, GLenum* outDst); + void setFactors(GLenum src, GLenum dst); + + void dump(); +private: + Blend(); + void invalidate(); + bool mEnabled; + GLenum mSrcMode; + GLenum mDstMode; +}; + +} /* namespace uirenderer */ +} /* namespace android */ + +#endif // RENDERSTATE_BLEND_H diff --git a/libs/hwui/renderstate/MeshState.cpp b/libs/hwui/renderstate/MeshState.cpp new file mode 100644 index 0000000..0521f65 --- /dev/null +++ b/libs/hwui/renderstate/MeshState.cpp @@ -0,0 +1,175 @@ +/* + * Copyright (C) 2015 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 "renderstate/MeshState.h" + +#include "Program.h" + +#include "ShadowTessellator.h" + +namespace android { +namespace uirenderer { + +MeshState::MeshState() + : mCurrentIndicesBuffer(0) + , mCurrentPixelBuffer(0) + , mCurrentPositionPointer(this) + , mCurrentPositionStride(0) + , mCurrentTexCoordsPointer(this) + , mCurrentTexCoordsStride(0) + , mTexCoordsArrayEnabled(false) + , mQuadListIndices(0) { + glGenBuffers(1, &mUnitQuadBuffer); + glBindBuffer(GL_ARRAY_BUFFER, mUnitQuadBuffer); + glBufferData(GL_ARRAY_BUFFER, sizeof(kUnitQuadVertices), kUnitQuadVertices, GL_STATIC_DRAW); + + mCurrentBuffer = mUnitQuadBuffer; + + uint16_t regionIndices[kMaxNumberOfQuads * 6]; + for (uint32_t i = 0; i < kMaxNumberOfQuads; i++) { + uint16_t quad = i * 4; + int index = i * 6; + regionIndices[index ] = quad; // top-left + regionIndices[index + 1] = quad + 1; // top-right + regionIndices[index + 2] = quad + 2; // bottom-left + regionIndices[index + 3] = quad + 2; // bottom-left + regionIndices[index + 4] = quad + 1; // top-right + regionIndices[index + 5] = quad + 3; // bottom-right + } + glGenBuffers(1, &mQuadListIndices); + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mQuadListIndices); + glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(regionIndices), regionIndices, GL_STATIC_DRAW); + mCurrentIndicesBuffer = mQuadListIndices; + + // position attribute always enabled + glEnableVertexAttribArray(Program::kBindingPosition); +} + +MeshState::~MeshState() { + glDeleteBuffers(1, &mUnitQuadBuffer); + mCurrentBuffer = 0; + + glDeleteBuffers(1, &mQuadListIndices); + mQuadListIndices = 0; +} + +void MeshState::dump() { + ALOGD("MeshState VBOs: unitQuad %d, current %d", mUnitQuadBuffer, mCurrentBuffer); + ALOGD("MeshState IBOs: quadList %d, current %d", mQuadListIndices, mCurrentIndicesBuffer); + ALOGD("MeshState vertices: vertex data %p, stride %d", + mCurrentPositionPointer, mCurrentPositionStride); + ALOGD("MeshState texCoord: data %p, stride %d", + mCurrentTexCoordsPointer, mCurrentTexCoordsStride); +} + +/////////////////////////////////////////////////////////////////////////////// +// Buffer Objects +/////////////////////////////////////////////////////////////////////////////// + +bool MeshState::bindMeshBuffer() { + return bindMeshBuffer(mUnitQuadBuffer); +} + +bool MeshState::bindMeshBuffer(GLuint buffer) { + if (!buffer) buffer = mUnitQuadBuffer; + return bindMeshBufferInternal(buffer); +} + +bool MeshState::unbindMeshBuffer() { + return bindMeshBufferInternal(0); +} + +bool MeshState::bindMeshBufferInternal(GLuint buffer) { + if (mCurrentBuffer != buffer) { + glBindBuffer(GL_ARRAY_BUFFER, buffer); + mCurrentBuffer = buffer; + return true; + } + return false; +} + +/////////////////////////////////////////////////////////////////////////////// +// Vertices +/////////////////////////////////////////////////////////////////////////////// + +void MeshState::bindPositionVertexPointer(bool force, const GLvoid* vertices, GLsizei stride) { + if (force || vertices != mCurrentPositionPointer || stride != mCurrentPositionStride) { + glVertexAttribPointer(Program::kBindingPosition, 2, GL_FLOAT, GL_FALSE, stride, vertices); + mCurrentPositionPointer = vertices; + mCurrentPositionStride = stride; + } +} + +void MeshState::bindTexCoordsVertexPointer(bool force, const GLvoid* vertices, GLsizei stride) { + if (force || vertices != mCurrentTexCoordsPointer || stride != mCurrentTexCoordsStride) { + glVertexAttribPointer(Program::kBindingTexCoords, 2, GL_FLOAT, GL_FALSE, stride, vertices); + mCurrentTexCoordsPointer = vertices; + mCurrentTexCoordsStride = stride; + } +} + +void MeshState::resetVertexPointers() { + mCurrentPositionPointer = this; + mCurrentTexCoordsPointer = this; +} + +void MeshState::resetTexCoordsVertexPointer() { + mCurrentTexCoordsPointer = this; +} + +void MeshState::enableTexCoordsVertexArray() { + if (!mTexCoordsArrayEnabled) { + glEnableVertexAttribArray(Program::kBindingTexCoords); + mCurrentTexCoordsPointer = this; + mTexCoordsArrayEnabled = true; + } +} + +void MeshState::disableTexCoordsVertexArray() { + if (mTexCoordsArrayEnabled) { + glDisableVertexAttribArray(Program::kBindingTexCoords); + mTexCoordsArrayEnabled = false; + } +} + +/////////////////////////////////////////////////////////////////////////////// +// Indices +/////////////////////////////////////////////////////////////////////////////// + +bool MeshState::bindIndicesBufferInternal(const GLuint buffer) { + if (mCurrentIndicesBuffer != buffer) { + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, buffer); + mCurrentIndicesBuffer = buffer; + return true; + } + return false; +} + +bool MeshState::bindQuadIndicesBuffer() { + return bindIndicesBufferInternal(mQuadListIndices); +} + +bool MeshState::unbindIndicesBuffer() { + if (mCurrentIndicesBuffer) { + glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, 0); + mCurrentIndicesBuffer = 0; + return true; + } + return false; +} + +} /* namespace uirenderer */ +} /* namespace android */ + diff --git a/libs/hwui/renderstate/MeshState.h b/libs/hwui/renderstate/MeshState.h new file mode 100644 index 0000000..e80f4d0 --- /dev/null +++ b/libs/hwui/renderstate/MeshState.h @@ -0,0 +1,144 @@ +/* + * Copyright (C) 2015 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. + */ +#ifndef RENDERSTATE_MESHSTATE_H +#define RENDERSTATE_MESHSTATE_H + +#include "Vertex.h" + +#include <GLES2/gl2.h> +#include <GLES2/gl2ext.h> +#include <memory> + +namespace android { +namespace uirenderer { + +class Program; + +// Maximum number of quads that pre-allocated meshes can draw +const uint32_t kMaxNumberOfQuads = 2048; + +// This array is never used directly but used as a memcpy source in the +// OpenGLRenderer constructor +const TextureVertex kUnitQuadVertices[] = { + { 0, 0, 0, 0 }, + { 1, 0, 1, 0 }, + { 0, 1, 0, 1 }, + { 1, 1, 1, 1 }, +}; + +const GLsizei kVertexStride = sizeof(Vertex); +const GLsizei kAlphaVertexStride = sizeof(AlphaVertex); +const GLsizei kTextureVertexStride = sizeof(TextureVertex); +const GLsizei kColorTextureVertexStride = sizeof(ColorTextureVertex); + +const GLsizei kMeshTextureOffset = 2 * sizeof(float); +const GLsizei kVertexAlphaOffset = 2 * sizeof(float); +const GLsizei kVertexAAWidthOffset = 2 * sizeof(float); +const GLsizei kVertexAALengthOffset = 3 * sizeof(float); +const GLsizei kUnitQuadCount = 4; + +class MeshState { +private: + friend class RenderState; + +public: + ~MeshState(); + void dump(); + /////////////////////////////////////////////////////////////////////////////// + // Buffer objects + /////////////////////////////////////////////////////////////////////////////// + /** + * Binds the VBO used to render simple textured quads. + */ + bool bindMeshBuffer(); + + /** + * Binds the specified VBO if needed. If buffer == 0, binds default simple textured quad. + */ + bool bindMeshBuffer(GLuint buffer); + + /** + * Unbinds the VBO used to render simple textured quads. + */ + bool unbindMeshBuffer(); + + /////////////////////////////////////////////////////////////////////////////// + // Vertices + /////////////////////////////////////////////////////////////////////////////// + /** + * Binds an attrib to the specified float vertex pointer. + * Assumes a stride of gTextureVertexStride and a size of 2. + */ + void bindPositionVertexPointer(bool force, const GLvoid* vertices, + GLsizei stride = kTextureVertexStride); + + /** + * Binds an attrib to the specified float vertex pointer. + * Assumes a stride of gTextureVertexStride and a size of 2. + */ + void bindTexCoordsVertexPointer(bool force, const GLvoid* vertices, + GLsizei stride = kTextureVertexStride); + + /** + * Resets the vertex pointers. + */ + void resetVertexPointers(); + void resetTexCoordsVertexPointer(); + + void enableTexCoordsVertexArray(); + void disableTexCoordsVertexArray(); + + /////////////////////////////////////////////////////////////////////////////// + // Indices + /////////////////////////////////////////////////////////////////////////////// + /** + * Binds a global indices buffer that can draw up to + * gMaxNumberOfQuads quads. + */ + bool bindQuadIndicesBuffer(); + bool unbindIndicesBuffer(); + + /////////////////////////////////////////////////////////////////////////////// + // Getters - for use in Glop building + /////////////////////////////////////////////////////////////////////////////// + GLuint getUnitQuadVBO() { return mUnitQuadBuffer; } + GLuint getQuadListIBO() { return mQuadListIndices; } +private: + MeshState(); + bool bindMeshBufferInternal(const GLuint buffer); + bool bindIndicesBufferInternal(const GLuint buffer); + + GLuint mUnitQuadBuffer; + + GLuint mCurrentBuffer; + GLuint mCurrentIndicesBuffer; + GLuint mCurrentPixelBuffer; + + const void* mCurrentPositionPointer; + GLsizei mCurrentPositionStride; + const void* mCurrentTexCoordsPointer; + GLsizei mCurrentTexCoordsStride; + + bool mTexCoordsArrayEnabled; + + // Global index buffer + GLuint mQuadListIndices; +}; + +} /* namespace uirenderer */ +} /* namespace android */ + +#endif // RENDERSTATE_MESHSTATE_H diff --git a/libs/hwui/renderstate/PixelBufferState.cpp b/libs/hwui/renderstate/PixelBufferState.cpp new file mode 100644 index 0000000..c23af52 --- /dev/null +++ b/libs/hwui/renderstate/PixelBufferState.cpp @@ -0,0 +1,45 @@ +/* + * Copyright (C) 2015 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 "renderstate/PixelBufferState.h" + +namespace android { +namespace uirenderer { + +PixelBufferState::PixelBufferState() + : mCurrentPixelBuffer(0) { +} + +bool PixelBufferState::bind(GLuint buffer) { + if (mCurrentPixelBuffer != buffer) { + glBindBuffer(GL_PIXEL_UNPACK_BUFFER, buffer); + mCurrentPixelBuffer = buffer; + return true; + } + return false; +} + +bool PixelBufferState::unbind() { + if (mCurrentPixelBuffer) { + glBindBuffer(GL_PIXEL_UNPACK_BUFFER, 0); + mCurrentPixelBuffer = 0; + return true; + } + return false; +} + +} /* namespace uirenderer */ +} /* namespace android */ + diff --git a/libs/hwui/renderstate/PixelBufferState.h b/libs/hwui/renderstate/PixelBufferState.h new file mode 100644 index 0000000..8dab21d --- /dev/null +++ b/libs/hwui/renderstate/PixelBufferState.h @@ -0,0 +1,37 @@ +/* + * Copyright (C) 2015 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. + */ +#ifndef RENDERSTATE_PIXELBUFFERSTATE_H +#define RENDERSTATE_PIXELBUFFERSTATE_H + +#include <GLES3/gl3.h> + +namespace android { +namespace uirenderer { + +class PixelBufferState { + friend class Caches; // TODO: move to RenderState +public: + bool bind(GLuint buffer); + bool unbind(); +private: + PixelBufferState(); + GLuint mCurrentPixelBuffer; +}; + +} /* namespace uirenderer */ +} /* namespace android */ + +#endif // RENDERSTATE_PIXELBUFFERSTATE_H diff --git a/libs/hwui/renderstate/RenderState.cpp b/libs/hwui/renderstate/RenderState.cpp new file mode 100644 index 0000000..7b44d6d --- /dev/null +++ b/libs/hwui/renderstate/RenderState.cpp @@ -0,0 +1,354 @@ +/* + * Copyright (C) 2014 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 "renderstate/RenderState.h" + +#include "renderthread/CanvasContext.h" +#include "renderthread/EglManager.h" +#include "utils/GLUtils.h" + +namespace android { +namespace uirenderer { + +RenderState::RenderState(renderthread::RenderThread& thread) + : mRenderThread(thread) + , mViewportWidth(0) + , mViewportHeight(0) + , mFramebuffer(0) { + mThreadId = pthread_self(); +} + +RenderState::~RenderState() { + LOG_ALWAYS_FATAL_IF(mBlend || mMeshState || mScissor || mStencil, + "State object lifecycle not managed correctly"); +} + +void RenderState::onGLContextCreated() { + LOG_ALWAYS_FATAL_IF(mBlend || mMeshState || mScissor || mStencil, + "State object lifecycle not managed correctly"); + mBlend = new Blend(); + mMeshState = new MeshState(); + mScissor = new Scissor(); + mStencil = new Stencil(); + + // This is delayed because the first access of Caches makes GL calls + if (!mCaches) { + mCaches = &Caches::createInstance(*this); + } + mCaches->init(); + mCaches->textureCache.setAssetAtlas(&mAssetAtlas); +} + +static void layerLostGlContext(Layer* layer) { + layer->onGlContextLost(); +} + +void RenderState::onGLContextDestroyed() { +/* + size_t size = mActiveLayers.size(); + if (CC_UNLIKELY(size != 0)) { + ALOGE("Crashing, have %d contexts and %d layers at context destruction. isempty %d", + mRegisteredContexts.size(), size, mActiveLayers.empty()); + mCaches->dumpMemoryUsage(); + for (std::set<renderthread::CanvasContext*>::iterator cit = mRegisteredContexts.begin(); + cit != mRegisteredContexts.end(); cit++) { + renderthread::CanvasContext* context = *cit; + ALOGE("Context: %p (root = %p)", context, context->mRootRenderNode.get()); + ALOGE(" Prefeteched layers: %zu", context->mPrefetechedLayers.size()); + for (std::set<RenderNode*>::iterator pit = context->mPrefetechedLayers.begin(); + pit != context->mPrefetechedLayers.end(); pit++) { + (*pit)->debugDumpLayers(" "); + } + context->mRootRenderNode->debugDumpLayers(" "); + } + + + if (mActiveLayers.begin() == mActiveLayers.end()) { + ALOGE("set has become empty. wat."); + } + for (std::set<const Layer*>::iterator lit = mActiveLayers.begin(); + lit != mActiveLayers.end(); lit++) { + const Layer* layer = *(lit); + ALOGE("Layer %p, state %d, texlayer %d, fbo %d, buildlayered %d", + layer, layer->state, layer->isTextureLayer(), layer->getFbo(), layer->wasBuildLayered); + } + LOG_ALWAYS_FATAL("%d layers have survived gl context destruction", size); + } +*/ + + // TODO: reset all cached state in state objects + std::for_each(mActiveLayers.begin(), mActiveLayers.end(), layerLostGlContext); + mAssetAtlas.terminate(); + + mCaches->terminate(); + + delete mBlend; + mBlend = nullptr; + delete mMeshState; + mMeshState = nullptr; + delete mScissor; + mScissor = nullptr; + delete mStencil; + mStencil = nullptr; +} + +void RenderState::setViewport(GLsizei width, GLsizei height) { + mViewportWidth = width; + mViewportHeight = height; + glViewport(0, 0, mViewportWidth, mViewportHeight); +} + + +void RenderState::getViewport(GLsizei* outWidth, GLsizei* outHeight) { + *outWidth = mViewportWidth; + *outHeight = mViewportHeight; +} + +void RenderState::bindFramebuffer(GLuint fbo) { + if (mFramebuffer != fbo) { + mFramebuffer = fbo; + glBindFramebuffer(GL_FRAMEBUFFER, mFramebuffer); + } +} + +void RenderState::invokeFunctor(Functor* functor, DrawGlInfo::Mode mode, DrawGlInfo* info) { + interruptForFunctorInvoke(); + (*functor)(mode, info); + resumeFromFunctorInvoke(); +} + +void RenderState::interruptForFunctorInvoke() { + mCaches->setProgram(nullptr); + mCaches->textureState().resetActiveTexture(); + meshState().unbindMeshBuffer(); + meshState().unbindIndicesBuffer(); + meshState().resetVertexPointers(); + meshState().disableTexCoordsVertexArray(); + debugOverdraw(false, false); +} + +void RenderState::resumeFromFunctorInvoke() { + glViewport(0, 0, mViewportWidth, mViewportHeight); + glBindFramebuffer(GL_FRAMEBUFFER, mFramebuffer); + debugOverdraw(false, false); + + glClearColor(0.0f, 0.0f, 0.0f, 0.0f); + + scissor().invalidate(); + blend().invalidate(); + + mCaches->textureState().activateTexture(0); + mCaches->textureState().resetBoundTextures(); +} + +void RenderState::debugOverdraw(bool enable, bool clear) { + if (mCaches->debugOverdraw && mFramebuffer == 0) { + if (clear) { + scissor().setEnabled(false); + stencil().clear(); + } + if (enable) { + stencil().enableDebugWrite(); + } else { + stencil().disable(); + } + } +} + +void RenderState::requireGLContext() { + assertOnGLThread(); + mRenderThread.eglManager().requireGlContext(); +} + +void RenderState::assertOnGLThread() { + pthread_t curr = pthread_self(); + LOG_ALWAYS_FATAL_IF(!pthread_equal(mThreadId, curr), "Wrong thread!"); +} + +class DecStrongTask : public renderthread::RenderTask { +public: + DecStrongTask(VirtualLightRefBase* object) : mObject(object) {} + + virtual void run() override { + mObject->decStrong(nullptr); + mObject = nullptr; + delete this; + } + +private: + VirtualLightRefBase* mObject; +}; + +void RenderState::postDecStrong(VirtualLightRefBase* object) { + mRenderThread.queue(new DecStrongTask(object)); +} + +/////////////////////////////////////////////////////////////////////////////// +// Render +/////////////////////////////////////////////////////////////////////////////// + +void RenderState::render(const Glop& glop) { + const Glop::Mesh& mesh = glop.mesh; + const Glop::Mesh::Vertices& vertices = mesh.vertices; + const Glop::Mesh::Indices& indices = mesh.indices; + const Glop::Fill& fill = glop.fill; + + // --------------------------------------------- + // ---------- Program + uniform setup ---------- + // --------------------------------------------- + mCaches->setProgram(fill.program); + + if (fill.colorEnabled) { + fill.program->setColor(fill.color); + } + + fill.program->set(glop.transform.ortho, + glop.transform.modelView, + glop.transform.canvas, + glop.transform.fudgingOffset); + + // Color filter uniforms + if (fill.filterMode == ProgramDescription::kColorBlend) { + const FloatColor& color = fill.filter.color; + glUniform4f(mCaches->program().getUniform("colorBlend"), + color.r, color.g, color.b, color.a); + } else if (fill.filterMode == ProgramDescription::kColorMatrix) { + glUniformMatrix4fv(mCaches->program().getUniform("colorMatrix"), 1, GL_FALSE, + fill.filter.matrix.matrix); + glUniform4fv(mCaches->program().getUniform("colorMatrixVector"), 1, + fill.filter.matrix.vector); + } + + // Round rect clipping uniforms + if (glop.roundRectClipState) { + // TODO: avoid query, and cache values (or RRCS ptr) in program + const RoundRectClipState* state = glop.roundRectClipState; + const Rect& innerRect = state->innerRect; + glUniform4f(fill.program->getUniform("roundRectInnerRectLTRB"), + innerRect.left, innerRect.top, + innerRect.right, innerRect.bottom); + glUniformMatrix4fv(fill.program->getUniform("roundRectInvTransform"), + 1, GL_FALSE, &state->matrix.data[0]); + + // add half pixel to round out integer rect space to cover pixel centers + float roundedOutRadius = state->radius + 0.5f; + glUniform1f(fill.program->getUniform("roundRectRadius"), + roundedOutRadius); + } + + // -------------------------------- + // ---------- Mesh setup ---------- + // -------------------------------- + // vertices + const bool force = meshState().bindMeshBufferInternal(vertices.bufferObject) + || (vertices.position != nullptr); + meshState().bindPositionVertexPointer(force, vertices.position, vertices.stride); + + // indices + meshState().bindIndicesBufferInternal(indices.bufferObject); + + if (vertices.attribFlags & VertexAttribFlags::kTextureCoord) { + const Glop::Fill::TextureData& texture = fill.texture; + // texture always takes slot 0, shader samplers increment from there + mCaches->textureState().activateTexture(0); + + if (texture.clamp != GL_INVALID_ENUM) { + texture.texture->setWrap(texture.clamp, true); + } + if (texture.filter != GL_INVALID_ENUM) { + texture.texture->setFilter(texture.filter, true); + } + + mCaches->textureState().bindTexture(texture.target, texture.texture->id); + meshState().enableTexCoordsVertexArray(); + meshState().bindTexCoordsVertexPointer(force, vertices.texCoord, vertices.stride); + + if (texture.textureTransform) { + glUniformMatrix4fv(fill.program->getUniform("mainTextureTransform"), 1, + GL_FALSE, &texture.textureTransform->data[0]); + } + } else { + meshState().disableTexCoordsVertexArray(); + } + int colorLocation = -1; + if (vertices.attribFlags & VertexAttribFlags::kColor) { + colorLocation = fill.program->getAttrib("colors"); + glEnableVertexAttribArray(colorLocation); + glVertexAttribPointer(colorLocation, 4, GL_FLOAT, GL_FALSE, vertices.stride, vertices.color); + } + int alphaLocation = -1; + if (vertices.attribFlags & VertexAttribFlags::kAlpha) { + // NOTE: alpha vertex position is computed assuming no VBO + const void* alphaCoords = ((const GLbyte*) vertices.position) + kVertexAlphaOffset; + alphaLocation = fill.program->getAttrib("vtxAlpha"); + glEnableVertexAttribArray(alphaLocation); + glVertexAttribPointer(alphaLocation, 1, GL_FLOAT, GL_FALSE, vertices.stride, alphaCoords); + } + // Shader uniforms + SkiaShader::apply(*mCaches, fill.skiaShaderData); + + // ------------------------------------ + // ---------- GL state setup ---------- + // ------------------------------------ + blend().setFactors(glop.blend.src, glop.blend.dst); + + // ------------------------------------ + // ---------- Actual drawing ---------- + // ------------------------------------ + if (indices.bufferObject == meshState().getQuadListIBO()) { + // Since the indexed quad list is of limited length, we loop over + // the glDrawXXX method while updating the vertex pointer + GLsizei elementsCount = mesh.elementCount; + const GLbyte* vertexData = static_cast<const GLbyte*>(vertices.position); + while (elementsCount > 0) { + GLsizei drawCount = MathUtils::min(elementsCount, (GLsizei) kMaxNumberOfQuads * 6); + + // rebind pointers without forcing, since initial bind handled above + meshState().bindPositionVertexPointer(false, vertexData, vertices.stride); + if (vertices.attribFlags & VertexAttribFlags::kTextureCoord) { + meshState().bindTexCoordsVertexPointer(false, + vertexData + kMeshTextureOffset, vertices.stride); + } + + glDrawElements(mesh.primitiveMode, drawCount, GL_UNSIGNED_SHORT, nullptr); + elementsCount -= drawCount; + vertexData += (drawCount / 6) * 4 * vertices.stride; + } + } else if (indices.bufferObject || indices.indices) { + glDrawElements(mesh.primitiveMode, mesh.elementCount, GL_UNSIGNED_SHORT, indices.indices); + } else { + glDrawArrays(mesh.primitiveMode, 0, mesh.elementCount); + } + + // ----------------------------------- + // ---------- Mesh teardown ---------- + // ----------------------------------- + if (vertices.attribFlags & VertexAttribFlags::kAlpha) { + glDisableVertexAttribArray(alphaLocation); + } + if (vertices.attribFlags & VertexAttribFlags::kColor) { + glDisableVertexAttribArray(colorLocation); + } +} + +void RenderState::dump() { + blend().dump(); + meshState().dump(); + scissor().dump(); + stencil().dump(); +} + +} /* namespace uirenderer */ +} /* namespace android */ diff --git a/libs/hwui/renderstate/RenderState.h b/libs/hwui/renderstate/RenderState.h new file mode 100644 index 0000000..4fd792c --- /dev/null +++ b/libs/hwui/renderstate/RenderState.h @@ -0,0 +1,130 @@ +/* + * Copyright (C) 2014 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. + */ +#ifndef RENDERSTATE_H +#define RENDERSTATE_H + +#include <set> +#include <GLES2/gl2.h> +#include <GLES2/gl2ext.h> +#include <utils/Mutex.h> +#include <utils/Functor.h> +#include <utils/RefBase.h> +#include <private/hwui/DrawGlInfo.h> +#include <renderstate/Blend.h> + +#include "AssetAtlas.h" +#include "Caches.h" +#include "Glop.h" +#include "renderstate/MeshState.h" +#include "renderstate/PixelBufferState.h" +#include "renderstate/Scissor.h" +#include "renderstate/Stencil.h" +#include "utils/Macros.h" + +namespace android { +namespace uirenderer { + +class Caches; +class Layer; + +namespace renderthread { +class CanvasContext; +class RenderThread; +} + +// TODO: Replace Cache's GL state tracking with this. For now it's more a thin +// wrapper of Caches for users to migrate to. +class RenderState { + PREVENT_COPY_AND_ASSIGN(RenderState); +public: + void onGLContextCreated(); + void onGLContextDestroyed(); + + void setViewport(GLsizei width, GLsizei height); + void getViewport(GLsizei* outWidth, GLsizei* outHeight); + + void bindFramebuffer(GLuint fbo); + GLint getFramebuffer() { return mFramebuffer; } + + void invokeFunctor(Functor* functor, DrawGlInfo::Mode mode, DrawGlInfo* info); + + void debugOverdraw(bool enable, bool clear); + + void registerLayer(Layer* layer) { + mActiveLayers.insert(layer); + } + void unregisterLayer(Layer* layer) { + mActiveLayers.erase(layer); + } + + void registerCanvasContext(renderthread::CanvasContext* context) { + mRegisteredContexts.insert(context); + } + + void unregisterCanvasContext(renderthread::CanvasContext* context) { + mRegisteredContexts.erase(context); + } + + void requireGLContext(); + + // TODO: This system is a little clunky feeling, this could use some + // more thinking... + void postDecStrong(VirtualLightRefBase* object); + + void render(const Glop& glop); + + AssetAtlas& assetAtlas() { return mAssetAtlas; } + Blend& blend() { return *mBlend; } + MeshState& meshState() { return *mMeshState; } + Scissor& scissor() { return *mScissor; } + Stencil& stencil() { return *mStencil; } + + void dump(); +private: + friend class renderthread::RenderThread; + friend class Caches; + + void interruptForFunctorInvoke(); + void resumeFromFunctorInvoke(); + void assertOnGLThread(); + + RenderState(renderthread::RenderThread& thread); + ~RenderState(); + + + renderthread::RenderThread& mRenderThread; + Caches* mCaches = nullptr; + + Blend* mBlend = nullptr; + MeshState* mMeshState = nullptr; + Scissor* mScissor = nullptr; + Stencil* mStencil = nullptr; + + AssetAtlas mAssetAtlas; + std::set<Layer*> mActiveLayers; + std::set<renderthread::CanvasContext*> mRegisteredContexts; + + GLsizei mViewportWidth; + GLsizei mViewportHeight; + GLuint mFramebuffer; + + pthread_t mThreadId; +}; + +} /* namespace uirenderer */ +} /* namespace android */ + +#endif /* RENDERSTATE_H */ diff --git a/libs/hwui/renderstate/Scissor.cpp b/libs/hwui/renderstate/Scissor.cpp new file mode 100644 index 0000000..95dcd18 --- /dev/null +++ b/libs/hwui/renderstate/Scissor.cpp @@ -0,0 +1,91 @@ +/* + * Copyright (C) 2015 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 "renderstate/Scissor.h" + +#include <utils/Log.h> + +namespace android { +namespace uirenderer { + +Scissor::Scissor() + : mEnabled(false) + , mScissorX(0) + , mScissorY(0) + , mScissorWidth(0) + , mScissorHeight(0) { +} + +bool Scissor::setEnabled(bool enabled) { + if (mEnabled != enabled) { + if (enabled) { + glEnable(GL_SCISSOR_TEST); + } else { + glDisable(GL_SCISSOR_TEST); + } + mEnabled = enabled; + return true; + } + return false; +} + +bool Scissor::set(GLint x, GLint y, GLint width, GLint height) { + if (mEnabled && (x != mScissorX || y != mScissorY + || width != mScissorWidth || height != mScissorHeight)) { + + if (x < 0) { + width += x; + x = 0; + } + if (y < 0) { + height += y; + y = 0; + } + if (width < 0) { + width = 0; + } + if (height < 0) { + height = 0; + } + glScissor(x, y, width, height); + + mScissorX = x; + mScissorY = y; + mScissorWidth = width; + mScissorHeight = height; + + return true; + } + return false; +} + +void Scissor::reset() { + mScissorX = mScissorY = mScissorWidth = mScissorHeight = 0; +} + +void Scissor::invalidate() { + mEnabled = glIsEnabled(GL_SCISSOR_TEST); + setEnabled(true); + reset(); +} + +void Scissor::dump() { + ALOGD("Scissor: enabled %d, %d %d %d %d", + mEnabled, mScissorX, mScissorY, mScissorWidth, mScissorHeight); +} + +} /* namespace uirenderer */ +} /* namespace android */ + diff --git a/libs/hwui/renderstate/Scissor.h b/libs/hwui/renderstate/Scissor.h new file mode 100644 index 0000000..b37ec58 --- /dev/null +++ b/libs/hwui/renderstate/Scissor.h @@ -0,0 +1,46 @@ +/* + * Copyright (C) 2015 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. + */ +#ifndef RENDERSTATE_SCISSOR_H +#define RENDERSTATE_SCISSOR_H + +#include <GLES2/gl2.h> +#include <GLES2/gl2ext.h> + +namespace android { +namespace uirenderer { + +class Scissor { + friend class RenderState; +public: + bool setEnabled(bool enabled); + bool set(GLint x, GLint y, GLint width, GLint height); + void reset(); + bool isEnabled() { return mEnabled; } + void dump(); +private: + Scissor(); + void invalidate(); + bool mEnabled; + GLint mScissorX; + GLint mScissorY; + GLint mScissorWidth; + GLint mScissorHeight; +}; + +} /* namespace uirenderer */ +} /* namespace android */ + +#endif // RENDERSTATE_SCISSOR_H diff --git a/libs/hwui/renderstate/Stencil.cpp b/libs/hwui/renderstate/Stencil.cpp new file mode 100644 index 0000000..cedb233 --- /dev/null +++ b/libs/hwui/renderstate/Stencil.cpp @@ -0,0 +1,133 @@ +/* + * Copyright (C) 2012 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 "renderstate/Stencil.h" + +#include "Caches.h" +#include "Debug.h" +#include "Extensions.h" +#include "Properties.h" + +#include <GLES2/gl2ext.h> + +namespace android { +namespace uirenderer { + +#if DEBUG_STENCIL +#define STENCIL_WRITE_VALUE 0xff +#define STENCIL_MASK_VALUE 0xff +#else +#define STENCIL_WRITE_VALUE 0x1 +#define STENCIL_MASK_VALUE 0x1 +#endif + +Stencil::Stencil() + : mState(kDisabled) { +} + +uint8_t Stencil::getStencilSize() { + return STENCIL_BUFFER_SIZE; +} + +GLenum Stencil::getSmallestStencilFormat() { +#if !DEBUG_STENCIL + const Extensions& extensions = Caches::getInstance().extensions(); + if (extensions.has1BitStencil()) { + return GL_STENCIL_INDEX1_OES; + } else if (extensions.has4BitStencil()) { + return GL_STENCIL_INDEX4_OES; + } +#endif + return GL_STENCIL_INDEX8; +} + +void Stencil::clear() { + glClearStencil(0); + glClear(GL_STENCIL_BUFFER_BIT); +} + +void Stencil::enableTest(int incrementThreshold) { + if (mState != kTest) { + enable(); + if (incrementThreshold > 0) { + glStencilFunc(GL_EQUAL, incrementThreshold, 0xff); + } else { + glStencilFunc(GL_EQUAL, STENCIL_WRITE_VALUE, STENCIL_MASK_VALUE); + } + // We only want to test, let's keep everything + glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); + glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); + glStencilMask(0); + mState = kTest; + } +} + +void Stencil::enableWrite(int incrementThreshold) { + if (mState != kWrite) { + enable(); + if (incrementThreshold > 0) { + glStencilFunc(GL_ALWAYS, 1, 0xff); + // The test always passes so the first two values are meaningless + glStencilOp(GL_INCR, GL_INCR, GL_INCR); + } else { + glStencilFunc(GL_ALWAYS, STENCIL_WRITE_VALUE, STENCIL_MASK_VALUE); + // The test always passes so the first two values are meaningless + glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE); + } + glColorMask(GL_FALSE, GL_FALSE, GL_FALSE, GL_FALSE); + glStencilMask(0xff); + mState = kWrite; + } +} + +void Stencil::enableDebugTest(GLint value, bool greater) { + enable(); + glStencilFunc(greater ? GL_LESS : GL_EQUAL, value, 0xffffffff); + // We only want to test, let's keep everything + glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP); + mState = kTest; +} + +void Stencil::enableDebugWrite() { + if (mState != kWrite) { + enable(); + glStencilFunc(GL_ALWAYS, 0x1, 0xffffffff); + // The test always passes so the first two values are meaningless + glStencilOp(GL_KEEP, GL_KEEP, GL_INCR); + glColorMask(GL_TRUE, GL_TRUE, GL_TRUE, GL_TRUE); + mState = kWrite; + } +} + +void Stencil::enable() { + if (mState == kDisabled) { + glEnable(GL_STENCIL_TEST); + } +} + +void Stencil::disable() { + if (mState != kDisabled) { + glDisable(GL_STENCIL_TEST); + mState = kDisabled; + } +} + +void Stencil::dump() { + ALOGD("Stencil: state %d", mState); +} + +}; // namespace uirenderer +}; // namespace android diff --git a/libs/hwui/renderstate/Stencil.h b/libs/hwui/renderstate/Stencil.h new file mode 100644 index 0000000..e4f0f3f --- /dev/null +++ b/libs/hwui/renderstate/Stencil.h @@ -0,0 +1,123 @@ +/* + * Copyright (C) 2012 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. + */ + +#ifndef ANDROID_HWUI_STENCIL_H +#define ANDROID_HWUI_STENCIL_H + +#ifndef LOG_TAG + #define LOG_TAG "OpenGLRenderer" +#endif + +#include <GLES2/gl2.h> + +#include <cutils/compiler.h> + +namespace android { +namespace uirenderer { + +/////////////////////////////////////////////////////////////////////////////// +// Stencil buffer management +/////////////////////////////////////////////////////////////////////////////// + +class ANDROID_API Stencil { +public: + Stencil(); + + /** + * Returns the desired size for the stencil buffer. If the returned value + * is 0, then no stencil buffer is required. + */ + ANDROID_API static uint8_t getStencilSize(); + + /** + * Returns the smallest stencil format accepted by render buffers. + */ + static GLenum getSmallestStencilFormat(); + + /** + * Clears the stencil buffer. + */ + void clear(); + + /** + * Enables stencil test. When the stencil test is enabled the stencil buffer is not written + * into. An increment threshold of zero causes the stencil to use a constant reference value + * and GL_EQUAL for the test. A non-zero increment threshold causes the stencil to use that + * value as the reference value and GL_EQUAL for the test. + */ + void enableTest(int incrementThreshold); + + /** + * Enables stencil write. When stencil write is enabled, the stencil + * test always succeeds and the value 0x1 is written in the stencil + * buffer for each fragment. An increment threshold of zero causes the stencil to use a constant + * reference value and GL_EQUAL for the test. A non-zero increment threshold causes the stencil + * to use that value as the reference value and GL_EQUAL for the test. + */ + void enableWrite(int incrementThreshold); + + /** + * The test passes only when equal to the specified value. + */ + void enableDebugTest(GLint value, bool greater = false); + + /** + * Used for debugging. The stencil test always passes and increments. + */ + void enableDebugWrite(); + + /** + * Disables stencil test and write. + */ + void disable(); + + /** + * Indicates whether either test or write is enabled. + */ + bool isEnabled() { + return mState != kDisabled; + } + + /** + * Indicates whether testing only is enabled. + */ + bool isTestEnabled() { + return mState == kTest; + } + + bool isWriteEnabled() { + return mState == kWrite; + } + + void dump(); + +private: + void enable(); + + enum StencilState { + kDisabled, + kTest, + kWrite + }; + + StencilState mState; + +}; // class Stencil + +}; // namespace uirenderer +}; // namespace android + +#endif // ANDROID_HWUI_STENCIL_H diff --git a/libs/hwui/renderstate/TextureState.cpp b/libs/hwui/renderstate/TextureState.cpp new file mode 100644 index 0000000..a211de7 --- /dev/null +++ b/libs/hwui/renderstate/TextureState.cpp @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2015 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 "renderstate/TextureState.h" + +namespace android { +namespace uirenderer { + +// Must define as many texture units as specified by kTextureUnitsCount +const GLenum kTextureUnits[] = { + GL_TEXTURE0, + GL_TEXTURE1, + GL_TEXTURE2 +}; + +TextureState::TextureState() + : mTextureUnit(0) { + glActiveTexture(kTextureUnits[0]); + resetBoundTextures(); + + GLint maxTextureUnits; + glGetIntegerv(GL_MAX_COMBINED_TEXTURE_IMAGE_UNITS, &maxTextureUnits); + LOG_ALWAYS_FATAL_IF(maxTextureUnits < kTextureUnitsCount, + "At least %d texture units are required!", kTextureUnitsCount); +} + +void TextureState::activateTexture(GLuint textureUnit) { + if (mTextureUnit != textureUnit) { + glActiveTexture(kTextureUnits[textureUnit]); + mTextureUnit = textureUnit; + } +} + +void TextureState::resetActiveTexture() { + mTextureUnit = -1; +} + +void TextureState::bindTexture(GLuint texture) { + if (mBoundTextures[mTextureUnit] != texture) { + glBindTexture(GL_TEXTURE_2D, texture); + mBoundTextures[mTextureUnit] = texture; + } +} + +void TextureState::bindTexture(GLenum target, GLuint texture) { + if (target == GL_TEXTURE_2D) { + bindTexture(texture); + } else { + // GLConsumer directly calls glBindTexture() with + // target=GL_TEXTURE_EXTERNAL_OES, don't cache this target + // since the cached state could be stale + glBindTexture(target, texture); + } +} + +void TextureState::deleteTexture(GLuint texture) { + // When glDeleteTextures() is called on a currently bound texture, + // OpenGL ES specifies that the texture is then considered unbound + // Consider the following series of calls: + // + // glGenTextures -> creates texture name 2 + // glBindTexture(2) + // glDeleteTextures(2) -> 2 is now unbound + // glGenTextures -> can return 2 again + // + // If we don't call glBindTexture(2) after the second glGenTextures + // call, any texture operation will be performed on the default + // texture (name=0) + + unbindTexture(texture); + + glDeleteTextures(1, &texture); +} + +void TextureState::resetBoundTextures() { + for (int i = 0; i < kTextureUnitsCount; i++) { + mBoundTextures[i] = 0; + } +} + +void TextureState::unbindTexture(GLuint texture) { + for (int i = 0; i < kTextureUnitsCount; i++) { + if (mBoundTextures[i] == texture) { + mBoundTextures[i] = 0; + } + } +} + +} /* namespace uirenderer */ +} /* namespace android */ + diff --git a/libs/hwui/renderstate/TextureState.h b/libs/hwui/renderstate/TextureState.h new file mode 100644 index 0000000..5a57b9f --- /dev/null +++ b/libs/hwui/renderstate/TextureState.h @@ -0,0 +1,88 @@ +/* + * Copyright (C) 2015 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. + */ +#ifndef RENDERSTATE_TEXTURESTATE_H +#define RENDERSTATE_TEXTURESTATE_H + +#include "Vertex.h" + +#include <GLES2/gl2.h> +#include <GLES2/gl2ext.h> +#include <SkXfermode.h> +#include <memory> + +namespace android { +namespace uirenderer { + +class TextureState { + friend class Caches; // TODO: move to RenderState +public: + /** + * Activate the specified texture unit. The texture unit must + * be specified using an integer number (0 for GL_TEXTURE0 etc.) + */ + void activateTexture(GLuint textureUnit); + + /** + * Invalidate the cached value of the active texture unit. + */ + void resetActiveTexture(); + + /** + * Binds the specified texture as a GL_TEXTURE_2D texture. + * All texture bindings must be performed with this method or + * bindTexture(GLenum, GLuint). + */ + void bindTexture(GLuint texture); + + /** + * Binds the specified texture with the specified render target. + * All texture bindings must be performed with this method or + * bindTexture(GLuint). + */ + void bindTexture(GLenum target, GLuint texture); + + /** + * Deletes the specified texture and clears it from the cache + * of bound textures. + * All textures must be deleted using this method. + */ + void deleteTexture(GLuint texture); + + /** + * Signals that the cache of bound textures should be cleared. + * Other users of the context may have altered which textures are bound. + */ + void resetBoundTextures(); + + /** + * Clear the cache of bound textures. + */ + void unbindTexture(GLuint texture); +private: + // total number of texture units available for use + static const int kTextureUnitsCount = 3; + + TextureState(); + GLuint mTextureUnit; + + // Caches texture bindings for the GL_TEXTURE_2D target + GLuint mBoundTextures[kTextureUnitsCount]; +}; + +} /* namespace uirenderer */ +} /* namespace android */ + +#endif // RENDERSTATE_BLEND_H |
