Wed, 31 Dec 2014 07:22:50 +0100
Correct previous dual key logic pending first delivery installment.
michael@0 | 1 | /* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*- |
michael@0 | 2 | * This Source Code Form is subject to the terms of the Mozilla Public |
michael@0 | 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this |
michael@0 | 4 | * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
michael@0 | 5 | |
michael@0 | 6 | package org.mozilla.gecko.gfx; |
michael@0 | 7 | |
michael@0 | 8 | import android.graphics.Rect; |
michael@0 | 9 | import android.opengl.GLES20; |
michael@0 | 10 | import android.util.Log; |
michael@0 | 11 | |
michael@0 | 12 | import java.nio.ByteBuffer; |
michael@0 | 13 | |
michael@0 | 14 | /** |
michael@0 | 15 | * Base class for tile layers, which encapsulate the logic needed to draw textured tiles in OpenGL |
michael@0 | 16 | * ES. |
michael@0 | 17 | */ |
michael@0 | 18 | public abstract class TileLayer extends Layer { |
michael@0 | 19 | private static final String LOGTAG = "GeckoTileLayer"; |
michael@0 | 20 | |
michael@0 | 21 | private final Rect mDirtyRect; |
michael@0 | 22 | private IntSize mSize; |
michael@0 | 23 | private int[] mTextureIDs; |
michael@0 | 24 | |
michael@0 | 25 | protected final CairoImage mImage; |
michael@0 | 26 | |
michael@0 | 27 | public enum PaintMode { NORMAL, REPEAT, STRETCH }; |
michael@0 | 28 | private PaintMode mPaintMode; |
michael@0 | 29 | |
michael@0 | 30 | public TileLayer(CairoImage image, PaintMode paintMode) { |
michael@0 | 31 | super(image.getSize()); |
michael@0 | 32 | |
michael@0 | 33 | mPaintMode = paintMode; |
michael@0 | 34 | mImage = image; |
michael@0 | 35 | mSize = new IntSize(0, 0); |
michael@0 | 36 | mDirtyRect = new Rect(); |
michael@0 | 37 | } |
michael@0 | 38 | |
michael@0 | 39 | protected boolean repeats() { return mPaintMode == PaintMode.REPEAT; } |
michael@0 | 40 | protected boolean stretches() { return mPaintMode == PaintMode.STRETCH; } |
michael@0 | 41 | protected int getTextureID() { return mTextureIDs[0]; } |
michael@0 | 42 | protected boolean initialized() { return mImage != null && mTextureIDs != null; } |
michael@0 | 43 | |
michael@0 | 44 | @Override |
michael@0 | 45 | protected void finalize() throws Throwable { |
michael@0 | 46 | try { |
michael@0 | 47 | if (mTextureIDs != null) |
michael@0 | 48 | TextureReaper.get().add(mTextureIDs); |
michael@0 | 49 | } finally { |
michael@0 | 50 | super.finalize(); |
michael@0 | 51 | } |
michael@0 | 52 | } |
michael@0 | 53 | |
michael@0 | 54 | public void destroy() { |
michael@0 | 55 | try { |
michael@0 | 56 | if (mImage != null) { |
michael@0 | 57 | mImage.destroy(); |
michael@0 | 58 | } |
michael@0 | 59 | } catch (Exception ex) { |
michael@0 | 60 | Log.e(LOGTAG, "error clearing buffers: ", ex); |
michael@0 | 61 | } |
michael@0 | 62 | } |
michael@0 | 63 | |
michael@0 | 64 | public void setPaintMode(PaintMode mode) { |
michael@0 | 65 | mPaintMode = mode; |
michael@0 | 66 | } |
michael@0 | 67 | |
michael@0 | 68 | /** |
michael@0 | 69 | * Invalidates the entire buffer so that it will be uploaded again. Only valid inside a |
michael@0 | 70 | * transaction. |
michael@0 | 71 | */ |
michael@0 | 72 | |
michael@0 | 73 | public void invalidate() { |
michael@0 | 74 | if (!inTransaction()) |
michael@0 | 75 | throw new RuntimeException("invalidate() is only valid inside a transaction"); |
michael@0 | 76 | IntSize bufferSize = mImage.getSize(); |
michael@0 | 77 | mDirtyRect.set(0, 0, bufferSize.width, bufferSize.height); |
michael@0 | 78 | } |
michael@0 | 79 | |
michael@0 | 80 | private void validateTexture() { |
michael@0 | 81 | /* Calculate the ideal texture size. This must be a power of two if |
michael@0 | 82 | * the texture is repeated or OpenGL ES 2.0 isn't supported, as |
michael@0 | 83 | * OpenGL ES 2.0 is required for NPOT texture support (without |
michael@0 | 84 | * extensions), but doesn't support repeating NPOT textures. |
michael@0 | 85 | * |
michael@0 | 86 | * XXX Currently, we don't pick a GLES 2.0 context, so always round. |
michael@0 | 87 | */ |
michael@0 | 88 | IntSize textureSize = mImage.getSize().nextPowerOfTwo(); |
michael@0 | 89 | |
michael@0 | 90 | if (!textureSize.equals(mSize)) { |
michael@0 | 91 | mSize = textureSize; |
michael@0 | 92 | |
michael@0 | 93 | // Delete the old texture |
michael@0 | 94 | if (mTextureIDs != null) { |
michael@0 | 95 | TextureReaper.get().add(mTextureIDs); |
michael@0 | 96 | mTextureIDs = null; |
michael@0 | 97 | |
michael@0 | 98 | // Free the texture immediately, so we don't incur a |
michael@0 | 99 | // temporarily increased memory usage. |
michael@0 | 100 | TextureReaper.get().reap(); |
michael@0 | 101 | } |
michael@0 | 102 | } |
michael@0 | 103 | } |
michael@0 | 104 | |
michael@0 | 105 | @Override |
michael@0 | 106 | protected void performUpdates(RenderContext context) { |
michael@0 | 107 | super.performUpdates(context); |
michael@0 | 108 | |
michael@0 | 109 | // Reallocate the texture if the size has changed |
michael@0 | 110 | validateTexture(); |
michael@0 | 111 | |
michael@0 | 112 | // Don't do any work if the image has an invalid size. |
michael@0 | 113 | if (!mImage.getSize().isPositive()) |
michael@0 | 114 | return; |
michael@0 | 115 | |
michael@0 | 116 | // If we haven't allocated a texture, assume the whole region is dirty |
michael@0 | 117 | if (mTextureIDs == null) { |
michael@0 | 118 | uploadFullTexture(); |
michael@0 | 119 | } else { |
michael@0 | 120 | uploadDirtyRect(mDirtyRect); |
michael@0 | 121 | } |
michael@0 | 122 | |
michael@0 | 123 | mDirtyRect.setEmpty(); |
michael@0 | 124 | } |
michael@0 | 125 | |
michael@0 | 126 | private void uploadFullTexture() { |
michael@0 | 127 | IntSize bufferSize = mImage.getSize(); |
michael@0 | 128 | uploadDirtyRect(new Rect(0, 0, bufferSize.width, bufferSize.height)); |
michael@0 | 129 | } |
michael@0 | 130 | |
michael@0 | 131 | private void uploadDirtyRect(Rect dirtyRect) { |
michael@0 | 132 | // If we have nothing to upload, just return for now |
michael@0 | 133 | if (dirtyRect.isEmpty()) |
michael@0 | 134 | return; |
michael@0 | 135 | |
michael@0 | 136 | // It's possible that the buffer will be null, check for that and return |
michael@0 | 137 | ByteBuffer imageBuffer = mImage.getBuffer(); |
michael@0 | 138 | if (imageBuffer == null) |
michael@0 | 139 | return; |
michael@0 | 140 | |
michael@0 | 141 | if (mTextureIDs == null) { |
michael@0 | 142 | mTextureIDs = new int[1]; |
michael@0 | 143 | GLES20.glGenTextures(mTextureIDs.length, mTextureIDs, 0); |
michael@0 | 144 | } |
michael@0 | 145 | |
michael@0 | 146 | int cairoFormat = mImage.getFormat(); |
michael@0 | 147 | CairoGLInfo glInfo = new CairoGLInfo(cairoFormat); |
michael@0 | 148 | |
michael@0 | 149 | bindAndSetGLParameters(); |
michael@0 | 150 | |
michael@0 | 151 | // XXX TexSubImage2D is too broken to rely on on Adreno, and very slow |
michael@0 | 152 | // on other chipsets, so we always upload the entire buffer. |
michael@0 | 153 | IntSize bufferSize = mImage.getSize(); |
michael@0 | 154 | if (mSize.equals(bufferSize)) { |
michael@0 | 155 | GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, glInfo.internalFormat, mSize.width, |
michael@0 | 156 | mSize.height, 0, glInfo.format, glInfo.type, imageBuffer); |
michael@0 | 157 | } else { |
michael@0 | 158 | // Our texture has been expanded to the next power of two. |
michael@0 | 159 | // XXX We probably never want to take this path, so throw an exception. |
michael@0 | 160 | throw new RuntimeException("Buffer/image size mismatch in TileLayer!"); |
michael@0 | 161 | } |
michael@0 | 162 | } |
michael@0 | 163 | |
michael@0 | 164 | private void bindAndSetGLParameters() { |
michael@0 | 165 | GLES20.glActiveTexture(GLES20.GL_TEXTURE0); |
michael@0 | 166 | GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mTextureIDs[0]); |
michael@0 | 167 | GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, |
michael@0 | 168 | GLES20.GL_LINEAR); |
michael@0 | 169 | GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, |
michael@0 | 170 | GLES20.GL_LINEAR); |
michael@0 | 171 | |
michael@0 | 172 | int repeatMode = repeats() ? GLES20.GL_REPEAT : GLES20.GL_CLAMP_TO_EDGE; |
michael@0 | 173 | GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, repeatMode); |
michael@0 | 174 | GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, repeatMode); |
michael@0 | 175 | } |
michael@0 | 176 | } |
michael@0 | 177 |